/// <summary>CreateByECDsa</summary> /// <param name="iss">client_id</param> /// <param name="aud">Token2 EndPointのuri</param> /// <param name="forExp">DateTimeOffset</param> /// <param name="scopes">scopes</param> /// <param name="ecdsaX509FilePath">ES256用の X.509秘密鍵 の File Path</param> /// <param name="ecdsaX509Password">ES256用の X.509秘密鍵 の Password</param> /// <returns>JwtAssertion</returns> public static string CreateByECDsa( string iss, string aud, TimeSpan forExp, string scopes, string ecdsaX509FilePath, string ecdsaX509Password) ///// <param name="eccPrivateKey">ES256用のECParameters秘密鍵</param> //ECParameters ecPrivateKey) // ECDsa.ExportParameters(true)が動かねぇ。 { string json = ""; //string jws = ""; #region ClaimSetの生成 Dictionary <string, object> jwtAssertionClaimSet = new Dictionary <string, object>(); jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.iss, iss); // client_id jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.aud, aud); // Token EndPointのuri。 jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.exp, CmnJwtToken.CreateExpClaim(forExp)); jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.iat, CmnJwtToken.CreateIatClaim()); jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.jti, CmnJwtToken.CreateJitClaim()); jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.scope, scopes); // scopes json = JsonConvert.SerializeObject(jwtAssertionClaimSet); #endregion #region JWT化 JWS_ES256_X509 jwtES256 = new JWS_ES256_X509(ecdsaX509FilePath, ecdsaX509Password); return(jwtES256.Create(json)); #endregion }
// - OpenID Connect Client Initiated Backchannel Authentication Flow - Core 1.0 draft-01 // https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html // - Financial-grade API: Client Initiated Backchannel Authentication Profile // https://openid.net/specs/openid-financial-api-ciba-ID1.html #region Create ///// <summary>CreateCiba</summary> ///// <param name="iss">string</param> ///// <param name="aud">string</param> ///// <param name="exp">string</param> ///// <param name="nbf">string</param> ///// <param name="scopes">string</param> ///// <param name="client_notification_token">string</param> ///// <param name="binding_message">string</param> ///// <param name="user_code">string</param> ///// <param name="requested_expiry">string</param> ///// <param name="login_hint">string</param> ///// <param name="requestContextAndIntent">Dictionary(string, object)</param> ///// <param name="jwkPrivateKey">ES256用のJWK秘密鍵</param> ///// <returns>RequestObject</returns> //public static string CreateCiba( // string iss, string aud, string exp, string nbf, string scopes, // string client_notification_token, string binding_message, // string user_code, string requested_expiry, string login_hint, // Dictionary<string, object> requestContextAndIntent, string jwkPrivateKey) //{ // EccPrivateKeyConverter epkc = new EccPrivateKeyConverter(); // return RequestObject.CreateCiba( // iss, aud, exp, nbf, scopes, // client_notification_token, binding_message, // user_code, requested_expiry, login_hint, // requestContextAndIntent, epkc.JwkToParam(jwkPrivateKey)); //} /// <summary>CreateCiba</summary> /// <param name="iss">string</param> /// <param name="aud">string</param> /// <param name="exp">string</param> /// <param name="nbf">string</param> /// <param name="scopes">string</param> /// <param name="client_notification_token">string</param> /// <param name="binding_message">string</param> /// <param name="user_code">string</param> /// <param name="requested_expiry">string</param> /// <param name="login_hint">string</param> /// <param name="requestContextAndIntent">Dictionary(string, object)</param> /// <param name="ecdsaX509FilePath">ES256用の X.509秘密鍵 の File Path</param> /// <param name="ecdsaX509Password">ES256用の X.509秘密鍵 の Password</param> /// <returns>RequestObject</returns> public static string CreateCiba( string iss, string aud, string exp, string nbf, string scopes, string client_notification_token, string binding_message, string user_code, string requested_expiry, string login_hint, Dictionary <string, object> requestContextAndIntent, string ecdsaX509FilePath, string ecdsaX509Password) ///// <param name="ecPrivateKey">ES256用のECParameters秘密鍵</param> //ECParameters ecPrivateKey) // ECDsa.ExportParameters(true)が動かねぇ。 { string json = ""; #region ClaimSetの生成 Dictionary <string, object> requestObjectClaimSet = new Dictionary <string, object>(); requestObjectClaimSet.Add(OAuth2AndOIDCConst.iss, iss); // client_id requestObjectClaimSet.Add(OAuth2AndOIDCConst.aud, aud); // ROS EndPointのuri。 requestObjectClaimSet.Add(OAuth2AndOIDCConst.exp, exp); requestObjectClaimSet.Add(OAuth2AndOIDCConst.iat, CmnJwtToken.CreateIatClaim()); requestObjectClaimSet.Add(OAuth2AndOIDCConst.nbf, nbf); requestObjectClaimSet.Add(OAuth2AndOIDCConst.jti, CmnJwtToken.CreateJitClaim()); requestObjectClaimSet.Add(OAuth2AndOIDCConst.scope, scopes); requestObjectClaimSet.Add(OAuth2AndOIDCConst.client_notification_token, client_notification_token); requestObjectClaimSet.Add(OAuth2AndOIDCConst.binding_message, binding_message); if (!string.IsNullOrEmpty(user_code)) { requestObjectClaimSet.Add(OAuth2AndOIDCConst.user_code, user_code); } if (!string.IsNullOrEmpty(requested_expiry)) { requestObjectClaimSet.Add(OAuth2AndOIDCConst.requested_expiry, requested_expiry); } requestObjectClaimSet.Add(OAuth2AndOIDCConst.login_hint, login_hint); if (requestContextAndIntent != null) { foreach (string key in requestContextAndIntent.Keys) { requestObjectClaimSet.Add(key, requestContextAndIntent[key]); } } json = JsonConvert.SerializeObject(requestObjectClaimSet); #endregion #region JWT化 JWS_ES256_X509 jwtES256 = new JWS_ES256_X509(ecdsaX509FilePath, ecdsaX509Password); return(jwtES256.Create(json)); #endregion }
/// <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 }