Re: [Python-Dev] frozenset C API?

I guess nobody has tried to create frozenset instances from C code before. Almost everyone uses set anyway. What are you trying to do? On 9/4/07, Bill Janssen <janssen@parc.com> wrote:
-- --Guido van Rossum (home page: http://www.python.org/~guido/)

I'm working on issue 1583946. Nagle pointed out that each DN (the "subject" and "issuer" fields in a certificate) may have multiple values for the same attribute name, and I haven't been able to rule this out yet. X.509 DNs are sets of X.500 attributes, and X.500 attributes may be either single-valued or multiple-valued. I haven't found anything in the X.509 standard that prohibits multiple-valued attributes (yet -- I'm still looking), so I'm working on an alternative to using dicts to represent the set of attributes in the certificate that's returned from ssl.sslsocket.getpeercert(). "frozenset" seems the most appropriate -- it's a non-ordered immutable set of attributes. Could use a tuple, but (1) that implies order, and (2) using set operations on the attribute set would be handy to test for various things, particularly "issubset" and "issuperset". I think frozenset is quite analogous to tuple at this level, and I suggest that a similar set of C construction functions would be a good thing. Bill

This is indeed common. In particular, DN= and OU= often occur multiple times.
X.509 DNs are sets of X.500 attributes, and X.500 attributes may be either single-valued or multiple-valued.
Conceptually perhaps (although I doubt that). Practically, Name is Name ::= CHOICE { RDNSequence } RDNSequence ::= SEQUENCE OF RelativeDistinguishedName RelativeDistinguishedName ::= SET OF AttributeTypeAndValue AttributeTypeAndValue ::= SEQUENCE { type AttributeType, value AttributeValue } So it's a sequence of sets of key/value pairs. If you want to have the same type twice, you have two options: either make multiple RDNs, each single-valued, or make a single RDN, with multiple kv-pairs. IIUC, the intention of the multi-valued RDNs is that you have an entity described by multiple attributes. For example, relative to O=Foo, neither GN=Bill nor SN=Janssen might correctly identify a person. So you would create O=Foo,GN=Bill+SN=Janssen. That's allowed, but not really common - instead, people both a) use CN as a unique identifier, and b) put separate attributes for a single object into separate RDNs, as if email=janssen@parc.com was a subnode in the DIT relative to CN="Bill Janssen".
Conceptually, it should be a list (order *is* relevant). It can then be debated whether the RDN can be represented as a dictionary; my understanding is that the intention of RDNs is that the AttributeType is unique within an RDN (but I may be wrong). Regards, Martin

I got that from David Chadwick's book at http://sec.cs.kent.ac.uk/x500book/. ``An attribute comprises an attribute type and one or more attribute values.'' The question is, how would a multiple-valued attribute be represented in a certificate Name? I'm presuming it would appear as multiple attributes with the same "type", but different values.
Order is important in the directory tree, but not (I think) in the DN; that name is just an unordered set of attributes, because the hierarchy information has already been lost (the RDN elements cannot be distinguished from each other using only the internal certificate information). In any case, it certainly sounds to me as if there can be multiple instances of AttributeTypeAndValue with the same "type" field in a single Name. So I'll represent them as tuples, which will preserve the order in which they occur in the certificate, and make the value immutable. Applications which need them as sets can create their own frozensets from that tuple. Bill

Here's an example of the new format: {'issuer': (('countryName', u'US'), ('organizationName', u'VeriSign, Inc.'), ('organizationalUnitName', u'VeriSign Trust Network'), ('organizationalUnitName', u'Terms of use at https://www.verisign.com/rpa (c)06'), ('commonName', u'VeriSign Class 3 Extended Validation SSL SGC CA')), 'notAfter': 'May 8 23:59:59 2009 GMT', 'notBefore': 'May 9 00:00:00 2007 GMT', 'subject': (('serialNumber', u'2497886'), ('1.3.6.1.4.1.311.60.2.1.3', u'US'), ('1.3.6.1.4.1.311.60.2.1.2', u'Delaware'), ('countryName', u'US'), ('postalCode', u'94043'), ('stateOrProvinceName', u'California'), ('localityName', u'Mountain View'), ('streetAddress', u'487 East Middlefield Road'), ('organizationName', u'VeriSign, Inc.'), ('organizationalUnitName', u'Production Security Services'), ('organizationalUnitName', u'Terms of use at www.verisign.com/rpa (c)06'), ('commonName', u'www.verisign.com')), 'version': 2} Bill

Ah, I see what you're driving at. You can inspect them yourself by looking at the certs with openssl: % openssl x509 -text -in attachment-0002.crt Certificate: Data: Version: 3 (0x2) Serial Number: a9:29:70:b4:3a:72:27:5a Signature Algorithm: sha1WithRSAEncryption Issuer: DC=org, DC=python, CN=foo, CN=bar Validity Not Before: Sep 5 05:38:20 2007 GMT Not After : Sep 4 05:38:20 2008 GMT Subject: DC=org, DC=python, CN=foo, CN=bar Subject Public Key Info ... % openssl x509 -text -in attachment-0003.crt Certificate: Data: Version: 3 (0x2) Serial Number: 82:0a:4f:36:0f:ab:1a:c3 Signature Algorithm: sha1WithRSAEncryption Issuer: DC=org, DC=python, CN=bar2, CN=foo2 Validity Not Before: Sep 5 05:43:26 2007 GMT Not After : Sep 4 05:43:26 2008 GMT Subject: DC=org, DC=python, CN=bar2, CN=foo2 Subject Public Key Info: The hierarchy information does not appear to be preserved. Bill

The hierarchy information does not appear to be preserved.
But it only appears so. OpenSSL does not know how to render it properly (hence I say it is not very common in PKI), but they started supporting that when generating certificates, with the -multivalue-rdn option for req, and if you do openssl asn1parse -in ca1.crt you see that they differ: (ca1) l= 17 cons: SEQUENCE l= 10 prim: OBJECT :domainComponent l= 3 prim: IA5STRING :org l= 22 cons: SET l= 20 cons: SEQUENCE l= 10 prim: OBJECT :domainComponent l= 6 prim: IA5STRING :python l= 12 cons: SET l= 10 cons: SEQUENCE l= 3 prim: OBJECT :commonName l= 3 prim: PRINTABLESTRING :foo l= 12 cons: SET l= 10 cons: SEQUENCE l= 3 prim: OBJECT :commonName l= 3 prim: PRINTABLESTRING :bar (ca2) l= 17 cons: SEQUENCE l= 10 prim: OBJECT :domainComponent l= 3 prim: IA5STRING :org l= 22 cons: SET l= 20 cons: SEQUENCE l= 10 prim: OBJECT :domainComponent l= 6 prim: IA5STRING :python l= 26 cons: SET l= 11 cons: SEQUENCE l= 3 prim: OBJECT :commonName l= 4 prim: PRINTABLESTRING :bar2 l= 11 cons: SEQUENCE l= 3 prim: OBJECT :commonName l= 4 prim: PRINTABLESTRING :foo2 In the first case, foo and bar are in different sets, in the second case, they are in the same set. For people concerned about security, that makes a difference. If OpenSSL actually supports that in its APIs, my proposal would be to make a multi-valued RDN a more-than-two-tuple, e.g. (('DC','org'),('DC','python'),('CN','bar2','CN','foo2')) That would make it possible to distinguish the names (pun intended), yet still don't produce structural overhead for the normal case of single-valued RDNs. Regards, Martin

More succinctly: % openssl x509 -subject -noout -in attachment-0002.crt subject= /DC=org/DC=python/CN=foo/CN=bar % openssl x509 -subject -noout -in attachment-0003.crt subject= /DC=org/DC=python/CN=bar2/CN=foo2 % Bill

Ah, ok. But then, the DN is not a *set* of such attributes, but a sequence.
Within a single RelativeDistinguishedName, yes.
Hmm. The directory tree only exists through the order in the DN. E.g from http://java.sun.com/products/jndi/tutorial/ldap/models/x500.html "The X.500 namespace is hierarchical. An entry is unambiguously identified by a distinguished name (DN). A distinguished name is the concatenation of selected attributes from each entry, called the relative distinguished name (RDN), in the tree along a path leading from the root down to the named entry." If the RDNs within a DN would not be ordered, you would not get a hierarchical tree, and you could not identify entries unambiguously.
Ok. I think this will still not support multi-valued RDNs properly, but those are uncommon in PKI. Regards, Martin

Yup, got it. I don't see a way in the OpenSSL library functions I'm using (X509_NAME_ENTRY_get_object, X509_NAME_ENTRY_get_data) to distinguish between different RDNs, but I'll take a look at the source for X509_NAME_print_ex, which does seem to be able to do this. Bill

See my other proposal as well. As nobody actually uses multi-valued RDNs, an option would be to make single tuple for each RDN, containing all attributes, with alternatingly type and value. Then, a single-valued RDN would turn out as a key-value pair (two-tuple), a multi-valued RDN would have a length of 2*number-of-attributes. As for accessor functions, I'd then rather see a get_attr_by_type, returning a list of all values of attributes of that type, across all RDNs in the DN (empty if no attribute was found). People would then do x = get_attr_by_type(subj, ssl.commonName) if len(x) != 1: unsupported_certificate() CN = x[0] Regards, Martin

OK, I can make it a tuple (list of RDNs) of tuples (one for each RDN) of tuples (one for each attribute in the RDN).
Which gets us to this: {'issuer': ((('countryName', u'US'),), (('stateOrProvinceName', u'Delaware'),), (('localityName', u'Wilmington'),), (('organizationName', u'Python Software Foundation'),), (('organizationalUnitName', u'SSL'),), (('commonName', u'somemachine.python.org'),)), 'notAfter': 'Feb 16 16:54:50 2013 GMT', 'notBefore': 'Aug 27 16:54:50 2007 GMT', 'subject': ((('countryName', u'US'),), (('stateOrProvinceName', u'Delaware'),), (('localityName', u'Wilmington'),), (('organizationName', u'Python Software Foundation'),), (('organizationalUnitName', u'SSL'),), (('commonName', u'somemachine.python.org'),)), 'version': 2} and {'issuer': ((('countryName', u'US'),), (('organizationName', u'VeriSign, Inc.'),), (('organizationalUnitName', u'VeriSign Trust Network'),), (('organizationalUnitName', u'Terms of use at https://www.verisign.com/rpa (c)06'),), (('commonName', u'VeriSign Class 3 Extended Validation SSL SGC CA'),)), 'notAfter': 'May 8 23:59:59 2009 GMT', 'notBefore': 'May 9 00:00:00 2007 GMT', 'subject': ((('serialNumber', u'2497886'),), (('1.3.6.1.4.1.311.60.2.1.3', u'US'),), (('1.3.6.1.4.1.311.60.2.1.2', u'Delaware'),), (('countryName', u'US'),), (('postalCode', u'94043'),), (('stateOrProvinceName', u'California'),), (('localityName', u'Mountain View'),), (('streetAddress', u'487 East Middlefield Road'),), (('organizationName', u'VeriSign, Inc.'),), (('organizationalUnitName', u'Production Security Services'),), (('organizationalUnitName', u'Terms of use at www.verisign.com/rpa (c)06'),), (('commonName', u'www.verisign.com'),)), 'version': 2} Ugly, but accurate. Or is it? Do you really think that "serialNumber" is at the top of a naming tree somewhere? Bill

Firefox claims the same order. To bad Verisign hasn't grasped the concept of distinguished names :-( Had they done it right, incorporationStateId, incorporationLocalityId, streetAddress, localityName, postalCode would all have been in the RDN with organizationName - they are all attributes of that organization (or the address attributes perhaps belong to the OU). Also, I doubt they have an organizationalUnit "Terms of use at ...". Regards, Martin

All of this makes me think that some folks may want to do more processing on certificates with more advanced tools, and for that they will need access to the full bits of the certificate. I'll add the ability to retrieve that as well. I'm wondering if I should try to pull some extension attributes out of the cert, and add them to the dict, as well. Like subjectAltName, for instance. Or should we just wait till someone wants it and files a bug report? Bill

If you have the time and inclination to do that, feel free to. Covering some of the most widely used extensions could be useful: subjectAltName, key usage, extended key usage. If you set up a framework for that, people will contribute others they like to see supported. Regards, Martin

Simpler to provide them all, though I should note that the purpose of the information provided here is mainly for authorization/accounting purposes, not for "other" use of the certificate. If that's desired, they should pull the binary form of the certificate (there's an interface for that), and use M2Crypto or PyOpenSSL to decode it in general. This certificate has already been validated; the issue is how to get critical information to the app so it can make authorization decisions (like subjectAltName when the subject field is empty). Reporting non-critical extensions like "extended key usage" is nifty, but seems pointless. Here's an example: {'extensions': {'Netscape Cert Type': u'SSL Server'}, 'issuer': ((('countryName', u'US'),), (('stateOrProvinceName', u'Delaware'),), (('localityName', u'Wilmington'),), (('organizationName', u'Python Software Foundation'),), (('organizationalUnitName', u'SSL'),), (('commonName', u'somemachine.python.org'),)), 'notAfter': 'Feb 16 16:54:50 2013 GMT', 'notBefore': 'Aug 27 16:54:50 2007 GMT', 'serialNumber': 'FFAA4ADBF570818D', 'subject': ((('countryName', u'US'),), (('stateOrProvinceName', u'Delaware'),), (('localityName', u'Wilmington'),), (('organizationName', u'Python Software Foundation'),), (('organizationalUnitName', u'SSL'),), (('commonName', u'somemachine.python.org'),)), 'version': 3} and {'extensions': {'1.3.6.1.5.5.7.1.12': u'', 'Authority Information Access': u'OCSP - URI:http://EVIntl-ocsp.verisign.com\n', 'X509v3 Authority Key Identifier': u'keyid:4E:43:C8:1D:76:EF:37:53:7A:4F:F2:58:6F:94:F3:38:E2:D5:BD:DF\n', 'X509v3 Basic Constraints': u'CA:FALSE', 'X509v3 CRL Distribution Points': u'URI:http://EVIntl-crl.verisign.com/EVIntl2006.crl\n', 'X509v3 Certificate Policies': u'Policy: 2.16.840.1.113733.1.7.23.6\n', 'X509v3 Extended Key Usage': u'TLS Web Server Authentication, TLS Web Client Authentication, Netscape Server Gated Crypto, Microsoft Server Gated Crypto', 'X509v3 Key Usage': u'Digital Signature, Key Encipherment', 'X509v3 Subject Key Identifier': u'F1:5A:89:93:55:47:4B:BA:51:F5:4E:E0:CB:16:55:F4:D7:CC:38:67'}, 'issuer': ((('countryName', u'US'),), (('organizationName', u'VeriSign, Inc.'),), (('organizationalUnitName', u'VeriSign Trust Network'),), (('organizationalUnitName', u'Terms of use at https://www.verisign.com/rpa (c)06'),), (('commonName', u'VeriSign Class 3 Extended Validation SSL SGC CA'),)), 'notAfter': 'May 8 23:59:59 2009 GMT', 'notBefore': 'May 9 00:00:00 2007 GMT', 'serialNumber': '6A4AC31B3110E6EB48F0FC51A39A171F', 'subject': ((('serialNumber', u'2497886'),), (('1.3.6.1.4.1.311.60.2.1.3', u'US'),), (('1.3.6.1.4.1.311.60.2.1.2', u'Delaware'),), (('countryName', u'US'),), (('postalCode', u'94043'),), (('stateOrProvinceName', u'California'),), (('localityName', u'Mountain View'),), (('streetAddress', u'487 East Middlefield Road'),), (('organizationName', u'VeriSign, Inc.'),), (('organizationalUnitName', u'Production Security Services'),), (('organizationalUnitName', u'Terms of use at www.verisign.com/rpa (c)06'),), (('commonName', u'www.verisign.com'),)), 'version': 3} Probably another thing that *should* be reported is the cipher used to protect the information on the channel, so that the app can decide whether it's strong enough for its taste. (If it's not, it can presumably reconnect using a different variant of SSL to try for a better result, or decide not to use the server (or talk to the client) at all.) Bill

I very much doubt that, at least if you want to report decoded information. Conceptually, there is an infinite number of extensions, and when you are done, I can show you lots of certificates that have extensions that you don't support.
Hmm. In this certificate, none of the extensions you report have been marked critical; they are all non-critical. Also, you are reporting the logotype (1.3.6.1.5.5.7.1.12) incorrectly; it's defined in RFC 3709, and it's definitely not an empty string in the certificate you've used. Regards, Martin

I'm not going to decode anything; I'm just using the OpenSSL functionality and providing whatever it provides.
Hmm. In this certificate, none of the extensions you report have been marked critical; they are all non-critical.
That's what I meant by "simpler to show everything".
Yes, I see. I'll poke at the OpenSSL code harder :-). Bill

I'm working on issue 1583946. Nagle pointed out that each DN (the "subject" and "issuer" fields in a certificate) may have multiple values for the same attribute name, and I haven't been able to rule this out yet. X.509 DNs are sets of X.500 attributes, and X.500 attributes may be either single-valued or multiple-valued. I haven't found anything in the X.509 standard that prohibits multiple-valued attributes (yet -- I'm still looking), so I'm working on an alternative to using dicts to represent the set of attributes in the certificate that's returned from ssl.sslsocket.getpeercert(). "frozenset" seems the most appropriate -- it's a non-ordered immutable set of attributes. Could use a tuple, but (1) that implies order, and (2) using set operations on the attribute set would be handy to test for various things, particularly "issubset" and "issuperset". I think frozenset is quite analogous to tuple at this level, and I suggest that a similar set of C construction functions would be a good thing. Bill

This is indeed common. In particular, DN= and OU= often occur multiple times.
X.509 DNs are sets of X.500 attributes, and X.500 attributes may be either single-valued or multiple-valued.
Conceptually perhaps (although I doubt that). Practically, Name is Name ::= CHOICE { RDNSequence } RDNSequence ::= SEQUENCE OF RelativeDistinguishedName RelativeDistinguishedName ::= SET OF AttributeTypeAndValue AttributeTypeAndValue ::= SEQUENCE { type AttributeType, value AttributeValue } So it's a sequence of sets of key/value pairs. If you want to have the same type twice, you have two options: either make multiple RDNs, each single-valued, or make a single RDN, with multiple kv-pairs. IIUC, the intention of the multi-valued RDNs is that you have an entity described by multiple attributes. For example, relative to O=Foo, neither GN=Bill nor SN=Janssen might correctly identify a person. So you would create O=Foo,GN=Bill+SN=Janssen. That's allowed, but not really common - instead, people both a) use CN as a unique identifier, and b) put separate attributes for a single object into separate RDNs, as if email=janssen@parc.com was a subnode in the DIT relative to CN="Bill Janssen".
Conceptually, it should be a list (order *is* relevant). It can then be debated whether the RDN can be represented as a dictionary; my understanding is that the intention of RDNs is that the AttributeType is unique within an RDN (but I may be wrong). Regards, Martin

I got that from David Chadwick's book at http://sec.cs.kent.ac.uk/x500book/. ``An attribute comprises an attribute type and one or more attribute values.'' The question is, how would a multiple-valued attribute be represented in a certificate Name? I'm presuming it would appear as multiple attributes with the same "type", but different values.
Order is important in the directory tree, but not (I think) in the DN; that name is just an unordered set of attributes, because the hierarchy information has already been lost (the RDN elements cannot be distinguished from each other using only the internal certificate information). In any case, it certainly sounds to me as if there can be multiple instances of AttributeTypeAndValue with the same "type" field in a single Name. So I'll represent them as tuples, which will preserve the order in which they occur in the certificate, and make the value immutable. Applications which need them as sets can create their own frozensets from that tuple. Bill

Here's an example of the new format: {'issuer': (('countryName', u'US'), ('organizationName', u'VeriSign, Inc.'), ('organizationalUnitName', u'VeriSign Trust Network'), ('organizationalUnitName', u'Terms of use at https://www.verisign.com/rpa (c)06'), ('commonName', u'VeriSign Class 3 Extended Validation SSL SGC CA')), 'notAfter': 'May 8 23:59:59 2009 GMT', 'notBefore': 'May 9 00:00:00 2007 GMT', 'subject': (('serialNumber', u'2497886'), ('1.3.6.1.4.1.311.60.2.1.3', u'US'), ('1.3.6.1.4.1.311.60.2.1.2', u'Delaware'), ('countryName', u'US'), ('postalCode', u'94043'), ('stateOrProvinceName', u'California'), ('localityName', u'Mountain View'), ('streetAddress', u'487 East Middlefield Road'), ('organizationName', u'VeriSign, Inc.'), ('organizationalUnitName', u'Production Security Services'), ('organizationalUnitName', u'Terms of use at www.verisign.com/rpa (c)06'), ('commonName', u'www.verisign.com')), 'version': 2} Bill

Ah, I see what you're driving at. You can inspect them yourself by looking at the certs with openssl: % openssl x509 -text -in attachment-0002.crt Certificate: Data: Version: 3 (0x2) Serial Number: a9:29:70:b4:3a:72:27:5a Signature Algorithm: sha1WithRSAEncryption Issuer: DC=org, DC=python, CN=foo, CN=bar Validity Not Before: Sep 5 05:38:20 2007 GMT Not After : Sep 4 05:38:20 2008 GMT Subject: DC=org, DC=python, CN=foo, CN=bar Subject Public Key Info ... % openssl x509 -text -in attachment-0003.crt Certificate: Data: Version: 3 (0x2) Serial Number: 82:0a:4f:36:0f:ab:1a:c3 Signature Algorithm: sha1WithRSAEncryption Issuer: DC=org, DC=python, CN=bar2, CN=foo2 Validity Not Before: Sep 5 05:43:26 2007 GMT Not After : Sep 4 05:43:26 2008 GMT Subject: DC=org, DC=python, CN=bar2, CN=foo2 Subject Public Key Info: The hierarchy information does not appear to be preserved. Bill

The hierarchy information does not appear to be preserved.
But it only appears so. OpenSSL does not know how to render it properly (hence I say it is not very common in PKI), but they started supporting that when generating certificates, with the -multivalue-rdn option for req, and if you do openssl asn1parse -in ca1.crt you see that they differ: (ca1) l= 17 cons: SEQUENCE l= 10 prim: OBJECT :domainComponent l= 3 prim: IA5STRING :org l= 22 cons: SET l= 20 cons: SEQUENCE l= 10 prim: OBJECT :domainComponent l= 6 prim: IA5STRING :python l= 12 cons: SET l= 10 cons: SEQUENCE l= 3 prim: OBJECT :commonName l= 3 prim: PRINTABLESTRING :foo l= 12 cons: SET l= 10 cons: SEQUENCE l= 3 prim: OBJECT :commonName l= 3 prim: PRINTABLESTRING :bar (ca2) l= 17 cons: SEQUENCE l= 10 prim: OBJECT :domainComponent l= 3 prim: IA5STRING :org l= 22 cons: SET l= 20 cons: SEQUENCE l= 10 prim: OBJECT :domainComponent l= 6 prim: IA5STRING :python l= 26 cons: SET l= 11 cons: SEQUENCE l= 3 prim: OBJECT :commonName l= 4 prim: PRINTABLESTRING :bar2 l= 11 cons: SEQUENCE l= 3 prim: OBJECT :commonName l= 4 prim: PRINTABLESTRING :foo2 In the first case, foo and bar are in different sets, in the second case, they are in the same set. For people concerned about security, that makes a difference. If OpenSSL actually supports that in its APIs, my proposal would be to make a multi-valued RDN a more-than-two-tuple, e.g. (('DC','org'),('DC','python'),('CN','bar2','CN','foo2')) That would make it possible to distinguish the names (pun intended), yet still don't produce structural overhead for the normal case of single-valued RDNs. Regards, Martin

More succinctly: % openssl x509 -subject -noout -in attachment-0002.crt subject= /DC=org/DC=python/CN=foo/CN=bar % openssl x509 -subject -noout -in attachment-0003.crt subject= /DC=org/DC=python/CN=bar2/CN=foo2 % Bill

Ah, ok. But then, the DN is not a *set* of such attributes, but a sequence.
Within a single RelativeDistinguishedName, yes.
Hmm. The directory tree only exists through the order in the DN. E.g from http://java.sun.com/products/jndi/tutorial/ldap/models/x500.html "The X.500 namespace is hierarchical. An entry is unambiguously identified by a distinguished name (DN). A distinguished name is the concatenation of selected attributes from each entry, called the relative distinguished name (RDN), in the tree along a path leading from the root down to the named entry." If the RDNs within a DN would not be ordered, you would not get a hierarchical tree, and you could not identify entries unambiguously.
Ok. I think this will still not support multi-valued RDNs properly, but those are uncommon in PKI. Regards, Martin

Yup, got it. I don't see a way in the OpenSSL library functions I'm using (X509_NAME_ENTRY_get_object, X509_NAME_ENTRY_get_data) to distinguish between different RDNs, but I'll take a look at the source for X509_NAME_print_ex, which does seem to be able to do this. Bill

See my other proposal as well. As nobody actually uses multi-valued RDNs, an option would be to make single tuple for each RDN, containing all attributes, with alternatingly type and value. Then, a single-valued RDN would turn out as a key-value pair (two-tuple), a multi-valued RDN would have a length of 2*number-of-attributes. As for accessor functions, I'd then rather see a get_attr_by_type, returning a list of all values of attributes of that type, across all RDNs in the DN (empty if no attribute was found). People would then do x = get_attr_by_type(subj, ssl.commonName) if len(x) != 1: unsupported_certificate() CN = x[0] Regards, Martin

OK, I can make it a tuple (list of RDNs) of tuples (one for each RDN) of tuples (one for each attribute in the RDN).
Which gets us to this: {'issuer': ((('countryName', u'US'),), (('stateOrProvinceName', u'Delaware'),), (('localityName', u'Wilmington'),), (('organizationName', u'Python Software Foundation'),), (('organizationalUnitName', u'SSL'),), (('commonName', u'somemachine.python.org'),)), 'notAfter': 'Feb 16 16:54:50 2013 GMT', 'notBefore': 'Aug 27 16:54:50 2007 GMT', 'subject': ((('countryName', u'US'),), (('stateOrProvinceName', u'Delaware'),), (('localityName', u'Wilmington'),), (('organizationName', u'Python Software Foundation'),), (('organizationalUnitName', u'SSL'),), (('commonName', u'somemachine.python.org'),)), 'version': 2} and {'issuer': ((('countryName', u'US'),), (('organizationName', u'VeriSign, Inc.'),), (('organizationalUnitName', u'VeriSign Trust Network'),), (('organizationalUnitName', u'Terms of use at https://www.verisign.com/rpa (c)06'),), (('commonName', u'VeriSign Class 3 Extended Validation SSL SGC CA'),)), 'notAfter': 'May 8 23:59:59 2009 GMT', 'notBefore': 'May 9 00:00:00 2007 GMT', 'subject': ((('serialNumber', u'2497886'),), (('1.3.6.1.4.1.311.60.2.1.3', u'US'),), (('1.3.6.1.4.1.311.60.2.1.2', u'Delaware'),), (('countryName', u'US'),), (('postalCode', u'94043'),), (('stateOrProvinceName', u'California'),), (('localityName', u'Mountain View'),), (('streetAddress', u'487 East Middlefield Road'),), (('organizationName', u'VeriSign, Inc.'),), (('organizationalUnitName', u'Production Security Services'),), (('organizationalUnitName', u'Terms of use at www.verisign.com/rpa (c)06'),), (('commonName', u'www.verisign.com'),)), 'version': 2} Ugly, but accurate. Or is it? Do you really think that "serialNumber" is at the top of a naming tree somewhere? Bill

Firefox claims the same order. To bad Verisign hasn't grasped the concept of distinguished names :-( Had they done it right, incorporationStateId, incorporationLocalityId, streetAddress, localityName, postalCode would all have been in the RDN with organizationName - they are all attributes of that organization (or the address attributes perhaps belong to the OU). Also, I doubt they have an organizationalUnit "Terms of use at ...". Regards, Martin

All of this makes me think that some folks may want to do more processing on certificates with more advanced tools, and for that they will need access to the full bits of the certificate. I'll add the ability to retrieve that as well. I'm wondering if I should try to pull some extension attributes out of the cert, and add them to the dict, as well. Like subjectAltName, for instance. Or should we just wait till someone wants it and files a bug report? Bill

If you have the time and inclination to do that, feel free to. Covering some of the most widely used extensions could be useful: subjectAltName, key usage, extended key usage. If you set up a framework for that, people will contribute others they like to see supported. Regards, Martin

Simpler to provide them all, though I should note that the purpose of the information provided here is mainly for authorization/accounting purposes, not for "other" use of the certificate. If that's desired, they should pull the binary form of the certificate (there's an interface for that), and use M2Crypto or PyOpenSSL to decode it in general. This certificate has already been validated; the issue is how to get critical information to the app so it can make authorization decisions (like subjectAltName when the subject field is empty). Reporting non-critical extensions like "extended key usage" is nifty, but seems pointless. Here's an example: {'extensions': {'Netscape Cert Type': u'SSL Server'}, 'issuer': ((('countryName', u'US'),), (('stateOrProvinceName', u'Delaware'),), (('localityName', u'Wilmington'),), (('organizationName', u'Python Software Foundation'),), (('organizationalUnitName', u'SSL'),), (('commonName', u'somemachine.python.org'),)), 'notAfter': 'Feb 16 16:54:50 2013 GMT', 'notBefore': 'Aug 27 16:54:50 2007 GMT', 'serialNumber': 'FFAA4ADBF570818D', 'subject': ((('countryName', u'US'),), (('stateOrProvinceName', u'Delaware'),), (('localityName', u'Wilmington'),), (('organizationName', u'Python Software Foundation'),), (('organizationalUnitName', u'SSL'),), (('commonName', u'somemachine.python.org'),)), 'version': 3} and {'extensions': {'1.3.6.1.5.5.7.1.12': u'', 'Authority Information Access': u'OCSP - URI:http://EVIntl-ocsp.verisign.com\n', 'X509v3 Authority Key Identifier': u'keyid:4E:43:C8:1D:76:EF:37:53:7A:4F:F2:58:6F:94:F3:38:E2:D5:BD:DF\n', 'X509v3 Basic Constraints': u'CA:FALSE', 'X509v3 CRL Distribution Points': u'URI:http://EVIntl-crl.verisign.com/EVIntl2006.crl\n', 'X509v3 Certificate Policies': u'Policy: 2.16.840.1.113733.1.7.23.6\n', 'X509v3 Extended Key Usage': u'TLS Web Server Authentication, TLS Web Client Authentication, Netscape Server Gated Crypto, Microsoft Server Gated Crypto', 'X509v3 Key Usage': u'Digital Signature, Key Encipherment', 'X509v3 Subject Key Identifier': u'F1:5A:89:93:55:47:4B:BA:51:F5:4E:E0:CB:16:55:F4:D7:CC:38:67'}, 'issuer': ((('countryName', u'US'),), (('organizationName', u'VeriSign, Inc.'),), (('organizationalUnitName', u'VeriSign Trust Network'),), (('organizationalUnitName', u'Terms of use at https://www.verisign.com/rpa (c)06'),), (('commonName', u'VeriSign Class 3 Extended Validation SSL SGC CA'),)), 'notAfter': 'May 8 23:59:59 2009 GMT', 'notBefore': 'May 9 00:00:00 2007 GMT', 'serialNumber': '6A4AC31B3110E6EB48F0FC51A39A171F', 'subject': ((('serialNumber', u'2497886'),), (('1.3.6.1.4.1.311.60.2.1.3', u'US'),), (('1.3.6.1.4.1.311.60.2.1.2', u'Delaware'),), (('countryName', u'US'),), (('postalCode', u'94043'),), (('stateOrProvinceName', u'California'),), (('localityName', u'Mountain View'),), (('streetAddress', u'487 East Middlefield Road'),), (('organizationName', u'VeriSign, Inc.'),), (('organizationalUnitName', u'Production Security Services'),), (('organizationalUnitName', u'Terms of use at www.verisign.com/rpa (c)06'),), (('commonName', u'www.verisign.com'),)), 'version': 3} Probably another thing that *should* be reported is the cipher used to protect the information on the channel, so that the app can decide whether it's strong enough for its taste. (If it's not, it can presumably reconnect using a different variant of SSL to try for a better result, or decide not to use the server (or talk to the client) at all.) Bill

I very much doubt that, at least if you want to report decoded information. Conceptually, there is an infinite number of extensions, and when you are done, I can show you lots of certificates that have extensions that you don't support.
Hmm. In this certificate, none of the extensions you report have been marked critical; they are all non-critical. Also, you are reporting the logotype (1.3.6.1.5.5.7.1.12) incorrectly; it's defined in RFC 3709, and it's definitely not an empty string in the certificate you've used. Regards, Martin

I'm not going to decode anything; I'm just using the OpenSSL functionality and providing whatever it provides.
Hmm. In this certificate, none of the extensions you report have been marked critical; they are all non-critical.
That's what I meant by "simpler to show everything".
Yes, I see. I'll poke at the OpenSSL code harder :-). Bill
participants (3)
-
"Martin v. Löwis"
-
Bill Janssen
-
Guido van Rossum