Sign in with Apple 后端验证(C#)
流程请参考:https://www.cnblogs.com/ljcgood66/p/12537389.html
公钥验证
/// <summary> /// 验证公钥 /// </summary> /// <param name="jwtToken"></param> /// <returns></returns> public static bool VerifyPublicKey(string jwtToken) { var parts = jwtToken.Split('.'); var header = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(FromBase64(parts[0]))); var payload = JsonConvert.DeserializeObject<JObject>(Encoding.UTF8.GetString(FromBase64(parts[1]))); // 第三部分是验证码,用来验证数据"{header}.{payload}" var signagure = FromBase64(parts[2]); var signedOver = Encoding.UTF8.GetBytes(parts[0] + "." + parts[1]); var keysJson = new WebClient().DownloadString("https://appleid.apple.com/auth/keys"); var keys = JsonConvert.DeserializeObject<JObject>(keysJson)["keys"] as JArray; var key = keys.OfType<JObject>().FirstOrDefault(x => (string)x["kid"] == (string)header["kid"]); // 这里只支持RS256签名。RS256就是使用RSA算法,用一个RSA公钥,来验证数据的SHA256摘要。 var alg = (string)key["alg"]; if (alg != "RS256"); var n = FromBase64((string)key["n"]); var e = FromBase64((string)key["e"]); using (var rsa = new RSACryptoServiceProvider()) { // 导入RSA公钥 rsa.ImportParameters(new RSAParameters() { Exponent = e, Modulus = n }); // 验证数据的SHA256摘要 var signatureVerified = rsa.VerifyData(signedOver, signagure, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return signatureVerified; } } public static byte[] FromBase64(string base64WithoutPadding) { var base64 = base64WithoutPadding.Length % 4 == 0 ? base64WithoutPadding : base64WithoutPadding + new string('=', 4 - base64WithoutPadding.Length % 4); return Convert.FromBase64String(base64.Replace("-", "+").Replace('_', '/')); }
创建client_secret
static void Main(string[] args) { var team_id = "xxxxx"; var ecdsaPrivateKey = ReadPrivateKey(); var now = DateTime.UtcNow; var signing = new SigningCredentials(new ECDsaSecurityKey(ecdsaPrivateKey), SecurityAlgorithms.EcdsaSha256); var header = new JwtHeader(signing); var jwtHandler = new JwtSecurityTokenHandler() { }; var payload = new JwtPayload(team_id, "https://appleid.apple.com", new List<Claim> { new Claim("sub", "xxx.xx.xxx") }, now, now.AddSeconds(86400 * 30), now); header.Add("kid", "xxxx"); header.Remove("typ"); var jwtSecurity = new JwtSecurityToken(header, payload); var jwtToken = jwtHandler.WriteToken(jwtSecurity); ecdsaPrivateKey.Dispose(); Console.WriteLine(jwtToken); } static ECDsa ReadPrivateKey() { // 可以用BouncyCastle的ArmoredInputStream来脱壳,处理比较周全。 var p8 =File.ReadAllText("p8文件"); // 这里直接用去头去尾的方法: var lines = p8.Split('\n'); var trimmed = string.Join("", lines.Skip(1).Take(lines.Length - 2).Select(l => l.Trim())); var keyBlob = Convert.FromBase64String(trimmed); // CngKey是微软的实现。如果你的密钥无法读出,试试用BouncyCastle来处理 var privateKey = CngKey.Import(keyBlob, CngKeyBlobFormat.Pkcs8PrivateBlob); return new ECDsaCng(privateKey); }