Could anybody please tell me how can I generate a public key from
a private one with OpenSSL (or any other lib/gem) in Ruby? What I’m
trying to do is something like this:

Setting the private_key works fine, but I need to calculate the
public_key
too.
I’m sure there’s a way to ask the key object to calculate the
corresponding
public key, I just don’t know how.

Thanks Carlo, but it’s not what I was looking for. I know about #generate_key.
It creates a random private key and calculates the corresponding public
key
too. But I already have a (non-random) private key. (I mean it is an
input
for my script.)

Now what I need is to set my own private key, and calculate the public
key
that matches my already existing private key. I have a private key
which is
not a random number. Well, maybe it was some time ago in another
software,
but for now, I can read it from a file, and I have no problem setting
that
data as a private key. The problem is that I need the public key that
matches the private one.

So what I need is to ask the ‘key’ object to be so kind and calculate
the
the
public key for me using my private key I’ve just set with #private_key=.

Setting the private_key works fine, but I need to calculate the public_key
too.
I’m sure there’s a way to ask the key object to calculate the corresponding
public key, I just don’t know how.

I have never done this myself before , but I was curious, and gave it
a look.

Your OpenSSL::PKey::EC instance has a method called #generate_key. After I run

key.generate_key

I find that key.private_key is populated with an instance of
OpenSSL::BN, and key.public_key is populated with an instance of
OpenSSL::PKey::EC::Point.

if (eckey->pub_key == NULL)
{
pub_key = EC_POINT_new(eckey->group);
if (pub_key == NULL)
goto err;
}

EC_POINT_new is used within Ruby when creating an
OpenSSL::PKey::EC::Point object, when passing it a ‘group’. Now, I
have no idea what a group is, but your key has a ‘group’. And it is
populated, even without calling #generate_key.

If I run

pub_key=OpenSSL::PKey::EC::Point.new(key.group)

I get something. Again, I hope this is what you are looking for.

Could anybody please tell me how can I generate a public key from
a private one with OpenSSL (or any other lib/gem) in Ruby?

I haven’t looked into how to do this in Ruby, but at least with the
command-line stuff from OpenSSL it seems to be trivial. (That’s quite
contrary to my initial assumptions. I was all set to tell you “no,
the whole point is that you can’t derive one from the other”, and was
fact-checking. Apparently asymmetrical crypto is even more
asymmetrical than I thought!) Check out the answers to this question
on SO:

I haven’t looked into how to do this in Ruby, but at least with the

command-line stuff from OpenSSL it seems to be trivial. (That’s quite
contrary to my initial assumptions. I was all set to tell you “no,
the whole point is that you can’t derive one from the other”, and was
fact-checking. Apparently asymmetrical crypto is even more
asymmetrical than I thought!) Check out the answers to this question
on SO:

Well, this is about RSA, not Elliptic Curve crypto (ECDSA), but the
main thing is all the same: it should be easy to calculate the public
key from the private key, but it must be virtually impossible to do it
the other way. The private key is just a huge random number that is
kept secret, and the public key is calculated from it somehow – we
actually don’t need to know how, that’s something OpenSSL does
internally.

I think the Ruby API just misses a very important piece here.

…I think EC_POINT_mul() could be a solution. It’s a C function
in OpenSSL, and it does the calculation I need. But I don’t know
much about how Ruby and C are married in native gems, and
I think the Ruby API does not allow me to call this C function.

Or does it?

These mappings are generally done with quite a lot of care. Indeed,
you can call EC_POINT_mul() - the OpenSSL::PKey::EC::Point class has a #mul method.

It can be called in four ways:

point.mul(bn) => point
point.mul(bn, bn) => point
point.mul([bn], [point]) => point
point.mul([bn], [point], bn) => point

The EC_POINT_mul function is called if you pass one or two OpenSSL::BN
objects to it.

If I execute this script:

require ‘openssl’
key=OpenSSL::PKey::EC.new(‘secp256k1’)
bn1=OpenSSL::BN::new(‘123’)
bn2=OpenSSL::BN::new(‘456’)
pubkey=OpenSSL::PKey::EC::Point.new(key.group).mul(bn1,bn2)
p pubkey.to_bn.to_s

I get a beautiful 155-digit number. Then, I have no idea about what
all these big numbers should be…

The Openssl mapping is included in Matz’s Ruby sources, under
ext/openssl.

I had a look at the ruby code. #generate_key just calls openssl

Thanks for looking at the source. I guess this is just the creation
(memory allocation?) of the point object (yes, the public key is a
point with X and Y coordinates in Elliptic Curve crypto), but the
calculation of this point using the private key (which is a number,
not a point) must be somewhere in the next few lines in the source.

As far as I can see this creates a point with (0, 0) coordinates.
(The .to_bn converts a point to an OpenSSL::BN, which is a
Big Number, where (I think) the first N bit belongs to the X
coordinate and the other bits to the Y, but to_bn answers 0.)

According to this…

…I think EC_POINT_mul() could be a solution. It’s a C function
in OpenSSL, and it does the calculation I need. But I don’t know
much about how Ruby and C are married in native gems, and
I think the Ruby API does not allow me to call this C function.

Or does it?

The API should support private-to-public key calculations, however…

I compile my own Ruby from Matz’s repository (this one on my laptop
was last updated a week ago). If you work under Linux, it should not
be too difficult to compile your own SSL extensions. Of course I have
never done it while running a different Ruby executable, but it should
be enough to do