Creating X509 Certificates for WSE or WCF
What certificate to use or where to get one of these, is perhaps is one the most common question I read often in the MS newsgroups.
Well, for starters, there are many kinds of X509 certificates around, each one for a different purpose. And to make the matters worse, each vendor offers similar certificates with different names.
Verisign, RSA, Thawte and others usually offer certificates for the following purposes:
Purpose | Description | Data Signing Support | Key Exchange support |
Code Signing | A code signing certificate is usually recommended for any publisher who plans to distribute code or content over the Internet or corporate extranets and needs to assure the integrity and authorship of that code | Yes | No |
E-Mail Security | An e-mail certificate can be used to digitally sign email messages or to encrypt email contents and attachments. | Yes | No |
SSL | A SSL certificate is usually configured in a web site to offer SSL protection (HTTPS) | Yes | No |
The WSE's turn-key assertions always require certificates with data signing and key exchange support. The data signing support is used to generate XML signatures and the key exchange function is used to encrypt data (It actually creates a EncryptedKey, for more information about this, read this blog entry). However, a custom security assertion can be created to use different certificates for data signing and data encryption. I did something like that in one of the projects where I worked, you can take a look to the code in this GDN workspace
On the other hand, WCF allows to specify different certificates for data signing and key interchange by means of the X509 Security Token providers.
Another approach and probably most attractive in many organizations is to create custom X509 certificates using an in-house certificate authority. Microsoft comes with two solutions out of the box to generate certificates, the tool "MakeCert.exe" to create test certificates for development environments and a more robust solution, Microsoft Certificate Server (MCS), to configure a real PKI in the organization.
Creating Certificates with MCS
In a nutshell, Microsoft Certificate Server is a product that allows to mount a real PKI in any organization. This product can run in two modes, Active directory integrated or stand alone. This last one is probably useful for software development organizations or independent consultants working on products that require certificates.
Microsoft also provides a component CertEnroll to automate the process of creating and asking certificates to the certificate authority.
In the following paragraphs I will show how to use this component to create a valid X509 certificate to be used in WSE or WCF.
The creation of a certificate in MCS involves two steps:
1. Send a certificate request to the Certificate Authority
2. Obtain the certificate from the Certificate Authority after the request was authorized
The CertEnroll COM contains methods to perform both steps, still some lines of code are required. The code below illustrates how to request and obtain a certificate from a CA (I wrote the code in vbscript).
Option Explicit const CERT_SYSTEM_STORE_LOCAL_MACHINE = &H20000 const CRYPT_EXPORTABLE = 1 Const AT_KEYEXCHANGE = 1 Const AT_SIGNATURE = 2 Const CRYPT_ARCHIVABLE = &H000004000 Const RSA1024BIT_KEY = &H04000000 Const XECR_PKCS10_V2_0 = 1 Const XECR_PKCS7 = 2 Const XECR_CMC = 3 Const XECR_PKCS10_V1_5 = 4 Const CR_OUT_BASE64HEADER = 0 Const CR_OUT_BASE64 = 1 Const CR_OUT_BINARY = 2 Const CR_OUT_CHAIN = &h100 Const CR_IN_BASE64HEADER = 0 Const CR_IN_BASE64 = &H1 Const CR_IN_PKCS10 = &H100 Const CR_IN_KEYGEN = &H200 Const CR_IN_ENCODEANY = &Hff Const CR_IN_FORMATANY = 0 Const CR_DISP_INCOMPLETE = 0 Const CR_DISP_ERROR = 1 Const CR_DISP_DENIED = 2 Const CR_DISP_ISSUED = 3 Const CR_DISP_ISSUED_OUT_OF_BAND = 4 Const CR_DISP_UNDER_SUBMISSION = 5 Const CR_DISP_REVOKED = 6 Const PROPTYPE_LONG = 1 Const PROPTYPE_DATE = 2 Const PROPTYPE_BINARY = 3 Const PROPTYPE_STRING = 4 Const CR_GEMT_HRESULT_STRING = 1 Const FR_PROP_NONE = 0 ' Invalid Const FR_PROP_FULLRESPONSE = 1 ' Binary Const FR_PROP_STATUSINFOCOUNT = 2 ' Long Const FR_PROP_BODYPARTSTRING = 3 ' String, Indexed Const FR_PROP_STATUS = 4 ' Long, Indexed Const FR_PROP_STATUSSTRING = 5 ' String, Indexed Const FR_PROP_OTHERINFOCHOICE = 6 ' Long, Indexed Const FR_PROP_FAILINFO = 7 ' Long, Indexed Const FR_PROP_PENDINFOTOKEN = 8 ' Binary, Indexed Const FR_PROP_PENDINFOTIME = 9 ' Date, Indexed Const FR_PROP_ISSUEDCERTIFICATEHASH =10 ' Binary, Indexed Const FR_PROP_ISSUEDCERTIFICATE =11 ' Binary, Indexed Const FR_PROP_ISSUEDCERTIFICATECHAIN =12 ' Binary, Indexed Const FR_PROP_ISSUEDCERTIFICATECRLCHAIN=13 ' Binary, Indexed Const FR_PROP_ENCRYPTEDKEYHASH =14 ' Binary, Indexed Const FR_PROP_FULLRESPONSENOPKCS7 =15 ' Binary Const KeyLength = 1024 Dim CertEnroll Set CertEnroll = CreateObject( "CEnroll.CEnroll" ) Dim RequestStr, CertRequest, Disposition, ID CertEnroll.ProviderName = "Microsoft Enhanced Cryptographic Provider v1.0" CertEnroll.KeySpec = AT_KEYEXCHANGE CertEnroll.GenKeyFlags = CRYPT_EXPORTABLE RequestStr = CertEnroll.createRequest( XECR_CMC, "CN=John Smith", "1.3.6.1.5.5.7.3.2" ) Set CertRequest = CreateObject( "CertificateAuthority.Request" ) Disposition = CertRequest.Submit( _ CR_IN_ENCODEANY Or CR_IN_FORMATANY, _ RequestStr, _ "", _ "Localhost\MyCertAuth" ) ID = CertRequest.GetRequestId MsgBox ID |
In a few words, the code above submits a certificate request to a MCS service running on "Localhost\MyCertAuth". The certificate distinguish name will be "CN=Jonh Smith" and the long number "1.3.6.1.5.5.7.3.2" only describes the purpose of the certificate. (MCS provides a set of templates, each one with a different number identifier).
It is important to store the RequestId in some place since it will be necessary later to retrieve the certificate from the CA (Once the request was authorized by one of the CA administrators).
Option Explicit const CERT_SYSTEM_STORE_LOCAL_MACHINE = &H20000 const CRYPT_EXPORTABLE = 1 Const AT_KEYEXCHANGE = 1 Const AT_SIGNATURE = 2 Const CRYPT_ARCHIVABLE = &H000004000 Const RSA1024BIT_KEY = &H04000000 Const XECR_PKCS10_V2_0 = 1 Const XECR_PKCS7 = 2 Const XECR_CMC = 3 Const XECR_PKCS10_V1_5 = 4 Const CR_OUT_BASE64HEADER = 0 Const CR_OUT_BASE64 = 1 Const CR_OUT_BINARY = 2 Const CR_OUT_CHAIN = &h100 Const CR_IN_BASE64HEADER = 0 Const CR_IN_BASE64 = &H1 Const CR_IN_PKCS10 = &H100 Const CR_IN_KEYGEN = &H200 Const CR_IN_ENCODEANY = &Hff Const CR_IN_FORMATANY = 0 Const CR_DISP_INCOMPLETE = 0 Const CR_DISP_ERROR = 1 Const CR_DISP_DENIED = 2 Const CR_DISP_ISSUED = 3 Const CR_DISP_ISSUED_OUT_OF_BAND = 4 Const CR_DISP_UNDER_SUBMISSION = 5 Const CR_DISP_REVOKED = 6 Const PROPTYPE_LONG = 1 Const PROPTYPE_DATE = 2 Const PROPTYPE_BINARY = 3 Const PROPTYPE_STRING = 4 Const CR_GEMT_HRESULT_STRING = 1 Const FR_PROP_NONE = 0 ' Invalid Const FR_PROP_FULLRESPONSE = 1 ' Binary Const FR_PROP_STATUSINFOCOUNT = 2 ' Long Const FR_PROP_BODYPARTSTRING = 3 ' String, Indexed Const FR_PROP_STATUS = 4 ' Long, Indexed Const FR_PROP_STATUSSTRING = 5 ' String, Indexed Const FR_PROP_OTHERINFOCHOICE = 6 ' Long, Indexed Const FR_PROP_FAILINFO = 7 ' Long, Indexed Const FR_PROP_PENDINFOTOKEN = 8 ' Binary, Indexed Const FR_PROP_PENDINFOTIME = 9 ' Date, Indexed Const FR_PROP_ISSUEDCERTIFICATEHASH =10 ' Binary, Indexed Const FR_PROP_ISSUEDCERTIFICATE =11 ' Binary, Indexed Const FR_PROP_ISSUEDCERTIFICATECHAIN =12 ' Binary, Indexed Const FR_PROP_ISSUEDCERTIFICATECRLCHAIN=13 ' Binary, Indexed Const FR_PROP_ENCRYPTEDKEYHASH =14 ' Binary, Indexed Const FR_PROP_FULLRESPONSENOPKCS7 =15 ' Binary Const KeyLength = 1024 Dim CertEnroll, CertRequest Dim Disposition Set CertEnroll = CreateObject( "CEnroll.CEnroll" ) Set CertRequest = CreateObject( "CertificateAuthority.Request" ) Disposition = CertRequest.GetIssuedCertificate("localhost\MyCertAuth", 24, "") If Disposition = CR_DISP_ISSUED Then Dim Cert Cert = CertRequest.GetFullResponseProperty( _ FR_PROP_FULLRESPONSE, _ 0, _ PROPTYPE_BINARY, _ CR_OUT_BASE64 ) Dim sCert sCert = CertEnroll.getCertFromResponse(Cert) CertEnroll.createFilePFX "password", "c:\cert.pfx" Else WScript.echo "Disposition = " + cstr( Disposition ) If CertRequest.GetLastStatus <> 0 Then WScript.echo "Error : " + cstr( CertRequest.GetErrorMessageText( _ CertRequest.GetLastStatus, CR_GEMT_HRESULT_STRING )) End If End If |
The code above retrieves the certificate for the request id 24 (It is only an example, the real id should be used there) and stores it on the file "c:\cert.pfx" (By the way, this is not a good practice since the certificate can be copied).
Creating Certificates with MakeCert
MakeCert is a tool provided by Microsoft to create test certificates that can be used during the development of a product (For developing and testing purposes only). These certificates have also performance problems, certain cryptographic operations may perform slowly when they are used. Certificates issued from a true Certificate Authority do not have this problem, and it is a know issue.
In order to create a certificate with this tool, the following arguments must be considered:
- sr: Store Location, it can be LocalMachine or CurrentUser
- ss: Store Folder, it can take different values but these are probably the most common, My (Personal) or Trusted (Trusted Folder).
- n: Certificate Distinguished name. It is very import to chose a right name for the certificate since it will identify it. (This name is also used to look for the certificate)
For example,
makecert.exe -sr LocalMachine -ss My -a sha1 -n CN=MyServerCert -sky exchange -pe
This tool only generates and stores the certificate in the certificate store, but it does not assign any permission on that certificate. If you planning to use the certificate from WSE or WSE, you may need to give read permission to the account running the service, for instance the account ASPNET for a normal web service.
Microsoft provides another tool to grant permissions on certificates, the name of this tool is winhttpcertcfg.
The following sample, grant permission on the certificate created above to the ASPNET account:
winhttpcertcfg -g -c LOCAL_MACHINE\My -s MyServerCert -a ASPNET