Freigeben über


Entschlüsseln eines Sicherheitstokens

Download sample

CardSpace bietet Benutzern die Möglichkeit, ihre digitalen Identitäten zu verwalten, und Websites können mithilfe von Internet Explorer 7.0 (oder einem anderen Browser, der Informationskarten unterstützt) eine digitale Identität vom Benutzer anfordern. Diese Identität wird in Form eines verschlüsselten Sicherheitstokens übertragen. Dieses Token kann für die Verwendung der im Token enthaltenen Ansprüche mit der Beispielklasse Token entschlüsselt werden. In diesem Thema wird das Token und dessen Funktionsweise untersucht.

Webserversetup

Zum Durchführen dieser Übungen muss die Website konfiguriert werden. Die Website wird mithilfe der folgenden, im Beispielordner enthaltenen Installationsbatchdatei konfiguriert:

Setup.bat

Weitere Informationen zur Installation der Website sowie Tipps für die Fehlerbehandlung finden Sie unter Installieren von CardSpace-Beispielzertifikaten.

Abrufen des Tokens

Der Code unter So verwenden Sie Windows CardSpace mit Internet Explorer 7.0 ist ein Beispiel für den HTML-Code, der für die Anzeige der Identitätsauswahl erforderlich ist.

Verwendete Dateien:

Sample.htm

Sie können die Identitätsauswahl anzeigen, indem Sie entweder ein <object>-Element oder ein Binärverhaltensobjekt verwenden. In diesem Beispiel wird das <object>-Element sowie einige JavaScript-Befehle verwendet, um das Token vom Benutzer abzurufen und vor der Übermittlung an die Website in einem <textarea> anzuzeigen.

Die Datei 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>

Wenn der Benutzer auf die Schaltfläche Senden klickt, wird das Token in einem Formular an den Server übermittelt, wo es entschlüsselt und überprüft werden muss, bevor die Ansprüche verwendet werden können.

Die Seite login.aspx, die das gesendete Token in C# akzeptiert.

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

Die Seite login.aspx die das gesendete Token in VB.NET akzeptiert:

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

Die Token-Klasse ist für die Entschlüsselung, Überprüfung und Anspruchsextrahierung des verschlüsselten XML-Tokens verantwortlich.

Verarbeiten des Tokens: Überprüfen des XML-Verschlüsselungsformats

Das vom Browser gesendete Token wird mit einer W3C-XML-Verschlüsselung verschlüsselt. Das Schema wird im Dokument XML-Verschlüsselungssyntax und -Verarbeitung (Seite ist möglicherweise nur in englischer Sprache verfügbar) beschrieben. Die Schemas: Kernschema und XML-Verschlüsselung (Seiten sind möglicherweise nur in englischer Sprache verfügbar). Im Folgenden sehen Sie ein typisches Beispiel für ein verschlüsseltes Token.

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

Wenn Sie die Bestandteile des gesendeten XML-Codes ansehen, werden Sie feststellen, dass <enc:EncryptedData> das Stammelement ist. Dieses enthält die drei Dinge, die für den Zugriff auf das Token erforderlich sind.

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

Zunächst enthält <enc:EncryptionMethod> die zum Verschlüsseln des Tokens verwendete Methode.

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

In diesem Beispiel ist dies AES-256-cbc, ein Algorithmus für symmetrische Verschlüsselung, wobei beide Seiten denselben Schlüssel für die Ver- und Entschlüsselung verwenden. Der symmetrische Schlüssel wird willkürlich vom Absender generiert und als flüchtiger Schlüssel bezeichnet. Er wird verschlüsselt und im <e:EncryptedKey>-Element gespeichert, das in ein <KeyInfo>-Element eingebunden ist.

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

Es wird empfohlen, das Token mit einem symmetrischen Algorithmus zu verschlüsseln und den zugehörigen Schlüssel mit dem Token zu senden, da die asymmetrische Verschlüsselung langsamer ist. Zudem ist es nicht ratsam, Daten, die größer sind als der Schlüssel, mit einem asymmetrischen Schlüssel zu verschlüsseln. Wenn Sie nur den Schlüssel mit dem asymmetrischen Algorithmus verschlüsseln, muss der Server erheblich weniger verarbeiten.

Der flüchtige Schlüssel wird mit dem öffentlichen Schlüssel (aus dem Zertifikat) der abhängigen Seite verschlüsselt. Die Verschlüsselungsmethode des flüchtigen Schlüssels befindet sich im <e:EncryptionMethod>-Element.

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

In diesem Fall wird der RSA-OAEP-MGF1P-Algorithmus für Verschlüsselung verwendet. Der Schlüssel für diesen Vorgang befindet sich im folgenden <KeyInfo>-Element.

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

Mithilfe von <o:KeyIdentifier> kann der Absender der abhängigen Seite durch Einfügen des Fingerabdrucks (base64-codiert) in das Element mitteilen, welcher Zertifikatsschlüssel zum Verschlüsseln des symmetrischen Schlüssels verwendet wurde.

Die abhängige Seite entschlüsselt die Daten in <e:CipherData>/<e:CypherValue>-Element mithilfe des eigenen privaten Schlüssels.

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

Das Token selbst wird nach dem Abrufen des symmetrischen Schlüssels mit diesem entschlüsselt.

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

Besonderheiten des Tokenprozessorcodes

Die folgenden Besonderheiten der Entschlüsselung sollten bekannt sein, um den Entschlüsselungsprozess zu verstehen. Den vollständigen Prozess finden Sie im Quellcode der Token-Klasse.

Der Token-Klassenkonstruktor verwendet die decryptToken-Methode zum Entschlüsseln der an den Konstruktor übergebenen, verschlüsselten XML-Daten.

private static byte[] decryptToken(string xmlToken)

Zum Durchlaufen der XML-Daten kann ein XmlReader verwendet werden.

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

Bei der Suche nach den XML-Elementen ist nur sehr wenig Flexibilität möglich, da bei einem ungültigen Token rasch ein Fehler (mit ArgumentException) auftritt. Als Erstes müssen Sie das <EncryptionMethod>-Element suchen, in dem auf das Algorithm-Element zugegriffen wird, um die Verschlüsselungsmethode des Tokens abzurufen.

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

Als Nächstes suchen Sie das <EncryptionMethod>-Element für den flüchtigen Schlüssel und rufen wieder das Algorithm-Element auf, das für eine schnellere Suche als HashCode gespeichert ist.

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

Das nächste Element ist das <KeyIdentifier>-Element, das den Fingerabdruck des Zertifikats enthält und aus der base64-Zeichenfolge extrahiert wird. (Der Fingerabdruck ist zum Entschlüsseln des symmetrischen Schlüssels erforderlich.)

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

Das <CypherValue>-Element enthält den symmetrischen Schlüssel (base64-codiert) in verschlüsselter Form.

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

Das <CypherValue>-Element enthält den eigentlichen verschlüsselten Schlüssel.

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

Stellen Sie sicher, dass der Reader geschlossen ist, um Ressourcen freizugeben.

reader.Close();

Die Verschlüsselung des Sicherheitstokens wird von einem von zwei symmetrischen Algorithmen (AES und Triple DES) unterstützt. Verwenden Sie den Standardverschlüsselungsalgorithmus URI zum Suchen.

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

Um den symmetrischen Schlüssel abzurufen, entschlüsseln Sie ihn mit dem privaten Schlüssel.

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

Entschlüsseln Sie das Token mit dem ermittelten symmetrischen Algorithmus.

  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;
}

Deserialisieren und Authentifizieren des Tokens

Um das eingebettete Token nach dem Entschlüsseln zu verwenden, wird es von .NET Framework 3.0 (über den WSSecurityTokenSerializer) mithilfe von SamlSecurityTokenAuthenticator deserialisiert und authentifiziert. Derzeit werden SAML-Token von der Token-Klasse unterstützt. Für den Fall, dass andere Tokentypen benötigt werden, muss der Entwickler zur entsprechenden Unterstützung einen Authentifikator bereitstellen. Nach der Bestätigung durch den Authentifikator extrahiert die Token-Klasse die Ansprüche in ein verwendbares Format.

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.");
  }
}

Verwendung der Token-Klasse

In der folgenden Tabelle werden die Eigenschaften beschrieben, die von der Token-Klasse offen gelegt werden und die Ansprüche aus dem Sicherheitstoken extrahieren.

Name Beschreibung

IdentityClaims

Ein ClaimSet der Identitätsansprüche im Token.

AuthorizationContext

Ein AuthorizationContext, aus dem Token generiert.

UniqueID

Ruft die eindeutige ID (Identitätsanspruch) des Tokens ab.

Verwendet standardmäßig die PPID und den öffentlichen Schlüssel des Ausstellers und berechnet daraus den Hashwert, um eine eindeutige ID zu generieren.

Um ein anderes Feld zu verwenden, fügen Sie die folgende Codezeile hinzu.

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

Ersetzen Sie den Wert durch den URI für Ihren eindeutigen Anspruch.

Claims

Eine schreibgeschützte Zeichenfolgenauflistung der Ansprüche im Token. Unterstützt einen indizierten Ansprüche-Accessor.

securityToken.Claims[ClaimsTypes.PPID]

IssuerIdentityClaim

Gibt den Identitätsanspruch (i. d. R. der öffentliche Schlüssel des Ausstellers) des Ausstellers zurück.

Footer image

Senden Sie Kommentare zu diesem Thema an Microsoft.

Copyright © 2007 by Microsoft Corporation. Alle Rechte vorbehalten.