/// <summary> /// code_challenge_method=S256 /// BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))</summary> /// <param name="code_verifier">string</param> /// <returns>code_challenge</returns> public static string PKCE_S256_CodeChallengeMethod(string code_verifier) { return(CustomEncode.ToBase64UrlString( GetHash.GetHashBytes( CustomEncode.StringToByte(code_verifier, CustomEncode.us_ascii), EnumHashAlgorithm.SHA256_M))); }
/// <summary>CreateJwkFromDictionary</summary> /// <param name="dic">Dictionary</param> /// <param name="settings">JsonSerializerSettings</param> /// <returns>JwkString</returns> internal static string CreateJwkFromDictionary( Dictionary <string, string> dic, JsonSerializerSettings settings = null) { // JSON Web Key (JWK) Thumbprint // https://openid-foundation-japan.github.io/rfc7638.ja.html // kid : https://openid-foundation-japan.github.io/rfc7638.ja.html#Example // https://openid-foundation-japan.github.io/rfc7638.ja.html#MembersUsed // kidには、JWK の JWK Thumbprint 値などが用いられるらしい。 // ★ EC 公開鍵の必須メンバを辞書順に並べると、crv, kty, x, y となる。 dic[JwtConst.kid] = CustomEncode.ToBase64UrlString( GetHash.GetHashBytes( CustomEncode.StringToByte( JsonConvert.SerializeObject(new { crv = dic[JwtConst.crv], kty = dic[JwtConst.kty], x = dic[JwtConst.x], y = dic[JwtConst.y] }), CustomEncode.UTF_8), EnumHashAlgorithm.SHA256_M)); //dic["ext"] = "false"; // 定義をRFC上に発見できない。 if (settings == null) { return(JsonConvert.SerializeObject(dic)); } else { return(JsonConvert.SerializeObject(dic, settings)); } }
/// <summary> /// SHA256でat_hash, c_hashを作成。 /// (現時点でRS256固定になっているので) /// </summary> /// <returns>hash</returns> public static string CreateHash(string input) { // ID Token の JOSE Header にある alg Header Parameterのアルゴリズムで使用されるハッシュアルゴリズムを用い、 // input(access_token や code) のASCII オクテット列からハッシュ値を求め、左半分を base64url エンコードした値。 return(CustomEncode.ToBase64UrlString( PubCmnFunction.ShortenByteArray( GetHash.GetHashBytes( CustomEncode.StringToByte(input, CustomEncode.us_ascii), EnumHashAlgorithm.SHA256Managed), (256 / 2)))); }
/// <summary>ハッシュ</summary> private void btnGetHash_Click(object sender, EventArgs e) { if (this.rbnHSString.Checked) { txtHSCode.Text = GetHash.GetHashString( txtHSString.Text, (EnumHashAlgorithm)cbxHSPV.SelectedValue); } else { txtHSCode.Text = CustomEncode.ToHexString(GetHash.GetHashBytes( CustomEncode.StringToByte(txtHSString.Text, CustomEncode.UTF_8), (EnumHashAlgorithm)cbxHSPV.SelectedValue)); } }
/// <summary> /// at_hash, c_hash, s_hashを作成 /// (SHA256→HS256,RS256,ES256対応可能) /// </summary> /// <param name="input">string</param> /// <returns>hash</returns> public static string CreateHash(string input) { // ID Token の JOSE Header にある // alg Header Parameterのアルゴリズムで使用されるハッシュアルゴリズムを用い、 // input(access_token や code) のASCII オクテット列からハッシュ値を求め、 byte[] bytes = GetHash.GetHashBytes( CustomEncode.StringToByte(input, CustomEncode.us_ascii), EnumHashAlgorithm.SHA256_M); // 左半分を base64url エンコードした値。 return(CustomEncode.ToBase64UrlString( ArrayOperator.ShortenByteArray(bytes, (bytes.Length / 2)))); }
// <参考> // JSON Web Key (JWK) // https://openid-foundation-japan.github.io/rfc7517.ja.html // を、"kty":"RSA"で検索するとイイ。 // // A.1. Example Public Keys // https://openid-foundation-japan.github.io/rfc7517.ja.html#PublicExample // A.2. Example Private Keys // https://openid-foundation-japan.github.io/rfc7517.ja.html#PrivateExample // C.1. Plaintext RSA Private Key // https://openid-foundation-japan.github.io/rfc7517.ja.html#example-privkey-plaintext // ECCurve support of EccKey. // https://github.com/dvsekhvalnov/jose-jwt/issues/105 // RSAParameters ⇔ Jwk // https://github.com/psteniusubi/jose-jwt/blob/master/jose-jwt/jwk/JwkRsa.cs // <Param to jwk> // <Jwk to param> //header.Set("kty", "RSA"); //RSAParameters parameters = new RSAParameters(); //header.Set("n", parameters.Modulus); //parameters.Modulus = header.GetBytes("n"); //header.Set("e", parameters.Exponent); //parameters.Exponent = header.GetBytes("e"); // // ココから下は秘密鍵の領域 // ココから下は秘密鍵の領域 //if (includePrivateParameters) //if (header.ContainsKey("d")) //{ //{ // header.Set("d", parameters.D); // parameters.D = header.GetBytes("d"); // header.Set("p", parameters.P); // parameters.P = header.GetBytes("p"); // header.Set("q", parameters.Q); // parameters.Q = header.GetBytes("q"); // header.Set("dp", parameters.DP); // parameters.DP = header.GetBytes("dp"); // header.Set("dq", parameters.DQ); // parameters.DQ = header.GetBytes("dq"); // header.Set("qi", parameters.InverseQ); // parameters.InverseQ = header.GetBytes("qi"); //} //} // // ↓↓↓ // //RSA rsa = RSA.Create(); // //rsa.ImportParameters(parameters); // //return rsa; #region ParamToJwk /// <summary>ParamToJwk</summary> /// <param name="param">RSAParameters</param> /// <param name="settings">JsonSerializerSettings</param> /// <returns>Jwk公開鍵</returns> public string ParamToJwk( RSAParameters param, JsonSerializerSettings settings = null) { Dictionary <string, string> dic = new Dictionary <string, string>(); dic[JwtConst.kty] = JwtConst.RSA; // 必須 dic[JwtConst.alg] = this.JwtConstRSnnn; // Public dic[JwtConst.n] = CustomEncode.ToBase64UrlString(param.Modulus); dic[JwtConst.e] = CustomEncode.ToBase64UrlString(param.Exponent); //"AQAB"; // JSON Web Key (JWK) Thumbprint // https://openid-foundation-japan.github.io/rfc7638.ja.html // kid : https://openid-foundation-japan.github.io/rfc7638.ja.html#Example // https://openid-foundation-japan.github.io/rfc7638.ja.html#MembersUsed // kidには、JWK の JWK Thumbprint 値などが用いられるらしい。 // ★ RSA 公開鍵の必須メンバを辞書順に並べると、e, kty, n となる。 dic[JwtConst.kid] = CustomEncode.ToBase64UrlString( GetHash.GetHashBytes( CustomEncode.StringToByte( JsonConvert.SerializeObject(new { e = dic[JwtConst.e], kty = dic[JwtConst.kty], n = dic[JwtConst.n] }), CustomEncode.UTF_8), this.HashAlgorithm)); //dic["ext"] = "false"; // 定義をRFC上に発見できない。 if (settings == null) { return(JsonConvert.SerializeObject(dic)); } else { return(JsonConvert.SerializeObject(dic, settings)); } }
/// <summary>ValidateSignature</summary> /// <param name="clientData">string</param> /// <param name="authenticatorData">string</param> /// <param name="signature">string</param> /// <returns> /// true : valid /// false : invalid /// </returns> public bool ValidateSignature( string clientData, string authenticatorData, string signature) { bool ret = false; byte[] clientDataBytes = CustomEncode.FromBase64UrlString(clientData); byte[] authenticatorDataBytes = CustomEncode.FromBase64UrlString(authenticatorData); byte[] signatureBytes = CustomEncode.FromBase64UrlString(signature); // Challengeの一致を確認する。 JObject clientJson = JObject.Parse(//Encoding.ASCII.GetString(clientDataBytes)); Encoding.ASCII.GetString(clientDataBytes).Replace("\0", "").Trim()); byte[] hashBytes = null; byte[] data = null; hashBytes = GetHash.GetHashBytes(clientDataBytes, EnumHashAlgorithm.SHA256Managed, 0); data = authenticatorDataBytes.Concat(hashBytes).ToArray(); if ((string)clientJson["challenge"] == this.Challenge) { // Challengeの一致 // Load public key RSACryptoServiceProvider rsaCryptoServiceProvider = RS256_KeyConverter.JwkToProvider(this.PublicKey); // VerifyData ret = rsaCryptoServiceProvider.VerifyData( data, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); return(ret); } else { // Challengeの不一致 return(false); } }