Hi folks!
A time ago I wrote a high-level description about the signatures in Digital signatures blog post. And today I want to demonstrate how this works in a real world.
In a real world there are too many signature types, including RSA signatures (plain), Authenticode, XML, Document-specific (MS Word, Adobe PDF, etc.). The simplest signature type is plain RSA signatures. This type of signatures is widely used in PKI (certificates, CRLs, signed BLOBs and so on). In ASN.1 modules (as well as in unmanaged structures), signed BLOB is written like this:
Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING } AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL }
tbsCertificate contains an arbitrary data to be signed (look at the prefix, tbs means ToBeSigned). This data is always wrapped to a SEQUENCE (SEQUENCE models an ordered collection of variables of different type) which acts as a generic container in ASN.1. The next field is signatureAlgorithm of AlgorithmIdentifier complex type and signatureValue field of primitive BIT STRING type.
AlgorithmIdentifier is exposed in next structure and contains 2 properties:
if you look into actual object you will see the following:
Here we see:
Here we have almost all required information to verify the signature. The only thing is missing — issuer certificate. Since issuer information is retrieved by using external means (through certificate chaining engine), it's certificate is not included in the certificate content. If issuer information is not retrieved by using external means, it is added explicitly. Such example is OCSP response:
Generally signed content retains the same almost in all cases and CryptoAPI has appropriate structure CERT_SIGNED_CONTENT_INFO:
typedef struct _CERT_SIGNED_CONTENT_INFO { CRYPT_DER_BLOB ToBeSigned; CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm; CRYPT_BIT_BLOB Signature; } CERT_SIGNED_CONTENT_INFO, *PCERT_SIGNED_CONTENT_INFO;
In .NET signature creation and verification stuff is implemented in RSACryptoServiceProvider class. As we remember, signature is verified against issuer's public key. Let's do some manual PowerShell stuff:
[↓] [vPodans] # get certificate objects. [↓] [vPodans] $digicert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 .\Desktop\digicert.c er [↓] [vPodans] $issuer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 .\Desktop\digicert_iss uer.cer [↓] [vPodans] # get tbsCertificate BLOB which starts at offset 4 and ends at 1407 (-1 to algorithmIdentifier) [↓] [vPodans] $tbsCertificate = $digicert.RawData[4..1407] [↓] [vPodans] $tbsCertificate.Length 1404 [↓] [vPodans] # get signature value (minus tag byte and tag length bytes): [↓] [vPodans] $signatureValue = $digicert.RawData[1428..$digicert.RawData.Length] [↓] [vPodans] $signatureValue.Length 256 [↓] [vPodans] # retrieve RSACryptoServiceProvider object of issuer' public key [↓] [vPodans] $pubkey = $issuer.PublicKey.Key [↓] [vPodans] $pubkey PublicOnly : True CspKeyContainerInfo : System.Security.Cryptography.CspKeyContainerInfo KeySize : 2048 KeyExchangeAlgorithm : RSA-PKCS1-KeyEx SignatureAlgorithm : http://www.w3.org/2000/09/xmldsig#rsa-sha1 PersistKeyInCsp : False LegalKeySizes : {System.Security.Cryptography.KeySizes} [↓] [vPodans] $pubkey.GetType().FullName System.Security.Cryptography.RSACryptoServiceProvider [↓] [vPodans]
We will use RSACryptoServiceProvider.VerifyData() method to verify it:
[↓] [vPodans] $pubkey.VerifyData($tbsCertificate,"sha1",$signatureValue) True [↓] [vPodans]
And here we go! If we take different certificate (which is not an issuer of the subject's certificate):
[↓] [vPodans] $issuer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 .\Desktop\pica-1.cer [↓] [vPodans] $pubkey = $issuer.PublicKey.Key [↓] [vPodans] $pubkey PublicOnly : True CspKeyContainerInfo : System.Security.Cryptography.CspKeyContainerInfo KeySize : 2048 KeyExchangeAlgorithm : RSA-PKCS1-KeyEx SignatureAlgorithm : http://www.w3.org/2000/09/xmldsig#rsa-sha1 PersistKeyInCsp : False LegalKeySizes : {System.Security.Cryptography.KeySizes} [↓] [vPodans] $pubkey.VerifyData($tbsCertificate,"sha1",$signatureValue) False [↓] [vPodans]
And signature verification fails.
Though, this manual stuff is just an example. In the real world it is more efficient to deal with unmanaged structures, rather than each part manual parsing. Because you can take entire signed BLOB, convert it to unmanaged structure and marshal back to managed parts. I'll demonstrate unmanaged stuff in next post.
Post your comment:
Comments: