2012-08-23 2 views
13

HTTP 리디렉션 바인딩 방법을 사용하여 SP에서 초기화 한 SAML 2.0 인증 트랜잭션을 만들어야합니다. 이것은 매우 쉽습니다. IdP URI를 가져 와서 단일 쿼리 문자열 매개 변수 SAMLRequest을 연결하면됩니다. param은 SAML 요청을 설명하는 xml의 인코딩 된 블록입니다. 여태까지는 그런대로 잘됐다.C#을 사용하여 'HTTP 리디렉션 바인딩'SAML 요청을 올바르게 준비하는 방법

SAML을 쿼리 문자열 param으로 변환 할 때 문제가 발생합니다. 나는 준비의 과정이 있어야한다고 생각 :

  1. 는 SAML 문자열을
  2. 압축
  3. 은 Base64로 문자열
  4. 를 UrlEncode 문자열을 인코딩이 문자열을 구축 할 수 있습니다.

SAML 요청

<samlp:AuthnRequest 
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" 
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" 
    ID="{0}" 
    Version="2.0" 
    AssertionConsumerServiceIndex="0" 
    AttributeConsumingServiceIndex="0"> 
    <saml:Issuer>URN:xx-xx-xx</saml:Issuer> 
    <samlp:NameIDPolicy 
     AllowCreate="true" 
     Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/> 
</samlp:AuthnRequest> 

강령

private string GetSAMLHttpRedirectUri(string idpUri) 
{ 
    var saml = string.Format(SAMLRequest, Guid.NewGuid()); 
    var bytes = Encoding.UTF8.GetBytes(saml); 
    using (var output = new MemoryStream()) 
    { 
     using (var zip = new DeflaterOutputStream(output)) 
     { 
      zip.Write(bytes, 0, bytes.Length); 
     } 
     var base64 = Convert.ToBase64String(output.ToArray()); 
     var urlEncode = HttpUtility.UrlEncode(base64); 
     return string.Concat(idpUri, "?SAMLRequest=", urlEncode); 
    } 
} 

나는 압축 비난 어떻게 든 것으로 판단됩니다. 난 SharpZipLib에서 DeflaterOutputStream 클래스를 사용하고 있습니다.이 클래스는 업계 표준의 deflate-algorithm을 구현하기로되어 있습니다. 그래서 여기에 몇 가지 설정이 있습니다.

인코딩 된 출력은 SAML2.0 Debugger (유용한 온라인 변환 도구)을 사용하여 테스트 할 수 있습니다. 이 도구를 사용하여 출력물을 디코딩하면 난센스가됩니다.

질문은 다음과 같습니다. SAML 문자열을 올바르게 축약되고 인코딩 된 SAMLRequest query-param으로 변환하는 방법을 알고 있습니까?

EDIT 1

허용 된 대답은 아래의 문제에 대한 답을 제공합니다 감사합니다. 모든 후속 코멘트와 답변에 의해 수정 된 최종 코드입니다.

는 인코딩 SAMLRequest - 작동 코드

private string GenerateSAMLRequestParam() 
{ 
    var saml = string.Format(SAMLRequest, Guid.NewGuid()); 
    var bytes = Encoding.UTF8.GetBytes(saml); 
    using (var output = new MemoryStream()) 
    { 
     using (var zip = new DeflateStream(output, CompressionMode.Compress)) 
     { 
      zip.Write(bytes, 0, bytes.Length); 
     } 
     var base64 = Convert.ToBase64String(output.ToArray()); 
     return HttpUtility.UrlEncode(base64); 
    } 
} 

SAMLRequest 변수는이 질문의 상단에 표시 SAML이 포함되어 있습니다.

디코드 SAMLResponse - 작동 코드

private string DecodeSAMLResponse(string response) 
{ 
    var utf8 = Encoding.UTF8; 
    var bytes = utf8.GetBytes(response); 
    using (var output = new MemoryStream()) 
    { 
     using (new DeflateStream(output, CompressionMode.Decompress)) 
     { 
      output.Write(bytes, 0, bytes.Length); 
     } 
     var base64 = utf8.GetString(output.ToArray()); 
     return utf8.GetString(Convert.FromBase64String(base64)); 
    } 
} 
+0

작성된 방식대로 "디코딩 SAMLResponse - 작업 코드"를 사용할 수 없었습니다. 입력을 출력 MemoryStream에서 분리해야했습니다. 또한 DeflateStream을 수행하기 전에 UrlDecode 및 Convert.FromBase64String (...)을 사용해야했습니다. 내가 다음 사람을 돕는 경우에 대비하여 메모를 할 것이라고 생각했습니다. –

+0

작업 코드를 게시 할 수 있습니까? – biofractal

+0

Hey Randall, "Declaration SAMLResponse - Working Code"변경 사항에 대한 코드를 제공 할 수 있습니까? 그게 많이 감사하겠습니다! 감사합니다 – Naner

답변

11

I했습니다 그냥 예를 들어, SAML에 다음 코드를 실행합니다 :

 var saml = string.Format(sample, Guid.NewGuid()); 
     var bytes = Encoding.UTF8.GetBytes(saml); 

     string middle; 
     using (var output = new MemoryStream()) 
     { 
      using (var zip = new DeflaterOutputStream(output)) 
       zip.Write(bytes, 0, bytes.Length); 

      middle = Convert.ToBase64String(output.ToArray()); 
     } 

     string decoded; 
     using (var input = new MemoryStream(Convert.FromBase64String(middle))) 
     using (var unzip = new InflaterInputStream(input)) 
     using (var reader = new StreamReader(unzip, Encoding.UTF8)) 
      decoded = reader.ReadToEnd(); 

     bool test = decoded == saml; 

테스트 변수가 true입니다. 즉, zip/base64/unbase64/unzip 왕복이 올바르게 수행됩니다. 나중에 오류가 발생해야합니다. 아마 URLEncoder가 그들을 파괴할까요? 비슷한 urlencode/decode 테스트를 시도해 볼 수 있습니까? 또한 결과가 얼마나 오래 있는지 확인하십시오. 결과 URL이 길이로 인해 잘릴 수 있습니다.

(편집 : 배열에 읽는 대신 StreamReader를 추가했습니다. 이전 샘플에서는 버퍼를 준비하기 위해 bytes.Length를 사용 했으므로 테스트가 손상 될 수 있습니다. 이제 읽기에서는 압축 된 스트림의 정보 만 사용합니다)

편집 :

 var saml = string.Format(sample, Guid.NewGuid()); 
     var bytes = Encoding.UTF8.GetBytes(saml); 

     string middle; 
     using (var output = new MemoryStream()) 
     { 
      using (var zip = new DeflateStream(output, CompressionMode.Compress)) 
       zip.Write(bytes, 0, bytes.Length); 

      middle = Convert.ToBase64String(output.ToArray()); 
     } 

     // MIDDLE is the thing that should be now UrlEncode'd 

     string decoded; 
     using (var input = new MemoryStream(Convert.FromBase64String(middle))) 
     using (var unzip = new DeflateStream(input, CompressionMode.Decompress)) 
     using (var reader = new StreamReader(unzip, Encoding.UTF8)) 
      decoded = reader.ReadToEnd(); 

     bool test = decoded == saml; 

이 코드는 한 번 urlencode되고있는 middle 변수를 생산 제대로 디버거를 통과한다. DeflateStream은 표준 .Net의 System.IO.Compression 네임 스페이스에서 제공됩니다. SharpZip의 Deflate가 '디버거'사이트에서 허용되지 않는 이유는 거의 없습니다. 그것은 데이터가 제대로 압축 풀기 관리로서 압축이 작동한다는 것은 부인할 수 있습니다 .. 그냥 알고리즘의 일부 차이가 있어야하지만, 내가이 수축과 수축 사이의 차이점을 말할 수 없습니다.

+0

제공 한 디버거 사이트는 인코딩 된 메시지의 예를 표시합니다. Deflate 및 GZip으로 압축을 풀려고했는데 가능하지 않았습니다. .Net의 GZipStream도 압축 해제를 관리합니다. 어떤 압축 알고리즘을 사용합니까? – quetzalcoatl

+0

샘플 사이트에서 작동하는 압축기를 찾았습니다. 아직 답을 다시 업데이트했습니다. 마지막 부분을 다시 읽어주세요 :) – quetzalcoatl

+0

위대한 작품. 코드가 아름답게 작동했습니다. 고맙습니다 :-) – biofractal

6

상단의 질문에 "SAMLResponse - 작업 코드 디코드"섹션이 있지만 해당 코드가 깨져 보였다. 몇 가지 시도를 한 후에 동시에 동일한 스트림을 읽고 쓰려고한다는 것을 발견했습니다.

인코딩 SAML 인증 요청 :

public static string EncodeSamlAuthnRequest(this string authnRequest) { 
    var bytes = Encoding.UTF8.GetBytes(authnRequest); 
    using (var output = new MemoryStream()) { 
     using (var zip = new DeflateStream(output, CompressionMode.Compress)) { 
     zip.Write(bytes, 0, bytes.Length); 
     } 
     var base64 = Convert.ToBase64String(output.ToArray()); 
     return HttpUtility.UrlEncode(base64); 
    } 
    } 

디코드 SAML 인증 나는 내 솔루션 (I 편의 및 명확성을 위해 요청 섹션을 제공하고있다) 여기에 읽기를 분리 스트림을 작성하여 재 응답 :

public static string DecodeSamlAuthnRequest(this string encodedAuthnRequest) { 
    var utf8 = Encoding.UTF8; 
    var bytes = Convert.FromBase64String(HttpUtility.UrlDecode(encodedAuthnRequest)); 
    using (var output = new MemoryStream()) { 
    using (var input = new MemoryStream(bytes)) { 
     using (var unzip = new DeflateStream(input, CompressionMode.Decompress)) { 
     unzip.CopyTo(output, bytes.Length); 
     unzip.Close(); 
     } 
     return utf8.GetString(output.ToArray()); 
    } 
    } 
} 
관련 문제