3

I'm playing around with Elliptic Curves using the Ruby 2.5.x OpenSSL library. I can easily generate a private and public key pair using

curve = OpenSSL::PKey::EC.new('secp256k1')
curve.generate_key

But given a private key I want to regenerate the public key.

I know OpenSSL can do it because the command line allows you to do it, and also the Ruby Bitcoin project does it. But the Ruby Bitcoin project has its own interface to OpenSSL using FFI rather than the one provided by Ruby.

Does Ruby 2.5.x openssl library not expose enough of the OpenSSL interfaces to be able to generate an elliptic curve public key from a private key, or that it can but it's not documented?

2
  • 1
    What methods are you trying to run that don't work as expected? Commented Apr 28, 2018 at 13:34
  • Suppose I save the private key in a file or database. I want to be able to get the public key from the private key. There's no method to do that, but the respondent below explained the required steps and it worked. Commented Apr 30, 2018 at 9:16

4 Answers 4

9

In case someone interested to get public key in pem format too :)

example_key = OpenSSL::PKey::EC.new('secp256k1').generate_key
puts example_key.to_pem
pkey = OpenSSL::PKey::EC.new(example_key.public_key.group)
pkey.public_key = example_key.public_key
puts pkey.to_pem
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much for this snippet. It's extremely useful and hard to find on the net
`public_key=': pkeys are immutable on OpenSSL 3.0 (OpenSSL::PKey::PKeyError) :/
5

The answer in https://stackoverflow.com/a/64023608/2603673 does not work with openssl 3.x because the key is now immutable, we need to change to:

key = OpenSSL::PKey::EC.new('secp256k1').generate_key
group = key.public_key.group
point = key.public_key
asn1 = OpenSSL::ASN1::Sequence(
  [
    OpenSSL::ASN1::Sequence([
      OpenSSL::ASN1::ObjectId('id-ecPublicKey'),
      OpenSSL::ASN1::ObjectId(group.curve_name)
    ]),
    OpenSSL::ASN1::BitString(point.to_octet_string(:uncompressed))
  ]
)
pkey = OpenSSL::PKey::EC.new(asn1.to_der)
puts pkey.to_pem

2 Comments

This works for me, except the key needs to be generated with OpenSSL::PKey::EC.generate('prime256v1') to avoid the pkeys are immutable on OpenSSL 3.0 error.
@code_monkey_steve if you just want to get the public key in pem format, you can also get it from the private key object using public_to_pem.
3

The Ruby OpenSSL bindings don’t allow you to directly get the public key from a PKey::EC object as far as I can tell, but they do expose enough to do the calculation yourself, which is straightforward.

Given a private key as an OpenSSL:BN object, which for the example we can generate like this:

example_key = OpenSSL::PKey::EC.new('secp256k1').generate_key
private_key = example_key.private_key

We can calculate the public key by multiplying the group base point (i.e. the generator) by the private key:

group = OpenSSL::PKey::EC::Group.new('secp256k1')
public_key = group.generator.mul(private_key)

The public key is an OpenSSL::PKey::EC::Point. You can compare with the original to see that is the same:

puts example_key.public_key == public_key # => true

2 Comments

Excellent, that worked. I thought it might be something to do will #mul but wasn't sure.
Now I have to work out how to DER encode a signature.
0

In OpenSSL 3.0, keys are immutable, which means you cannot modify an existing OpenSSL::PKey::EC object after it has been created. Instead of trying to set the public key on an existing object, you should construct the public key using ASN.1 encoding.

Here’s how you can refactor the code to work with OpenSSL 3.0:

  private_key = OpenSSL::PKey::EC.generate("secp256k1")
  asn1 = OpenSSL::ASN1::Sequence(
    [
      OpenSSL::ASN1::Sequence(
        [
          OpenSSL::ASN1::ObjectId("id-ecPublicKey"),
          OpenSSL::ASN1::ObjectId(private_key.public_key.group.curve_name)
        ]
      ),
      OpenSSL::ASN1::BitString(private_key.public_key.to_octet_string(:uncompressed))
    ]
  )
  public_key = OpenSSL::PKey::EC.new(asn1.to_der)
  puts public_key.to_pem

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.