/// <summary>CreateByRsa</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="rsaPrivateKey">RS256用のRSAParameters秘密鍵</param> /// <returns>JwtAssertion</returns> public static string CreateByRsa( string iss, string aud, TimeSpan forExp, string scopes, RSAParameters rsaPrivateKey) { 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_RS256_Param jwtRS256 = new JWS_RS256_Param(rsaPrivateKey); return(jwtRS256.Create(json)); #endregion }
/// <summary>Create</summary> /// <param name="iss">string</param> /// <param name="aud">string</param> /// <param name="response_type">string</param> /// <param name="response_mode">string</param> /// <param name="redirect_uri">string</param> /// <param name="scopes">string</param> /// <param name="state">string</param> /// <param name="nonce">string</param> /// <param name="max_age">string</param> /// <param name="prompt">string</param> /// <param name="login_hint">string</param> /// <param name="claims">ClaimsInRO</param> /// <param name="rsaPrivateKey">RS256用のRSAParameters秘密鍵</param> /// <returns>RequestObject</returns> public static string Create( string iss, string aud, string response_type, string response_mode, string redirect_uri, string scopes, string state, string nonce, string max_age, string prompt, string login_hint, ClaimsInRO claims, RSAParameters rsaPrivateKey) { 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.response_type, response_type); requestObjectClaimSet.Add(OAuth2AndOIDCConst.client_id, iss); if (!string.IsNullOrEmpty(response_mode)) { requestObjectClaimSet.Add(OAuth2AndOIDCConst.response_mode, response_mode); } if (!string.IsNullOrEmpty(redirect_uri)) { requestObjectClaimSet.Add(OAuth2AndOIDCConst.redirect_uri, redirect_uri); } requestObjectClaimSet.Add(OAuth2AndOIDCConst.scope, scopes); requestObjectClaimSet.Add(OAuth2AndOIDCConst.state, state); if (!string.IsNullOrEmpty(nonce)) { requestObjectClaimSet.Add(OAuth2AndOIDCConst.nonce, nonce); } if (!string.IsNullOrEmpty(max_age)) { requestObjectClaimSet.Add(OAuth2AndOIDCConst.max_age, max_age); } if (!string.IsNullOrEmpty(prompt)) { requestObjectClaimSet.Add(OAuth2AndOIDCConst.prompt, prompt); } if (!string.IsNullOrEmpty(login_hint)) { requestObjectClaimSet.Add(OAuth2AndOIDCConst.login_hint, login_hint); } requestObjectClaimSet.Add(OAuth2AndOIDCConst.claims, claims.Claims); json = JsonConvert.SerializeObject(requestObjectClaimSet); #endregion #region JWT化 JWS_RS256_Param jwtRS256 = new JWS_RS256_Param(rsaPrivateKey); return(jwtRS256.Create(json)); #endregion }
/// <summary>Verify</summary> /// <param name="ro">string</param> /// <param name="iss">string</param> /// <param name="rsaPublicKey">RS256用のRSAParameters公開鍵</param> /// <returns>検証結果</returns> public static bool Verify(string ro, out string iss, RSAParameters rsaPublicKey) { iss = ""; string aud = ""; string response_type = ""; string scopes = ""; string state = ""; string nonce = ""; JWS_RS256_Param jwtRS256 = new JWS_RS256_Param(rsaPublicKey); if (jwtRS256.Verify(ro)) { string jwtPayload = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(ro.Split('.')[1]), CustomEncode.UTF_8); JObject jobj = ((JObject)JsonConvert.DeserializeObject(jwtPayload)); iss = (string)jobj[OAuth2AndOIDCConst.iss]; aud = (string)jobj[OAuth2AndOIDCConst.aud]; response_type = (string)jobj[OAuth2AndOIDCConst.response_type]; scopes = (string)jobj[OAuth2AndOIDCConst.scope]; state = (string)jobj[OAuth2AndOIDCConst.state]; nonce = (string)jobj[OAuth2AndOIDCConst.nonce]; if (!string.IsNullOrEmpty(iss) && !string.IsNullOrEmpty(aud) && !string.IsNullOrEmpty(response_type) && !string.IsNullOrEmpty(scopes) && !string.IsNullOrEmpty(state) && !string.IsNullOrEmpty(nonce)) { // OK return(true); } else { // 必須項目の不足 return(true); } } else { // JWTの署名検証に失敗 return(false); } }
/// <summary>VerifyJwtBearerTokenFlowAssertion</summary> /// <param name="jwtAssertion">string</param> /// <param name="iss">client_id</param> /// <param name="aud">Token2 EndPointのuri</param> /// <param name="scopes">scopes</param> /// <param name="jobj">JObject</param> /// <param name="rsaPublicKey">RS256用のRSAParameters公開鍵</param> /// <returns>検証結果</returns> public static bool VerifyJwtBearerTokenFlowAssertion(string jwtAssertion, out string iss, out string aud, out string scopes, out JObject jobj, RSAParameters rsaPublicKey) { iss = ""; aud = ""; scopes = ""; jobj = null; JWS_RS256_Param jwtRS256 = new JWS_RS256_Param(rsaPublicKey); if (jwtRS256.Verify(jwtAssertion)) { string jwtPayload = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(jwtAssertion.Split('.')[1]), CustomEncode.UTF_8); jobj = ((JObject)JsonConvert.DeserializeObject(jwtPayload)); iss = (string)jobj[OAuth2AndOIDCConst.iss]; aud = (string)jobj[OAuth2AndOIDCConst.aud]; //string iat = (string)jobj[OAuth2AndOIDCConst.iat]; scopes = (string)jobj[OAuth2AndOIDCConst.scope]; long unixTimeSeconds = 0; #if NET45 unixTimeSeconds = PubCmnFunction.ToUnixTime(DateTimeOffset.Now); #else unixTimeSeconds = DateTimeOffset.Now.ToUnixTimeSeconds(); #endif string exp = (string)jobj[OAuth2AndOIDCConst.exp]; if (long.Parse(exp) >= unixTimeSeconds) { return(true); } else { // JWTの内容検証に失敗 } } else { // JWTの署名検証に失敗 } // 認証に失敗 return(false); }
/// <summary>CreateJwtBearerTokenFlowAssertion</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="rsaPrivateKey">RS256用のRSAParameters秘密鍵</param> /// <returns>JwtAssertion</returns> public static string CreateJwtBearerTokenFlowAssertion( string iss, string aud, TimeSpan forExp, string scopes, RSAParameters rsaPrivateKey) { 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); // Token2 EndPointのuri。 #if NET45 jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.exp, PubCmnFunction.ToUnixTime(DateTimeOffset.Now.Add(forExp)).ToString()); jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.iat, PubCmnFunction.ToUnixTime(DateTimeOffset.Now).ToString()); #else jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.exp, (DateTimeOffset.Now.Add(forExp)).ToUnixTimeSeconds().ToString()); jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.iat, DateTimeOffset.Now.ToUnixTimeSeconds().ToString()); #endif jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.jti, Guid.NewGuid().ToString("N")); jwtAssertionClaimSet.Add(OAuth2AndOIDCConst.scope, scopes); // scopes json = JsonConvert.SerializeObject(jwtAssertionClaimSet); #endregion #region JWT化 JWS_RS256_Param jwtRS256 = new JWS_RS256_Param(rsaPrivateKey); return(jwtRS256.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 }
/// <summary>Unprotect</summary> /// <param name="jwt">JWT文字列</param> /// <returns>AuthenticationTicket</returns> public AuthenticationTicket Unprotect(string jwt) { // 空のケースあり。 if (string.IsNullOrEmpty(jwt)) { return(null); } // 検証 JWS_RS256 jwsRS256 = null; // 証明書を使用するか、Jwkを使用するか判定 Dictionary <string, string> header = JsonConvert.DeserializeObject <Dictionary <string, string> >( CustomEncode.ByteToString(CustomEncode.FromBase64UrlString(jwt.Split('.')[0]), CustomEncode.UTF_8)); if (header.Keys.Any(s => s == JwtConst.kid)) { if (string.IsNullOrEmpty(header[JwtConst.kid])) { // 証明書を使用 jwsRS256 = new JWS_RS256_X509(OAuth2AndOIDCParams.RS256Cer, ""); } else { JwkSet jwkSetObject = JwkSet.LoadJwkSet(OAuth2AndOIDCParams.JwkSetFilePath); JObject jwkObject = JwkSet.GetJwkObject(jwkSetObject, header[JwtConst.kid]); if (jwkObject == null) { // 証明書を使用 jwsRS256 = new JWS_RS256_X509(OAuth2AndOIDCParams.RS256Cer, ""); } else { // Jwkを使用 jwsRS256 = new JWS_RS256_Param( RS256_KeyConverter.JwkToProvider(jwkObject).ExportParameters(false)); } } } if (jwsRS256.Verify(jwt)) { // 検証できた。 // デシリアライズ、 string[] temp = jwt.Split('.'); string json = CustomEncode.ByteToString(CustomEncode.FromBase64UrlString(temp[1]), CustomEncode.UTF_8); Dictionary <string, object> authTokenClaimSet = JsonConvert.DeserializeObject <Dictionary <string, object> >(json); // 以下の検証処理 // ★ "iss": accounts.google.com的な, // ★ "aud": client_id(クライアント識別子) // ★ "sub": ユーザーの一意識別子(uname, email) // ★ "exp": JWT の有効期限(Unix時間) // ☆ "jti": JWT のID(OAuth Token Revocation) DateTime?datetime = OAuth2RevocationProvider.GetInstance().Get((string)authTokenClaimSet[OAuth2AndOIDCConst.jti]); if (datetime == null) { // authToken.iss, authToken.expの検証 if ((string)authTokenClaimSet[OAuth2AndOIDCConst.iss] == ASPNETIdentityConfig.OAuth2IssuerId && OAuth2Helper.GetInstance().GetClientSecret((string)authTokenClaimSet[OAuth2AndOIDCConst.aud]) != null && long.Parse((string)authTokenClaimSet[OAuth2AndOIDCConst.exp]) >= DateTimeOffset.Now.ToUnixTimeSeconds()) { // authToken.subの検証 // ApplicationUser を取得する。 ApplicationUserManager userManager = HttpContext.Current.GetOwinContext().GetUserManager <ApplicationUserManager>(); ApplicationUser user = userManager.FindByName((string)authTokenClaimSet[OAuth2AndOIDCConst.sub]); // 同期版でOK。 if (user != null) { // User Accountの場合 // ユーザーに対応するClaimsIdentityを生成し、 ClaimsIdentity identity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ExternalBearer); // aud、scopes、nonceなどのClaimを追加する。 List <string> scopes = new List <string>(); foreach (string s in (JArray)authTokenClaimSet[OAuth2AndOIDCConst.scopes]) { scopes.Add(s); } OAuth2Helper.AddClaim(identity, (string)authTokenClaimSet[OAuth2AndOIDCConst.aud], "", scopes, (string) authTokenClaimSet[OAuth2AndOIDCConst.nonce]); // その他、所定のClaimを追加する。 identity.AddClaim(new Claim(OAuth2AndOIDCConst.Claim_ExpirationTime, (string)authTokenClaimSet[OAuth2AndOIDCConst.exp])); identity.AddClaim(new Claim(OAuth2AndOIDCConst.Claim_NotBefore, (string)authTokenClaimSet[OAuth2AndOIDCConst.nbf])); identity.AddClaim(new Claim(OAuth2AndOIDCConst.Claim_IssuedAt, (string)authTokenClaimSet[OAuth2AndOIDCConst.iat])); identity.AddClaim(new Claim(OAuth2AndOIDCConst.Claim_JwtId, (string)authTokenClaimSet[OAuth2AndOIDCConst.jti])); // AuthenticationPropertiesの生成 AuthenticationProperties prop = new AuthenticationProperties(); prop.IssuedUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)authTokenClaimSet[OAuth2AndOIDCConst.iat])); prop.ExpiresUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)authTokenClaimSet[OAuth2AndOIDCConst.exp])); AuthenticationTicket auth = new AuthenticationTicket(identity, prop); // 認証結果を返す。 return(auth); } else { // Client Accountの場合 // ClaimとStoreのAudience(aud)に対応するSubject(sub)が一致するかを確認し、一致する場合のみ、認証する。 // ※ でないと、UserStoreから削除されたUser Accountが、Client Accountに化けることになる。 if ((string)authTokenClaimSet[OAuth2AndOIDCConst.sub] == OAuth2Helper.GetInstance().GetClientName((string)authTokenClaimSet[OAuth2AndOIDCConst.aud])) { // ClaimsIdentityを生成し、 ClaimsIdentity identity = new ClaimsIdentity(DefaultAuthenticationTypes.ExternalBearer); // sub(client_idに対応するclient_name)Claimを設定する。 identity.AddClaim(new Claim(ClaimTypes.Name, (string)authTokenClaimSet[OAuth2AndOIDCConst.sub])); // aud、scopes、nonceなどのClaimを追加する。 List <string> scopes = new List <string>(); foreach (string s in (JArray)authTokenClaimSet[OAuth2AndOIDCConst.scopes]) { scopes.Add(s); } OAuth2Helper.AddClaim(identity, (string)authTokenClaimSet[OAuth2AndOIDCConst.aud], "", scopes, (string)authTokenClaimSet[OAuth2AndOIDCConst.nonce]); // その他、所定のClaimを追加する。 identity.AddClaim(new Claim(OAuth2AndOIDCConst.Claim_ExpirationTime, (string)authTokenClaimSet[OAuth2AndOIDCConst.exp])); identity.AddClaim(new Claim(OAuth2AndOIDCConst.Claim_NotBefore, (string)authTokenClaimSet[OAuth2AndOIDCConst.nbf])); identity.AddClaim(new Claim(OAuth2AndOIDCConst.Claim_IssuedAt, (string)authTokenClaimSet[OAuth2AndOIDCConst.iat])); identity.AddClaim(new Claim(OAuth2AndOIDCConst.Claim_JwtId, (string)authTokenClaimSet[OAuth2AndOIDCConst.jti])); // AuthenticationPropertiesの生成 AuthenticationProperties prop = new AuthenticationProperties(); prop.IssuedUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)authTokenClaimSet[OAuth2AndOIDCConst.iat])); prop.ExpiresUtc = DateTimeOffset.FromUnixTimeSeconds(long.Parse((string)authTokenClaimSet[OAuth2AndOIDCConst.exp])); AuthenticationTicket auth = new AuthenticationTicket(identity, prop); // 認証結果を返す。 return(auth); } } } else { // クレーム検証の失敗 } } else { // 取り消し済み } } else { // JWT署名検証の失敗 } // 検証、認証ナドナド、できなかった。 return(null); }
/// <summary>JWS検証</summary> private void btnJWSVerify_Click(object sender, EventArgs e) { bool ret = false; if (rbnJWSHS256.Checked) { // HS256 // 入力 string[] temp = this.txtJWSSign.Text.Split('.'); // 改変可能なフィールドから入力 string newJWS = CustomEncode.ToBase64UrlString(CustomEncode.StringToByte(this.txtJWSHeader.Text, CustomEncode.UTF_8)) + "." + CustomEncode.ToBase64UrlString(CustomEncode.StringToByte(this.txtJWSPayload.Text, CustomEncode.UTF_8)) + "." + temp[2]; // 検証 //JWS_HS256 jwsHS256 = new JWS_HS256(CustomEncode.StringToByte(this.txtJWSKey.Text, CustomEncode.UTF_8)); JWS_HS256 jwsHS256 = new JWS_HS256(this.txtJWSJWK.Text); ret = jwsHS256.Verify(newJWS); } else if (rbnJWSRS256_XML.Checked) { // RS256 (XML) // 入力 string[] temp = this.txtJWSSign.Text.Split('.'); // 改変可能なフィールドから入力 string newJWS = CustomEncode.ToBase64UrlString(CustomEncode.StringToByte(this.txtJWSHeader.Text, CustomEncode.UTF_8)) + "." + CustomEncode.ToBase64UrlString(CustomEncode.StringToByte(this.txtJWSPayload.Text, CustomEncode.UTF_8)) + "." + temp[2]; // 検証 JWS_RS256_XML jwsRS256 = new JWS_RS256_XML(this.txtJWSKey.Text); ret = jwsRS256.Verify(newJWS); } else if (rbnJWSRS256_Param.Checked) { // RS256 (Param) // 入力 string[] temp = this.txtJWSSign.Text.Split('.'); // 改変可能なフィールドから入力 string newJWS = CustomEncode.ToBase64UrlString(CustomEncode.StringToByte(this.txtJWSHeader.Text, CustomEncode.UTF_8)) + "." + CustomEncode.ToBase64UrlString(CustomEncode.StringToByte(this.txtJWSPayload.Text, CustomEncode.UTF_8)) + "." + temp[2]; // 検証 //JWS_RS256_Param jwsRS256 = new JWS_RS256_Param( // RS256_KeyConverter.XmlToProvider(this.txtJWSKey.Text).ExportParameters(false)); JWS_RS256_Param jwsRS256 = new JWS_RS256_Param( RS256_KeyConverter.JwkToProvider(this.txtJWSJWK.Text).ExportParameters(false)); ret = jwsRS256.Verify(newJWS); } else { // RS256 (X509) // 入力 string[] temp = this.txtJWSSign.Text.Split('.'); // 改変可能なフィールドから入力 string newJWS = CustomEncode.ToBase64UrlString(CustomEncode.StringToByte(this.txtJWSHeader.Text, CustomEncode.UTF_8)) + "." + CustomEncode.ToBase64UrlString(CustomEncode.StringToByte(this.txtJWSPayload.Text, CustomEncode.UTF_8)) + "." + temp[2]; // 検証 JWS_RS256_X509 jwsRS256 = new JWS_RS256_X509(this.CertificateFilePath_cer, ""); ret = jwsRS256.Verify(newJWS); } if (ret) { MessageBox.Show("検証成功"); } else { MessageBox.Show("検証失敗"); } }
/// <summary>JWS生成</summary> private void btnJWSSign_Click(object sender, EventArgs e) { if (rbnJWSHS256.Checked) { // HS256 string password = GetPassword.Generate(20, 10); JWS_HS256 jwsHS256 = new JWS_HS256(CustomEncode.StringToByte(password, CustomEncode.UTF_8)); // 生成 string jws = jwsHS256.Create(this.txtJWSPayload.Text); // 出力 this.txtJWSKey.Text = password; this.txtJWSJWK.Text = jwsHS256.JWK; this.txtJWSSign.Text = jws; // 改竄可能なフィールドに出力 string[] temp = jws.Split('.'); this.txtJWSHeader.Text = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(temp[0]), CustomEncode.UTF_8); this.txtJWSPayload.Text = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(temp[1]), CustomEncode.UTF_8); } else if (rbnJWSRS256_XML.Checked) { // RS256 (XML) JWS_RS256_XML jwsRS256 = new JWS_RS256_XML(); // 生成 string jws = jwsRS256.Create(this.txtJWSPayload.Text); // 出力 this.txtJWSKey.Text = jwsRS256.XMLPublicKey; this.txtJWSJWK.Text = RS256_KeyConverter.ParamToJwkPublicKey( RS256_KeyConverter.XmlToProvider(jwsRS256.XMLPublicKey).ExportParameters(false)); this.txtJWSSign.Text = jws; // 改竄可能なフィールドに出力 string[] temp = jws.Split('.'); this.txtJWSHeader.Text = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(temp[0]), CustomEncode.UTF_8); this.txtJWSPayload.Text = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(temp[1]), CustomEncode.UTF_8); } else if (rbnJWSRS256_Param.Checked) { // RS256 (Param) JWS_RS256_Param jwsRS256 = new JWS_RS256_Param(); // 生成 string jws = jwsRS256.Create(this.txtJWSPayload.Text); // 出力 this.txtJWSKey.Text = RS256_KeyConverter.ParamToXmlPublicKey(jwsRS256.RsaPublicParameters); this.txtJWSJWK.Text = RS256_KeyConverter.ParamToJwkPublicKey(jwsRS256.RsaPublicParameters); this.txtJWSSign.Text = jws; // 改竄可能なフィールドに出力 string[] temp = jws.Split('.'); this.txtJWSHeader.Text = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(temp[0]), CustomEncode.UTF_8); this.txtJWSPayload.Text = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(temp[1]), CustomEncode.UTF_8); } else { // RS256 (X509) JWS_RS256_X509 jwsRS256 = new JWS_RS256_X509(this.CertificateFilePath_pfx, this.CertificateFilePassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); // 生成 string jws = jwsRS256.Create(this.txtJWSPayload.Text); // 出力 this.txtJWSKey.Text = jwsRS256.DigitalSignX509.X509PublicKey; this.txtJWSJWK.Text = RS256_KeyConverter.ParamToJwkPublicKey( RS256_KeyConverter.X509CerToProvider( this.CertificateFilePath_cer).ExportParameters(false)); this.txtJWSSign.Text = jws; // 改竄可能なフィールドに出力 string[] temp = jws.Split('.'); this.txtJWSHeader.Text = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(temp[0]), CustomEncode.UTF_8); this.txtJWSPayload.Text = CustomEncode.ByteToString( CustomEncode.FromBase64UrlString(temp[1]), CustomEncode.UTF_8); } }