2011年7月10日 星期日

Beginning with Digital Signatures in .NET Framework


This article explains how to get started with digital signatures, using X509 certificates in .NET.

The purpose of digital signatures is to identify data in a way that cannot easily be faked. Phishing, infected software and illegal contents published by unknown subjects can be prevented with digital signatures. Digital signatures will allow data and digital documents to be used as if they were signed paper. Browsers are now able to recognize X.509 certificates and know which Certificate Authorities are trusted. The X.509 system has grown to be the standard format for public key certificates, and is therefore the best way of proving that a document comes from the source it claims to come from.

This article will introduce X509 certificates, explain a little about the asymmetric cryptography that is at their heart, and end by describing how to use and manage these certificates within the .NET Framework classes.

Asymmetric Cryptography and Digital Signatures

Digital signatures are created using asymmetric cryptography, the approach on which digital signatures are based. Asymmetric Cryptography is distinguished by having two different keys, a private key to encrypt messages and a public key to decrypt them. The cryptographic private key K0 (a suitable array of bytes) is used with an appropriate algorithm to transform the initial human-readable message into a different message that is encrypted.

A second public cryptographic key K1, which is related to the private one, is used to change the encrypted message back to its original decrypted form via a second related algorithm.

With this mechanism, your recipient is sure that the message that she/he received is your message, because only you hold the private key that is related to the public, shared, key. You digitally ‘sign’ your message.

In practice, you will hash the message beforehand (with hash algorithm such as MD5 or SHA1), obtaining the hashed message M1. Then you will encrypt M1 with your private key K0, digitally signing your message, and, finally, you will send your message M, the encrypted hash M1 (the signature) and the public key K1 to your recipient. Your recipient will compute the hash of your message M and will compare it with the decrypted value of M1. If the two hashes matches, the signature is valid.

You will notice that the signature is obtained by encrypting the hash of a message, rather than the message itself. This is done for performance reasons. Asymmetric cryptography is a slow process and the time required to encrypt, or decrypt, a message is directly related to the message length. You can make better use of the processor by reducing the amount of data to be processed. Sometimes, a very large (in bytes) message, can be reduced, by hashing it, to a much smaller hashed message. It is more convenient to transmit a the bulk of the data as clear text and just attach less than a hundred encrypted bytes attached to it than to encrypt the entire message and send it in the encrypted form.

Asymmetric key encryption by itself is not enough because it is necessary to trust the public key received. An attacker can deceive you by signing a message with his private key and send you a digitally confirmed message with its (related) public key, whilst pretending he is someone else.

The public-key infrastructure (PKI) avoids this by utilizing a third-party entity, called Certification Authority that, under its responsibility, binds a public key to its owner. The binding occurs when the Certification Authority digitally sign a message that contains the public key and the identity of its owner. A digital certificate is obtained.

The X509 Standard for Digital Certificate

Today, the standard that has been adopted for digital certificate format is the X509 standard. Over the years since X.509 was developed in 1988, the initial X509 certificate format has evolved beyond the simple purpose of associating the identity of the subject with the public key, since it allows extended information to be held. The current certificate format is X509 v3 format, defined on RFC 5280. The most important information fields that it holds and stores is

  • Issuer: The identity of the certification authority that signed the certificate, validating it. It is expressed in ITU-T X.501 Distinguished Name format.
  • Validity: it defines the dates between which the certificate can be used.
  • Subject: it defines the identity of the public key owner. It is expressed as ITU-T X.501 Distinguished Name format.
  • SubjectPublicKeyInfo: it contains an encoded version of the public key.
  • X509 v3 extensions: it defines a set of extensions that establishes the purpose of the certificate and set some properties related to its management. Each extension can be critical (e.g. cannot be ignored) or not.

    Among the X509 v3 extensions, the two most important are

    • Key Usage: it defines the usage allowed for the private key, for example digitalSignature (allow the digital signature), keyCertSign (allow the signature of certificates by a certification authority), keyAgreement (allow the key exchange on protocol such as TLS/SSL).
    • Extended Key Usage: it defines extended properties in relation to the usage of the private key. For example Server Authentication (for TLS/SSL certificate), Code Signing (for authenticode signature), Email Protection (for protocol such as S/MIME).

You can view the certificates installed on a Windows® machine by using certmgr.msc

In the details tab, you can see the binding between the subject identity (Subject field) and the Public Key (Public Key field). This is highlighted to show its content. It consists of a sequence of bytes in hexadecimal form.

An important feature of the X509 standard is the assignment of a unique identifier to each entity inside a X509 certificate which is in the form of a sequence of numbers organized hierarchically; For example, the sequence 1.3.6.1.5.5.7.3.1 identifies the Server Authentication extended-key usage. Those sequences, called OIDs (object identifiers) are assigned by the authorized organization (IANA, ISO and ITU-T).

The most commonly used Certificate file format today is the PKCS#12 (personal information exchange standard) format. This standard permits you to prepare a X509 certificate file that may contain the private key as well, encrypted with a secret password. This is easiest imagined as a bag that contains the X509 certificate and the encrypted private key.

Manage X509 Certificates Stores with .NET Framework

The .NET classes involved on the X509 certificate management are those under the System.Security.Cryptography.X509Certificates namespace.

Before it can be used, a digital certificate must be located and loaded. X509 certificates are stored on Microsoft® Windows machines in a container that can be browsed with certmgr.msc command. Launching the command, you will see something like this:

This shows the so called “current user” certificate container, the container associated with the user currently logged on the machine. The “Personal” subfolder contains one certificate with a ‘friendly name’ given by webmail.

X509 certificates can be stored on a ‘per machine’ basis too. To open the certificate container for a local or remote machine, run mmc.exe and add the certificates snap-in, selecting the computer you want to administer.

Within the .NET Framework base classes, a subfolder is called “store”. A certificates store can be opened utilizing the X509Store class:

X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);

store.Open(OpenFlags.OpenExistingOnly);

In this example, the “current user” container is opened and the certificates that reside on the “Personal” store are loaded.

To open a store on the “local machine” container, you must change the StoreLocation enumeration value with the value:

StoreLocation.LocalMachine

The store that you wish to open is set with the StoreName enumeration. Others enumeration values allow you to select other stores inside the container. These are stores related to the X509 certificates that are issued to other entities involved in the PKI system. Those are beyond the scope of this article.

Notice now the Open method. This accepts, as an input parameter, an OpenFlags enumeration value given by OpenExistingOnly. This states that the Open method can only open an existing store inside the container. It might seem to imply to you that stores can be created too. And you’d be right. Using the appropriate override of theX509Store class constructor, a new store can be created:

X509Store store = new X509Store(“MyStore”, StoreLocation.CurrentUser);

store.Open(OpenFlags.ReadWrite);

In this example, a new store named “MyStore” is created and opened for read write operations.

Notice that, if a new store is created, the .NET Framework doesn’t allow you to remove it anymore. There is no method that permits a store deletion. It can be deleted only with another tool such as CAPICOM or by using Microsoft® CryptoAPI directly.

It’s time now to work with certificates on the stores. To do so, the X509Store class provides a Certificates property of type X509Certificate2Collection:

X509Certificate2Collection certificates = store.Certificates;

The certificates object now contains all the certificates stored on the opened store. A certificate can be retrieved from the collection with the following code:

X509Certificate2 certificate = certificates[n];

with n being the selected index.

A certificate can also be loaded from a certificate file:

X509Certificate2 myCertificate = new X509Certificate2(“c:\....\mycertificate.pfx”);

and then saved into the opened store:

store.Add(myCertificate);

Finally, a certificate can be removed from a store with the method:

store.Remove(certificates[n]);

Manage X509 Certificates with .NET Framework

The X509Certificate2 class allows you to manage the “certificate data units” programmatically. First of all, notice the suffix 2 on the class name. This happens because the X509Certificate2 class extends methods and properties of its base class, the X509Certificate class. It extends the X509Certificate class by allowing, above all, the management of the private key (if present, as seen for the PKCS#12 certificate format), and the browsing of the X509 v3 extensions.

The X509Certificate2 class allows you to retrieve, from the certificate that is loaded, its representative data through a set of properties. Among them:

Name

Type

Description

Issuer

string

Identify the issuer of the certificate (the certification authority that signed the certificate).

Subject

string

Identify the subject, owner of the private key.

NotBefore

DateTime

It states that the certificate can be used only after the NotBefore data.

NotAfter

DateTime

It states that the certificate can be used until the NotAfter data.

PublicKey

PublicKey

Represents the Public Key associated to the subject.

Extensions

X509ExtensionCollection

Collection of X509 v3 extensions.

In those, the public key is retrieved as a PublicKey object. This object contains all the information related to the public key itself, but this information are not easy to explain in this article because the reader would require a knowledge of asymmetric cryptographic algorithms, ASN.1 encoding rules and OID assignment.

You can however get a string representation of the public hey by utilizing a X509Certificate class method, from which X509CertificateClass2 derives:

string publicKey = certificate.GetPublicKeyString();

The publicKey string now contains the same string representation of the public key seen on the first image proposed on this article (without white spaces).

The X509 v3 Extensions can be analyzed through browsing on the collection elements. Those are of type X509Extension. This type defines the OID assigned to the related extension and its ASN.1 encoded value.

Performing Signatures with the .NET Framework

To perform a signature using an X509 certificate and .NET Framework base classes, the X509 certificate must have the private key too. In fact, as stated previously, a signature consists of an encryption with the private key (that must be present) of hashes computed on messages to sign. If an object of type X509Certificate2 has the private key (due to the fact that the PKCS#12 file imported on the store has the private key). This can be retrieved with the following code:

AsymmetricAlgorithm privateKey = certificate.PrivateKey;

You'll have noticed the type of PrivateKey property. It is of type AsymmetricAlgorithm, a class that can be found under the System.Security.Cryptography namespace. Is seems to have nothing to do with private keys. But remember that the private key is an element, inside the PKI elements, that require an high level of protection. When importing a PKCS#12 file into your X509 store, the private key became a more complex “object” inside the store, in the sense that it is saved “keeping in mind the purpose of the same and its protection requirements”. It need an unique identifier to search for it, it need protection against attacker, it need access control, it need a suitable, but flexible, storage. Without entering more deeply on the argument, think of this object as a sort of “pipeline” between your code and the cryptographic subsystem of your operating system that manages, in a secure way, all the cryptographic operation based on your key pairs.

In reality, the AsymmetricAlgorithm class is only a base class of more complex classes. It is the base class for all the classes that implement specific asymmetric algorithms standards. Today, a widely-adopted standard for digital signature is RSA asymmetric encryption with SHA-1 hash algorithm. If the certificate contains RSA asymmetric keys pairs, the previous method does not, as one might expect, return an AsymmetricAlgorithm object, but an RSACryptoServiceProvider object, whose class derives from the AsymmetricAlgorithm class. The RSACryptoServiceProvider class contains all the properties and methods that are related to what we said previously.

So, the next step is to cast the privateKey object to a RSACryptoServiceProvider object, or, in more elegant way:

RSACryptoServiceProvider privateKey

= certificate.PrivateKey as RSACryptoServiceProvider;

Now a signature can be performed. To do so, the SignData method of the privateKey object can be used. It accepts, as input, (1) the data to sign, as array of bytes, and (2) the object that represents the hash algorithm to use:

byte[] buffer = Encoding.Default.GetBytes("Hello World ... !");

byte[] signature = privateKey.SignData(buffer, new SHA1Managed());

The signature can also be verified. To do so you must utilize the public key of the certificate.

RSACryptoServiceProvider publicKey

= certificate.PublicKey.Key as RSACryptoServiceProvider;

bool verify = publicKey.VerifyData

(buffer, new SHA1Managed(), signature);

From the above example you see that the certificate.PublicKey.Key property is again an object of type AsymmetricAlgortihm. This is for the same reasons seen for the private key.

The certificate to use for the verification can be the same certificate used for the generation of the signature but even a version of it that contains only the public key. No private key is required. And this is what always happens. Remember that the recipient receive only the signed message plus the certificate without the private key, that remain secret and accessible only by its owner. This means that verifications occurs always with the utilization of the public key “extracted” from a certificate that doesn’t have private key.

Another way to perform signature is to use the class RSAPKCS1SignatureFormatter. It brings to the same result of the previous method. To do so, the hash of data to sign must be computed first:

byte[] buffer = Encoding.Default.GetBytes("Hello World ... !");

byte[] hash = SHA1Managed.Create().ComputeHash(buffer);

RSAPKCS1SignatureFormatter formatter

= new RSAPKCS1SignatureFormatter(certificate.PrivateKey);

formatter.SetHashAlgorithm("SHA1");

signature = formatter.CreateSignature(hash);

To verify the signature, use the RSAPKCS1SignatureDeformatter class:

RSAPKCS1SignatureDeformatter deformatter

= new RSAPKCS1SignatureFormatter(certificate.PublicKey.Key);

deformatter.SetHashAlgorithm("SHA1");

bool verify = formatter.VerifySignature(hash,signature);

Conclusion

This article gave an outline of what a digital signature is, and how to digitally sign data with the .NET Framework. Digital signatures, and cryptographic services in general, are very complex subjects and are not easy to summarize. If you feel we haven't really helped to make it clearer for you, please say so by leaving a comment to this article and even ask the author for other articles on the subject, indicating what you would like to read more about.

If you would like to experiment digital signatures with X509 certificate, probably you need some X509 certificate for testing. We end this paper by providing you some way to gets digital certificates:

  • You can download the openssl projects files at The OpenSSL Project and set up a smart certification authority.

  • You can use makecert.exe command (see Certificate Creation Tool (Makecert.exe) ) that you can find on Microsoft® Windows SDK.

  • You can generate X509 certificate on-line using X509 Builder web application at the author’s site wecoffee.

source: http://www.simple-talk.com/content/print.aspx?article=833

沒有留言: