セキュリティ トークンの復号化

Download sample

CardSpace により、ユーザーによるデジタル ID の管理が可能となり、Internet Explorer 7.0 (または情報カードをサポートする他のブラウザ) を介して、Web サイトがユーザーのデジタル ID を要求することができます。この ID は、暗号化されたセキュリティ トークンの形式で転送されます。Token サンプル クラスでトークンを復号化して、そのトークンに含まれているクレームを使用できます。このトピックでは、トークンとその動作について調べます。

Web サーバーのセットアップ

この演習を行うには、Web サイトの構成が必要です。構成は、サンプル フォルダに格納されている次のインストール バッチ ファイルを使用して行います。

Setup.bat

Web サイトのインストールの詳細およびトラブルシューティングのヒントについては、「CardSpace サンプル証明書のインストール」を参照してください。

トークンの取得

Internet Explorer 7.0 で Windows CardSpace を使用する方法」のサンプル コードでは、ID セレクタの表示に必要な HTML コードを示します。

使用するファイル :

Sample.htm

ID セレクタは、<object> 要素またはバイナリ動作オブジェクトのどちらかを使用して表示されます。このサンプルは <object> 要素といくつかの JavaScript を使用して、ユーザーにトークンを要求し、そのトークンを <textarea> に表示した後で Web サイトに送信します。

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>

ユーザーが [送信] ボタンをクリックすると、トークンはサーバーへのフォームにポストされます。ここで、クレームが使用される前にトークンを復号化して検証する必要があります。

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 Schema」および「XML 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> です。これには、トークンにアクセスするために必要な、次の 3 つが含まれます。

<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();

次の要素は <KeyIdentifier> です。この要素は、対称キーの復号化に必要な証明書のサムプリントを格納し、base-64 文字列から抽出されます。

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

<CypherValue> 要素は、base-64 でエンコードされた対称キーを暗号化フォームに格納します。

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();

セキュリティ トークンの暗号化は、2 つの対称アルゴリズム (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;
}

トークンの逆シリアル化および認証

埋め込まれたトークンを使用するには、復号化後に .NET Framework 3.0 で WSSecurityTokenSerializer を使用してトークンを逆シリアル化し、次に 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 クラスの使用

セキュリティ トークンからのクレームを抽出する 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.