opensslrsaelliptic-curvecsrlibssl

OpenSSL generates non-deterministic signature on CSR using P-256 or P-384 key, but not RSA key?


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

Solution

  • 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.