hessu / ham-cert-web-demo

Amateur Radio Certificate Authentication demo web site
10 stars 2 forks source link

Question: Format of SSL_CLIENT_S_DN #1

Open FriedrichKayak opened 10 years ago

FriedrichKayak commented 10 years ago

This is a question, not a bug/issue.

As I have implemented your suggested design, I have found a difference in the way SSL_CLIENT_S_DN is returned. The PHP does not work properly, because the parsing doesn't happen correctly. When I echo the SSL_CLIENT_S_DN variable to the page, I get:

emailAddress=someone@gmail.com,CN=John Doe,1.3.6.1.4.1.12348.1.1=#13054E34434E43

Of course, your code expects something else, but in any case, certainly not a blank call sign. And what of the trailing numbers?

I wonder if this is due to some environmental setting or perhaps a different version of Apache? I am running this on Apache 2.4.6 (Ubuntu).

FriedrichKayak commented 10 years ago

If it matters, the box is running OpenSSL 1.0.1e

hessu commented 10 years ago

1.3.6.1.4.1.12348.1.1 is the expected OID for the callsign, so that part's good. It seems like your openssl setup does not decode the callsign as a string for some reason (#13054E34434E43 is the callsign, encoded).

I'm running Apache 2.2.22 on Ubuntu, openssl 1.0.1-4ubuntu5.10 package.

Did you create the client certificate yourself, or is it one from the ARRL LoTW? Does it work against https://authtest.aprs.fi/ ?

If the certificate's callsign has been encoded correctly as a string, the workaround is probably to let openssl know about the callsign OID. This is the openssl config for my CA setup:

[ new_oids ] callSign = 1.3.6.1.4.1.12348.1.1

The trick getting this injected in the openssl instance within apache. I wonder if it'd read a default openssl.cnf from some directory on startup?

FriedrichKayak commented 10 years ago

I must be off to to work now, but I thought I'd make a short reply to let you know that it is a valid ARRL LotW certificate and it works against your authtest.aprs.fi implementation. I will work toward this later tonight around 23:00 UTC 2013-12-30.

hessu commented 10 years ago

I'm a bit busy, could you research a bit more on configuring openssl within Apache mod_ssl, if it's possible to inject openssl config file options to it somehow to add the custom OID?

FriedrichKayak commented 10 years ago

Update: I found a workaround for now, by using the +LegacyDNStringFormat option in the apache config. It manages to decode the OID properly. I haven't analyzed the root cause yet, but this works for now. Excerpt from mod_ssl documentation:

LegacyDNStringFormat This option influences how values of the SSL{CLIENT,SERVER}{I,S}_DN variables are formatted. Since version 2.3.11, Apache HTTPD uses a RFC 2253 compatible format by default. This uses commas as delimiters between the attributes, allows the use of non-ASCII characters (which are converted to UTF8), escapes various special characters with backslashes, and sorts the attributes with the "C" attribute last.

If LegacyDNStringFormat is set, the old format will be used which sorts the "C" attribute first, uses slashes as separators, and does not handle non-ASCII and special characters in any consistent way.

hessu commented 10 years ago

Ok, that's a good find.

I'm afraid the correct fix might be writing a decoder for the #13054E34434E43 representation of the callsign. I'd guess it's an ASN.1 encoded UTF8 string.

kd7lxl commented 10 years ago

I'm going to add a "me too". I updated Ubuntu to Apache 2.4.6 and it changed the SSL_CLIENT_S_DN format, breaking my callsign decoder.

hessu commented 10 years ago

Alright, a colleague happened to bump into this at work and solve it for us. The hex string is an ASN.1 encoded PrintableString. For Tom, here's the Python decoder (apt-get install python python-pyasn1, or equivalent):

from pyasn1.codec.der import decoder as der_decoder

subj = "emailAddress=someone@gmail.com,CN=John Doe,1.3.6.1.4.1.12348.1.1=#13054E34434E43"

for kv in subj.split(','):
    k, v = kv.split('=')

    print "%s: %s" % (k, v)

    if v.startswith('#'):
       b = v[1:].decode("hex")
       v = der_decoder.decode(b)[0]
       print "decoded: %s" % v

And then there might be some escaping for = and , appearing in values, and that escaping needs to be decoded. Still have to sort out how to do the ASN1 decoding in PHP...

hessu commented 10 years ago

phpseclib (http://phpseclib.sourceforge.net/) appears to have an ASN.1 parser function in pure PHP, that should work just fine.

kd7lxl commented 10 years ago

My patch is very naive. It works great with my callsign, but not others!

I played with some of the PHP ASN.1 libraries but wasn't able to get any of them decoding.