If you haven't read through the ASN.1 documentation, you probably
had better do so; this library relies heavily on that code.
Here's the ASN.1 for the certificate itself:
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING }
TBSCertificate ::= SEQUENCE {
version [0] Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version shall be v2 or v3
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version shall be v2 or v3
extensions [3] Extensions OPTIONAL
-- If present, version shall be v3 -- }
Now let's look at X509 as the library defines it:
typedef struct x509_cinf_st
{
ASN1_INTEGER *version; /* [ 0 ] default of v1 */
ASN1_INTEGER *serialNumber;
X509_ALGOR *signature;
X509_NAME *issuer;
X509_VAL *validity;
X509_NAME *subject;
X509_PUBKEY *key;
ASN1_BIT_STRING *issuerUID; /* [ 1 ] optional in v2 */
ASN1_BIT_STRING *subjectUID; /* [ 2 ] optional in v2 */
STACK /* X509_EXTENSION */ *extensions; /* [ 3 ] optional in v3 */
} X509_CINF;
typedef struct x509_st
{
X509_CINF *cert_info;
X509_ALGOR *sig_alg;
ASN1_BIT_STRING *signature;
int valid;
int references;
char *name;
} X509;
Treating the certificate as a more-or-less opaque type,
we have the following operations we can perform on it:
make a new one, free an old one, copy it
X509_new, X509_free, X509_dup
convert it to/from DER-encoded form
i2d_X509, d2i_X509
write it to a file, or read it from a file, either in
DER-encoded form or in human-readable text or in PEM
(base64-encoded) form
d2i_X509_fp, i2d_X509_fp,
d2i_X509_bio, i2d_X509_bio, X509_print_fp, X509_print, PEM_write_bio_X509,
PEM_write_X509, PEM_read_bio_X509, PEM_read_X509
verify it, sign it, get the digest of it,
X509_verify, X509_sign, X509_digest,
If we want to deal with the subfields of the structure instead, we can
retrieve a pointer to any of them
X509_get_version, X509_get_serialNumber, X509_get_notBefore,
X509_get_notAfter, X509_get_issuer_name, X509_get_subject_name
stuff any of them with a value or structure of our choice
X509_set_version, X509_set_serialNumber, X509_set_issuer_name,
X509_set_subject_name, X509_set_notBefore, X509_set_notAfter
compare subfields of two of them
X509_issuer_and_serial_cmp, X509_issuer_name_cmp, X509_subject_name_cmp
We also have a few functions that deal with verifying this certificate
against some internal list we have, either by creating a key
by which it can be indexed in our list, or trying to retrieve
it from our list
X509_issuer_and_serial_hash, X509_issuer_name_hash, X509_subject_name_hash,
X509_find_by_issuer_and_serial, X509_find_by_subject
There is also a collection of functions that deal with
X.509v3 extensions, determining how many there are, which
ones are critical, what each one actually it, adding them and deleting
them to the cerificate
X509_get_ext_count, X509_get_ext_by_NID, X509_get_ext_by_OBJ,
X509_get_ext_by_critical, X509_get_ext, X509_delete_ext, X509_add_ext
And finally there is a collection of routines that deal with the public
or private key associated with the certificate on a more detailed
level
X509_extract_key (=X509_get_pubkey), X509_get_pubkey,
X509_set_pubkey, X509_check_private_key
and a couple of routines that get other detailed information about the
certificate by prying it out of complex substructures:
X509_get_signature_type, X509_certificate_type
We'll look at each of these in turn.
X509_new
creates a new X509 structure and returns a pointer to it; if
memory cannot be allocated it returns NULL.
X509_free
frees the memory of the X509 structure pointed
to by x, unless the argument is NULL, in which case
it does nothing.
X509_dup is actually a macro:
#define X509_dup(x509) (X509 *)ASN1_dup((int (*)())i2d_X509,
(char *(*)())d2i_X509,(char *)x509)
It makes a copy of the X509 structure that x points to and returns
it, or NULL on error.
i2d_X509
converts an X.509 structure pointed to by a to DER-encoded form;
it places the results in *pp and then increments *pp to
point to the end of the byte string it has just written, so you
can call several i2d functions in a row. It returns the number of bytes
written to the string or 0 on error.
d2i_X509
converts length bytes of the
DER-encoded string in *pp to an X509 structure, updates *pp
to point to the next byte to be processed, places the new X509 structure
in *a, and returns it, or NULL on error.
The following macros are provided for the convenience of the user:
#define d2i_X509_fp(fp,x509)
(X509 *)ASN1_d2i_fp((char *(*)())X509_new,
(char *(*)())d2i_X509, (fp),(unsigned char **)(x509))
#define i2d_X509_fp(fp,x509)
ASN1_i2d_fp(i2d_X509,fp,(unsigned char *)x509)
#define d2i_X509_bio(bp,x509)
(X509 *)ASN1_d2i_bio((char *(*)())X509_new,
(char *(*)())d2i_X509, (bp),(unsigned char **)(x509))
#define i2d_X509_bio(bp,x509)
ASN1_i2d_bio(i2d_X509,bp,(unsigned char *)x509)
d2i_X509_fp
reads from fp as much
data as it can, in DER-encoded form, and then uses d2i_X509 to
translate that into an X509 structure, which is placed in x509
if that argument is not NULL, or into a newly malloced
structure otherwise. A pointer to the populated structure
is returned, or NULL on error.
i2d_X509_fp
converts the X509 structure pointed to by x509 to DER-encoded
form and writes it out to the file pointer fp.
It returns 1 on success and 0 on error.
d2i_X509_bio
does exactly what d2i_X509_fp does, except that it reads from a
BIO instead of a file pointer.
i2d_X509_bio
does exactly what i2d_X509_fp does, except that it writes to a
BIO instead of a file pointer.
X509_print_fp
translates the X509 structure x into human-readable format
and writes the result to the file pointer fp.
It returns 1 on success or 0 on error.
It uses a fixed format to display the certificate
information. An example is below:
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 0 (0x0)
Signature Algorithm: md5WithRSAEncryption
Issuer: C=AU, ST=Queensland, O=CryptSoft Pty Ltd, CN=Test CA (1024 bit)
Validity
Not Before: Jun 9 13:57:46 1997 GMT
Not After : Jun 9 13:57:46 1998 GMT
Subject: C=AU, ST=Queensland, O=CryptSoft Pty Ltd, CN=Server test cert (512 bit)
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (512 bit)
Modulus (512 bit):
00:9f:b3:c3:84:27:95:ff:12:31:52:0f:15:ef:46:
11:c4:ad:80:e6:36:5b:0f:dd:80:d7:61:8d:e0:fc:
72:45:09:34:fe:55:66:45:43:4c:68:97:6a:fe:a8:
a0:a5:df:5f:78:ff:ee:d7:64:b8:3f:04:cb:6f:ff:
2a:fe:fe:b9:ed
Exponent: 65537 (0x10001)
Signature Algorithm: md5WithRSAEncryption
78:4c:c4:76:0a:f6:9c:28:4f:70:02:cb:bd:8f:20:97:f1:88:
96:9a:00:8c:f9:8a:0f:ea:4b:78:66:48:f6:cf:92:11:7e:6e:
d6:af:27:0a:b2:7a:67:39:1f:ad:19:ea:80:8e:40:9e:67:af:
f7:ea:8f:65:f7:54:6f:3e:73:75:54:9f:e2:c5:0b:f6:75:fd:
0a:15:e3:2b:d3:42:24:0d:d4:cd:01:d2:16:a8:59:12:b1:94:
c0:63:64:00:76:07:a7:6f:b3:01:d6:3a:1e:8e:ab:98:cc:ed:
83:40:83:bb:fb:0a:47:8a:b3:9b:a4:44:b1:01:92:f2:48:29:
8f:3e
X509_print
does exactly the same thing as X509_print_fp, except that
it writes to a BIO instead of a file pointer.
The following macros are provided for the convenience of the user:
#define PEM_write_X509(fp,x)
PEM_ASN1_write((int (*)())i2d_X509,PEM_STRING_X509,fp,(char *)x, NULL,NULL,0,NULL)
#define PEM_write_bio_X509(bp,x)
PEM_ASN1_write_bio((int (*)())i2d_X509,PEM_STRING_X509,bp,(char *)x, NULL,NULL,0,NULL)
#define PEM_read_bio_X509(bp,x,cb)
(X509 *)PEM_ASN1_read_bio((char *(*)())d2i_X509,PEM_STRING_X509,bp,(char **)x,cb)
#define PEM_read_X509(fp,x,cb)
(X509 *)PEM_ASN1_read((char *(*)())d2i_X509,PEM_STRING_X509,fp,(char **)x,cb)
PEM_write_X509
converts the X509 structure pointed to by x to DER-encoded form.
The data is then base64-encoded
and written out to fp with BEGIN CERTIFICATE and END headers
around it. 1 is returned on success, or 0 on error.
PEM_write_bio_X509
does the same thing as PEM_write_X509 except that it uses a BIO
instead of a FILE.
PEM_read_X509
reads a PEM-encoded X509 certificate
from the file fp using PEM_read;
calls PEM_get_EVP_CIPHER_INFO to process any DEK-Info header
that might be present, in particular determining the cipher
that was used to encrypt the message body and retrieving the
ivec from the header; calls PEM_do_header to decrypt the
message body if it was encrypted (see the description of that function
in PEM library routines for details
about how a pass phrase (key string) is retrieved from the user, using
the callback cb,
or retrieved from somewhere else); and then
converts it to an X509 structure and places the result in x.
It then returns a pointer to that structure, or NULL on error.
The file must contain a BEGIN CERTIFICATE header or it will not be
parsed properly.
Note that since this function calls PEM_get_EVP_CIPHER_INFO,
the appropriate cipher type (according to the name that is
present in the PEM header) must be loaded into the static cipher and possibly
static alias stack in order for the function to succeed;
EVP cipher/digest lookup routines
for more on this. Ordinaily this will not be a problem, since the
certificate will not have been written out in encrypted form.
This function can be used to read a list of certificates from a file.
PEM_read_bio_X509
does the same thing as PEM_X509_read but uses a BIO instead of a
file pointer.
The following macros are provided for the convenience of the user:
#define X509_verify(a,r)
ASN1_verify((int (*)())i2d_X509_CINF,a->sig_alg,a->signature,(char *)a->cert_info,r)
#define X509_sign(x,pkey,md)
ASN1_sign((int (*)())i2d_X509_CINF, x->cert_info->signature,
x->sig_alg, x->signature, (char *)x->cert_info,pkey,md)
#define X509_digest(data,type,md,len)
ASN1_digest((int (*)())i2d_X509,type,(char *)data,md,len)
X509_verify
converts the X509 structure a to DER-encoded form;
computes the message digest of it,
using a->sig_alg as the message digest algorithm;
signs it using r as the key for signing; and finally compares
the results against a->signature. 1 is returned if they
match, 0 on failure or error.
This function actually just calls ASN1_verify,
which calls EVP_VerifyInit, EVP_VerifyUpdate,
and EVP_VerifyFinal;
see
EVP digest handling, signing, verification
if you want details on these functions.
NOTE: this routine calls the dreaded EVP_get_digestbyname()
which means that the static digest and alias stacks must be
initialized; see EVP cipher/digest lookup routines
for more on this.
X509_sign
sets both x->cert_info->signature and x->sig_alg to hold
the appropriate information for the algorithm md->pkey_type,
which might be for example NID_shaWithRSAEncryption.
Then it converts x->cert_info to DER-encoded form,
uses the digest algorithm specified by type
to compute a message digest of the DER-encoded string and uses
the pkey to sign it. The result is placed in
x->signature->data
with the length in x->signature->length.
That same length is returned to the caller, or 0 on error.
This function actually just calls ASN1_sign, which
calls EVP_SignInit, EVP_SignUpdate, and EVP_SignFinal;
see
EVP digest handling, signing, verification
if you want details on these functions.
X509_digest
converts the X509 structure a to DER-encoded form;
computes the message digest of it,
using as the message digest algorithm;
and returns the results in md, with the length
of the returned digest in len.
1 is returned on success, or 0 on error.
This function actually just calls ASN1_digest, which calls
EVP_DigestInit, EVP_DigestUpdate, and EVP_DigestFinal;
see
EVP digest handling, signing, verification
if you want details on these functions.
The following macros are provided for the convenience of the user:
#define X509_get_version(x) ASN1_INTEGER_get((x)->cert_info->version)
#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore)
#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter)
X509_get_version
translates the ASN1_INTEGER version
subfield of the certificate pointed to
by x, to a long and returns it.
X509_get_notBefore
returns a pointer to the notBefore
subfield of the certificate pointed to
by x.
X509_get_notAfter
returns a pointer to the notAfter subfield of the certificate pointed to
by x.
X509_get_issuer_name
returns a pointer to the issuer subfield of the certificate pointed to
by a.
X509_get_subject_name
returns a pointer to the subject subfield of the certificate pointed to
by a.
X509_set_version
translates the long version to an ASN1_INTEGER and stuffs it
into the version subfield of the certificate pointed to by x.
It returns 1 on success or 0 on error.
X509_set_serialNumber
makes a copy of the ASN1_INTEGER pointed to by serial and
stuffs it into the serialNumber subfield of the certificate
pointed to by x.
It returns 1 on success or 0 on error.
X509_set_issuer_name
copies the X509_NAME pointed to by name into the
issuer subfield of the certificate pointed to by x.
It returns 1 on success or 0 on error.
X509_set_subject_name
copies the X509_NAME pointed to by name into the
subject subfield of the certificate pointed to by x.
It returns 1 on success or 0 on error.
X509_set_notBefore
copies the ASN1_UTCTIME structure pointed to by tm into the
notBefore subfield of the certificate pointed to by x.
It returns 1 on success or 0 on error.
X509_set_notAfter
copies the ASN1_UTCTIME structure pointed to by tm into the
notAfter subfield of the certificate pointed to by x.
It returns 1 on success or 0 on error.
X509_issuer_and_serial_cmp
returns an integer less than, equal to, or greater than zero,
depending on whether the serial number and issuer of a
are less than, equal to, or greater than the serial number
and issuer of b. The serial numbers are checked first and
only if those match are the issuers checked.
X509_issuer_name_cmp
returns an integer less than, equal to, or greater than zero,
depending on whether the issuer of a
is less than, equal to, or greater than the
issuer of b.
X509_subject_name_cmp
returns an integer less than, equal to, or greater than zero,
depending on whether the subject of a
is less than, equal to, or greater than the
issuer of b.
X509_issuer_and_serial_hash
stuffs the X509_NAME issuer into one printable line
and then computes the MD5 hash of it concatenated with the
ASN1_INTEGER form of the serial number of a.
It returns the first four bytes of the hash in an unsigned long.
This function is used in the library's routines which retrieve
a certificate from a directory.
X509_issuer_name_hash
converts the issuer name into DER-encoded form, applies an MD5 hash,
and returns an unsigned long with the first four bytes of the hash.
X509_subject_name_hash
converts the subject name into DER-encoded form, applies an MD5 hash,
and returns an unsigned long with the first four bytes of the hash.
X509_find_by_issuer_and_serial
searches the passed STACK sk of X509
structures and returns
a pointer to the first one which has the same issuer and serial number
as name and serial. If none do, NULL is returned.
This function calls
X509_issuer_and_serial_cmp.
X509_find_by_subject
searches the passed STACK sk of X509
structures and returns
a pointer to the first one which has the same subject
as name. If none do, NULL is returned.
This function calls
X509_NAME_cmp.
The following functions all rely on X509v3_* functions that manipulate
extenstions;
see X.509 Extension Handling for details on these.
X509_get_ext_count
just calls X509v3_get_ext_count
on the extenstions subfield of x.
X509_get_ext_by_NID
just calls X509v3_get_ext_by_NID
on the extension subfield of x,
passing the other arguments through unchanged.
X509_get_ext_by_OBJ
just calls X509v3_get_ext_by_OBJ on the extension subfield of x,
passing the other arguments through unchanged.
X509_get_ext_by_critical
just calls X509v3_get_ext_by_critical
on the extension subfield of x,
passing the other arguments through unchanged.
X509_get_ext
just calls X509v3_get_ext on the extension subfield of x,
passing the other argument through unchanged.
X509_delete_ext
just calls X509v3_delete_ext on the extension subfield of x,
passing the other argument through unchanged.
X509_add_ext
just calls X509v3_add_ext on the extension subfield of x,
passing the other arguments through unchanged.
The following functions all manipulate keys associated
with an X509 certificate. See X.509 Certificate Subfields Handling for more details on the functions
they call.
The following macro is provided for the convenience of the user:
#define X509_extract_key(x) X509_get_pubkey(x) /*****/
X509_get_pubkey
calls X509_PUBKEY_get on x->cert_info->key to extract
the public key from the certificate and turn it into
an EVP_PKEY
which can be used for encryption with EVP routines.
See Overview of EVP interfaces
for a discussion of the EVP library.
The EVP_PKEY structure is cached in the X509
structure for later use as well.
If this function is called again, a pointer to the cached copy is
used rather than doing the conversion again.
It returns a pointer to the EVP_PKEY or NULL on error.
X509_set_pubkey
calls X509_PUBKEY_set to
convert the EVP_PKEY structure pointed to by
pkey to DER-encoded form and stuff it and its various subfields
into
x->cert_info->key as appropriate.
It returns 1 on success and 0 on error.
X509_check_private_key
checks to see if the private key stored in
pkey is the same as that stored in key xk->pkey;
if it is, 1 is returned, else 0 is returned.
Note that this function always fails if a DH key is passed in pkey.
This function is called indirectly by SSL_use_RSAPrivateKey.
The following macro is provided for the convenience of the user:
#define X509_get_signature_type(x) EVP_PKEY_type(OBJ_obj2nid((x)->sig_alg->algorithm))
This just translates the x->sig_alg->algorithm field to a NID.
See EVP public/private key handling
for more on the function it invokes.
X509_certificate_type
returns a collection of flags that describe the kind of key pubkey is
and what it
can be used for. It also calls X509_get_signature_type
on x and adds that information to the flags. Types include:
EVP_PK_RSA RSA key
EVP_PK_DSA DSA key
EVP_PK_DH DH key
EVP_PKT_SIGN key used for signing
EVP_PKT_ENC key used for encryption
EVP_PKT_EXCH key used for (symmetric) key exchange
EVP_PKS_RSA signature done with RSA
EVP_PKS_DSA signature done with DSA
EVP_PKT_EXP key is <= 512 bits long (exportable)
If pubkey is NULL, x->pkey will be transformed into an EVP_PKEY
and that will be used instead.
X509_CINF_new
creates a new X509_CINF structure
and returns a pointer to it; if
memory cannot be allocated it returns NULL.
X509_CINF_free
frees the memory of a
unless the argument is NULL, in which case
it does nothing.
int i2d_X509_CINF(a, pp)
converts an X509_CINF structure
pointed to by a to DER-encoded form;
it places the results in *pp and then increments *pp to
point to the end of the byte string it has just written, so you
can call several i2d functions in a row. It returns the number of bytes
written to the string or 0 on error.
d2i_X509_CINF
converts length bytes of the
DER-encoded string in *pp to an X509_CINF structure,
updates *pp
to point to the next byte to be processed, places a pointer to
the new structure
in a, and returns it, or NULL on error.