-
-
Notifications
You must be signed in to change notification settings - Fork 925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unable to load EllipticalCurve EC class from JRuby's OpenSSL library #1261
Comments
Below is a partial implementation (mostly taken from Bitcoinj). It may be useful to someone or may be a starting point for an actual JRuby implementation. The script below signs and verifies a message. It works on both MRI 1.9.2 and JRuby 1.7.8: # JRUBY_OPTS='-J-classpath ./bcprov-jdk15on-150.jar' jruby ec_bouncy_castle.rb
require 'openssl'
if RUBY_PLATFORM == 'java'
import 'java.math.BigInteger'
import 'java.security.SecureRandom'
import 'java.io.ByteArrayOutputStream'
import 'org.bouncycastle.asn1.sec.SECNamedCurves'
import 'org.bouncycastle.asn1.x9.X9ECParameters'
import 'org.bouncycastle.asn1.ASN1InputStream'
import 'org.bouncycastle.asn1.DERInteger'
import 'org.bouncycastle.asn1.DERSequenceGenerator'
import 'org.bouncycastle.asn1.DLSequence'
import 'org.bouncycastle.crypto.signers.ECDSASigner'
import 'org.bouncycastle.crypto.params.ECDomainParameters'
import 'org.bouncycastle.crypto.params.ECPrivateKeyParameters'
import 'org.bouncycastle.crypto.params.ECPublicKeyParameters'
import 'org.bouncycastle.math.ec.ECPoint'
module ::OpenSSL::PKey
class EC
attr_accessor :private_key, :public_key
attr_reader :group
def initialize(curve_name)
params = SECNamedCurves.getByName(curve_name)
@curve = ECDomainParameters.new(params.getCurve(), params.getG(), params.getN(), params.getH())
@group = ::OpenSSL::PKey::EC::Group.new
rescue
raise OpenSSL::PKey::ECError.new($!.message)
end
def generate_key
@private_key = random_bn
@group = ::OpenSSL::PKey::EC::Group.new
point = @curve.getG().multiply(BigInteger.new(get_java_bytes(@private_key)))
point = compressPoint(point) if @group.point_conversion_form == :compressed
public_key_java_bytes = point.getEncoded()
public_key_bn = get_bn_from_java_bytes(public_key_java_bytes)
@public_key = ::OpenSSL::PKey::EC::Point.new(self.group, public_key_bn)
self
rescue
raise OpenSSL::PKey::ECError.new($!.message)
end
def dsa_sign_asn1(data)
private_key_big_integer = BigInteger.new(get_java_bytes(@private_key))
signer = ECDSASigner.new
signer.init(true, ECPrivateKeyParameters.new(private_key_big_integer, @curve))
components = signer.generateSignature(data.bytes.to_a)
signature = get_der_java_bytes(components[0], components[1])
signatureBytes = signature.collect(&:to_i).pack('c*')
rescue
raise OpenSSL::PKey::ECError.new($!.message)
end
def dsa_verify_asn1(data, signature)
d = data.unpack('c*').to_java(:byte)
r, s = get_r_and_s_from_bytes(signature.unpack('c*').to_java(:byte))
pub = get_java_bytes(public_key.to_bn)
signer = ECDSASigner.new
params = ECPublicKeyParameters.new(@curve.getCurve().decodePoint(pub), @curve)
signer.init(false, params)
signer.verifySignature(d, r, s)
rescue
raise OpenSSL::PKey::ECError.new($!.message)
end
private
def get_r_and_s_from_bytes(der_encoded_java_bytes)
decoder = ASN1InputStream.new(der_encoded_java_bytes)
seq = decoder.readObject()
[seq.getObjectAt(0).getPositiveValue(), seq.getObjectAt(1).getPositiveValue()]
ensure
decoder.close
end
def get_der_java_bytes(r, s)
bos = ByteArrayOutputStream.new(72)
seq = DERSequenceGenerator.new(bos)
seq.addObject(DERInteger.new(r))
seq.addObject(DERInteger.new(s))
seq.close()
bos.toByteArray()
end
def get_java_bytes(number)
hex_string = number.to_s(16)
bytes = []
if hex_string.length % 2 == 1
bytes << ['0' + hex_string[0]].pack('H*').unpack('c*')[0]
hex_string = hex_string[1..-1]
end
bytes += [hex_string].pack('H*').unpack('c*')
bytes.to_java(:byte)
end
def get_bn_from_java_bytes(java_bytes)
ruby_hex_s = java_bytes.collect(&:to_i).pack('c*').unpack('H*').first
::OpenSSL::BN.new(ruby_hex_s, 16)
end
def random_bn
OpenSSL::BN.new(OpenSSL::Random.random_bytes(32).unpack('H*').first, 16)
end
end
class ECError < OpenSSL::PKey::PKeyError
end
class EC::Point
attr_accessor :group
def initialize(group, bn)
self.group = group
@bn = bn
end
def to_bn
@bn
end
end
class EC::Group
attr_accessor :point_conversion_form
def initialize
self.point_conversion_form = :uncompressed
end
def point_conversion_form=(form)
raise ArgumentError.new("only :compressed and :uncompressed form supported: #{form}") unless [:uncompressed, :compressed].include?(form)
@point_conversion_form = form
end
end
end
end
key = ::OpenSSL::PKey::EC.new("secp256k1")
key.generate_key
private_key_int = key.private_key.to_s.to_i
private_key_hex_string = key.private_key.to_s(16)
puts "Private key: #{key.private_key.to_s(16)}"
puts "Public key: #{key.public_key.to_bn.to_s(16)}"
message = "this is a random message: #{rand}"
signature = key.dsa_sign_asn1(message)
puts "The signature for '#{message}' is: #{signature.unpack('H*')[0]}"
verified = key.dsa_verify_asn1(message, signature)
puts "Verified? #{verified}" |
Very nice! I think we can incorporate your port of this code, since the bitcoinj code is licensed under Apache-2.0, a license compatible with EPL. We should add some note attributing the original logic to bitcoinj. Does this do everything you would need to resolve this issue? Can you turn this into a pull request? |
It's a near full implementation as far as i can tell. Is there a document on testing practices? |
Just saw this: https://github.com/michaelgpearce/jruby/blob/master/docs/README.test.md. I assume adding specs to RubySpec. |
What's the status of this issue ? I've just stumbled on this problem with JRuby 1.7.10. |
I've not done anything about adding this to JRuby as i switched away from the Ruby library that had this as a dependency and am instead using a Java library. |
I tried using this in bitcoin-ruby by just dropping in your code. It already goes a long way, but there still seem to be a few issues left. For example generating a key: |
FWIW this issue is still present in JRuby 9k, I'm going to add the beginner label to this issue and maybe somebody can use @michaelgpearce's code as a launching point to implementing this behavior |
Still seeing this issue in jRuby 9.0.5.0 |
@kares @mkristian: We've had a request to finally address this in the JRuby 9.1 timeframe. What do you think? |
tried the above script before and I did not put this in since it was quite uncomplete. not sure how much works but it failed my attempts. |
Moving this to jruby/jruby-openssl#90 |
just posted a $100 bounty with bountysource.com on the new issue at jruby/jruby-openssl#90, if anyone is interested in working on it, or contributing to the bounty:) |
I hope this is the right place to report this bug (i think it is!).
The class OpenSSL::PKey::EC does not load on JRuby version 1.7.8. The library loads with the same commands in MRI version 1.9.3p125 and 2.0.0p195.
In MRI:
The text was updated successfully, but these errors were encountered: