When I use openssl
's command line to generate an RSA-2048 keypair, and then construct two Certificate Signing Requests (CSRs) for identical domain names using identical private keys, I get identical output.
$ openssl genrsa -f4 -out rsa.key | head -1
Generating RSA private key, 2048 bit long modulus
$ openssl req -new -sha256 -key rsa.key -out rsa1.csr -subj "/CN=example.com"
$ openssl req -new -sha256 -key rsa.key -out rsa2.csr -subj "/CN=example.com"
$ diff rsa1.csr rsa2.csr
But, when I generate an elliptic curve (P-256) keypair, and make two CSRs for identical domain names using identical private keys, I get two different outputs!
$ openssl ecparam -genkey -name prime256v1 -noout -out p256.key
$ openssl req -new -sha256 -key p256.key -out ec1.csr -subj "/CN=example.com"
$ openssl req -new -sha256 -key p256.key -out ec2.csr -subj "/CN=example.com"
$ diff -U999 ec1.csr ec2.csr
--- ec1.csr 2019-08-14 12:20:55.000000000 -0400
+++ ec2.csr 2019-08-14 12:20:59.000000000 -0400
@@ -1,7 +1,7 @@
-----BEGIN CERTIFICATE REQUEST-----
-MIHRMHgCAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggq
+MIHPMHgCAQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wWTATBgcqhkjOPQIBBggq
hkjOPQMBBwNCAASKkrbzoJCjHgvI95U1ZYPG5AQUtN+ImrutI2KNAne/BvktGaHW
-ep2CEc5bliuYzxeC68cUG0MBmDrLZbRwaMS7oAAwCgYIKoZIzj0EAwIDSQAwRgIh
-AO1VziY7sHIKNFvCQnm+g7fguFSPoopHw+Jh3CKpjTKYAiEAmvlilKQkiN134T07
-LCDWfF/IlGeWv6nv1VhgsD3SEBU=
+ep2CEc5bliuYzxeC68cUG0MBmDrLZbRwaMS7oAAwCgYIKoZIzj0EAwIDRwAwRAIg
+AxHKjgwyXbeMqWK8XF/F6KztweW/tpY1U55pXyHeKgECID9jgdAQp7FetjbRGY7A
+GY0Y37x8XY5O3o5rEZSnsA1C
-----END CERTIFICATE REQUEST-----
It's not a one-time thing. I generate 1000 CSRs using this P-256 key, I get 1000 different outputs. I generate 1000 CSRs using this RSA-2048 key, I get 1000 identical outputs.
$ for i in `seq 1 1000`; do openssl req -new -sha256 -key p256.key -out ec$i.csr -subj "/CN=example.com"; done
real 0m8.147s
user 0m5.972s
sys 0m1.810s
$ md5sum ec*.csr | cut -f 1 -d ' ' | sort | uniq | wc -l
1000
$ time for i in `seq 1 1000`; do openssl req -new -sha256 -key rsa.key -out rsa$i.csr -subj "/CN=example.com"; done
real 0m43.940s
user 0m41.386s
sys 0m2.049s
$ md5sum rsa*.csr | cut -f 1 -d ' ' | sort | uniq | wc -l
1
What on earth is going on here? Is there any way to force OpenSSL to generate reproducible output? Is there any reason I wouldn't want OpenSSL to generate reproducible output?
Is there any reason I would want OpenSSL to generate reproducible output when using RSA keys, but not when using EC keys?
I should add that I used https://certlogik.com/decoder/ to look at the CSRs being generated, and they look exactly the same except for the BIT STRING
at the end, which I assume is supposed to be the SHA-256 signature?
I also see the same nondeterminism happening with P-384 keys:
$ openssl ecparam -genkey -name secp384r1 -noout -out p384.key
$ openssl req -new -sha256 -key p384.key -out ec1.csr -subj "/CN=example.com"
$ openssl req -new -sha256 -key p384.key -out ec2.csr -subj "/CN=example.com"
$ cmp ec1.csr ec2.csr
ec1.csr ec2.csr differ: char 275, line 5
Standard ECDSA signatures are non-deterministic in nature. This is a fundamental characteristic of the algorithm. RSA signatures on the other hand are deterministic. So this is the reason that you see the difference between these types of keys.
There is an RFC which describes a process for generating deterministic ECDSA signatures (see RFC6979). However OpenSSL does not currently support it. There is an open pull request adding that capability here.