/// <summary>VerifyRedirect</summary> /// <param name="queryString">string</param> /// <param name="dsRSAwithSHA1">DigitalSign</param> /// <returns>bool</returns> public static bool VerifyRedirect(string queryString, DigitalSign dsRSAwithSHA1) { // EcodeRedirectの逆 // Signatureの抽出 string signature = StringExtractor.GetParameterFromQueryString("Signature", queryString); // Signatureの削除 queryString = queryString.Replace("&Signature=" + signature, ""); // queryString : ASCIIデコード // signature : パラメタ → URLデコード → Base64デコード if (dsRSAwithSHA1.Verify( CustomEncode.StringToByte(queryString, CustomEncode.us_ascii), CustomEncode.FromBase64String(CustomEncode.UrlDecode(signature)))) { // 署名検証 OK return(true); } else { // 署名検証 NG return(false); } }
/// <summary>DecodeRedirect</summary> /// <param name="queryString">string</param> /// <returns>デコードされたsaml</returns> public static string DecodeRedirect(string queryString) { // EcodeRedirectの逆 // -------------------------------------------------- // Saml → URLデコード → Base64デコード // → DEFLATE解凍 → XML宣言のエンコーディング → XML // -------------------------------------------------- // Samlの抽出 string saml = ""; if (queryString.IndexOf("SAMLRequest") != -1) { saml = StringExtractor.GetParameterFromQueryString("SAMLRequest", queryString); } else if (queryString.IndexOf("SAMLResponse") != -1) { saml = StringExtractor.GetParameterFromQueryString("SAMLResponse", queryString); } else { return(""); } byte[] tempByte = DeflateCompression.Decompress( CustomEncode.FromBase64String(CustomEncode.UrlDecode(saml))); //// XML宣言部分を取得するために、us_asciiでデコード //string tempString = CustomEncode.ByteToString(tempByte, CustomEncode.us_ascii); //// エンコーディング オブジェクトの取得 //Encoding enc = XmlLib.GetEncodingFromXmlDeclaration(tempString); return(CustomEncode.ByteToString(tempByte, CustomEncode.us_ascii)); // enc.CodePage); }
public static void UrlDecodeTest(string str) { try { // Performs encryption using the components touryo. string strValue = CustomEncode.UrlEncode(str); // Performs decrypted using the components touryo. string str1 = CustomEncode.UrlDecode(strValue); // Check whether it is decrypted into the original string. Assert.AreEqual(str, str1); } catch (Exception ex) { // Print a stack trace when an exception occurs. Console.WriteLine(ex.StackTrace); throw; } }
/// <summary>OnPreSendRequestHeaders</summary> /// <param name="sender">object</param> /// <param name="e">EventArgs</param> private void OnPreSendRequestHeaders(object sender, EventArgs e) { // PreSendRequestHeadersのロジックはここに挿入 #region 数 #region Context HttpApplication application = (HttpApplication)sender; HttpContext context = application.Context; HttpResponse response = context.Response; #endregion #region ワーク // code string code = ""; // state string state = ""; // expires_in ulong expires_in = 0; // redirect_uri string redirect_url = ""; // access_token string access_token = ""; // id_token string id_token = ""; #endregion #endregion if (context.Request.Url.AbsolutePath.IndexOf(ASPNETIdentityConfig.OAuth2AuthorizeEndpoint2) == -1 && context.Request.Url.AbsolutePath.IndexOf(ASPNETIdentityConfig.OAuth2AuthorizeEndpoint) != -1) { #region response_type if (this.RewritedResponseTypeFrom_IdToken || this.RewritedResponseTypeFrom_IdTokenToken) { // OpenID Connect : Implicit Flowに対応 // - [response_type=id_token] // - or [response_type=id_token token] // レスポンス内容を参照して書き換え(fragmentにid_tokenを追加) string location = response.Headers[OAuth2AndOIDCConst.HttpHeader_Location]; if (!string.IsNullOrEmpty(location) && location.IndexOf("#access_token=") != -1) { string pattern = ""; // ・正規表現でaccess_tokenを抜き出す。 pattern = "(\\#access_token=)(?<accessToken>.+?)(\\&)"; access_token = Regex.Match(location, pattern).Groups["accessToken"].Value; // ・正規表現でstateを抜き出す。 pattern = "&state=(?<state>.+)"; state = Regex.Match(location, pattern).Groups["state"].Value; // at_hash と s_hashを付与 id_token = IdToken.ChangeToIdTokenFromAccessToken( access_token, "", state, HashClaimType.AtHash | HashClaimType.SHash, // ★ ASPNETIdentityConfig.OAuth2JWT_pfx, ASPNETIdentityConfig.OAuth2JWTPassword); if (!string.IsNullOrEmpty(id_token)) { // responseにid_tokenとして、このJWTを追加する。 if (this.RewritedResponseTypeFrom_IdTokenToken) { response.Headers[OAuth2AndOIDCConst.HttpHeader_Location] = location + "&id_token=" + id_token; } else if (this.RewritedResponseTypeFrom_IdToken) { // ココは未サポート状態なので、テストできていない。 location = location.Replace("access_token=" + access_token + "&", ""); location = location.Replace("&token_type=beara", ""); response.Headers[OAuth2AndOIDCConst.HttpHeader_Location] = location + "&id_token=" + id_token; } } } } else if (this.RewritedResponseTypeFrom_CodeIdToken || this.RewritedResponseTypeFrom_CodeToken || this.RewritedResponseTypeFrom_CodeIdTokenToken) { // OpenID Connect : Hybrid Flowに対応 // - [response_type=code id_token] // - or [response_type=code token] // - or [response_type=code id_token token] //レスポンス内容を参照して書き換え(RedirectをFragmentに変更) if (response.StatusCode == 302) // 302 Found { // ・Rewriteした為か、 // 何故か、以下が、 // "?code=code値&state=state値&redirect_uri=Urlエンコードされたredirect_uri値" // となっている。 // ※ redirect_uriがlocationのQueryStringに混じる。 // // ・本来は、 // "http(s)://redirect_uri値?code=code値&state=state値" // を期待していた。 string location = response.Headers[OAuth2AndOIDCConst.HttpHeader_Location]; string paramStrCode = "?code="; string paramStrState = "&state="; string paramStrRedirectUri = "&redirect_uri="; if (location.IndexOf(paramStrCode) != -1 || location.IndexOf(paramStrState) != -1 || location.IndexOf(paramStrRedirectUri) != -1) { response.Headers.Remove(OAuth2AndOIDCConst.HttpHeader_Location); // 以下は、"?code=XXX&state=YYY&" という並びが前提。 MatchCollection matches = StringChecker.Matches( location, "\\?code=(?<code>.+)&state=(?<state>.+)"); foreach (Match match in matches) { GroupCollection groups = match.Groups; code = groups[OAuth2AndOIDCConst.code].Value; state = groups[OAuth2AndOIDCConst.state].Value; } redirect_url = location.Substring(0, location.IndexOf('?')); // ★ Hybrid Flow対応なので、expを短縮してもイイ。 expires_in = ulong.Parse( ASPNETIdentityConfig.OAuth2AccessTokenExpireTimeSpanFromMinutes.TotalSeconds.ToString()); // Fragmentに組み込む string fragment = ""; if (this.RewritedResponseTypeFrom_CodeIdToken) { // id_tokenを取得 string access_token_payload = AuthorizationCodeProvider.GetInstance().GetAccessTokenPayload(code); // c_hash, s_hashを付与 id_token = IdToken.ChangeToIdTokenFromAccessToken( OidcTokenEditor.ProtectFromAccessTokenPayload(access_token_payload, expires_in), code, state, HashClaimType.CHash | HashClaimType.SHash, ASPNETIdentityConfig.OAuth2JWT_pfx, ASPNETIdentityConfig.OAuth2JWTPassword); fragment = "#id_token={0}&token_type=Bearer&code={1}&expires_in={2}&state={3}"; fragment = string.Format(fragment, new object[] { id_token, code, expires_in, state }); } else if (this.RewritedResponseTypeFrom_CodeToken) { // access_tokenを取得 string access_token_payload = AuthorizationCodeProvider.GetInstance().GetAccessTokenPayload(code); access_token = OidcTokenEditor.ProtectFromAccessTokenPayload(access_token_payload, expires_in); fragment = "#access_token={0}&token_type=Bearer&code={1}&expires_in={2}&state={3}"; fragment = string.Format(fragment, new object[] { access_token, code, expires_in, state }); } else if (this.RewritedResponseTypeFrom_CodeIdTokenToken) { // id_token, access_tokenを取得 string access_token_payload = AuthorizationCodeProvider.GetInstance().GetAccessTokenPayload(code); access_token = OidcTokenEditor.ProtectFromAccessTokenPayload(access_token_payload, expires_in); // at_hash, c_hash, s_hashを付与 id_token = IdToken.ChangeToIdTokenFromAccessToken(access_token, code, state, HashClaimType.AtHash | HashClaimType.CHash | HashClaimType.SHash, ASPNETIdentityConfig.OAuth2JWT_pfx, ASPNETIdentityConfig.OAuth2JWTPassword); fragment = "#access_token={0}&id_token={1}&token_type=Bearer&code={2}&expires_in={3}&state={4}"; fragment = string.Format(fragment, new object[] { access_token, id_token, code, expires_in, state }); } // Locationを追加(redirect_url + fragment)。 response.Headers.Add(OAuth2AndOIDCConst.HttpHeader_Location, redirect_url + fragment); } } } #endregion #region response_mode if (this.RewritedResponseModeFrom_FromPost) { // OAuth2.0, OpenID Connect : response_mode=form_postに対応 //レスポンス内容を参照して書き換え //(Redirect(get)をAuto-Submit Form(post)に変更) if (response.StatusCode == 302) // 302 Found { // ・Rewriteした為か、 // 何故か、以下が、 // "?code=code値&state=state値&redirect_uri=Urlエンコードされたredirect_uri値" // となっている。 // ※ redirect_uriがlocationのQueryStringに混じる。 // // ・本来は、 // "http(s)://redirect_uri値?code=code値&state=state値" // を期待していた。 string location = response.Headers[OAuth2AndOIDCConst.HttpHeader_Location]; string paramStrCode = "?code="; string paramStrState = "&state="; string paramStrRedirectUri = "&redirect_uri="; if (location.IndexOf(paramStrCode) != -1 || location.IndexOf(paramStrState) != -1 || location.IndexOf(paramStrRedirectUri) != -1) { // 302 Found ---> 200 OK response.StatusCode = 200; response.Headers.Remove(OAuth2AndOIDCConst.HttpHeader_Location); // 以下は、"?code=XXX&state=YYY&redirect_uri=ZZZ" という並びが前提。 MatchCollection matches = StringChecker.Matches( location, "\\?code=(?<code>.+)&state=(?<state>.+)&redirect_uri=(?<redirect_uri>.+)"); foreach (Match match in matches) { GroupCollection groups = match.Groups; code = groups[OAuth2AndOIDCConst.code].Value; state = groups[OAuth2AndOIDCConst.state].Value; redirect_url = CustomEncode.UrlDecode(groups[OAuth2AndOIDCConst.redirect_uri].Value); } // form_postに必要な、HTTP response message body string body = "<html>" + " <body onload=\"javascript: document.forms[0].submit()\">" + " <form method=\"post\" action =\"{0}\">" + " <input type=\"hidden\" name =\"code\" value =\"{1}\"/>" + " <input type=\"hidden\" name =\"state\" value =\"{2}\"/>" + " </form>" + " </body>" + "</html>"; // bodyに組み込んで body = string.Format(body, redirect_url, code, state); // HTTP Responseに書き出し byte[] buffer = response.ContentEncoding.GetBytes(body); response.OutputStream.Write(buffer, 0, buffer.Length); } } } #endregion if (this.RewritedResponseTypeFrom_IdToken || this.RewritedResponseTypeFrom_IdTokenToken || this.RewritedResponseTypeFrom_CodeIdToken || this.RewritedResponseTypeFrom_CodeToken || this.RewritedResponseTypeFrom_CodeIdTokenToken) //|| this.RewritedResponseModeFrom_FromPost) { context.RewritePath(this.OriginalVirtualPath); } #region 再利用されるinstance memberの初期化を忘れずに!! // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ this.OriginalVirtualPath = ""; this.RewritedResponseTypeFrom_IdTokenToken = false; this.RewritedResponseTypeFrom_IdToken = false; this.RewritedResponseTypeFrom_CodeIdToken = false; this.RewritedResponseTypeFrom_CodeToken = false; this.RewritedResponseTypeFrom_CodeIdTokenToken = false; this.RewritedResponseModeFrom_FromPost = false; #endregion } }