Entschlüsseln eines Sicherheitstokens
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 |
---|---|
|
Ein ClaimSet der Identitätsansprüche im Token. |
|
Ein AuthorizationContext, aus dem Token generiert. |
|
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.
Ersetzen Sie den Wert durch den URI für Ihren eindeutigen Anspruch. |
|
Eine schreibgeschützte Zeichenfolgenauflistung der Ansprüche im Token. Unterstützt einen indizierten Ansprüche-Accessor.
|
|
Gibt den Identitätsanspruch (i. d. R. der öffentliche Schlüssel des Ausstellers) des Ausstellers zurück. |
Senden Sie Kommentare zu diesem Thema an Microsoft.
Copyright © 2007 by Microsoft Corporation. Alle Rechte vorbehalten.