OpenSSL – How to Create a Digital Signature

encryptionlinuxopensslSecurity

I am trying to sign and verify a signature using an RSA key-pair.

Here is how I generate the key pair (which work fine for encryption / decryption):

ssh-keygen -t rsa -f mykey -N '' -b 2048
mv mykey mykey-priv.pem
ssh-keygen -f mykey.pub -e -m pem > mykey-pub.pem ; rm -f mykey.pub

However, when I try to encrypt a hash (using the private key) and then verify it (using the public key), it fails:

openssl dgst -sha256 /etc/hosts > /tmp/hash 
openssl rsautl -sign -inkey mykey-priv.pem -in /tmp/hash -out /tmp/signature
openssl rsautl -verify -pubin -inkey mykey-pub.pem -in /tmp/signature

The last line gives me unable to load Public Key

What I am missing?

In case it is relevant, am using CentOS 3.9 (old & legacy, but I have no choice here).

Lastly, here is a sample key pair:

-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA8nVhTRuinf4bGAda1ufF+VGG6f8kIFGt8/oCK74n9E6lXgpu
7KqeRzadaiONDh8GgQXn5bX9O2vOL+sL0xYa3W13eCoT7+4U51C/+8HqBxujRAVm
i4r/Ju+52kober+GSuIfoNF2nMA24EDy9tid0JgHcBJ0NTyhv6sPvNfcFR1Flbpo
LixTGCcn5S9A9NzJXiOkZ6abnxmyEmZsEaboowMay027GLWAw186GODbrGBByhjq
W6W6jcijb7EFdIYCEqtC6RsZmAiuPBK0LfW5078GE05oIZrG8GtQR8f7k8HDpEDF
+ZI53CZiLhrcXq6+tS4U57FqQs8ytUf6Gno7JwIDAQABAoIBAQCn7zd64gZLqmJ3
zThVG+obGyX4U3lhTVHQaD0ysR4ZcJPHxDA6ip7gsmprxr3/puupWD7b86a3jp8c
v4/MIEZxUk3qlDKFAAHIijy/kvuW+sSl65uwUZETFf5DvQq1hYzttxuzFwIx5kzc
HQBsi3MbtQGJ1a5Z5WofSMu4wEa289tAjHQiaXs8WEbgOctwS11lhFbDLwLagBFz
FL+J0C0oGjgNYaLJUihZKsMHCPZ0/LVFDcCus0ep7mQmvAEQ3FhSNNVakUL07XmM
MC6PTUxUvQa8vGDlRnXwGlIWVFNevvudvZjs02J7KWc3hUH9DqXiEm7cClJfoISB
HIq4WFvpAoGBAP0UHfCRyv4/2nZmizX05NG/qMNuz97G6Rir2ltw9wmrpvqK/n4r
d1JX19ECCx2nQM04C4Q7jQMmyqpF21Wf903PrGXPBa8j5XIDJy+YPC2RrifGsaHm
wN1EmYVfYZCphwmTIO2D05n/4Zkgze/KmhMnqpWn27De+LLfgcxsbET7AoGBAPVB
4RvpQCfKdP+ieKRopDE0v+UkdQCogdUz0rhwAdwzhXmUZIJ/Xb4a15DIN+zxKz2h
aiIJFkifKw5bQQQQjiIuRN73YpjQ6D9jWTdEs3E1zaWYFNPNAYqSi/1g/udgLL0N
RAOw5aNCDyOV4OZRESa9rhUUngfK4VrbqWIm1pLFAoGBAOBGWZn9uaTDNXjDuw6f
7b+rV4WJyBEmuR8x/JoYa/RX9+wEDTAGmQGR8yG3693lgFndFueiVn66e9OVgKBK
2MBOD/tRETp6VzVIcguNn5bKiUmanYRamAP+bQZy1mV6tr7XcdDKiFTrHCO1nIqq
QwxClLt3PAtsLX1m8QIV+4TNAoGBAMA161hWi2Mj9mHKUUZ4hAXUU3ggBFqJtYcD
4GeP0MVk03yfYc4sR6mPm9XqNHpL4BmjAWy/Nmmf5LyRo/itiNcc7/jWZL1jLEFR
eUApZYCaLBtVfy0nA8g4ZeIkPGHVK/rWBLHn13EFepvnAKVAb3KvQVlgGSH+THNK
qAs2aQAJAoGAabSXLpXsZTdy1lMUtZlvUhH4tLVgHFJGILMQ3bh2TTMnkaVbPFyy
KDeK+QJGaqjTx1SIXYgANR5cYI1xNge6aVA6T2aY2Dlq3qpAOK3sJWhEZYDxJKeq
Oy97h96/n14jdfh1U4TValcZfgVXqAfBMjxx0fPtSQjIn2RVjZRBpFs=
-----END RSA PRIVATE KEY-----


-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA8nVhTRuinf4bGAda1ufF+VGG6f8kIFGt8/oCK74n9E6lXgpu7Kqe
RzadaiONDh8GgQXn5bX9O2vOL+sL0xYa3W13eCoT7+4U51C/+8HqBxujRAVmi4r/
Ju+52kober+GSuIfoNF2nMA24EDy9tid0JgHcBJ0NTyhv6sPvNfcFR1FlbpoLixT
GCcn5S9A9NzJXiOkZ6abnxmyEmZsEaboowMay027GLWAw186GODbrGBByhjqW6W6
jcijb7EFdIYCEqtC6RsZmAiuPBK0LfW5078GE05oIZrG8GtQR8f7k8HDpEDF+ZI5
3CZiLhrcXq6+tS4U57FqQs8ytUf6Gno7JwIDAQAB
-----END RSA PUBLIC KEY-----

Best Answer

Aside: ssh-keygen -m is only in fairly recent versions of OpenSSH, much more recent than I would expect on CentOS 3.9. Did you use some side repository or build it yourself or something? Anyway...

What OpenSSH calls 'PEM' is not the kind of PEM OpenSSL uses here.

Background: OpenSSL library has long supported two types of publickey structures, in both PEM and DER formats:

  • a 'legacy' structure specific to each supported algorithm, with PEM headers like
    -----BEGIN RSA PUBLIC KEY-----
    -----BEGIN DSA PUBLIC KEY-----
    -----BEGIN EC PUBLIC KEY-----
    implemented by routines PEM_{write,read}{RSA,DSA,EC}PublicKey

  • one generic structure defined by X.509 named SubjectPublicKeyInfo with PEM header
    -----BEGIN PUBLIC KEY----- !! note no algorithm name
    implemented by routines PEM_{write,read}{,RSA,DSA,EC}PUBKEY

However, OpenSSL commandline uses the latter almost exclusively, including for rsautl -pubin. You can see you have the former. Why?

OpenSSH, when it recently added the -m format option to ssh-keygen -e export, inexplicably named the OpenSSL legacy structures PEM but the OpenSSL X.509 generic structure PKCS8 -- although it is not at all PKCS#8, which is a standard and common format for private keys.

So your options are:

  • use the wrong-looking but actually correct format option ssh-keygen -e -m pkcs8

  • convert the file you have using the only operation that does handle legacy structure:
    openssl rsa -in sshpub_called_pem -RSAPublicKey_in -out goodpub -pubout
    but only in OpenSSL 1.0.0 and up.

  • given your OpenSSH privatekey file is in OpenSSL-compatible format (-----BEGIN RSA PRIVATE KEY----- not -----BEGIN OPENSSH PRIVATE KEY----- which is also a recent addition to OpenSSH) you can use OpenSSL instead of ssh-keygen -e:
    openssl rsa -in sshprivate -pubout -out pubforopenssl
    or in 1.0.0 and up, the shiny new alternative openssl pkey -in sshprivate -pubout -out pubforopenssl

ALSO BE WARNED: rsautl -sign|-verify (by default) uses PKCS#1-v1.5 (aka type 01) padding but does NOT obey the standard which requires you also encode the hash in an ASN.1 SEQUENCE. Signatures you create this way will NOT verify in other software that obeys the standard, and signatures created in other software will give different (though PARTIALLY matching) results. Consider instead using

 openssl {dgst -hashname | hashname} {-sign privatekey | -verify publickey}

which does the complete operation: hash the data, ASN.1 encode, pad and RSA sign, or conversely RSA verify, unpad, decode, and match; or

 openssl pkeyutl -sign|-verify -inkey key [-pubin] -pkeyopt digest:hashname

where the key is an RSA key, which does all of PKCS#1-v1.5 except hashing the data. E.g.:
https://stackoverflow.com/questions/9380856/openssl-signature-difference-when-using-c-routines-and-openssl-dgst-rsautl-
https://stackoverflow.com/questions/13419201/why-are-the-rsa-sha256-signatures-i-generate-with-openssl-and-java-different
https://stackoverflow.com/questions/9951559/difference-between-openssl-rsautl-and-dgst
https://crypto.stackexchange.com/questions/27079/what-is-the-difference-between-openssl-pkeyutl-sign-and-openssl-rsautl-sign
https://crypto.stackexchange.com/questions/27892/verify-a-rsa-signature-using-only-rsa-encryption