Share via


보안 토큰 해독

Download sample

CardSpace에서는 Internet Explorer 7.0(또는 정보 카드를 지원하는 다른 브라우저)을 사용하여 웹 사이트에서 사용자에게 요청할 수 있는 디지털 ID를 관리하는 기능을 제공합니다. 이 ID는 암호화된 보안 토큰의 형태로 전송됩니다. Token 샘플 클래스를 사용하여 토큰에 포함된 클레임을 사용하기 위해 토큰을 해독할 수 있습니다. 이 항목에서는 토큰과 토큰의 작동 방식에 대해 살펴봅니다.

웹 서버 설치

연습을 진행하려면 웹 사이트 구성이 필요합니다. 샘플 폴더에 제공된 다음 설치 배치 파일을 사용하여 구성이 수행됩니다.

Setup.bat

웹 사이트 설치에 대한 자세한 내용과 문제 해결 팁은 CardSpace 샘플 인증서 설치를 참조하십시오.

토큰 가져오기

Internet Explorer 7.0에서 Windows CardSpace를 사용하는 방법 샘플의 코드에서는 ID 선택기를 표시하는 데 필요한 HTML 코드를 보여 줍니다.

사용되는 파일:

Sample.htm

ID 선택기는 <object> 요소나 이진 동작 개체를 사용하여 표시됩니다. 이 예제에서는 <object> 요소와 JavaScript를 사용하여 사용자에게 토큰을 요청하고 웹 사이트에 토큰을 제출하기 전에 <textarea>에 표시합니다.

Sample.htm 파일:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
  <title>Sample 2</title>
  <object type="application/x-informationcard" name="_xmlToken">
    <param name="tokenType" value="urn:oasis:names:tc:SAML:1.0:assertion" />
    <param name="issuer" value="https://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self" />
    <param name="requiredClaims" 
        value="https://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
        https://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
        https://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
        https://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" />
  </object>
  <script language="javascript">
    function GetIdentity(){
      var xmltkn=document.getElementById("_xmltoken");
      var thetextarea = document.getElementById("xmltoken");
      thetextarea.value = xmltkn.value ;
    }
  </script>
</head>
<body>
  <form id="form1" method="post" action="login.aspx">
    <button name="go" id="go" onclick="javascript:GetIdentity();">Click here to get the token.</button>
    <button type="submit">Click here to send the card to the server</button>
    <textarea cols=100 rows=20 id="xmltoken" name="xmlToken" ></textarea>
  </form>
</body>
</html>

사용자가 Submit 단추를 클릭하면 토큰이 폼으로 서버에 게시됩니다. 클레임을 사용하려면 서버에서 토큰이 해독되고 확인되어야 합니다.

게시된 토큰을 받아들이는 C#으로 작성된 login.aspx 페이지:

<%@ Page Language="C#"  Debug="true" ValidateRequest="false" %>
<%@ Import Namespace="Microsoft.IdentityModel.Samples" %>
<%@ Import Namespace="Microsoft.IdentityModel.TokenProcessor" %>

<script runat="server">
    protected void ShowError(string text) {
        fields.Visible = false;
        errors.Visible = true;
        errtext.Text = text;
    }
    protected void Page_Load(object sender, EventArgs e) {
        string xmlToken;
        xmlToken = Request.Params["xmlToken"];
        if (xmlToken == null || xmlToken.Equals(""))
            ShowError("Token presented was null");
        else
        {
                Token token = new Token (xmlToken);
                givenname.Text = token.Claims[SelfIssued.GivenName];
                surname.Text = token.Claims[SelfIssued.Surname];
                email.Text = token.Claims[SelfIssued.EmailAddress];
                uid.Text = token.UniqueID;
        }
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Login Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div runat="server" id="fields">
        Given Name:<asp:Label ID="givenname" runat="server" Text=""></asp:Label><br/>
        Surname:<asp:Label ID="surname" runat="server" Text=""></asp:Label><br/>
        Email Address:<asp:Label ID="email" runat="server" Text=""></asp:Label><br/>
        Unique ID:<asp:Label ID="uid" runat="server" Text=""></asp:Label><br/>
    </div>
    <div runat="server" id="errors" visible="false">
        Error:<asp:Label ID="errtext" runat="server" Text=""></asp:Label><br/>
    </div>
        
    </form>
</body>
</html>

게시된 토큰을 받아들이는 VB.NET으로 작성된 login.aspx 페이지:

<%@ Page Language="VB"  Debug="true" ValidateRequest="false" %>
<%@ Import Namespace="Microsoft.IdentityModel.Samples" %>
<%@ Import Namespace="Microsoft.IdentityModel.TokenProcessor" %>

<script runat="server">
Protected  Sub ShowError(ByVal text As String) 
   fields.Visible = False 
   errors.Visible = True 
   errtext.Text = text 
End Sub

Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
   Dim xmlToken As String
   xmlToken = Request.Params("xmlToken")
   If xmlToken = Nothing Or xmlToken.Equals("") Then
      ShowError("Token presented was null")
   Else
      Dim token As New Token(xmlToken)
      givenname.Text = token.Claims(ClaimTypes.GivenName)
      surname.Text = token.Claims(ClaimTypes.Surname)
      email.Text = token.Claims(ClaimTypes.Email)
      uid.Text = token.UniqueID
   End If
End Sub

</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
    <title>Login Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div runat="server" id="fields">
        Given Name:<asp:Label ID="givenname" runat="server" Text=""></asp:Label><br/>
        Surname:<asp:Label ID="surname" runat="server" Text=""></asp:Label><br/>
        Email Address:<asp:Label ID="email" runat="server" Text=""></asp:Label><br/>
        Unique ID:<asp:Label ID="uid" runat="server" Text=""></asp:Label><br/>
    </div>
    <div runat="server" id="errors" visible="false">
        Error:<asp:Label ID="errtext" runat="server" Text=""></asp:Label><br/>
    </div>
        
    </form>
</body>
</html>

Token 클래스는 암호화된 XML 토큰의 해독, 확인 및 클레임 추출을 처리합니다.

토큰 처리: XML 암호화 형식 검사

브라우저에서 게시된 토큰은 W3C XML 암호화를 사용하여 암호화됩니다. 스키마에 대해 설명하는 문서는 XML Encryption Syntax and Processing입니다. 스키마는 Core SchemaXML Encryption입니다. 다음은 일반적인 암호화된 토큰의 예입니다.

<enc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" 
    xmlns:enc="http://www.w3.org/2001/04/xmlenc#">
  <enc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
  <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
    <e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
      <e:EncryptionMethod 
       Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
      </e:EncryptionMethod>
      <KeyInfo>
        <o:SecurityTokenReference 
           xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-
                    1.0.xsd">
          <o:KeyIdentifier 
            ValueType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-
                      1.1#ThumbprintSHA1"
            EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-
                      message-security-1.0#Base64Binary">
          1H3mV/pJAlVZAst/Dt0rqbBd67g=
          </o:KeyIdentifier>
        </o:SecurityTokenReference>
      </KeyInfo>
      <e:CipherData>
        <e:CipherValue>
        YJHp...==</e:CipherValue>
      </e:CipherData>
    </e:EncryptedKey>
  </KeyInfo>
  <enc:CipherData>
    <enc:CipherValue>
    ctct...9A==</enc:CipherValue>
  </enc:CipherData>
</enc:EncryptedData>

게시된 XML의 구성 부분을 살펴보면 루트 요소가 <enc:EncryptedData>입니다. 이 요소에는 토큰에 액세스하는 데 필요한 세 가지 항목이 포함되어 있습니다.

<enc:EncryptedData>
  <enc:EncryptionMethod />
  <KeyInfo /> 
  <enc:CipherData />
</enc:EncryptedData>

<enc:EncryptionMethod>에는 토큰을 암호화하는 데 사용되는 방법이 포함되어 있습니다.

  <enc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />

이 경우에는 두 당사자 모두 암호화와 해독에 동일한 키를 사용하는 대칭 키 암호화 알고리즘인 AES-256-cbc입니다. 대칭 키는 작성기에서 무작위로 생성되며 임시 키라고 합니다. 대칭 키는 암호화되어 <e:EncryptedKey> 요소에 저장되며 이 요소는 <KeyInfo> 요소로 래핑됩니다.

<KeyInfo >
  <e:EncryptedKey />
    <e:EncryptionMethod / >
    <KeyInfo />
    <e:CipherData />
  </e:EncryptedKey>
</KeyInfo>

비대칭 암호화는 대칭 암호화보다 속도가 느리며 비대칭 키를 사용하여 키의 크기보다 큰 데이터를 암호화하는 것은 적합하지 않으므로 대칭 알고리즘을 사용하여 토큰을 암호화하고 토큰을 사용하여 암호화된 키를 보내는 것이 좋습니다. 비대칭 알고리즘을 사용하여 키만 암호화하면 서버에서 필요한 처리량이 크게 줄어듭니다.

임시 키는 신뢰하는 상대의 인증서에 있는 공개 키를 사용하여 암호화됩니다. 임시 키의 암호화 방법은 <e:EncryptionMethod> 요소에 있습니다.

<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
   <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
</e:EncryptionMethod>

이 경우에는 RSA-OAEP-MGF1P 알고리즘이 암호화에 사용됩니다. 이 작업의 키는 다음 <KeyInfo> 요소에 있습니다.

<KeyInfo>
  <o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-
                                     wssecurity-secext-1.0.xsd">
    <o:KeyIdentifier 
      ValueType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-
                 1.1#ThumbprintSHA1"
      EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-
                    security-1.0#Base64Binary">

        1H3mV/pJAlVZAst/Dt0rqbBd67g=

   </o:KeyIdentifier>
  </o:SecurityTokenReference>
</KeyInfo>

보낸 사람은 <o:KeyIdentifier>를 통해 base64로 인코딩된 지문을 요소에 삽입하여 대칭 키를 암호화하는 데 사용된 인증서의 키를 신뢰하는 상대에게 알릴 수 있습니다.

신뢰하는 상대는 개인 키를 사용하여 <e:CipherData>/<e:CypherValue> 요소에 있는 데이터를 해독합니다.

<e:CipherData>
    <e:CipherValue>
    YJHp...==</e:CipherValue>
</e:CipherData>

대칭 키를 검색한 후에는 토큰 자체가 대칭 키를 사용하여 해독됩니다.

<enc:CipherData>
    <enc:CipherValue>
    ctct...9A==</enc:CipherValue>
</enc:CipherData>

토큰 프로세서 코드의 주요 사항

해독 프로세스를 이해하려면 해독 프로세스의 다음 주요 사항을 알아두어야 합니다. 전체 프로세스를 보려면 Token 클래스의 소스 코드를 참조하십시오.

Token 클래스 생성자는 decryptToken 메서드를 사용하여 생성자에 전달되는 암호화된 XML 데이터를 해독합니다.

private static byte[] decryptToken(string xmlToken)

XML 데이터를 반복하는 데는 XmlReader가 사용됩니다.

XmlReader reader = new XmlTextReader(new StringReader(xmlToken));

XML 요소를 검색할 때 토큰이 잘못되면 ArgumentException을 사용하여 신속하게 오류를 발생시키기 위해 매우 적은 융통성이 허용됩니다. 시작하려면 <EncryptionMethod> 요소를 찾아야 합니다. 이 요소에서 Algorithm 요소가 토큰의 암호화 방법을 검색하기 위해 액세스됩니다.

if (!reader.ReadToDescendant(XmlEncryptionStrings.EncryptionMethod,  
                             XmlEncryptionStrings.Namespace))
  throw new ArgumentException("Cannot find token EncryptedMethod.");
encryptionAlgorithm = reader.GetAttribute(XmlEncryptionStrings.Algorithm).GetHashCode();

그런 다음 임시 키의 <EncryptionMethod> 요소를 찾아 빠른 조회를 위해 HashCode로 저장되는 Algorithm을 가져옵니다.

if (!reader.ReadToFollowing(XmlEncryptionStrings.EncryptionMethod, 
                            XmlEncryptionStrings.Namespace))
   throw new ArgumentException("Cannot find key EncryptedMethod.");
m_keyEncryptionAlgorithm = reader.GetAttribute(XmlEncryptionStrings.Algorithm).GetHashCode();

다음 요소는 대칭 키의 해독에 필요한 인증서의 지문이 포함되어 있고 base64 문자열에서 추출되는 <KeyIdentifier>입니다.

if (!reader.ReadToFollowing(WSSecurityStrings.KeyIdentifier, WSSecurityStrings.Namespace))
  throw new ArgumentException("Cannot find Key Identifier.");
reader.Read();
thumbprint = Convert.FromBase64String(reader.ReadContentAsString());

<CypherValue> 요소에는 암호화된 형태로 대칭 키(base64로 인코딩됨)가 포함되어 있습니다.

if (!reader.ReadToFollowing(XmlEncryptionStrings.CipherValue, XmlEncryptionStrings.Namespace))
    throw new ArgumentException("Cannot find symmetric key.");
reader.Read();
symmetricKeyData = Convert.FromBase64String(reader.ReadContentAsString());

실제 암호화된 토큰이 포함된 <CypherValue>:

if (!reader.ReadToFollowing(XmlEncryptionStrings.CipherValue, XmlEncryptionStrings.Namespace))
  throw new ArgumentException("Cannot find encrypted security token.");
reader.Read();
securityTokenData = Convert.FromBase64String(reader.ReadContentAsString());

리소스를 해제하기 위해 판독기를 닫아야 합니다.

reader.Close();

보안 토큰의 암호화는 두 대칭 알고리즘(AES 및 Triple DES) 중 하나로 지원됩니다. 암호화 알고리즘 URI를 조회로 사용합니다.

SymmetricAlgorithm alg = null;
X509Certificate2 certificate = FindCertificate(thumbprint );
              
foreach( int i in Aes )
if (encryptionAlgorithm == i)
{
  alg= new RijndaelManaged();
  break;
}
if ( null == alg )
foreach (int i in TripleDes)
if (encryptionAlgorithm == i)
{
  alg = new TripleDESCryptoServiceProvider();
  break;
}
  
if (null == alg)
  throw new ArgumentException("Could not determine Symmetric Algorithm");

대칭 키를 가져오려면 개인 키로 대칭 키를 해독합니다.

alg.Key=(certificate.PrivateKey as RSACryptoServiceProvider).Decrypt(symmetricKeyData,true);

검색된 알고리즘을 사용하여 대칭 알고리즘으로 토큰을 해독합니다.

  int ivSize = alg.BlockSize / 8;
  byte[] iv = new byte[ivSize];
  Buffer.BlockCopy(securityTokenData, 0, iv, 0, iv.Length);
  alg.Padding = PaddingMode.ISO10126;
  alg.Mode = CipherMode.CBC;
  ICryptoTransform decrTransform = alg.CreateDecryptor(alg.Key, iv);
  byte[] plainText = decrTransform.TransformFinalBlock(securityTokenData, iv.Length,   
                                                       securityTokenData.Length iv.Length);
  decrTransform.Dispose();
  return plainText;
}

토큰 Deserialize 및 인증

해독된 포함 토큰을 사용하기 위해 .NET Framework 3.0에서는 포함 토큰을 WSSecurityTokenSerializer를 통해 deserialize하고 SamlSecurityTokenAuthenticator를 사용하여 인증합니다. 현재 Token 클래스는 SAML 토큰을 지원합니다. 다른 토큰 형식이 필요한 경우 개발자는 해당 토큰 형식을 지원하기 위해 인증자를 제공해야 합니다. 인증자가 토큰 형식의 유효성을 검사한 후 Token 클래스는 클레임을 사용 가능한 형태로 추출합니다.

public Token(String xmlToken)
{
  byte[] decryptedData = decryptToken(xmlToken);
  
  XmlReader reader = new XmlTextReader(
            new StreamReader(new MemoryStream(decryptedData), Encoding.UTF8));

  m_token = (SamlSecurityToken)WSSecurityTokenSerializer.DefaultInstance.ReadToken(
            reader, null);

  SamlSecurityTokenAuthenticator authenticator = 
            new SamlSecurityTokenAuthenticator(new List<SecurityTokenAuthenticator>(
                 new SecurityTokenAuthenticator[]{
                 new RsaSecurityTokenAuthenticator(),
                 new X509SecurityTokenAuthenticator() }), MaximumTokenSkew);
  
  if (authenticator.CanValidateToken(m_token))
  {
    ReadOnlyCollection<IAuthorizationPolicy> policies = authenticator.ValidateToken(m_token);
    m_authorizationContext = AuthorizationContext.CreateDefaultAuthorizationContext(policies);
    FindIdentityClaims();
  }
  else
  {
    throw new Exception("Unable to validate the token.");
  }
}

토큰 클래스의 사용

다음 표에서는 보안 토큰에서 클레임을 추출하는 Token 클래스에서 노출하는 속성에 대해 설명합니다.

이름 설명

IdentityClaims

토큰에 있는 ID 클레임의 ClaimSet

AuthorizationContext

토큰에서 생성된 AuthorizationContext

UniqueID

토큰의 UniqueID(IdentityClaim)를 가져옵니다.

기본적으로 PPID와 발급자의 공개 키를 사용하고 둘을 함께 해시하여 UniqueID를 생성합니다.

다른 필드를 사용하려면 다음 코드 줄을 추가합니다.

<add name="IdentityClaimType"  
     value="https://schemas.xmlsoap.org/ws/2005/05/identity/
     claims/privatepersonalidentifier" />

값을 고유 클레임에 대한 URI로 바꿉니다.

Claims

토큰에 있는 클레임의 읽기 전용 문자열 컬렉션. 인덱싱된 클레임 접근자를 지원합니다.

securityToken.Claims[ClaimsTypes.PPID]

IssuerIdentityClaim

발급자의 ID 클레임(일반적으로 발급자의 공개 키)을 반환합니다.

Footer image

이 항목에 대한 의견을 Microsoft에 보내 주십시오.

Copyright © 2007 by Microsoft Corporation. All rights reserved.