Old Miraheze Wiki Pages:RepostedBlogArticle 3103202401: Difference between revisions
THIS>Liaochina Create the page to be edited later with the visual editor. |
THIS>Liaochina Pasted the blog post. Original URL: https://cipherious.wordpress.com/2013/05/13/constructing-an-x-509-certificate-using-asn-1/ |
||
Line 1: | Line 1: | ||
{{DISPLAYTITLE:Constructing an X.509 Certificate Using ASN.1}} | |||
Digital certificates (also called X.509 certificates) are defined using ASN.1 and encoded using Distinguished Encoding Rules (DER). A number of cryptography libraries (Bouncy Castle, NSS etc.) provide high-level APIs which can be used to create digital certificates. Behind the scenes, however, they are using ASN.1 structures. To gain a deeper understanding of X.509 certificates, I think it’s instructive to look at how to create them using ASN.1 by following the construct presented in <nowiki>RFC 5280</nowiki> – Internet X.509 Public Key Infrastructure and Certificate Revocation List (CRL) Profile. | |||
<nowiki>RFC 5280</nowiki> describes the content of X.509 certificates and CRL. We are only going to consider X.509v3 certificates (refer to <nowiki>RFC 5280</nowiki> for differences between X.509 certificate versions). | |||
An X.509v3 certificate is defined as: | |||
Certificate ::= SEQUENCE { | |||
tbsCertificate TBSCertificate, | |||
signatureAlgorithm AlgorithmIdentifier, | |||
signatureValue BIT STRING } | |||
I am not going to explain ASN.1 syntax in detail, but the syntax above defines a Certificate, a SEQUENCE of tbsCertificate, signatureAlgorithm and a signatureValue, where tbsCertificate and signatureAlgorithm are complex structures (to be defined later) and signatureValue is a BIT STRING primitive structure. | |||
Let’s construct each structure piece-by-piece. We start with TBSCertificate, where “TBS” stands for “To Be Signed”. The signature will be computed over this data structure and placed in the signatureValue field and . | |||
The TBSCertificate structure is defined as: | |||
TBSCertificate ::= SEQUENCE { | |||
version [0] EXPLICIT Version DEFAULT v1, | |||
serialNumber CertificateSerialNumber, | |||
signature AlgorithmIdentifier, | |||
issuer Name, | |||
validity Validity, | |||
subject Name, | |||
subjectPublicKeyInfo SubjectPublicKeyInfo, | |||
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, | |||
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, | |||
extensions [3] EXPLICIT Extensions OPTIONAL } | |||
As you can see TBSCertificate is composed of several complex structures. | |||
To create the ASN.1 structures I will use Java and Bouncy Castle 1.48 (BC 1.48) library. The certificate will be self-signed and will contain a number of extensions. I don’t construct all the extensions defined in <nowiki>RFC 5280</nowiki>. Once you get the hang of constructing complex ASN.1 structures, creating the extensions I left out shouldn’t be too difficult. | |||
The first item in TBSCertificate is Version. As I mentioned previously, we focus solely on X.509 v3 certificates. Version is “tagged” (tags helps in resolving ambiguities during decoding ASN.1) with “0” and is also marked EXPLICIT. We use DERTaggedObject, mark the first argument true which sets it as EXPLICIT and then assign it the tag 0. The third argument is encoded as DERInteger and is assigned the value 2, which indicates that this is an X.509v3 certificate. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
|<code>//Version ::= INTEGER { v1(0), v2(1), v3(2) }</code> | |||
<code>DERTaggedObject Version = new</code> <code>DERTaggedObject(true, 0, new</code> <code>DERInteger(2));</code> | |||
|} | |||
The next item in TBSCertificate is CertificateSerialNumber. This is the serial number of the certificate. Each Certificate Authority (CA) is supposed to assign an unique serial number to every certificate it issues. The certificate serial number should be positive. | |||
Advertisement | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
|<code>//CertificateSerialNumber ::= INTEGER</code> | |||
<code>DERInteger CertificateSerialNumber = new</code> <code>DERInteger(BigInteger.valueOf(Math.abs(new</code> <code>Random().nextInt())));</code> | |||
|} | |||
The next item in TBSCertificate is AlgorithmIdentifier. This value indicates the algorithm used by the CA to sign the certificate. A signature involves both a hashing algorithm (SHA-1) and an asymmetric encryption algorithm (RSA). AlgorithmIdentifier is defined as an ASN.1 SEQUENCE. ASN.1 structures are composed of either simple types (INTEGER, BOOLEAN, OCTET STRING etc.) or structured types (SEQUENCE, SET, SEQUENCE OF, SET OF etc.) or a combination of both. The first object in AlgorithmIdentifier, algorithm, is an Object Identifier (OID) which is assigned the identifier for “SHA-1 With RSA Encryption” and the second object, parameters, is set to NULL (for this particular algorithm). In our code, the two ASN.1 objects are created separately and then wrapped in a ASN.1 SEQUENCE. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
|<code>/*AlgorithmIdentifier ::= SEQUENCE {</code> | |||
<code> algorithm OBJECT IDENTIFIER,</code> | |||
<code> parameters ANY DEFINED BY algorithm OPTIONAL }*/</code> | |||
<code>ASN1EncodableVector SignatureAlgorithmIdentifier_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>SignatureAlgorithmIdentifier_ASN.add(new</code> <code>DERObjectIdentifier("1.2.840.113549.1.1.5"));</code> | |||
<code>SignatureAlgorithmIdentifier_ASN.add(DERNull.INSTANCE);</code> | |||
<code>DERSequence SignatureAlgorithm = new</code> <code>DERSequence(SignatureAlgorithmIdentifier_ASN);</code> | |||
|} | |||
Next, we consider the issuer name. This is typically the distinguished name (DN) of the CA. Since this is a self-signed certificate it will be the same as the subject name (which is defined later). | |||
The Name structure is more involved than the ones we have seen previously. Let’s break it down. As seen below, the Name structure is a RDNSequence structure. The RDNSequence, as the name indicates, is a SEQUENCE of RelativeDistinguishedNames. The RelativeDistinguishedNames structure is a SET of AttributeTypeAndValue, which in turn is a SEQUENCE composed of an AttributeType and AttributeValue. The AttributeType is an OID, while the AttributeValue is a DirectoryString (specifically, a CHOICE between TeletexString, PrintableString, UniversalString, UTF8String and BMPString). We choose to encode each component of the DN as a PrintableString. Ultimately, the DN will look like CN=SecureCA,OU=PKI,O=Cyberdyne,C=US. If you are confused by the syntax of the issuer name, look up Lightweight Directory Access Protocol (LDAP) naming conventions. | |||
Don’t worry if this is confusing. It takes a while to sink in. When writing the code it’s easier to construct the innermost structure first (AttributeTypeAndValue) and build it up to the outermost structure (Name). Remember that Name is a SEQUENCE, so ultimately it will be an ASN.1 SEQUENCE. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
17 | |||
18 | |||
19 | |||
20 | |||
21 | |||
22 | |||
23 | |||
24 | |||
25 | |||
26 | |||
27 | |||
28 | |||
29 | |||
30 | |||
31 | |||
32 | |||
33 | |||
34 | |||
35 | |||
36 | |||
37 | |||
38 | |||
39 | |||
40 | |||
41 | |||
42 | |||
43 | |||
|<code>/*Name ::= CHOICE { rdnSequence RDNSequence }</code> | |||
<code>RDNSequence ::= SEQUENCE OF RelativeDistinguishedName</code> | |||
<code>RelativeDistinguishedName ::=</code> | |||
<code> SET SIZE (1..MAX) OF AttributeTypeAndValue*/</code> | |||
<code>/*AttributeTypeAndValue ::= SEQUENCE {</code> | |||
<code> type AttributeType,</code> | |||
<code> value AttributeValue }*/</code> | |||
<code>ASN1EncodableVector countryNameATV_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>countryNameATV_ASN.add(new</code> <code>DERObjectIdentifier("2.5.4.6"));</code> | |||
<code>countryNameATV_ASN.add(new</code> <code>DERPrintableString("US"));</code> | |||
<code>DERSequence countryNameATV = new</code> <code>DERSequence(countryNameATV_ASN);</code> | |||
<code>ASN1EncodableVector organizationNameATV_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>organizationNameATV_ASN.add(new</code> <code>DERObjectIdentifier("2.5.4.10"));</code> | |||
<code>organizationNameATV_ASN.add(new</code> <code>DERPrintableString("Cyberdyne"));</code> | |||
<code>DERSequence organizationNameATV = new</code> <code>DERSequence(organizationNameATV_ASN);</code> | |||
<code>ASN1EncodableVector organizationalUnitNameATV_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>organizationalUnitNameATV_ASN.add(new</code> <code>DERObjectIdentifier("2.5.4.11"));</code> | |||
<code>organizationalUnitNameATV_ASN.add(new</code> <code>DERPrintableString("PKI"));</code> | |||
<code>DERSequence organizationalUnitNameATV = new</code> <code>DERSequence(organizationalUnitNameATV_ASN);</code> | |||
<code>ASN1EncodableVector issuerCommonNameATV_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>issuerCommonNameATV_ASN.add(new</code> <code>DERObjectIdentifier("2.5.4.3"));</code> | |||
<code>issuerCommonNameATV_ASN.add(new</code> <code>DERPrintableString("SecureCA"));</code> | |||
<code>DERSequence issuerCommonNameATV = new</code> <code>DERSequence(issuerCommonNameATV_ASN);</code> | |||
<code>//RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue</code> | |||
<code>DERSet countryName = new</code> <code>DERSet(countryNameATV);</code> | |||
<code>DERSet organizationName = new</code> <code>DERSet(organizationNameATV);</code> | |||
<code>DERSet organizationalUnitName = new</code> <code>DERSet(organizationalUnitNameATV);</code> | |||
<code>DERSet issuerCommonName = new</code> <code>DERSet(issuerCommonNameATV);</code> | |||
<code>ASN1EncodableVector IssuerRelativeDistinguishedName = new</code> <code>ASN1EncodableVector();</code> | |||
<code>IssuerRelativeDistinguishedName.add(countryName);</code> | |||
<code>IssuerRelativeDistinguishedName.add(organizationName);</code> | |||
<code>IssuerRelativeDistinguishedName.add(organizationalUnitName);</code> | |||
<code>IssuerRelativeDistinguishedName.add(issuerCommonName);</code> | |||
<code>//RDNSequence ::= SEQUENCE OF RelativeDistinguishedName</code> | |||
<code>DERSequence IssuerName = new</code> <code>DERSequence(IssuerRelativeDistinguishedName);</code> | |||
|} | |||
The validity field defines the time frame in which the certificate is valid. We use Java’s Date class to create a three year validity period. Validity is composed of two Time objects, notBefore and notAfter. The names should be self-explanatory. The Time objects are created separately and then included in the Validity SEQUENCE object. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
|<code>//Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime }</code> | |||
<code>DERUTCTime notBefore = new</code> <code>DERUTCTime(new</code> <code>Date(System.currentTimeMillis()));</code> | |||
<code>DERUTCTime notAfter = new</code> <code>DERUTCTime(new</code> <code>Date(System.currentTimeMillis() + (((1000L*60*60*24*30))*12)*3));</code> | |||
<code>ASN1EncodableVector Time = new</code> <code>ASN1EncodableVector();</code> | |||
<code>Time.add(notBefore);</code> | |||
<code>Time.add(notAfter);</code> | |||
<code>/*Validity ::= SEQUENCE {</code> | |||
<code> notBefore Time,</code> | |||
<code> notAfter Time } */</code> | |||
<code>DERSequence Validity = new</code> <code>DERSequence(Time);</code> | |||
|} | |||
Next, we create the subject name. A certificate is basically a binding between the public key and its holder (also called subscriber in literature). The subject name, like the issuer name, is defined in the form of a DN and identifies the holder of the certificate. Usually, only the common name is different for the subject name. The relative DN (RDN) (i.e. OU=PKI,O=Cyberdyne,C=US) is the same as the issuer’s, so we reuse the code we wrote earlier. For our example, since it is a self-signed certificate, the common is also the same. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
|<code>//SubjectName - only need to change the common name</code> | |||
<code>ASN1EncodableVector subjCommonNameATV_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>subjCommonNameATV_ASN.add(new</code> <code>DERObjectIdentifier("2.5.4.3"));</code> | |||
<code>subjCommonNameATV_ASN.add(new</code> <code>DERPrintableString("SecureCA"));</code> | |||
<code>DERSequence subjectCommonNameATV = new</code> <code>DERSequence(subjCommonNameATV_ASN);</code> | |||
<code>//RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue</code> | |||
<code>DERSet subjectCommonName = new</code> <code>DERSet(subjectCommonNameATV);</code> | |||
<code>ASN1EncodableVector SubjectRelativeDistinguishedName = new</code> <code>ASN1EncodableVector();</code> | |||
<code>SubjectRelativeDistinguishedName.add(countryName);</code> | |||
<code>SubjectRelativeDistinguishedName.add(organizationName);</code> | |||
<code>SubjectRelativeDistinguishedName.add(organizationalUnitName);</code> | |||
<code>SubjectRelativeDistinguishedName.add(subjectCommonName);</code> | |||
<code>//RDNSequence ::= SEQUENCE OF RelativeDistinguishedName</code> | |||
<code>DERSequence SubjectName = new</code> <code>DERSequence(SubjectRelativeDistinguishedName);</code> | |||
|} | |||
As I mentioned previously, a certificate is a binding between an identity and the public key. The public key in a X.509 certificate is contained in the SubjectPublicKeyInfo structure, which is an ASN.1 SEQUENCE composed of the algorithm identifier (RSA) and the public key encoded as a BIT STRING. We generate the key pair using the KeyPairGenerator class and save the public key by calling the getPublic() method. Since this is a key pair, the private key can also be obtained by calling getPrivate(). If you read the documentation for the Java Key interface, you will notice that if you call the getEncoded() method on PublicKey, it returns the key in the SubjectPublicKeyInfo form (as a byte array), which the documentation calls the “standard format”. So all you have to do is form it into an ASN.1 SEQUENCE by using the ASN1Sequence.getInstance() static method. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
|<code>/*SubjectPublicKeyInfo ::= SEQUENCE {</code> | |||
<code> algorithm AlgorithmIdentifier,</code> | |||
<code> subjectPublicKey BIT STRING }*/</code> | |||
<code>///Generate the 2048-bit RSA Public Key - PublicKey returns SubjectPublicKeyInfo by default (X.509 format)</code> | |||
<code>SecureRandom random = SecureRandom.getInstance("SHA1PRNG");</code> | |||
<code>KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");</code> | |||
<code>kpGen.initialize(2048, random);</code> | |||
<code>KeyPair keyPair = kpGen.generateKeyPair();</code> | |||
<code>PublicKey RSAPubKey = keyPair.getPublic();</code> | |||
<code>//Convert public key bytes (in SubjectPublicKeyInfo format) to ASN1Sequence</code> | |||
<code>byte[] RSAPubKeyBytes = RSAPubKey.getEncoded();</code> | |||
<code>ASN1Sequence SubjectPublicKeyInfo = ASN1Sequence.getInstance(RSAPubKeyBytes);</code> | |||
|} | |||
We are done with the major portion of the TBSCertificate structure as the next three ASN.1 structures are marked OPTIONAL. The issuerUniqueID and subjectUniqueID are not usually used so let’s focus on extensions. <nowiki>RFC 5280</nowiki> defines a number of extensions but we only focus on the following: Subject Key Identifier, Authority Key Identifier, Key Usage, Extended Key Usage, Basic Constraints, Certificate Policies, Subject Alternative Names, Authority Information Access and CRL Distribution Points. | |||
Advertisement | |||
The first two extensions we consider are Subject Key Identifier and Authority Key Identifier. These extensions are helpful during certificate path construction. Both involve a keyIdentifier value which is a SHA-1 hash of the value of the BIT STRING subjectPublicKey (from the SubjectPublicKeyInfo structure). The subjectKeyIdentifier is the hash over the subject’s public key (i.e. the public key in the certificate). The authorityKeyIdentifier is the hash over the issuer’s public key. Since we are constructing a self-signed certificate, both values are the same. To calculate the key Identifier, we extract the public key value from SubjectPublicKeyInfo and save it as a BIT STRING. Then we create a SHA-1 digest and calculate the hash over this value. The last step is to construct the ASN.1 structure as defined in <nowiki>RFC 5280</nowiki>. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
17 | |||
18 | |||
19 | |||
20 | |||
21 | |||
22 | |||
23 | |||
24 | |||
25 | |||
26 | |||
|<code>//Get the subjectPublicKey from SubjectPublicKeyInfo to calculate the keyIdentifier</code> | |||
<code>DERBitString subjectPublicKey = (DERBitString)SubjectPublicKeyInfo.getObjectAt(1).toASN1Primitive();</code> | |||
<code>//Calculate the keyIdentifier</code> | |||
<code>byte[] pubKeyBitStringBytes = subjectPublicKey.getBytes();</code> | |||
<code>Digest sha1 = new</code> <code>SHA1Digest();</code> | |||
<code>byte[] pubKeydigestBytes = new</code> <code>byte[sha1.getDigestSize()];</code> | |||
<code>sha1.update(pubKeyBitStringBytes,0,pubKeyBitStringBytes.length);</code> | |||
<code>sha1.doFinal(pubKeydigestBytes,0);</code> | |||
<code>DEROctetString keyIdentifier = new</code> <code>DEROctetString(pubKeydigestBytes);</code> | |||
<code>//Subject Key Identifier</code> | |||
<code>ASN1EncodableVector subjectKeyIdentifier_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>subjectKeyIdentifier_ASN.add(new</code> <code>DERObjectIdentifier("2.5.29.14"));</code> | |||
<code>subjectKeyIdentifier_ASN.add(new</code> <code>DEROctetString(keyIdentifier));</code> | |||
<code>DERSequence subjectKeyIdentifier = new</code> <code>DERSequence(subjectKeyIdentifier_ASN);</code> | |||
<code>//Authority Key Identifier</code> | |||
<code>DERTaggedObject aki = new</code> <code>DERTaggedObject(false,0,keyIdentifier);</code> | |||
<code>ASN1EncodableVector akiVec = new</code> <code>ASN1EncodableVector();</code> | |||
<code>akiVec.add(aki);</code> | |||
<code>DERSequence akiSeq = new</code> <code>DERSequence(akiVec);</code> | |||
<code>ASN1EncodableVector authorityKeyIdentifier_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>authorityKeyIdentifier_ASN.add(new</code> <code>DERObjectIdentifier("2.5.29.35"));</code> | |||
<code>authorityKeyIdentifier_ASN.add(new</code> <code>DEROctetString(akiSeq));</code> | |||
<code>DERSequence authorityKeyIdentifier = new</code> <code>DERSequence(authorityKeyIdentifier_ASN);</code> | |||
|} | |||
Next, we consider the Key Usage extension. This extension defines the purpose of the key contained in the certificate. For example, in an email signing certificate you will likely see digitalSignature and nonRepudiation key usages, while in an email encryption certificate you will see keyEncipherment (RSA). For our example, we assert digitalSignature, keyCertSign and cRLSign. The ASN.1 structure is fairly simple. We define the different key usages from <nowiki>RFC 5280</nowiki> and then form the BIT STRING by using bitwise OR. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
17 | |||
|<code>//KeyUsage</code> | |||
<code>final</code> <code>int</code> <code>digitalSignature = (1</code> <code><< 7);</code> | |||
<code>final</code> <code>int</code> <code>nonRepudiation = (1</code> <code><< 6);</code> | |||
<code>final</code> <code>int</code> <code>keyEncipherment = (1</code> <code><< 5);</code> | |||
<code>final</code> <code>int</code> <code>dataEncipherment = (1</code> <code><< 4);</code> | |||
<code>final</code> <code>int</code> <code>keyAgreement = (1</code> <code><< 3);</code> | |||
<code>final</code> <code>int</code> <code>keyCertSign = (1</code> <code><< 2);</code> | |||
<code>final</code> <code>int</code> <code>cRLSign = (1</code> <code><< 1);</code> | |||
<code>final</code> <code>int</code> <code>encipherOnly = (1</code> <code><< 0);</code> | |||
<code>final</code> <code>int</code> <code>decipherOnly = (1</code> <code><< 15);</code> | |||
<code>//Set digitalSignature, keyCertSign and cRLSign</code> | |||
<code>DERBitString keyUsageBitString = new</code> <code>DERBitString(digitalSignature | keyCertSign | cRLSign);</code> | |||
<code>ASN1EncodableVector keyUsage_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>keyUsage_ASN.add(new</code> <code>DERObjectIdentifier("2.5.29.15"));</code> | |||
<code>keyUsage_ASN.add(new</code> <code>DEROctetString(keyUsageBitString));</code> | |||
<code>DERSequence KeyUsage = new</code> <code>DERSequence(keyUsage_ASN);</code> | |||
|} | |||
Related to Key Usage is the Extended Key Usage (EKU) extension. This extension indicates one or more purposes (for specific applications) for which the public key in the certificate can be used. For example, in a SSL server certificate you will likely see the Server Authentication EKU and in a certificate used for code signing you will see a Code Signing EKU. The EKUs are identified by specific OIDs. For our example we assert the Server Authentication EKU and the Email Protection EKU. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
|<code>//Extended Key Usage</code> | |||
<code>DERObjectIdentifier serverAuthEKU = new</code> <code>DERObjectIdentifier("1.3.6.1.5.5.7.3.1");</code> | |||
<code>DERObjectIdentifier emailProtectionEKU = new</code> <code>DERObjectIdentifier("1.3.6.1.5.5.7.3.4");</code> | |||
<code>ASN1EncodableVector EKU_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>EKU_ASN.add(serverAuthEKU);</code> | |||
<code>EKU_ASN.add(emailProtectionEKU);</code> | |||
<code>DERSequence EKUSeq = new</code> <code>DERSequence(EKU_ASN);</code> | |||
<code>ASN1EncodableVector ExtendedKeyUsage_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>ExtendedKeyUsage_ASN.add(new</code> <code>DERObjectIdentifier("2.5.29.37"));</code> | |||
<code>ExtendedKeyUsage_ASN.add(new</code> <code>DEROctetString(EKUSeq));</code> | |||
<code>DERSequence ExtendedKeyUsage = new</code> <code>DERSequence(ExtendedKeyUsage_ASN);</code> | |||
|} | |||
Next, we create the Basic Constraints extension. This extension is defined as a SEQUENCE composed of two objects, a BOOLEAN value specifying whether or not the certificate is a CA certificate and a path length constraint which gives the number of non self-issued intermediate certificates that can follow this certificate in a valid certificate path. In a CA certificate the BOOLEAN value is set to true, however, sometimes you may see this extension in an end-entity certificate set to false. For our example we assert that the certificate is a CA certificate and set the path length constraint to 0, indicating that the only certificate which may follow this certificate in a certificate path is an end-entity certificate. We also mark this extension as critical. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
|<code>//Basic Constraints</code> | |||
<code>DERBoolean isCA = DERBoolean.getInstance(ASN1Boolean.TRUE);</code> | |||
<code>DERInteger pathLenConstraint = new</code> <code>DERInteger(0);</code> | |||
<code>ASN1EncodableVector basicConstraintStructure_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>basicConstraintStructure_ASN.add(isCA);</code> | |||
<code>basicConstraintStructure_ASN.add(pathLenConstraint);</code> | |||
<code>DERSequence basicConstraintSeq = new</code> <code>DERSequence(basicConstraintStructure_ASN);</code> | |||
<code>ASN1EncodableVector basicConstraintExtension = new</code> <code>ASN1EncodableVector();</code> | |||
<code>basicConstraintExtension.add(new</code> <code>DERObjectIdentifier("2.5.29.19"));</code> | |||
<code>basicConstraintExtension.add(ASN1Boolean.TRUE); //Mark critical</code> | |||
<code>basicConstraintExtension.add(new</code> <code>DEROctetString(basicConstraintSeq));</code> | |||
<code>DERSequence BasicConstraints = new</code> <code>DERSequence(basicConstraintExtension);</code> | |||
|} | |||
The Certificate Policies extension indicates the policy under which this certificate was issued. The policy is usually defined in a separate certificate policies document. The extension is defined as a SEQUENCE of certificate policies where each policy is identified by an OID and may contain other information regarding the policy. For our example we specify two policies (using policy OIDs I found online). | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
17 | |||
18 | |||
19 | |||
20 | |||
21 | |||
|<code>//Certificate Policies</code> | |||
<code>DERObjectIdentifier policyIdentifierOne = new</code> <code>DERObjectIdentifier("2.16.840.1.101.2.1.11.5");</code> | |||
<code>DERObjectIdentifier policyIdentifierTwo = new</code> <code>DERObjectIdentifier("2.16.840.1.101.2.1.11.18");</code> | |||
<code>ASN1EncodableVector policyInformationOne_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>policyInformationOne_ASN.add(policyIdentifierOne);</code> | |||
<code>DERSequence policyInformationSeqOne = new</code> <code>DERSequence(policyInformationOne_ASN);</code> | |||
<code>ASN1EncodableVector policyInformationTwo_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>policyInformationTwo_ASN.add(policyIdentifierTwo);</code> | |||
<code>DERSequence policyInformationSeqTwo = new</code> <code>DERSequence(policyInformationTwo_ASN);</code> | |||
<code>ASN1EncodableVector certificatePolicies_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>certificatePolicies_ASN.add(policyInformationSeqOne);</code> | |||
<code>certificatePolicies_ASN.add(policyInformationSeqTwo);</code> | |||
<code>DERSequence certificatePoliciesSeq = new</code> <code>DERSequence(certificatePolicies_ASN);</code> | |||
<code>ASN1EncodableVector certificatePoliciesExtension = new</code> <code>ASN1EncodableVector();</code> | |||
<code>certificatePoliciesExtension.add(new</code> <code>DERObjectIdentifier("2.5.29.32"));</code> | |||
<code>certificatePoliciesExtension.add(new</code> <code>DEROctetString(certificatePoliciesSeq));</code> | |||
<code>DERSequence CertificatePolicies = new</code> <code>DERSequence(certificatePoliciesExtension);</code> | |||
|} | |||
The Subject Alternative Name extension specifies more information about the identity asserted in the subject name. For an email certificate this extension may specify an email address, while for server certificates this extension may specify a DNS name. We specify an email address (rfc822Name) and a subject name (directoryName). | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
|<code>//Subject Alternative Name</code> | |||
<code>DERTaggedObject rfc822Name = new</code> <code>DERTaggedObject(false, 1, new</code> <code>DERIA5String("john.smith@gmail.com"));</code> | |||
<code>DERTaggedObject directoryName = new</code> <code>DERTaggedObject(true, 4, SubjectName); //directoryName explicitly tagged</code> | |||
<code>ASN1EncodableVector GeneralNamesVec = new</code> <code>ASN1EncodableVector();</code> | |||
<code>GeneralNamesVec.add(rfc822Name);</code> | |||
<code>GeneralNamesVec.add(directoryName);</code> | |||
<code>DERSequence GeneralNamesSeq = new</code> <code>DERSequence(GeneralNamesVec);</code> | |||
<code>ASN1EncodableVector subjectAltname_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>subjectAltname_ASN.add(new</code> <code>DERObjectIdentifier("2.5.29.17"));</code> | |||
<code>subjectAltname_ASN.add(new</code> <code>DEROctetString(GeneralNamesSeq));</code> | |||
<code>DERSequence SubjectAlternativeName = new</code> <code>DERSequence(subjectAltname_ASN);</code> | |||
|} | |||
The Authority Information Access (AIA) extension provides information on how to access CA information. It defines two access descriptors: caIssuers and Online Certificate Status Protocol (OCSP). The caIssuers access descriptor provides information (location of the CA certificates) about the CA which issued the certificate which contains the AIA extension. It is often used to aid certificate path construction. The OCSP access descriptor provides the OCSP responder location which can be used to query the revocation status of the certificate. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
17 | |||
18 | |||
19 | |||
20 | |||
21 | |||
|<code>//Authority Information Access</code> | |||
<code>DERTaggedObject caIssuers= new</code> <code>DERTaggedObject(false, 6, new</code> <code>DERIA5String("<nowiki>http://www.somewebsite.com/ca.cer</nowiki>"));</code> | |||
<code>DERTaggedObject ocspURL= new</code> <code>DERTaggedObject(false, 6, new</code> <code>DERIA5String("<nowiki>http://ocsp.somewebsite.com</nowiki>"));</code> | |||
<code>ASN1EncodableVector caIssuers_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>caIssuers_ASN.add(new</code> <code>DERObjectIdentifier("1.3.6.1.5.5.7.48.2"));</code> | |||
<code>caIssuers_ASN.add(caIssuers);</code> | |||
<code>DERSequence caIssuersSeq = new</code> <code>DERSequence(caIssuers_ASN);</code> | |||
<code>ASN1EncodableVector ocsp_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>ocsp_ASN.add(new</code> <code>DERObjectIdentifier("1.3.6.1.5.5.7.48.1"));</code> | |||
<code>ocsp_ASN.add(ocspURL);</code> | |||
<code>DERSequence ocspSeq = new</code> <code>DERSequence(ocsp_ASN);</code> | |||
<code>ASN1EncodableVector accessSyn_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>accessSyn_ASN.add(caIssuersSeq);</code> | |||
<code>accessSyn_ASN.add(ocspSeq);</code> | |||
<code>DERSequence AIASyntaxSeq = new</code> <code>DERSequence(accessSyn_ASN);</code> | |||
<code>ASN1EncodableVector AIA_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>AIA_ASN.add(new</code> <code>DERObjectIdentifier("1.3.6.1.5.5.7.1.1"));</code> | |||
<code>AIA_ASN.add(new</code> <code>DEROctetString(AIASyntaxSeq));</code> | |||
<code>DERSequence AuthorityInformationAccess = new</code> <code>DERSequence(AIA_ASN);</code> | |||
|} | |||
The last extension we consider is the CRL Distribution Points extension. As the name suggests, this extension provides information on how to access the CRL for the certificate. While OCSP is the preferred method of obtaining revocation information, it’s useful to provide this extension for applications which may not support OCSP. We can define multiple distribution points in this extension. For our example, we define two CRL distribution points. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
15 | |||
16 | |||
17 | |||
18 | |||
19 | |||
20 | |||
21 | |||
22 | |||
23 | |||
24 | |||
25 | |||
26 | |||
27 | |||
28 | |||
29 | |||
30 | |||
31 | |||
32 | |||
33 | |||
34 | |||
35 | |||
36 | |||
37 | |||
38 | |||
39 | |||
40 | |||
|<code>//CRL Distribution Points</code> | |||
<code>DERTaggedObject crlDPURL_One = new</code> <code>DERTaggedObject(false, 6, new</code> <code>DERIA5String("<nowiki>http://crl.somewebsite.com/master.crl</nowiki>"));</code> | |||
<code>ASN1EncodableVector crlDPURL_One_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>crlDPURL_One_ASN.add(crlDPURL_One);</code> | |||
<code>DERSequence crlDPURL_OneSeq = new</code> <code>DERSequence(crlDPURL_One_ASN);</code> | |||
<code>DERTaggedObject crlDPURL_Two = new</code> <code>DERTaggedObject(false, 6, new</code> <code>DERIA5String("ldap://crl.somewebsite.com/cn%3dSecureCA%2cou%3dPKI%2co%3dCyberdyne%2cc%3dUS?certificaterevocationlist;binary"));</code> | |||
<code>ASN1EncodableVector crlDPURL_Two_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>crlDPURL_Two_ASN.add(crlDPURL_Two);</code> | |||
<code>DERSequence crlDPURL_TwoSeq = new</code> <code>DERSequence(crlDPURL_Two_ASN);</code> | |||
<code>DERTaggedObject DPName_One = new</code> <code>DERTaggedObject(false, 0, crlDPURL_OneSeq);</code> | |||
<code>ASN1EncodableVector DPName_One_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>DPName_One_ASN.add(DPName_One);</code> | |||
<code>DERSequence DPName_One_Seq = new</code> <code>DERSequence(DPName_One_ASN);</code> | |||
<code>DERTaggedObject DPName_Two = new</code> <code>DERTaggedObject(false, 0, crlDPURL_TwoSeq);</code> | |||
<code>ASN1EncodableVector DPName_Two_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>DPName_Two_ASN.add(DPName_Two);</code> | |||
<code>DERSequence DPName_Two_Seq = new</code> <code>DERSequence(DPName_Two_ASN);</code> | |||
<code>DERTaggedObject DPOne = new</code> <code>DERTaggedObject(false, 0, DPName_One_Seq);</code> | |||
<code>ASN1EncodableVector DPOne_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>DPOne_ASN.add(DPOne);</code> | |||
<code>DERSequence DistributionPointOne = new</code> <code>DERSequence(DPOne_ASN);</code> | |||
<code>DERTaggedObject DPTwo = new</code> <code>DERTaggedObject(false, 0, DPName_Two_Seq);</code> | |||
<code>ASN1EncodableVector DPTwo_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>DPTwo_ASN.add(DPTwo);</code> | |||
<code>DERSequence DistributionPointTwo = new</code> <code>DERSequence(DPTwo_ASN);</code> | |||
<code>ASN1EncodableVector CRLDistributionPoints_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>CRLDistributionPoints_ASN.add(DistributionPointOne);</code> | |||
<code>CRLDistributionPoints_ASN.add(DistributionPointTwo);</code> | |||
<code>DERSequence CRLDistributionPointsSeq = new</code> <code>DERSequence(CRLDistributionPoints_ASN);</code> | |||
<code>ASN1EncodableVector CRLDP_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>CRLDP_ASN.add(new</code> <code>DERObjectIdentifier("2.5.29.31"));</code> | |||
<code>CRLDP_ASN.add(new</code> <code>DEROctetString(CRLDistributionPointsSeq));</code> | |||
<code>DERSequence CRLDistributionPoints = new</code> <code>DERSequence(CRLDP_ASN);</code> | |||
|} | |||
Now that we have constructed the extensions, we construct the ASN.1 SEQUENCE which will be included in the TBSCertificate structure. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
13 | |||
14 | |||
|<code>//Create Extensions</code> | |||
<code>ASN1EncodableVector Extensions_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>Extensions_ASN.add(subjectKeyIdentifier);</code> | |||
<code>Extensions_ASN.add(authorityKeyIdentifier);</code> | |||
<code>Extensions_ASN.add(KeyUsage);</code> | |||
<code>Extensions_ASN.add(ExtendedKeyUsage);</code> | |||
<code>Extensions_ASN.add(BasicConstraints);</code> | |||
<code>Extensions_ASN.add(CertificatePolicies);</code> | |||
<code>Extensions_ASN.add(SubjectAlternativeName);</code> | |||
<code>Extensions_ASN.add(AuthorityInformationAccess);</code> | |||
<code>Extensions_ASN.add(CRLDistributionPoints);</code> | |||
<code>DERSequence Extensions = new</code> <code>DERSequence(Extensions_ASN);</code> | |||
<code>DERTaggedObject extensions = new</code> <code>DERTaggedObject(true, 3, Extensions);</code> | |||
|} | |||
The next step is to construct the final TBSCertificate structure | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 | |||
10 | |||
11 | |||
12 | |||
|<code>//TBSCertificate := SEQUENCE</code> | |||
<code>ASN1EncodableVector TBSCertificate_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>TBSCertificate_ASN.add(Version);</code> | |||
<code>TBSCertificate_ASN.add(CertificateSerialNumber);</code> | |||
<code>TBSCertificate_ASN.add(SignatureAlgorithm);</code> | |||
<code>TBSCertificate_ASN.add(IssuerName);</code> | |||
<code>TBSCertificate_ASN.add(Validity);</code> | |||
<code>TBSCertificate_ASN.add(SubjectName);</code> | |||
<code>TBSCertificate_ASN.add(SubjectPublicKeyInfo);</code> | |||
<code>TBSCertificate_ASN.add(extensions);</code> | |||
<code>DERSequence TBSCertificate = new</code> <code>DERSequence(TBSCertificate_ASN);</code> | |||
|} | |||
Looking back at the Certificate structure, we still have two more objects we need to construct: signatureAlgorithm and signatureValue. We already constructed the signatureAlgorithm structure in TBSCertificate. The signatureValue structure contains the digital signature computed over the ASN.1 DER encoded tbsCertificate. A digital signature is an encryption operation over a hash value using a private key. Since this is a self-signed certificate, the private key is the private key of the subject. We get the private key from the key pair, use Java’s Signature class to construct the signature and encode the signature as a BIT STRING. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
|<code>//Create the signature value</code> | |||
<code>byte[] TBSCertificateBytes = TBSCertificate.getEncoded();</code> | |||
<code>PrivateKey RSAPrivKey = keyPair.getPrivate();</code> | |||
<code>Signature signer = Signature.getInstance("SHA1WithRSA","BC");</code> | |||
<code>signer.initSign(RSAPrivKey);</code> | |||
<code>signer.update(TBSCertificateBytes);</code> | |||
<code>byte[] signature = signer.sign();</code> | |||
<code>DERBitString signatureValue = new</code> <code>DERBitString(signature);</code> | |||
|} | |||
Finally we construct the actual certificate. | |||
{| class="wikitable" | |||
|1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
|<code>//Create the certificate structure</code> | |||
<code>ASN1EncodableVector cert_ASN = new</code> <code>ASN1EncodableVector();</code> | |||
<code>cert_ASN.add(TBSCertificate);</code> | |||
<code>cert_ASN.add(SignatureAlgorithm);</code> | |||
<code>cert_ASN.add(signatureValue);</code> | |||
<code>DERSequence Certificate = new</code> <code>DERSequence(cert_ASN);</code> | |||
|} | |||
To view the certificate we created I used Apache Common’s IO library to output the certificate as a file. | |||
{| class="wikitable" | |||
|1 | |||
|<code>FileUtils.writeByteArrayToFile(new</code> <code>File("cert.crt"), Certificate.getEncoded());</code> | |||
|} | |||
As you can see from the image below, Windows parses the certificate and displays the extensions and other components. Normally you would never create an X.509 certificate by individually constructing the ASN.1 structures, but instead use higher level APIs provided by your cryptography library. In a future post I will create this same certificate using a higher level API with fewer lines of code. | |||
[[File:Image 3103202401.png|alt=A window displaying portions of information of an X.509 certificate.]] |
Revision as of 19:02, 31 March 2024
Digital certificates (also called X.509 certificates) are defined using ASN.1 and encoded using Distinguished Encoding Rules (DER). A number of cryptography libraries (Bouncy Castle, NSS etc.) provide high-level APIs which can be used to create digital certificates. Behind the scenes, however, they are using ASN.1 structures. To gain a deeper understanding of X.509 certificates, I think it’s instructive to look at how to create them using ASN.1 by following the construct presented in RFC 5280 – Internet X.509 Public Key Infrastructure and Certificate Revocation List (CRL) Profile.
RFC 5280 describes the content of X.509 certificates and CRL. We are only going to consider X.509v3 certificates (refer to RFC 5280 for differences between X.509 certificate versions).
An X.509v3 certificate is defined as:
Certificate ::= SEQUENCE { tbsCertificate TBSCertificate, signatureAlgorithm AlgorithmIdentifier, signatureValue BIT STRING }
I am not going to explain ASN.1 syntax in detail, but the syntax above defines a Certificate, a SEQUENCE of tbsCertificate, signatureAlgorithm and a signatureValue, where tbsCertificate and signatureAlgorithm are complex structures (to be defined later) and signatureValue is a BIT STRING primitive structure.
Let’s construct each structure piece-by-piece. We start with TBSCertificate, where “TBS” stands for “To Be Signed”. The signature will be computed over this data structure and placed in the signatureValue field and .
The TBSCertificate structure is defined as:
TBSCertificate ::= SEQUENCE { version [0] EXPLICIT Version DEFAULT v1, serialNumber CertificateSerialNumber, signature AlgorithmIdentifier, issuer Name, validity Validity, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, extensions [3] EXPLICIT Extensions OPTIONAL }
As you can see TBSCertificate is composed of several complex structures.
To create the ASN.1 structures I will use Java and Bouncy Castle 1.48 (BC 1.48) library. The certificate will be self-signed and will contain a number of extensions. I don’t construct all the extensions defined in RFC 5280. Once you get the hang of constructing complex ASN.1 structures, creating the extensions I left out shouldn’t be too difficult.
The first item in TBSCertificate is Version. As I mentioned previously, we focus solely on X.509 v3 certificates. Version is “tagged” (tags helps in resolving ambiguities during decoding ASN.1) with “0” and is also marked EXPLICIT. We use DERTaggedObject, mark the first argument true which sets it as EXPLICIT and then assign it the tag 0. The third argument is encoded as DERInteger and is assigned the value 2, which indicates that this is an X.509v3 certificate.
1
2 |
//Version ::= INTEGER { v1(0), v2(1), v3(2) }
|
The next item in TBSCertificate is CertificateSerialNumber. This is the serial number of the certificate. Each Certificate Authority (CA) is supposed to assign an unique serial number to every certificate it issues. The certificate serial number should be positive.
Advertisement
1
2 |
//CertificateSerialNumber ::= INTEGER
|
The next item in TBSCertificate is AlgorithmIdentifier. This value indicates the algorithm used by the CA to sign the certificate. A signature involves both a hashing algorithm (SHA-1) and an asymmetric encryption algorithm (RSA). AlgorithmIdentifier is defined as an ASN.1 SEQUENCE. ASN.1 structures are composed of either simple types (INTEGER, BOOLEAN, OCTET STRING etc.) or structured types (SEQUENCE, SET, SEQUENCE OF, SET OF etc.) or a combination of both. The first object in AlgorithmIdentifier, algorithm, is an Object Identifier (OID) which is assigned the identifier for “SHA-1 With RSA Encryption” and the second object, parameters, is set to NULL (for this particular algorithm). In our code, the two ASN.1 objects are created separately and then wrapped in a ASN.1 SEQUENCE.
1
2 3 4 5 6 7 8 |
/*AlgorithmIdentifier ::= SEQUENCE {
|
Next, we consider the issuer name. This is typically the distinguished name (DN) of the CA. Since this is a self-signed certificate it will be the same as the subject name (which is defined later).
The Name structure is more involved than the ones we have seen previously. Let’s break it down. As seen below, the Name structure is a RDNSequence structure. The RDNSequence, as the name indicates, is a SEQUENCE of RelativeDistinguishedNames. The RelativeDistinguishedNames structure is a SET of AttributeTypeAndValue, which in turn is a SEQUENCE composed of an AttributeType and AttributeValue. The AttributeType is an OID, while the AttributeValue is a DirectoryString (specifically, a CHOICE between TeletexString, PrintableString, UniversalString, UTF8String and BMPString). We choose to encode each component of the DN as a PrintableString. Ultimately, the DN will look like CN=SecureCA,OU=PKI,O=Cyberdyne,C=US. If you are confused by the syntax of the issuer name, look up Lightweight Directory Access Protocol (LDAP) naming conventions.
Don’t worry if this is confusing. It takes a while to sink in. When writing the code it’s easier to construct the innermost structure first (AttributeTypeAndValue) and build it up to the outermost structure (Name). Remember that Name is a SEQUENCE, so ultimately it will be an ASN.1 SEQUENCE.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
/*Name ::= CHOICE { rdnSequence RDNSequence }
|
The validity field defines the time frame in which the certificate is valid. We use Java’s Date class to create a three year validity period. Validity is composed of two Time objects, notBefore and notAfter. The names should be self-explanatory. The Time objects are created separately and then included in the Validity SEQUENCE object.
1
2 3 4 5 6 7 8 9 10 11 12 |
//Time ::= CHOICE { utcTime UTCTime, generalTime GeneralizedTime }
|
Next, we create the subject name. A certificate is basically a binding between the public key and its holder (also called subscriber in literature). The subject name, like the issuer name, is defined in the form of a DN and identifies the holder of the certificate. Usually, only the common name is different for the subject name. The relative DN (RDN) (i.e. OU=PKI,O=Cyberdyne,C=US) is the same as the issuer’s, so we reuse the code we wrote earlier. For our example, since it is a self-signed certificate, the common is also the same.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//SubjectName - only need to change the common name
|
As I mentioned previously, a certificate is a binding between an identity and the public key. The public key in a X.509 certificate is contained in the SubjectPublicKeyInfo structure, which is an ASN.1 SEQUENCE composed of the algorithm identifier (RSA) and the public key encoded as a BIT STRING. We generate the key pair using the KeyPairGenerator class and save the public key by calling the getPublic() method. Since this is a key pair, the private key can also be obtained by calling getPrivate(). If you read the documentation for the Java Key interface, you will notice that if you call the getEncoded() method on PublicKey, it returns the key in the SubjectPublicKeyInfo form (as a byte array), which the documentation calls the “standard format”. So all you have to do is form it into an ASN.1 SEQUENCE by using the ASN1Sequence.getInstance() static method.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
/*SubjectPublicKeyInfo ::= SEQUENCE {
|
We are done with the major portion of the TBSCertificate structure as the next three ASN.1 structures are marked OPTIONAL. The issuerUniqueID and subjectUniqueID are not usually used so let’s focus on extensions. RFC 5280 defines a number of extensions but we only focus on the following: Subject Key Identifier, Authority Key Identifier, Key Usage, Extended Key Usage, Basic Constraints, Certificate Policies, Subject Alternative Names, Authority Information Access and CRL Distribution Points.
Advertisement
The first two extensions we consider are Subject Key Identifier and Authority Key Identifier. These extensions are helpful during certificate path construction. Both involve a keyIdentifier value which is a SHA-1 hash of the value of the BIT STRING subjectPublicKey (from the SubjectPublicKeyInfo structure). The subjectKeyIdentifier is the hash over the subject’s public key (i.e. the public key in the certificate). The authorityKeyIdentifier is the hash over the issuer’s public key. Since we are constructing a self-signed certificate, both values are the same. To calculate the key Identifier, we extract the public key value from SubjectPublicKeyInfo and save it as a BIT STRING. Then we create a SHA-1 digest and calculate the hash over this value. The last step is to construct the ASN.1 structure as defined in RFC 5280.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//Get the subjectPublicKey from SubjectPublicKeyInfo to calculate the keyIdentifier
|
Next, we consider the Key Usage extension. This extension defines the purpose of the key contained in the certificate. For example, in an email signing certificate you will likely see digitalSignature and nonRepudiation key usages, while in an email encryption certificate you will see keyEncipherment (RSA). For our example, we assert digitalSignature, keyCertSign and cRLSign. The ASN.1 structure is fairly simple. We define the different key usages from RFC 5280 and then form the BIT STRING by using bitwise OR.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//KeyUsage
|
Related to Key Usage is the Extended Key Usage (EKU) extension. This extension indicates one or more purposes (for specific applications) for which the public key in the certificate can be used. For example, in a SSL server certificate you will likely see the Server Authentication EKU and in a certificate used for code signing you will see a Code Signing EKU. The EKUs are identified by specific OIDs. For our example we assert the Server Authentication EKU and the Email Protection EKU.
1
2 3 4 5 6 7 8 9 10 11 12 |
//Extended Key Usage
|
Next, we create the Basic Constraints extension. This extension is defined as a SEQUENCE composed of two objects, a BOOLEAN value specifying whether or not the certificate is a CA certificate and a path length constraint which gives the number of non self-issued intermediate certificates that can follow this certificate in a valid certificate path. In a CA certificate the BOOLEAN value is set to true, however, sometimes you may see this extension in an end-entity certificate set to false. For our example we assert that the certificate is a CA certificate and set the path length constraint to 0, indicating that the only certificate which may follow this certificate in a certificate path is an end-entity certificate. We also mark this extension as critical.
1
2 3 4 5 6 7 8 9 10 11 12 13 |
//Basic Constraints
|
The Certificate Policies extension indicates the policy under which this certificate was issued. The policy is usually defined in a separate certificate policies document. The extension is defined as a SEQUENCE of certificate policies where each policy is identified by an OID and may contain other information regarding the policy. For our example we specify two policies (using policy OIDs I found online).
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//Certificate Policies
|
The Subject Alternative Name extension specifies more information about the identity asserted in the subject name. For an email certificate this extension may specify an email address, while for server certificates this extension may specify a DNS name. We specify an email address (rfc822Name) and a subject name (directoryName).
1
2 3 4 5 6 7 8 9 10 11 12 |
//Subject Alternative Name
|
The Authority Information Access (AIA) extension provides information on how to access CA information. It defines two access descriptors: caIssuers and Online Certificate Status Protocol (OCSP). The caIssuers access descriptor provides information (location of the CA certificates) about the CA which issued the certificate which contains the AIA extension. It is often used to aid certificate path construction. The OCSP access descriptor provides the OCSP responder location which can be used to query the revocation status of the certificate.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//Authority Information Access
|
The last extension we consider is the CRL Distribution Points extension. As the name suggests, this extension provides information on how to access the CRL for the certificate. While OCSP is the preferred method of obtaining revocation information, it’s useful to provide this extension for applications which may not support OCSP. We can define multiple distribution points in this extension. For our example, we define two CRL distribution points.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
//CRL Distribution Points
|
Now that we have constructed the extensions, we construct the ASN.1 SEQUENCE which will be included in the TBSCertificate structure.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 |
//Create Extensions
|
The next step is to construct the final TBSCertificate structure
1
2 3 4 5 6 7 8 9 10 11 12 |
//TBSCertificate := SEQUENCE
|
Looking back at the Certificate structure, we still have two more objects we need to construct: signatureAlgorithm and signatureValue. We already constructed the signatureAlgorithm structure in TBSCertificate. The signatureValue structure contains the digital signature computed over the ASN.1 DER encoded tbsCertificate. A digital signature is an encryption operation over a hash value using a private key. Since this is a self-signed certificate, the private key is the private key of the subject. We get the private key from the key pair, use Java’s Signature class to construct the signature and encode the signature as a BIT STRING.
1
2 3 4 5 6 7 8 |
//Create the signature value
|
Finally we construct the actual certificate.
1
2 3 4 5 6 |
//Create the certificate structure
|
To view the certificate we created I used Apache Common’s IO library to output the certificate as a file.
1 | FileUtils.writeByteArrayToFile(new File("cert.crt"), Certificate.getEncoded());
|
As you can see from the image below, Windows parses the certificate and displays the extensions and other components. Normally you would never create an X.509 certificate by individually constructing the ASN.1 structures, but instead use higher level APIs provided by your cryptography library. In a future post I will create this same certificate using a higher level API with fewer lines of code.
A window displaying portions of information of an X.509 certificate.