access to raw public key, or keyid
RFC5280, section 4.2.1.2 defines keyid as the SHA1 of the subjectPublicKey encoding.
OpenSSL::PKey::EC's to_text
reveals the public key info via an OpenSSL method that produces text. This is the input that we need for the SHA1, and I guess I could decode the text back to binary if I had to, but this seems really wrong....
While there is some code in openssl/crypto/x509v3/v3_skey.c that calculates what I want, it's buried inside an extension definition, and can't (AFAIK) be used directly.
I can get what I want via: a1 = OpenSSL::ASN1.decode(pubkey.to_der) a1.value[1].value
but that just seems wrong. Is there another way to pull the encoded public key out? Should getting this keyid be ruby or C code?
RFC5280 has an example, which is available at: https://csrc.nist.gov/projects/pki-testing/sample-certificates-and-crls
you want example C.2. Download it as rfc5280_cert2.cer.
Start an irb:
> cert = OpenSSL::X509::Certificate.new(IO::read("rfc5280_cert2.cer"))
2.4.1 :005 > cert.extensions[1].to_s
=> "subjectKeyIdentifier =
17:7B:92:30:FF:44:D6:66:E1:90:10:22:6C:16:4F:C0:8E:41:DD:6D"
(matches line 7930 of rfc5280, so this is the right object)
2.4.1 :013 > der = cert.public_key.to_der
2.4.1 :014 > asn1 = OpenSSL::ASN1.decode(der)
2.4.1 :015 > rawpubkey = nil
=> nil
2.4.1 :016 > asn1.value.each {|v|
2.4.1 :017 > if v.tag == 3
2.4.1 :018?> rawpubkey = v.value
2.4.1 :019?> end
2.4.1 :020?> }
2.4.1 :039 > Digest::SHA1.digest(rawpubkey).unpack("H*")
=> ["177b9230ff44d666e19010226c164fc08e41dd6d"]
YEAH. So getting it out of openssl/ruby-openssl is a bit daft, but it works and gives the right answer. Should this be added as ruby code until we find a better way to get it out of the OpenSSL API?
With the current Ruby/OpenSSL, it has to be extracted from the output of a_public_pkey.to_der. If what you need is to (re)calculate just the SHA-1 for a subject key identifier, you might be able to use OpenSSL::X509::ExtensionFactory.
cert = OpenSSL::X509::Certificate.new
cert.public_key = ...
ef = OpenSSL::X509::ExtensionFactory.new(nil, cert)
p ext = ef.create_extension("subjectKeyIdentifier", "hash")
Actually, OpenSSL provides X509_get0_pubkey_bitstr() function that does the job. It's possible to add a binding to that, but I wonder what use case this would benefit.