流程請參考: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); }
