Andrew Andrew - 1 year ago 123
Ruby Question

Not sure how to generate an ECDSA signature, given a private key and a message

I'm following Apple's guide towards composing a CloudKit Web Services request. The bit I'm having trouble with is Step 2, under "Authenticate Web Service Requests":

  1. Compute the ECDSA signature of this message with your private key.

Before getting to this point, I generated my certificate, a .pem file, which when opening it in a text editor shows me my private key, so I have that in string format too.

I've also followed the steps for generating what it refers to as a message, which I now have as a string.

So given that I have a private key, (or the .pem file if required), and a message as a string, it should in theory be fairly simple for me to get a ECDSA signature of the message, computed with my private key. But here's where I'm struggling. Libraries that I've found online seem to take a far more complicated approach, with different moving parts, no reference to a .pem file and talk of generating new public/private keys.

Any help with this step would be greatly appreciated.

Answer Source

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 ='eckey.pem')) 

# This should be the appropriately formatted string
data = "some data to sign"

# The hash algorithm, I assume SHA256 is being used
digest =

# 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 ='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 =
pub.public_key = priv_key.public_key

data = "some data to sign"
digest =

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

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download