OpenPGP Signatures Incorporating X.509 Certificates
Copyright 2005 by PGP Corporation. All Rights Reserved.
Abstract
This document is published to document the procedure used by
commercial PGP [PGP] products up through version 9 for incorporating
X.509 [X.509-00] certificates into OpenPGP [RFC2440] format
signatures, and for cryptographically validating such signatures. It
describes only the format and methods needed to import X.509
certificates, and to validate key signature packets containing such
certificates. It does not deal with storage and implementation
questions.
1. Introduction
The OpenPGP [RFC2440] specification describes data formats for
representing public keys, userids, and key signatures that
cryptographically bind keys, userids, and fields from signature
packets. It further describes the cryptographic procedures used to
sign and verify the signature packets which comply with that
specification.
An alternative set of standards and formats is widely used in network
applications for communicating cryptographic bindings of public keys
and associated name data, based on the X.509 [X.509-00] certificate
format. X.509 certificates communicate similar information to
OpenPGP key, userid and signature packets. However, the details of
the data formats are incompatible and it is not possible to transform
X.509 certificates into sequences of OpenPGP packets that can be
cryptographically validated by the mechanisms described in RFC2440.
This document presents an alternative mechanism to allow X.509
certificates to be imported into OpenPGP data formats and
cryptographically validated. It describes both the detailed data
formats used to import and represent X.509 data in OpenPGP packets,
and the procedures necessary to cryptographically validate such X.509
based OpenPGP signatures. These mechanisms allow the cryptographic
infrastructure based on X.509 certificates to be imported and used in
the context of OpenPGP messages and data.
1.1. Overview of Importing Certificates
X.509 certificates are imported into OpenPGP signature packets in
three steps, which will be described in more detail in section 2.
[Page 1]
-2-
First, the numeric key material (the RSA modulus and exponent, and
corresponding values for other key types) is used to construct an
OpenPGP key packet. Second, the name field(s) of the X.509
certificate are used to construct an OpenPGP userid packet. And
third, an OpenPGP signature packet is constructed from the
certificate. Some fields of the OpenPGP signature packet are set
from corresponding fields in the certificate. In addition, the
certificate itself is inserted as a whole into the OpenPGP signature
packet, in a signature subpacket.
During the import process, the keyring is searched for a pre-existing
OpenPGP key which matches the numeric key material being imported.
If so, the userid and signature are added to the existing key. This
also requires checking to see whether there is already a userid on
the key matching the userid constructed by the import process; if so,
the new signature packet is added to the pre-existing userid. This
allows OpenPGP keys to hold a combination of X.509 based signatures
and regular OpenPGP signatures.
Once the X.509 certificate has been imported, the resulting OpenPGP
keys and userids can be used interchangeably with regular OpenPGP
keys. The only difference is in the format of the X.509 signatures.
1.2. Validating Certificates
Cryptographically validating an X.509 signature packet requires a
special process. It is not possible to use the normal OpenPGP
signature validation process because no cryptographically valid
signature exists over the OpenPGP data. Instead, a two step process
is used.
First, the X.509 certificate is tested for consistency with the
OpenPGP packets. This requires extracting the X.509 certificate from
the OpenPGP signature packet. It is then put through a process
similar to when it was first imported into the OpenPGP format per
this specification, to create temporary OpenPGP key, userid and
signature packets from the certificate data. These temporary packets
are compared with the OpenPGP key, userid and signature packets from
the OpenPGP key holding the X.509 certificate signature. There must
be an exact, byte-for-byte match between the two sets of packets for
this step of the validation to be considered successful. This
guarantees that the material in the OpenPGP packets is consistent
with the contents of the X.509 certificate.
Second, the X.509 certificate is itself cryptographically validated.
This is a straightforward matter of computing the hash over the "to
be signed" portion of the certificate and running the cryptographic
signature verification algorithm using that hash and using the
[Page 2]
-3-
signature field of the certificate. This step requires that the
cryptographic key material of the issuing certificate must be
present; for example, it may be available on the keyring as an
OpenPGP key if it was previously imported into the OpenPGP format.
Together, these two steps assure that the issuing key issued the
certificate, and that the resulting OpenPGP key, userid and signature
packets faithfully reflect the information put into the certificate
by the issuer. On this basis the X.509 certificate signature is
considered to be valid.
1.3. Identifying Signing Keys
The OpenPGP specification uses keyids to link signatures to the keys
which issued them. X.509 uses a different method, based on the
issuer name. As a result, X.509 signature packets do not have keyid
subpackets. Instead, to find the key which issued an X.509 signature
packet it is necessary to extract the X.509 certificate, parse it to
extract the issuer field, and then to search the other X.509
signature packets on the keyring for an X.509 certificate whose
subject name matches. The key to which that certificate signature is
attached is the one which issued the X.509 signature being validated.
2. Importing X.509 Certificates
Note that the rules and procedures below for importing an X.509
certificate must be followed precisely by any compatible
implementation. This is because checking the cryptographic validity
of an X.509 signature requires extracting the certificate from the
X.509 signature and repeating the import process, comparing the
results byte for byte with the OpenPGP key, userid and signature
packets. Any variation between implementations, even seemingly
inconsequential ones like changing the order of various subpackets or
userid fields, will cause this bytewise comparison to fail and cause
the signature to be treated as invalid.
2.1. Creating the Key Packet
The first step in importing an X.509 certificate into OpenPGP is to
create an OpenPGP key packet which includes the numeric key material
from the certificate. X.509 certificates holding RSA, DSS or ElGamal
keys may be imported. The data is extracted from the
SubjectPublicKeyInfo field of the certificate and converted into
OpenPGP public key format.
OpenPGP public key packets contain four additional pieces of
information. First, they have a version number. Version 3 and 4
packets are presently in use. Second, they have a creation date
[Page 3]
-4-
field. Third, version 3 packets have a field encoding the duration
until key expiration. And fourth, they hold an algorithm identifier
for the public key algorithm the key represents.
In importing the key, the keyring should be searched for an existing
key whose numeric key material matches that of the certificate being
imported. If a match is found, that key packet is used as the basis
for filling in these other fields. This uses the version number,
creation date and expiration period fields from the pre-existing key.
If no matching key exists, the new OpenPGP packet is created with
version 4, and the public key algorithm field is set from the type of
public key in the X.509 certificate. V4 keys do not have expiration
periods, so that leaves only the creation date.
X.509 certificates normally do not specify a key creation date, only
a certificate creation date. A somewhat complicated mechanism is
available to facilitate setting a reliable key creation date field in
the OpenPGP public key packet.
This mechanism is intended to facilitate allowing pre-existing
OpenPGP keys to acquire X.509 certificates on their key material.
These X.509 certificates could then be distributed and imported into
other users' OpenPGP compatible software. In order to keep the
resulting OpenPGP keys compatible with the ones which were certified,
OpenPGP encodes the key creation date into the X.509 certificate
request which is sent to be certified. With a cooperative
certificate issuing authority (CA), the key creation date is then
embedded in the certificate in a special format. This ensures that
when other users import the X.509 certificate, they will create the
OpenPGP key with the same creation date that the original key had.
This is especially important in case the importing user did not
previously have a copy of the original OpenPGP key, but acquired it
by importing the X.509 certificate; and then he later imports the
original OpenPGP key as a conventional-format OpenPGP key, rather
than as a certificate. Without the special mechanism for handling
creation dates, the creation dates wouldn't match, and the keyids of
the two keys would be different, making them appear to be two
different keys. Putting the key creation date into the X.509
certificate helps to insure that importing the certificate will
correctly reconstruct the OpenPGP key.
Two alternative mechanisms are available for encoding this
information into the X.509 certificate. The preferred mechanism is a
custom extension created for this purpose. The OID for this
extension is (1 3 6 1 4 1 3401 8 1 1), encoded as the octet string
{0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x9a, 0x49, 0x08, 0x01,
[Page 4]
-5-
0x01}. The data for the extension is defined as:
PGPExtension ::= SEQUENCE {
version Version DEFAULT v1,
keyCreation Time
}
The OpenPGP key creation data is stored in X.509 Time format, the
same format used for the notBefore and notAfter subfields of the
certificate validity field. This is converted to the four byte
OpenPGP time format specified in RFC2440 and used as the key creation
field in the new OpenPGP key packet.
The second method for encoding the OpenPGP creation date into an
X.509 certificate is used if the custom extension is not supported by
the CA. It stores the OpenPGP creation date in a field in the
subject Distinguished Name. It uses either an Organizational Unit or
Description field in the subject name. The creation date is stored
in one of these field types, with associated data in the format
"PGPKeyCreation=0x........" where the dots are replaced by 8 hex
digits that encode the creation date in OpenPGP time format. If such
a field is found, the hex value after "0x" is converted to binary
data and used as the creation date in the OpenPGP key packet.
On import, the certificate is searched sequentially for these two
possible encodings of creation date. The first match is used,
although typically there would not be more than one such encoding
present in a certificate. Because subject name comes before
extensions, the subject name fields are checked first, then the
extensions.
If none of these methods work to find an OpenPGP creation date, as
will typically be the case on a certificate which was created via
X.509 mechanisms and not by certifying an OpenPGP key, the
certificate notBefore field is converted to OpenPGP time format and
used as the key creation date.
2.2. Creating the UserID Packet
Two alternative methods are available to create an OpenPGP UserID
packet from an X.509 certificate, the short name format and the long
name format. The short name format is used if the conditions
described below are met, otherwise the long name format is used.
The short name format for the userid is of the form "common_name
<email_address>", a widely used format for OpenPGP userids. For this
format to be used it must be possible to extract a common name and
email address from the certificate. The common name must come from
[Page 5]
-6-
the subject name field. The email address may come from either the
subject name, where its OID is (1 2 840 113549 1 9 1), or from a
SubjectAlternativeName field in an extension, where the choice type
is rfc822Name.
As a special case, the short name format is also used if the subject
name has only a single field, which is an email address, but there is
no common name. In that case the userid is created as
"<email_address>".
If it is not possible to satisfy these conditions for creating the
userid in the short format, a long format is used. This is a slight
modification of "Lightweight Directory Access Protocol (v3): UTF-8
String Representation of Distinguished Names" [RFC2253], applied to
the subject name field of the certificate.
RFC2253 puts the DN fields into reverse order, but this specification
modifies that rule somewhat. The common name field (if present) is
always output first, before the other fields, to improve readability.
Also, any name field corresponding to the special OpenPGP key
creation date field described in the section 2.1, if present, is
skipped and is not output.
The only DN field names which get converted into printable form for
the userid are CN, C, L, ST, STREET, O, OU and EMAIL. Other fields
in the DN are ignored when converting to an OpenPGP userid. Note
that any SubjectAlternativeName extensions are not used when
outputting a long format name. Only the fields from the subject name
of the certificate are put into the OpenPGP userid packet.
If no fields from the list above are found in the DN, the userid
packet is created with the text "(Unknown X509 name)".
2.3. Creating the Signature Packet
Creating the OpenPGP signature packet from an X.509 certificate
involves two steps. The first is to include the certificate itself,
in total, into a new subpacket of the OpenPGP signature packet. The
second is to set up the various signature packet fields and
subpackets which encode the supported semantics of the X.509
certificate.
2.3.1. X.509 Signature Subpacket Format
This specification defines a new OpenPGP subpacket type. It uses
subpacket type 100, defined in RFC2440 as within the private range of
subpackets. The first byte of the subpacket is 1, indicating that
the type of the private-range subpacket is an X.509 certificate. The
[Page 6]
-7-
next two bytes are major and minor version numbers. The major
version is 1 for all compatible implementations of this
specification. The minor version reflects changes in the detailed
rules for how X.509 certificates are converted to OpenPGP keys. The
current minor version is 4, and this value should be used in newly
created certificate subpackets.
When verifying an X.509 certificate signature, the minor version
number must be parsed and used to guide the conversion process.
Since part of the verification involves checking for consistency
between the X.509 certificate and the OpenPGP packets, any
differences would cause the signature to be treated as invalid. When
changes are made for how this conversion is done, the minor version
must be upgraded.
The only operational difference at this point is between minor
version 4 vs. earlier minor version numbers of 3 or less. There is a
change to the public key algorithm field used in the signature
packet, as described below. No other changes presently exist based
on minor version number, for importing signature, userid or key
packets.
The remaining data in the certificate subpacket is the X.509
certificate itself. Its length is determined implicitly by the
subpacket length minus the fields already described. X.509
certificates are self-delimiting, and the certificate must end at the
end of the data range in the subpacket allocated to hold it.
2.3.2. Other Signature Fields
In addition to inserting the certificate bodily into a new subpacket,
other fields of the OpenPGP signature packet are created to be
consistent with the data from the certificate. Note that it is
necessary to follow the description in this section precisely in
order to create X.509 signature packets which will interoperate with
other software implementing this specification. No flexibility is
possible in the set of signature packets created, the contents of the
packet, or the ordering of the packets. This consistency is
necessary in order for the byte-for-byte packet comparison to succeed
when signatures are verified.
OpenPGP signature packets created from X.509 certificates are version
4 packets. The signature type field is 0x10, "generic certification
of a user id and public key packet". The next field is the public
key algorithm, which guides the signature verification process. As
described in the introduction, X.509 signature packets cannot be
cryptographically verified by the normal OpenPGP verification rules.
To flag this fact and make sure an OpenPGP implementation of this
[Page 7]
-8-
specification does not try to verify the packet, a reserved value is
used for the public key algorithm field.
This is the one place the minor version number from the certificate
subpacket is used. For minor versions 0-3, the reserved value is 0.
For minor version 4, the reserved value is 100, which is in the
reserved range for RFC2440. In either case, it is meant to indicate
that the regular OpenPGP certificate verification process will not
work, and to prevent noncompliant OpenPGP implementations from
attempting to verify signatures created by this specification.
The next field in the V4 signature packet is the hash algorithm ID,
which is set to the hash algorithm from the X.509 certificate. If a
certificate uses a public key or hash algorithm not recognized by
RFC2440, it is not allowed to be imported per this specification.
The next part of the signature packet contains the so-called hashed
subpackets. Note that these subpackets are not actually hashed, in
an X.509 signature packet; rather, their validity is verified based
on comparison with the X.509 certificate, which does get
cryptographically hashed and verified. Several such subpackets are
created, based on X.509 certificate data. The order of these
subpackets is significant in that the certificate verification
process described in section 1.2 will attempt to re-convert the X.509
certificate into an OpenPGP signature packet and do a byte-for-byte
match between the re-converted data and the original signature data.
The packets must be identical for the X.509 signature to be
considered valid.
The first subpacket is signature creation time, subpacket ID 2. This
is taken from the notBefore subfield of the validity field in the
certificate, and converted to OpenPGP time format.
The second subpacket is signature expiration time, subpacket ID 3.
This is taken from the notAfter subfield of the validity field in the
certificate, and converted to OpenPGP time format. The signature
creation time is then subtracted, because OpenPGP actually stores the
signature validity duration in seconds in the expiration time
subpacket.
Next is an optional trust signature subpacket, ID 5. It is used if
the imported certificate has a BasicConstraints extension with the
boolean cA field being true, representing a CA certificate. The
trust signature subpacket holds two bytes. The first is the trust
"level", which means the depth to which trust is delegated. This is
set based on any PathLenConstraint field of the BasicConstraints
extension. If there is no PathLenConstraint, the level value stored
in the trust packet is the maximum value of 255. If there is a
[Page 8]
-9-
PathLenConstraint value in the certificate, the trust level stored is
equal to that integer value, plus one (then truncated to one byte).
The trust packet also has a second byte representing the validity of
the trust being extended, which is set to 120, meaning complete
validity.
Next there is an optional a key flags subpacket, ID 27. It is used
if there is a KeyUsage extension in the imported certificate, to
encode any key usage restrictions. Not all bits in the KeyUsage
extension are recognized and converted by this specification, but the
key flags subpacket is created even if none of the bits are
recognized, in which case the key flags subpacket will be output with
a flag value of zero. The bits which are recognized, and the
corresponding OpenPGP key flags values, are as follows:
keyCertSign ---> flag 0x01, key can certify other keys
cRLSign ---> flag 0x01, key can certify other keys
digitalSignature ---> flag 0x02, key can sign data
nonRepudiation ---> flag 0x02, key can sign data
keyEncipherment ---> flags 0x14, key can encrypt communications
and key can encrypt storage
dataEncipherment ---> flags 0x14, key can encrypt communications
and key can encrypt storage
keyAgreement ---> flags 0x14, key can encrypt communications
and key can encrypt storage
All other key flag bits in the four byte field are output as zero.
The last subpacket output is the X.509 certificate subpacket, ID 100,
as described in section 2.3.1. above.
Following the hashed data subpackets in the signature packet are the
unhashed packets. No such packets are used when importing an X.509
signature, so the length of the unhashed region must be zero. Note
in particular that X.509 signature packets do not have an issuing
keyid field. This is because X.509 certificates refer to their
issuer by name, rather than by keyid, as discussed in section 1.3.
Next comes a two byte checksum, which is set to two bytes of zero.
Finally there is the MPI data for the OpenPGP signature. No MPI data
is used for signatures in this specification, so a dummy MPI value of
1 is stored, represented as the three byte sequence 0, 1, 1. That
ends the OpenPGP signature packet.
[Page 9]
-10-
3. References
[PGP] PGP Corporation, http://www.pgp.com.
[RFC2253] M. Wahl, S. Kille, T. Howes, "Lightweight Directory
Access Protocol (v3): UTF-8 String Representation of Distinguished
Names", RFC 2253, December, 1997.
[RFC2440] J. Callas, L. Donnerhacke, H. Finney, R. Thayer, "OpenPGP
Message Format", RFC 2440, November, 1998.
[X.509-00] ITU-T. Recommendation X.509: The Directory -
Authentication Framework. 2000.
[Page 10]