Search code examples
javascriptrubyopensslelliptic-curvediffie-hellman

How to Format OpenSSL Keys to Match Online Sample (JSBN-ECC)


I want to be able to format the public key of Elliptic Curve Diffie-Hellman in OpenSSL - Ruby into something like this online example (link), as I have been using that JS library.

My code below generates an OpenSSL::PKey::EC public and private keys

#Ruby
ec = OpenSSL::PKey::EC.new('secp128r1')
ec.generate_key

ec.private_key
#--> 205607153615223513963863936713567041725

ec.public_key.to_bn
#--> 499599043529551953518354858381998373780459818901085313561109939106744612770290

Try copying the private key above 205607153615223513963863936713567041725 and pasting it on the online (link) as Alices' Private value. But click the secp1284r1 button first to have same curve parameters, and then click Compute Public button.

That will generate a public key from the inputted private key. However, the Ruby OpenSSL documentation isn't really helpful, and I am stuck on figuring out how to convert the generated public key above:

499599043529551953518354858381998373780459818901085313561109939106744612770290

Into something like this (as seen from the online site):

x: 107060165679262225845922473865530329196
y: 109296969851421346147544217212275741170

I've assumed that by properly converting one, it can somehow become equal to the other since they have same curve parameters. Or am I wrong? (And also because the default format of point_conversion_form is :uncompressed, as I just have tested) Please help.

P.S. You might wonder why I need to convert the public key into the other. No, I don't really have to. I just want to learn how to convert it as I'll be using that method to convert something similar. And this is the simplified question for your testing-convenience.


Solution

  • Finally! I somehow managed to convert it properly but it's somehow weird.

    #From above code
    c.public_key.to_bn
    #--> 499599043529551953518354858381998373780459818901085313561109939106744612770290
    
    #irb:
    require 'openssl'
    
    key_int = '499599043529551953518354858381998373780459818901085313561109939106744612770290'
    key_bn = OpenSSL::BN.new(key_int, 10) #Convert to OpenSSL::BN (Big Number, with 10=Decimal as base)
    key_hex = key_bn.to_s(16) #Convert to Hex String (16=Hexadecimal)
    #--> "04508B09B35FA8C21820BE19C16B38486C5239D4A932D081DD56B90F91120551F2"
    
    #I don't really know why, but removing '04' above will finally convert it properly
    key_hex = key_hex[2..-1] #Remove first 2 chars: '04'
    #--> "508B09B35FA8C21820BE19C16B38486C5239D4A932D081DD56B90F91120551F2"
    
    #Split key_hex into halves
    key_hexarr = key_hex.chars.each_slice( (key_hex.length/2.0).round ).map(&:join)
    #--> ["508B09B35FA8C21820BE19C16B38486C", "5239D4A932D081DD56B90F91120551F2"]
    
    #Convert first value into BN (input: 16=hexadecimal), then convert to string(output: 10=decimal)
    key_x_int = OpenSSL::BN.new(key_hexarr[0], 16).to_s(10)
    #--> "107060165679262225845922473865530329196"
    
    #Convert second value into BN (input: 16=hexadecimal), then convert to string(output: 10=decimal)
    key_y_int = OpenSSL::BN.new(key_hexarr[1], 16).to_s(10)
    #--> "109296969851421346147544217212275741170"
    

    Finally, key_x_int and key_y_int now matches the result from the online link