共用方式為


解密安全性權杖

Download sample

CardSpace 提供使用者管理他們的數位身分識別的能力,而且在結合 Internet Explorer 7.0 (或其他支援資訊卡的瀏覽器) 後,網站可以向使用者要求數位身分識別。 以加密的安全性權杖形式傳輸此身分識別。 解密權杖以使用包含在語彙元中的宣告可藉由 Token 範例類別完成。 這個主題檢查權杖以及其運作方式。

Web 伺服器設定

要使用練習必須設定網站。 使用相同資料夾中提供的下列安裝批次檔來完成設定:

Setup.bat

如需安裝網站,以及一些疑難排解提示的詳細資訊,請參閱安裝 CardSpace 範例憑證

取得權杖

來自範例 HOW TO:配合 Internet Explorer 7.0 使用 Windows CardSpace 的程式碼示範顯示身分識別選取器所需的 HTML 程式碼。

使用的檔案:

Sample.htm

使用 <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>

使用者一按下 [送出] 權杖以表單的形式張貼至伺服器,使用宣告之前必須在伺服器上解密和驗證權杖。

login.aspx 頁面接受在 C# 中的已張貼權杖。

<%@ 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>

login.aspx 頁面接受在 VB.NET 中的已張貼權杖:

<%@ 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 加密語法和處理 (本頁面可能為英文)。 結構描述是核心結構描述 (本頁面可能為英文) 和 XML 加密 (本頁面可能為英文)。 下列範例是典型的加密權杖。

<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,這是一個對稱的金鑰加密演算法,雙方都在其中使用相同金鑰進行加密和解密。 對稱金鑰是由建立者隨機產生,而且又稱為暫時性金鑰。 這在 <KeyInfo> 項目包裝的 <e:EncryptedKey> 項目中加密並儲存。

<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> 項目,再次取得 Algorithm,儲存做為其較快速查詢的 HashCode

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> 項目在其加密表單中包含對稱金鑰 (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;
}

還原序列化與驗證權杖

為了使用內嵌的權杖,解密後,.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 類別所公開的屬性,這些屬性會從安全性權杖擷取宣告。

名稱 描述

IdentityClaims

權杖中身分識別宣告的 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

傳回發行者的身分識別宣告 (通常是發行者的公開金鑰)。

Footer image

請將您對這個主題的意見傳送至 Microsoft。

Copyright © 2007 by Microsoft Corporation. All rights reserved.