Enveloped PKCS #7 Signatures
One of the new cryptography features in the v2.0 framework is the ability to work with PKCS #7 formatted messages. The PKCS features live in the new System.Security.Cryptography.Pkcs namespace in System.Security.dll, and are thin wrappers around the CAPI PKCS #7 implementation. In fact, the actual code was provided to the CLR team by the Windows team.
These classes make it quick and easy to sign some arbitrary data, enveloping it in a p7s signature file. On the other end, you can load up a p7s signature, and verify that the data is still valid, then extract the enveloped data. These operations are centered around the SignedCms class.
Here's how a simple enveloped signature would be created:
public static byte[] Sign(byte[] data, X509Certificate2 certificate)
{
if(data == null)
throw new ArgumentNullException("data");
if(certificate == null)
throw new ArgumentNullException("certificate");
// setup the data to sign
ContentInfo content = new ContentInfo(data);
SignedCms signedCms = new SignedCms(content, false);
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate);
// create the signature
signedCms.ComputeSignature(signer);
return signedCms.Encode();
}
First we wrap the data in a ContentInfo object. Then we create a SignedCms object to create the signature, passing in the content to sign and indicating that we want an enveloped signature by passing false for the detached parameter. The last setup step is to create a CmsSigner with the certificate which has the private key that will be used to create the signature. We'll record this certificate by making note of its serial number and who issued it. After the setup is done, all that's left to do is to compute the signature and encode the p7s data.
On the other end, verifying the signature is even easier:
public static bool Verify(byte[] signature, X509Certificate2 certificate)
{
if(signature == null)
throw new ArgumentNullException("signature");
if(certificate == null)
throw new ArgumentNullException("certificate");
// decode the signature
SignedCms verifyCms = new SignedCms();
verifyCms.Decode(signature);
// verify it
try
{
verifyCms.CheckSignature(new X509Certificate2Collection(certificate), false);
return true;
}
catch(CryptographicException)
{
return false;
}
}
First we create a SignedCms to do the verification. Then the p7s data is decoded, and we check the signature passing in the certificate we expect it to be signed with. By passing false as the last parameter to CheckSignature, we're telling the CLR to also reject a signature which is valid but was created by an invalid X.509 certificate.
CheckSignature works differently from other signature verification code that ships with the runtime in that it will do nothing if the signature is valid, but throw a CryptographicException if the signature is invalid. In order to convert this to a boolean result, we just catch the exception and return false. The lack of an exception means we can return true.
Another operation we might want to do once we've verified an enveloped signature is to extract the data which was signed. The SignedCms class also makes this a simple operation:
public static byte[] ExtractEnvelopedData(byte[] signature)
{
if(signature == null)
throw new ArgumentNullException("signature");
// decode the signature
SignedCms cms = new SignedCms();
cms.Decode(signature);
if(cms.Detached)
throw new InvalidOperationException("Cannot extract enveloped content from a detached signature.");
return cms.ContentInfo.Content;
}
Again, we simply decode the signature, and assuming that it was an enveloped signature return the content from within.
Comments
Anonymous
March 01, 2006
The comment has been removedAnonymous
March 02, 2006
Hi Harris,
If you don't want to check the certificates as well as the signature, then you can just pass true to CheckSignature rather than false as I have in the code sample.
-ShawnAnonymous
March 02, 2006
With the release of a new version of .NET 2.0 in November, 2005, the question can be raised, What new...Anonymous
March 06, 2006
I wonder if there is anything new to allow signing of html pages over the internet. Up to now you could use CAPICOM for that, but is there any way of using this .net library for this?Anonymous
March 06, 2006
So, will it be easy to send a s/mime mail with SMTPmail?Anonymous
May 07, 2006
Is there a way to use a stream instead of a byte array for ContentInfo, I need to sign a file of 80 MB
John,Anonymous
May 08, 2006
Hi John,
There's no methods to work with a stream in the v2.0 implementation, however you may file a bug on the Product Feedback Center and we can consider it for a future release.
-ShawnAnonymous
May 08, 2006
Can I use ComputeHash and store it somehow in the PKCS7 encoding ?
JohnAnonymous
May 15, 2006
Sure -- call ComputeHash to get a byte array, and then pass it to the utility function I showed above.
-ShawnAnonymous
October 24, 2006
How i can signer a document with my private key (file .pfx)? because when i try signer a document the CryptographicException is throw. say:{"Key does not exist.rn"} i believe that problem is ocorred, because i donĀ“t put the private key on sign. Am i sure? How will i do to resolve this problem? I am from Brazil and my english is bad, sorry! Cristiano SilvaAnonymous
October 30, 2009
I got one of these as an attachment to one of my friend's emails using yahoo mail. How do I open it where I can actually see what is inside? I click it and it offers the typical open/save option to my computer. Nothing seems to open it on my computer.. just curious as to what it is....and how to view it in it's true viewable form? Any suggestions?Anonymous
November 12, 2009
Hello. Exists any solution to use SHA256 in PKCS7 message ?Anonymous
December 16, 2009
Hello. Greetings from Peru. I followed your steps to create a p7s signture file. It works perfect. My problem is when I try to validate the signature on a p7s viewer or similar. When I do that, usually I get the Certificate not valid message. Which are the possible reasons for that? Can we trade some messages to try to solve the problem? Christian curibe01@hotmail.comAnonymous
December 22, 2009
Is there a way to use CmsSigner work with SHA256Anonymous
December 22, 2009
The comment has been removedAnonymous
February 06, 2010
I have the same problem with SHA256, i think it might be bug in CAPIBase.. See http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/217776fa-5dcd-41a6-a800-b401509fbbf0