It appears that the documentation and the actual API for Ruby’s OpenSSL EC support are both currently rather lacking. In particular, in Ruby <= 2.3.1 the OpenSSL::PKey::EC
doesn’t follow the same API as RSA and DSA keys for signing and verifying. What you would want to do, but currently can’t with EC keys, is this (all the code here assumes you have called require 'openssl'
somewhere):
# Get the key, here I'm reading the file
priv_key = OpenSSL::PKey.read(File.read('eckey.pem'))
# This should be the appropriately formatted string
data = "some data to sign"
# The hash algorithm, I assume SHA256 is being used
digest = OpenSSL::Digest::SHA256.new
# This doesn't work in 2.3.1, but does in 2.4.0-preview1
signature = priv_key.sign(digest, data)
As I note in the comments, this does work in Ruby 2.4.0-preview1, but that’s likely not much use to you.
To get it working with current Ruby, you need to do something like this:
# As before:
priv_key = OpenSSL::PKey.read(File.read('eckey.pem'))
data = "some data to sign"
signature = priv_key.dsa_sign_asn1(OpenSSL::Digest::SHA256.digest(data))
Both these techniques give you a binary string. I think you will need to base64 encode it before adding it as your request header.
To extract the public key to check the signature verifies is also a bit tricky (although you could just use the openssl
command line and read in the file). The public_key
methods returns an OpenSSL::PKey::EC::Point
object rather than an actual key, so we need to recreate one from the private key. The verify
method does work on Ruby 2.3.1:
pub = OpenSSL::PKey::EC.new(priv_key.group)
pub.public_key = priv_key.public_key
data = "some data to sign"
digest = OpenSSL::Digest::SHA256.new
puts pub.verify(digest, sig, data)
The Apple page doesn’t appear to specify the hash algorithm to use, but from what I’ve seen it looks like SHA-256 is right. (Also I could have got this completely wrong and Apple are using a completely different format. I’d be keen to know whether or not this code works you you).