Decrittografia di un token di protezione.

Download sample

CardSpace fornisce agli utenti la possibilità di gestire le proprie identità digitali e, con Internet Explorer 7.0, (o altri browser che supportano Schede informazioni) i siti Web possono richiedere all’utente un'identità digitale. Questa identità è trasportata nel modulo di un token di protezione crittografato. La decrittografia del token per utilizzare le attestazioni contenute all'interno del token, può essere eseguita con la classe dell'esempio Token. In questo argomento ne viene illustrato il token e il suo funzionamento.

Installazione del server Web

Per eseguire gli esercizi, è necessario configurare il sito Web. La configurazione viene eseguita utilizzando il file batch di installazione seguente fornito nella cartella di esempio:

Setup.bat

Per ulteriori informazioni sull’installazione del sito Web e sulla risoluzione dei problemi, vedere Installazione dei certificati di esempio di CardSpace.

Ottenimento del token.

Il codice dell’esempio Utilizzo di Windows CardSpace con Internet Explorer 7.0 illustra il codice HTML richiesto per visualizzare il selettore di identità.

File utilizzati:

Sample.htm

Il selettore di identità viene visualizzato utilizzando l'elemento <object> o un oggetto di comportamento binario. In questo esempio vengono utilizzati l'elemento <object> e JavaScript per richiedere il token dall'utente e visualizzarlo in <textarea> prima di inviarlo al sito Web.

File di esempio .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>

Quando l’utente fa clic su Invia, il token viene inserito in un modulo nel server, dove deve essere decrittografato e verificato prima che le attestazioni possano essere utilizzate.

La pagina login.aspx che accetta il token inserito in 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>

La pagina login.aspx che accetta il token inserito in 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>

La classe Token gestisce la decrittografia, la verifica e l’estrazione delle attestazioni del token XML crittografato.

Elaborazione del token: esame del formato di crittografia XML

Il token inserito dal browser viene crittografato utilizzando la crittografia W3C XML. Il documento nel quale è illustrato lo schema è XML Encryption Syntax and Processing. Gli schemi sono Schema principale e Crittografia XML. L'esempio seguente è si riferisce a un tipico token crittografato.

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

Dall'analisi delle parti costitutive dell’XML inserito, si evince che l’elemento radice è <enc:EncryptedData>. Tale elemento contiene i tre requisiti di accesso al token.

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

Il primo requisito è che <enc:EncryptionMethod> contenga il metodo utilizzato per crittografare il token.

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

In questo caso, si tratta di AES-256-cbc, un algoritmo di crittografia di chiavi simmetriche, nel quale entrambe le parti utilizzano la stessa chiave per crittografia e decrittografia. La chiave simmetrica viene generata in modo causale dal creatore ed è denominata chiave temporanea. Viene quindi crittografata e archiviata nell'elemento <e:EncryptedKey>, racchiuso nell'elemento <KeyInfo>.

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

È preferibile crittografare il token con un algoritmo simmetrico e inviare la chiave crittografata con il token, in quanto la crittografia asimmetrica è più lenta della crittografia simmetrica e la crittografia di dati di dimensioni superiori rispetto alla chiave con una chiave asimmetrica non è pertanto consigliabile. La crittografia della sola chiave con l'algoritmo asimmetrico riduce notevolmente la quantità di elaborazione richiesta al server.

La chiave temporanea viene crittografata con la chiave pubblica (del certificato) del componente. Il metodo di crittografia della chiave temporanea è presente nell'elemento <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>

In questo caso, l’algoritmo RSA-OAEP-MGF1P viene utilizzato per la crittografia. La chiave per questa operazione è presente nell'elemento <KeyInfo> seguente.

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

Il <o:KeyIdentifier> consente al mittente di comunicare al componente la chiave del certificato utilizzata per crittografare la chiave simmetrica, inserendo la propria identificazione digitale (con codifica Base64) nell'elemento.

Il componente utilizza quindi la chiave privata per decrittografare i dati nell'elemento <e:CipherData>/<e:CypherValue>.

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

Dopo avere recuperato la chiave simmetrica, il token stesso viene de crittografato utilizzando la chiave simmetrica.

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

Caratteristiche salienti del codice del processore del token

Per comprendere il processo di decrittografia, è opportuno tenere presente le caratteristiche salienti di tale processo di seguito riportate. Per visualizzare il processo completo, consultare il codice sorgente della classe Token.

Il costruttore della classe Token utilizza il metodo decryptToken per eseguire la decrittografia dei dati XML crittografati passati nel costruttore.

private static byte[] decryptToken(string xmlToken)

Per scorrere I dati XML, viene utilizzato un XmlReader.

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

Durante la ricerca degli elementi XML, è permessa una flessibilità molto ridotta, per consentire un’interruzione immediata nel caso di un token non valido (con l’utilizzo di ArgumentException). Per iniziare è necessario trovare l'elemento <EncryptionMethod>, di accesso dell'elemento Algorithm per recuperare il metodo di crittografia del token.

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

Successivamente, è necessario trovare l'elemento <EncryptionMethod> per la chiave temporanea, ottenendo nuovamente Algorithm, archiviato come HashCode per una ricerca più veloce.

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

L’elemento successivo è <KeyIdentifier>, contenente l'identificazione digitale del certificato (necessario per decifrare la chiave simmetrica), che viene estratto dalla stringa Base64.

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

L'elemento <CypherValue> contiene la chiave simmetrica (con codifica Base64), nel rispettivo modulo crittografato.

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

<CypherValue> contenente l’effettivo token crittografato.

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

Assicurarsi che il lettore sia chiuso per liberare le risorse.

reader.Close();

La crittografia del token di protezione è supportata da uno dei due algoritmi simmetrici (AES e Triple DES). Utilizzare l'algoritmo di crittografia URI come ricerca.

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

Per ottenere la chiave simmetrica, eseguire la decrittografia con la chiave privata.

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

Utilizzo dell'algoritmo individuato, decrittografia del token con l'algoritmo simmetrico.

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

Deserializzazione e autenticazione del token

Per utilizzare il token incorporato, decrittografato, .NET Framework 3.0 lo deserializza (tramite il WSSecurityTokenSerializer) e autentica utilizzando SamlSecurityTokenAuthenticator. Attualmente la classe Token supporta token SAML. Nel caso in cui siano necessari altri tipi di token, lo sviluppatore deve fornire un autenticatore per supportarlo. Dopo l’avvenuta convalida da parte dell’autenticatore, la classe Token estrae le attestazioni in un modulo utilizzabile.

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

Utilizzo della classe del token.

Nella tabella seguente vengono descritte le proprietà esposte dalla classe Token che estrae le attestazioni dal token di protezione.

Name Descrizione

IdentityClaims

ClaimSet delle attestazioni di identità del token.

AuthorizationContext

AuthorizationContext generata dal token.

UniqueID

Ottiene l’UniqueID (IdentityClaim) del token.

Per impostazione predefinita, vengono utilizzati PPID e la chiave pubblica dell'autorità emittente e viene generato un hash comune per creare un UniqueID.

Per utilizzare un campo diverso, aggiungere la riga di codice seguente:

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

Sostituire il valore con l’URI per la attestazione univoca.

Claims

Insieme di stringhe di sola lettura delle attestazioni del token. Fornisce supporto per una funzione di accesso delle attestazioni indicizzata.

securityToken.Claims[ClaimsTypes.PPID]

IssuerIdentityClaim

Restituisce l’attestazione di identità dell'autorità emittente (in genere, la chiave pubblica dell'autorità emittente).

Footer image

Invia commenti su questo argomento a Microsoft.

Copyright © 2007 Microsoft Corporation. Tutti i diritti riservati.