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

Comments

No Comments