/// <summary>汎用認証サイトの発行したJWT形式のTokenを検証する。</summary> /// <param name="jwtToken">JWT形式のToken</param> /// <param name="jwtPayload"> /// JWS, JWS + JEWの場合があるのでペイロードを返す。 /// </param> /// <returns>検証結果</returns> public static bool Verify(string jwtToken, out string jwtPayload) { jwtPayload = ""; JWE jwe = null; JWS jws = null; // 復号化(JWEの場合) bool isJWE_FAPI2 = false; if (3 < jwtToken.Split('.').Length) { isJWE_FAPI2 = true; // ヘッダ JWE_Header jweHeader = JsonConvert.DeserializeObject <JWE_Header>( CustomEncode.ByteToString(CustomEncode.FromBase64UrlString(jwtToken.Split('.')[0]), CustomEncode.UTF_8)); if (jweHeader.alg == JwtConst.RSA_OAEP) { jwe = new JWE_RsaOaepAesGcm_X509( CmnClientParams.RsaPfxFilePath, CmnClientParams.RsaPfxPassword); } else if (jweHeader.alg == JwtConst.RSA1_5) { jwe = new JWE_Rsa15A128CbcHS256_X509( CmnClientParams.RsaPfxFilePath, CmnClientParams.RsaPfxPassword); } else { throw new NotSupportedException(string.Format( "This jwe alg of {0} is not supported.", jweHeader.alg)); } jwe.Decrypt(jwtToken, out jwtToken); } else { isJWE_FAPI2 = false; } // 検証 // ヘッダ JWS_Header jwsHeader = JsonConvert.DeserializeObject <JWS_Header>( CustomEncode.ByteToString(CustomEncode.FromBase64UrlString(jwtToken.Split('.')[0]), CustomEncode.UTF_8)); if (jwsHeader.alg == JwtConst.ES256 && isJWE_FAPI2) { } // 正常 else if (jwsHeader.alg == JwtConst.RS256 && !isJWE_FAPI2) { } // 正常 else { throw new NotSupportedException("Unexpected combination of JWS and JWE."); } // 証明書を使用するか、Jwkを使用するか判定 if (string.IsNullOrEmpty(jwsHeader.jku) || string.IsNullOrEmpty(jwsHeader.kid)) { // 旧バージョン(証明書を使用 if (isJWE_FAPI2) { #if NET45 || NET46 throw new NotSupportedException("FAPI2 is not supported in this dotnet version."); #else jws = new JWS_ES256_X509(CmnClientParams.EcdsaCerFilePath, ""); #endif } else { jws = new JWS_RS256_X509(CmnClientParams.RsaCerFilePath, ""); } } else { // 新バージョン(Jwkを使用 if (string.IsNullOrEmpty(OAuth2AndOIDCParams.JwkSetFilePath)) { // jku(jwks_uri)使用のカバレッジ // Client側 JObject jwkObject = JwkSetStore.GetInstance().GetJwkObject(jwsHeader.kid); // チェック if (jwkObject == null) { // 書込 jwkObject = JwkSetStore.GetInstance().SetJwkSetObject(jwsHeader.jku, jwsHeader.kid); } // チェック if (jwkObject == null) { // 証明書を使用 if (isJWE_FAPI2) { #if NET45 || NET46 throw new NotSupportedException("FAPI2 is not supported in this dotnet version."); #else jws = new JWS_ES256_X509(CmnClientParams.EcdsaCerFilePath, ""); #endif } else { jws = new JWS_RS256_X509(CmnClientParams.RsaCerFilePath, ""); } } else { // Jwkを使用 if (isJWE_FAPI2) { #if NET45 || NET46 throw new NotSupportedException("FAPI2 is not supported in this dotnet version."); #else EccPublicKeyConverter epkc = new EccPublicKeyConverter(); jws = new JWS_ES256_Param(epkc.JwkToParam(jwkObject), false); #endif } else { RsaPublicKeyConverter rpkc = new RsaPublicKeyConverter(); jws = new JWS_RS256_Param(rpkc.JwkToParam(jwkObject)); } } } else { // JwkSet使用のカバレッジ // AuthZ側でClient側テストを行うためのカバレージ JObject jwkObject = null; if (ResourceLoader.Exists(OAuth2AndOIDCParams.JwkSetFilePath, false)) { JwkSet jwkSet = JwkSet.LoadJwkSet(OAuth2AndOIDCParams.JwkSetFilePath); jwkObject = JwkSet.GetJwkObject(jwkSet, jwsHeader.kid); } if (jwkObject == null) { // 証明書を使用 if (isJWE_FAPI2) { #if NET45 || NET46 throw new NotSupportedException("FAPI2 is not supported in this dotnet version."); #else jws = new JWS_ES256_X509(CmnClientParams.EcdsaCerFilePath, ""); #endif } else { jws = new JWS_RS256_X509(CmnClientParams.RsaCerFilePath, ""); } } else { // Jwkを使用 if (isJWE_FAPI2) { #if NET45 || NET46 throw new NotSupportedException("FAPI2 is not supported in this dotnet version."); #else EccPublicKeyConverter epkc = new EccPublicKeyConverter(); jws = new JWS_ES256_Param(epkc.JwkToParam(jwkObject), false); #endif } else { RsaPublicKeyConverter rpkc = new RsaPublicKeyConverter(); jws = new JWS_RS256_Param(rpkc.JwkToParam(jwkObject)); } } } } bool ret = jws.Verify(jwtToken); if (ret) { jwtPayload = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(jwtToken.Split('.')[1]), CustomEncode.us_ascii); } return(ret); }
/// <summary>MyJwt</summary> private static void MyJwt() { #region Variables string temp = ""; bool ret = false; #region Env OperatingSystem os = Environment.OSVersion; // https://github.com/dotnet/corefx/issues/29404#issuecomment-385287947 // *.pfxから証明書を開く場合、X509KeyStorageFlags.Exportableの指定が必要な場合がある。 // Linuxのキーは常にエクスポート可能だが、WindowsやMacOSでは必ずしもそうではない。 X509KeyStorageFlags x509KSF = 0; if (os.Platform == PlatformID.Win32NT) { x509KSF = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable; } else //if (os.Platform == PlatformID.Unix) { x509KSF = X509KeyStorageFlags.DefaultKeySet; } #endregion #region Token string token = ""; IDictionary <string, object> payload = null; payload = new Dictionary <string, object>() { { "sub", "*****@*****.**" }, { "exp", 1300819380 } }; string payloadString = JsonConvert.SerializeObject(payload); #endregion #region Keys string jwk = ""; #endregion #region JWS // RS256 JWS_RS256_X509 jWS_RS256_X509 = null; JWS_RS256_Param jWS_RS256_Param = null; // ES256 #if NETCORE || NET47 JWS_ES256_X509 jWS_ES256_X509 = null; JWS_ES256_Param jWS_ES256_Param = null; #endif #endregion #region JWE JWE jwe = null; #endregion #endregion #region Jws if (os.Platform == PlatformID.Win32NT) { #region RSA(RS256) // 署名(X509) jWS_RS256_X509 = new JWS_RS256_X509(Program.PrivateRsaX509Path, Program.PfxPassword, x509KSF); token = jWS_RS256_X509.Create(payloadString); MyDebug.InspectJwt("JWS_RS256_X509.Create", token); // 鍵の相互変換 jwk = RsaPublicKeyConverter.ParamToJwk(((RSA)jWS_RS256_X509.DigitalSignX509.AsymmetricAlgorithm).ExportParameters(false)); MyDebug.OutputDebugAndConsole("RSA JWK", jwk); // 検証(X509) jWS_RS256_X509 = new JWS_RS256_X509(Program.PublicRsaX509Path, "", x509KSF); MyDebug.OutputDebugAndConsole("JWS_RS256_X509.Verify", jWS_RS256_X509.Verify(token).ToString()); // 検証(Param) jWS_RS256_Param = new JWS_RS256_Param(RsaPublicKeyConverter.JwkToParam(jwk)); MyDebug.OutputDebugAndConsole("JWS_RS256_Param.Verify", jWS_RS256_Param.Verify(token).ToString()); #endregion // DSA #if NETCORE || NET47 #region ECDsa(ES256) // 署名(X509) jWS_ES256_X509 = new JWS_ES256_X509(Program.PrivateECDsaX509Path, Program.PfxPassword); token = jWS_ES256_X509.Create(payloadString); MyDebug.InspectJwt("JWS_ES256_X509.Create", token); // 鍵の相互変換 jwk = EccPublicKeyConverter.ParamToJwk( ((ECDsa)jWS_ES256_X509.DigitalSignECDsaX509.AsymmetricAlgorithm).ExportParameters(false)); MyDebug.OutputDebugAndConsole("ECDSA JWK", jwk); // 検証(X509) jWS_ES256_X509 = new JWS_ES256_X509(Program.PublicECDsaX509Path, ""); MyDebug.OutputDebugAndConsole("JWS_ES256_X509.Verify", jWS_ES256_X509.Verify(token).ToString()); #if NET47 || NETCOREAPP3_0 // 検証(Param) //// Core2.0-2.2 on WinでVerifyがエラーになる。 //// DigitalSignECDsaOpenSslを試してみるが生成できない、 //// Core on Win に OpenSSLベースのプロバイダは無いため) jWS_ES256_Param = new JWS_ES256_Param(EccPublicKeyConverter.JwkToParam(jwk), false); MyDebug.OutputDebugAndConsole("JWS_ES256_Param.Verify", jWS_ES256_Param.Verify(token).ToString()); #elif NETCOREAPP2_0 // Core2.0-2.2 on Winで ECDsaCngは動作しない。 #endif // ★ xLibTest Program.VerifyResult("JwsAlgorithm.xLibTest", token, jWS_ES256_X509.DigitalSignECDsaX509.AsymmetricAlgorithm, JwsAlgorithm.ES256); #endregion #endif } else //if (os.Platform == PlatformID.Unix) { #if NETCORE #region RSA(RS256) // 署名(X509) jWS_RS256_X509 = new JWS_RS256_X509(Program.PrivateRsaX509Path, Program.PfxPassword, x509KSF); token = jWS_RS256_X509.Create(payloadString); MyDebug.InspectJwt("JWS_RS256_X509.Create", token); // 鍵の相互変換 jwk = RsaPublicKeyConverter.ParamToJwk(((RSA)jWS_RS256_X509.DigitalSignX509.AsymmetricAlgorithm).ExportParameters(false)); MyDebug.OutputDebugAndConsole("RSA JWK", jwk); // 検証(X509) jWS_RS256_X509 = new JWS_RS256_X509(Program.PublicRsaX509Path, "", x509KSF); MyDebug.OutputDebugAndConsole("JWS_RS256_X509.Verify", jWS_RS256_X509.Verify(token).ToString()); // 検証(Param) jWS_RS256_Param = new JWS_RS256_Param(RsaPublicKeyConverter.JwkToParam(jwk)); MyDebug.OutputDebugAndConsole("JWS_RS256_Param.Verify", jWS_RS256_Param.Verify(token).ToString()); #endregion // DSA #region ECDsa(ES256) // 署名(X509) jWS_ES256_X509 = new JWS_ES256_X509(Program.PrivateECDsaX509Path, Program.PfxPassword); token = jWS_ES256_X509.Create(payloadString); MyDebug.InspectJwt("JWS_ES256_X509.Create", token); // 鍵の相互変換 jwk = EccPublicKeyConverter.ParamToJwk( ((ECDsa)jWS_ES256_X509.DigitalSignECDsaX509.AsymmetricAlgorithm).ExportParameters(false)); MyDebug.OutputDebugAndConsole("ECDSA JWK", jwk); // 検証(X509) jWS_ES256_X509 = new JWS_ES256_X509(Program.PublicECDsaX509Path, ""); MyDebug.OutputDebugAndConsole("JWS_ES256_X509.Verify", jWS_ES256_X509.Verify(token).ToString()); // 検証(Param) jWS_ES256_Param = new JWS_ES256_Param(EccPublicKeyConverter.JwkToParam(jwk), false); MyDebug.OutputDebugAndConsole("JWS_ES256_Param.Verify", jWS_ES256_X509.Verify(token).ToString()); // ★ xLibTest Program.VerifyResult("JwsAlgorithm.xLibTest", token, jWS_ES256_X509.DigitalSignECDsaX509.AsymmetricAlgorithm, JwsAlgorithm.ES256); #endregion #endif } #endregion #region Jwe #region RsaOaepAesGcm // 暗号化 jwe = new JWE_RsaOaepAesGcm_X509(Program.PublicRsaX509Path, "", x509KSF); token = jwe.Create(payloadString); // 復号化 jwe = new JWE_RsaOaepAesGcm_X509(Program.PrivateRsaX509Path, Program.PfxPassword, x509KSF); ret = jwe.Decrypt(token, out temp); MyDebug.OutputDebugAndConsole("JWE_RsaOaepAesGcm_X509.Decrypt", ret.ToString() + " : " + temp); // ★ xLibTest Program.VerifyResult("JweAlgorithm.xLibTest", token, jwe.ASymmetricCryptography.AsymmetricAlgorithm, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM); #endregion #region Rsa15A128CbcHS256 // 暗号化 jwe = new JWE_Rsa15A128CbcHS256_X509(Program.PublicRsaX509Path, "", x509KSF); token = jwe.Create(payloadString); // 復号化 jwe = new JWE_Rsa15A128CbcHS256_X509(Program.PrivateRsaX509Path, Program.PfxPassword, x509KSF); ret = jwe.Decrypt(token, out temp); MyDebug.OutputDebugAndConsole("JWE_Rsa15A128CbcHS256_X509.Decrypt", ret.ToString() + " : " + temp); // ★ xLibTest Program.VerifyResult("JweAlgorithm.xLibTest", token, jwe.ASymmetricCryptography.AsymmetricAlgorithm, JweAlgorithm.RSA1_5, JweEncryption.A128CBC_HS256); #endregion #endregion }