On Thursday, 30 July 2015 01:35:52 UTC+2, David Keeler wrote:
> [cc'd to dev-security for visibility. This discussion is intended to
> happen on dev-platform; please reply to that list.]
>
> Ryan Sleevi recently announced the pre-intention to deprecate and
> eventually remove support for the <keygen> element and special-case
> handling of the application/x-x509-*-cert MIME types from the blink
> platform (i.e. Chrome).
>
> Rather than reiterate his detailed analysis, I'll refer to the post here:
>
>
https://groups.google.com/a/chromium.org/d/msg/blink-dev/pX5NbX0Xack/kmHsyMGJZAMJ
>
One of the elements of his detailed analysis is under fire, namely that the reliance of keygen on PEM is a key security hole. I made this case in more detail here:
https://github.com/whatwg/html/issues/102
But to avoid your needing to click on the link here it is in full:
<keygen> has been deprecated a few days ago, and the issue has been taken up by @timbl on the Technical Architecture Group as it removes a useful tool in the asymmetric public key cryptography available in browsers.
One reason given for deprecating that recurs often is that keygen uses MD5 which opens an attack vector presented in a very good paper "MD5 considered harmful today" at the Chaos Communication Congress in Berlin in 2008 by a number of researchers of which Jacob Appelbaum ( aka. @ioerror ) . ( That was the time when Julian Assange was working freely on Wikileaks, so you can even hear him ask a question at the end )
The slides are here:
https://www.trailofbits.com/resources/creating_a_rogue_ca_cert_slides.pdf
The video is here:
http://chaosradio.ccc.de/25c3_m4v_3023.html
In short they were able to create a fake Certificate Authority (CA) because the CA signed its certificates with MD5 and they were able to create hash collisions, to use the certificate signed by the CA
and change some information in it to produce their own top level certificate, with which
they could create a certificate for any site they wished to! ( Pretty awesomely bad - though they did this carefully to avoid misuse ). This is why projects such as IETFs DANE, DNSSEC, and many other improvements to the internet infrastructure are vital.
This was 7 years ago, so all of this should be fixed by now. There should be no CA signing
Server Certificates with MD5 anymore.
Great. But that has nothing to do with what is going on with <keygen>. The problem
may well be that the documentation of <keygen> is misleading here. The WHATWG documentation on keygen currently states:
If the keytype attribute is in the RSA state: Generate an RSA key pair using the settings given by the user, if appropriate, using the md5WithRSAEncryption RSA signature algorithm (the signature algorithm with MD5 and the RSA encryption algorithm) referenced in section 2.2.1 ("RSA Signature Algorithm") of RFC 3279, and defined in RFC 3447. [RFC3279] [RFC3447]
By whether or not keygen wraps the key and signs it with MD5 is of not much importance, since this is the keyrequest we are speaking of here, not the generated certificate!
To summarise how the keygen is actually used:
1. The browser creates a public/private key, saves the private key in the secure keychain
2. and sends the public key in an spkac request to the server which
3. which on receipt of the certificate request and verification of the data, uses that to create a Client Certificate using any signature algorithm it wants for the creation of the certificate ( And so it SHOULD NOT USE MD5: see CCC talk above )
4. which it returns using one of the x509 mime types available to it,
Here is an illustration of the flow that we use in the WebID-TLS spec to illustrate this:
Certificate Creation Flow
http://www.w3.org/2005/Incubator/webid/spec/tls/#certificate-creation
To see some real code implementing this I point you to my ClientCertificateApp.scala code that receives a certificate Request, and either returns an error or a certificate.
The key parts of the code are extracted below
https://github.com/read-write-web/rww-play/blob/f587382935c85e9f8916d5065434f7525c328ab9/app/controllers/ClientCertificateApp.scala
def generate = Action { implicit request =>
certForm.bindFromRequest.fold(
errors => BadRequest(html.webid.cert.genericCertCreator(errors)),
certreq => {
Result(
//
https://developer.mozilla.org/en-US/docs/NSS_Certificate_Download_Specification
header = ResponseHeader(200, Map("Content-Type" -> "application/x-x509-user-cert")),
body = Enumerator(certreq.certificate.getEncoded)
)
}
)
}
CertForm just takes the data from the html form (verifies all fields are ok) and generates a CertReq object. ( or it can also take a CertReq object and generate a form, so that errors can be shown to the user )
val certForm = Form(
mapping(
"CN" -> email,
"webids" -> list(of[Option[URI]]).
transform[List[URI]](_.flatten,_.map(e=>Some(e))).
verifying("require at least one WebID", _.size > 0),
"spkac" -> of(spkacFormatter),
"years" -> number(min=1,max=20)
)((CN, webids, pubkey,years) => CertReq(CN,webids,pubkey,tenMinutesAgo,yearsFromNow(years)))
((req: CertReq) => Some(
req.cn,req.webids,null,2))
)
The spkacFormatter just returns a public key. ( It plays around with testing the challenge, but I am not sure what that is for - would like to know ).
Anyway as I wrote above: if successful the generate method returns an encoded certificate with the right mime type. And as you can see we create a certificate with SHA1withRSA
val sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA")
val digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId)
val rsaParams = CertReq.issuerKey.getPrivate match {
case k: RSAPrivateCrtKey =>
new RSAPrivateCrtKeyParameters(
k.getModulus(), k.getPublicExponent(), k.getPrivateExponent(),
k.getPrimeP(), k.getPrimeQ(), k.getPrimeExponentP(), k.getPrimeExponentQ(),
k.getCrtCoefficient());
case k: RSAPrivateKey =>
new RSAKeyParameters(true, k.getModulus(), k.getPrivateExponent());
}
val sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(rsaParams);
x509Builder.build(sigGen)
So the MD5 plays no serious role in all this.
This should not be a big surprise. The only thing of value sent to the server is the public key. It sends back a certificate based on that public key ( and other information it may have on the user ). But the only one to be able to use that certificate is the person owning the private key.
Now my code could presumably be improved in many places I doubt not. But this should show how
<keygen> is actually used. After all remember that <keygen> was added 10 years after it appeared in browsers, and that there was not that much discussion about the documentation when it was added.
I then posted a certificate I created using keygen and the server code described above. As you can see from the openssl command there is no MD5 in the resulting certificate.
$ openssl pkcs12 -clcerts -nokeys -in ~/Certificates.p12 | openssl x509 -noout -text
Enter Import Password:
MAC verified OK
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
01:4c:19:67:ea:05
Signature Algorithm: sha1WithRSAEncryption
Issuer: CN=WebID, O={}
Validity
Not Before: Mar 14 17:39:42 2015 GMT
Not After : Mar 13 17:49:42 2019 GMT
Subject: dnQualifier=
he...@bblfish.net
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public Key: (2048 bit)
Modulus (2048 bit):
00:da:b9:d1:e9:41:f6:f8:5a:08:63:16:9d:0d:b6:
32:8d:1d:4a:15:a7:1d:ff:e3:d4:f4:d0:87:52:a5:
2f:b1:45:4d:73:58:e4:a5:ec:f3:50:1e:39:24:bc:
02:52:f3:00:4b:0b:b2:1a:0d:6b:64:ca:05:3f:0f:
bc:b5:a5:4e:c9:3e:be:2d:c9:b9:1e:4c:43:2b:82:
78:84:c4:cc:2a:d8:a1:02:b4:6d:2a:20:17:bf:45:
d9:d4:c8:8a:56:4d:42:02:34:48:4a:1b:2e:44:6d:
bb:4c:d4:38:e7:9c:24:66:ce:31:0f:32:77:73:a7:
79:d2:4e:d7:b6:0a:05:a6:18:b9:84:75:7b:94:6d:
67:ba:79:f2:e0:64:e6:ae:d3:8b:d6:55:9c:e7:fc:
95:02:72:08:23:d5:6d:b1:c0:34:09:93:67:d7:db:
27:b6:bd:af:da:8c:c4:83:47:13:3f:4a:14:67:5f:
67:5f:b4:84:ce:32:df:66:c1:1a:36:38:fa:84:d5:
be:69:b1:a6:f2:38:11:5d:ef:9b:0f:79:bb:25:c0:
cb:7e:4a:39:45:9a:08:29:b1:fd:35:c0:d1:db:dd:
60:f9:c6:79:d8:94:15:ed:7e:a4:1e:b0:2f:bc:01:
6f:c0:e7:92:cb:96:98:c9:f4:db:84:2c:da:d5:b5:
f5:c9
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Alternative Name: critical
URI:
http://bblfish.net/people/henry/card#me
X509v3 Key Usage: critical
Digital Signature, Non Repudiation, Key Encipherment, Key Agreement, Certificate Sign
X509v3 Basic Constraints: critical
CA:FALSE
Netscape Cert Type:
SSL Client, S/MIME
Signature Algorithm: sha1WithRSAEncryption
03:25:38:47:76:34:ba:da:0b:40:ea:75:63:98:6b:b0:0b:b6:
11:85:c7:b1:c4:91:cc:5c:99:a5:b5:01:24:6f:1f:8c:03:39:
80:03:e7:50:59:9f:b0:48:6e:e7:16:b8:b7:92:6f:31:cd:cc:
ba:60:40:08:9e:3c:38:5d:19:94:fd:2c:be:6d:84:57:d4:ea:
7f:54:a7:69:73:aa:37:a4:b8:81:21:0c:65:dc:f1:f6:a3:40:
d1:18:cf:04:a4:d6:8b:9a:1f:43:c2:67:4a:0e:8d:00:b7:e8:
49:e3:b7:d5:f9:00:0f:98:32:b2:09:5e:ca:c0:44:37:dc:df:
3b:57:e0:c2:5a:8a:79:0d:55:7a:4a:73:4a:24:64:27:e5:16:
78:d4:c9:35:5e:f8:67:9c:e9:41:bd:c6:25:6b:1b:d7:03:c1:
af:64:d0:e3:0a:ea:58:a4:bc:3a:a4:8f:51:8d:33:58:ed:ba:
af:3d:b7:75:28:32:33:76:65:80:56:ae:ec:43:db:9e:7e:4b:
74:f5:88:07:9f:2d:e8:74:f1:89:d1:af:52:34:07:52:f3:54:
2f:60:fd:de:96:f6:00:67:2e:8f:10:23:e6:af:95:bf:a6:3c:
61:0d:8c:24:47:cf:52:45:0f:96:ee:ca:3a:69:82:69:3b:20:
87:06:5c:58
Watch out that if you look at this certificate with viewers such as OSX keychain they will nevertheless show an MD5 signature - but that is not in the certificate itself. I checked with the openssl folks.