The specification For multiple signers requires a mechanism to determine who signed the token and then determine I’d the signer had the authority to issue a token for the scope of the token. These are the steps he he necessary to perform that validation.
The CMS document is signed by a certificate, but, due to size constraints, the certificate has been stripped out of the token. All that remains in the token is the ‘signer info’ section of the CMS document, as defined here http://tools.ietf.org/html/rfc5652#page-13 as
SignerIdentifier ::= CHOICE {
issuerAndSerialNumber IssuerAndSerialNumber,
subjectKeyIdentifier [0] SubjectKeyIdentifier }
IssuerAndSerialNumber is defined as:
The IssuerAndSerialNumber type identifies a certificate, and thereby
an entity and a public key, by the distinguished name of the
certificate issuer and an issuer-specific certificate serial number.The definition of Name is taken from X.501 [X.501-88], and the
definition of CertificateSerialNumber is taken from X.509 [X.509-97].IssuerAndSerialNumber ::= SEQUENCE {
issuer Name,
serialNumber CertificateSerialNumber }CertificateSerialNumber ::= INTEGER
http://tools.ietf.org/html/rfc5652#section-10.2.4
SubjectKeyIdentifier is a more general approach which allows the user to identify if a specific certificate matches (it has the Subject Key Identifier in it as well) but does not provide a way to identify who signed it, or where to fetch the corresponding certificate.
With the Keystone server acting as the system of reference, we know where to fetch the certificate. All signing certificates can be fetched using the OS-SIMPLE-CERT extension. So, regardless of which form the signing info takes, we could determine which certificate to use in order to verify the token.
To date, only a single certificate is used to sign tokens. Identifying the signer of the token is the first step to expanding that in the future.
What does the current OpenSSL binary call produce: I took the DER form of one of the sample tokens and passed it through /usr/lib64/nss/unsupported-tools/derdump (one of the most useful of tools) and saw:
C-Sequence (816) Object Identifier (9) 1 2 840 113549 1 7 2 (PKCS #7 Signed Data) C-[0] (801) C-Sequence (797) Integer (1) 01 C-Set (13) C-Sequence (11) Object Identifier (9) 2 16 840 1 101 3 4 2 1 (SHA-256) C-Sequence (309) Object Identifier (9) 1 2 840 113549 1 7 1 (PKCS #7 Data) C-[0] (294) Octet String (290) 7b 22 61 63 63 65 73 73 22 3a 20 7b 22 74 6f 6b 65 6e 22 3a 20 7b 22 65 78 70 69 72 65 73 22 3a 20 22 32 31 31 32 2d 30 38 2d 31 37 54 31 35 3a 33 35 3a 33 34 5a 22 2c 20 22 69 64 22 3a 20 22 30 31 65 30 33 32 63 39 39 36 65 66 34 34 30 36 62 31 34 34 33 33 35 39 31 35 61 34 31 65 37 39 22 7d 2c 20 22 73 65 72 76 69 63 65 43 61 74 61 6c 6f 67 22 3a 20 7b 7d 2c 20 22 75 73 65 72 22 3a 20 7b 22 75 73 65 72 6e 61 6d 65 22 3a 20 22 75 73 65 72 5f 6e 61 6d 65 31 22 2c 20 22 72 6f 6c 65 73 5f 6c 69 6e 6b 73 22 3a 20 5b 5d 2c 20 22 69 64 22 3a 20 22 63 39 63 38 39 65 33 62 65 33 65 65 34 35 33 66 62 66 30 30 63 37 39 36 36 66 36 64 33 66 62 64 22 2c 20 22 72 6f 6c 65 73 22 3a 20 5b 7b 22 6e 61 6d 65 22 3a 20 22 72 6f 6c 65 31 22 7d 2c 20 7b 22 6e 61 6d 65 22 3a 20 22 72 6f 6c 65 32 22 7d 5d 2c 20 22 6e 61 6d 65 22 3a 20 22 75 73 65 72 5f 6e 61 6d 65 31 22 7d 7d 7d C-Set (462) C-Sequence (458) Integer (1) 01 C-Sequence (164) C-Sequence (158) C-Set (10) C-Sequence (8) Object Identifier (3) 2 5 4 5 (X520 Serial Number) Printable String (1) "5" C-Set (11) C-Sequence (9) Object Identifier (3) 2 5 4 6 (X520 Country Name) Printable String (2) "US" C-Set (11) C-Sequence (9) Object Identifier (3) 2 5 4 8 (X520 State Or Province Name) Printable String (2) "CA" C-Set (18) C-Sequence (16) Object Identifier (3) 2 5 4 7 (X520 Locality Name) Printable String (9) "Sunnyvale" C-Set (18) C-Sequence (16) Object Identifier (3) 2 5 4 10 (X520 Organization Name) Printable String (9) "OpenStack" C-Set (17) C-Sequence (15) Object Identifier (3) 2 5 4 11 (X520 Organizational Unit Name) Printable String (8) "Keystone" C-Set (37) C-Sequence (35) Object Identifier (9) 1 2 840 113549 1 9 1 (PKCS #9 Email Address) IA5 String (22) "keystone@openstack.org" C-Set (20) C-Sequence (18) Object Identifier (3) 2 5 4 3 (X520 Common Name) Printable String (11) "Self Signed" Integer (1) 11 C-Sequence (11) Object Identifier (9) 2 16 840 1 101 3 4 2 1 (SHA-256) C-Sequence (13) Object Identifier (9) 1 2 840 113549 1 1 1 (PKCS #1 RSA Encryption) NULL (0) Octet String (256) 6e 93 08 58 52 dd 52 db 65 b9 aa 9b f5 87 37 bc 56 f2 b5 25 05 a5 9b 37 68 cc 9e 2e f3 80 49 e2 58 d8 70 01 35 0a 7b 66 c7 15 2c 65 6b b3 15 31 e6 8b 8e 27 eb 12 d5 70 cd 71 b1 ae 68 fe b6 cf a6 b5 d7 a3 a6 84 d9 0d 52 d9 e6 cd 38 fa b9 7e c5 09 63 76 99 14 3a f6 5b 71 9c b7 90 9b 36 64 b5 f3 77 e6 5e ca e1 06 d1 bb a9 fc 39 4c a5 e4 b2 0b 86 ae 46 d7 40 67 9d 82 38 3c 4e 69 ee 00 d0 0d a8 d4 38 f9 8d a3 96 36 4d ed 18 6a 2f c4 09 9f 13 9e 71 b4 31 5a f9 24 57 80 52 a2 dc 69 6e e4 76 96 1b ef ae 2a cb 2d f7 fe 6d d9 6e db 3d e2 03 d1 00 00 8d 8e 2c 13 49 bf 0a 10 09 74 c0 d9 25 2f 7d 1e 8e f2 f0 ff 79 a4 ce 45 a0 4d a5 8d 4c c5 18 44 66 8a 90 a5 55 c8 6d a9 53 1c a6 d0 47 c1 26 40 45 9f 05 91 41 00 ad e0 03 6a 15 8b fc d4 7c c4 d1 26 34 0d a2 9b a7 e6 95 c7
Th field X520 Serial Number Shows it is serial number 5 from the CA. The CA is then identified by its X500 Attributes such as
X520 Common Name
Which is “Self Signed”. The following Python code will dump the signing info for the PEM version of a token. Its basically a stripped down version of this code.
#!/usr/bin/python import argparse import base64 import errno import hashlib import logging import zlib from pyasn1_modules import rfc2315, rfc2459, pem from pyasn1.codec.der import encoder, decoder from pyasn1.type import univ, char from pyasn1.codec.der import decoder as der_decoder infile=None parser = argparse.ArgumentParser() parser.add_argument('infile', metavar='infile', help='token file to decode') args = parser.parse_args() in_file = open(args.infile, 'r') idx, substrate = pem.readPemBlocksFromFile( in_file, ('-----BEGIN CMS-----', '-----END CMS-----')) contentInfo, rest = der_decoder.decode(substrate, asn1Spec=rfc2315.ContentInfo()) contentType = contentInfo.getComponentByName('contentType') contentInfoMap = { (1, 2, 840, 113549, 1, 7, 1): rfc2315.Data(), (1, 2, 840, 113549, 1, 7, 2): rfc2315.SignedData(), (1, 2, 840, 113549, 1, 7, 3): rfc2315.EnvelopedData(), (1, 2, 840, 113549, 1, 7, 4): rfc2315.SignedAndEnvelopedData(), (1, 2, 840, 113549, 1, 7, 5): rfc2315.DigestedData(), (1, 2, 840, 113549, 1, 7, 6): rfc2315.EncryptedData() } content, _ = decoder.decode( contentInfo.getComponentByName('content'), asn1Spec=contentInfoMap[contentType] ) signerInfos = content.getComponentByName('signerInfos')[0] issuer_and_sn = signerInfos.getComponentByName('issuerAndSerialNumber') issuer = issuer_and_sn.getComponentByName('issuer') for n in issuer[0]: print n[0][0] print n[0][1]
It does something wrong with the text fields, I suspect because they are some form of Not–quite-text that needs to be parsed. I don’t think I need to take this farther, however. INstead, I plan on following an example from the OCSP generating sample code that hashes the issuer and compares the hash.
UPDATE: Thanks to gsilvis who pointed out a spurious import in the code sample.