Jruby OpenSSL RSA Base64 private key failed to instantiate object

670 Views Asked by At

In Ruby i have simple old source line which create RSA public or private keys:

OpenSSL::PKey::RSA.new(File.read(key_file))

My RSA public and private keys formatted like this, i use exactly same key files for MRI and Jruby:

-----BEGIN RSA PRIVATE KEY-----
MIIEvwIBADANBgkq...
-----END RSA PRIVATE KEY-----

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqh...
-----END PUBLIC KEY-----

When i run it on MRI v2.4.0 - any issues, i can instantiate public and private keys:

$> irb
> k = OpenSSL::PKey::RSA.new(File.read("private.pem"))
=> #<OpenSSL::PKey::RSA:0x007fda1096a6a8> 
> k = OpenSSL::PKey::RSA.new(File.read("public.pem"))
=> #<OpenSSL::PKey::RSA:0x007fda1096a6a8> 

But using Jruby 9.0.4.0 and 9.1.8.0 i can successfully initiate public but failed on init private key:

> set JRUBY_OPTS=-J-Djruby.openssl.debug=true
> irb
irb(main):001:0> require "openssl"
using provider: BC version 1.56
irb(main):002:0> k = OpenSSL::PKey::RSA.new(File.read("public.pem"))
=> #<OpenSSL::PKey::RSA:0x1c9e76b>

But failed on creating private key object:

    irb(main):003:0> k = OpenSSL::PKey::RSA.new(File.read("private.pem"))
java.io.IOException: problem creating RSA private key: java.lang.ClassCastException: org.bouncycastle.asn1.DLSequence cannot be ca
st to org.bouncycastle.asn1.ASN1Integer
        at org.jruby.ext.openssl.x509store.PEMInputOutput.mapReadException(PEMInputOutput.java:383)
        at org.jruby.ext.openssl.x509store.PEMInputOutput.readPrivateKey(PEMInputOutput.java:328)
        at org.jruby.ext.openssl.PKey.readPrivateKey(PKey.java:395)
        at org.jruby.ext.openssl.PKeyRSA.initialize(PKeyRSA.java:260)
        at org.jruby.ext.openssl.PKeyRSA$INVOKER$i$0$0$initialize.call(PKeyRSA$INVOKER$i$0$0$initialize.gen)
        at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodN.call(JavaMethod.java:720)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:171)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:177)
        at org.jruby.RubyClass.newInstance(RubyClass.java:1001)
        at org.jruby.RubyClass$INVOKER$i$newInstance.call(RubyClass$INVOKER$i$newInstance.gen)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:200)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:338)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:163)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:314)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.ir.interpreter.Interpreter.INTERPRET_EVAL(Interpreter.java:122)
        at org.jruby.ir.interpreter.Interpreter.evalCommon(Interpreter.java:176)
        at org.jruby.ir.interpreter.Interpreter.evalWithBinding(Interpreter.java:200)
        at org.jruby.RubyKernel.evalCommon(RubyKernel.java:1033)
        at org.jruby.RubyKernel.eval19(RubyKernel.java:1000)
        at org.jruby.RubyKernel$INVOKER$s$0$3$eval19.call(RubyKernel$INVOKER$s$0$3$eval19.gen)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:77)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:83)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:428)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:355)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:109)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:95)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:77)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:83)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:428)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:355)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:89)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:214)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:200)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:208)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:193)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:323)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:132)
        at org.jruby.runtime.MixedModeIRBlockBody.commonYieldPath(MixedModeIRBlockBody.java:148)
        at org.jruby.runtime.IRBlockBody.yieldSpecific(IRBlockBody.java:80)
        at org.jruby.runtime.Block.yieldSpecific(Block.java:136)
        at org.jruby.ir.runtime.IRRuntimeHelpers.yieldSpecific(IRRuntimeHelpers.java:450)
        at org.jruby.ir.instructions.YieldInstr.interpret(YieldInstr.java:74)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.processOtherOp(StartupInterpreterEngine.java:187)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:111)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:83)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:179)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:165)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:171)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:177)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:332)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:132)
        at org.jruby.runtime.MixedModeIRBlockBody.commonYieldPath(MixedModeIRBlockBody.java:148)
        at org.jruby.runtime.IRBlockBody.doYield(IRBlockBody.java:189)
        at org.jruby.runtime.BlockBody.yield(BlockBody.java:120)
        at org.jruby.runtime.Block.yieldValues(Block.java:183)
        at org.jruby.ir.instructions.YieldInstr.interpret(YieldInstr.java:80)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.processOtherOp(StartupInterpreterEngine.java:187)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:111)
        at org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:132)
        at org.jruby.runtime.MixedModeIRBlockBody.commonYieldPath(MixedModeIRBlockBody.java:148)
        at org.jruby.runtime.IRBlockBody.yieldSpecific(IRBlockBody.java:80)
        at org.jruby.runtime.Block.yieldSpecific(Block.java:136)
        at org.jruby.RubyKernel.loop(RubyKernel.java:1298)
        at org.jruby.RubyKernel$INVOKER$s$0$0$loop.call(RubyKernel$INVOKER$s$0$0$loop.gen)
        at org.jruby.internal.runtime.methods.JavaMethod$JavaMethodZeroBlock.call(JavaMethod.java:497)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:298)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:79)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:83)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:428)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:355)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:132)
        at org.jruby.runtime.MixedModeIRBlockBody.commonYieldPath(MixedModeIRBlockBody.java:148)
        at org.jruby.runtime.IRBlockBody.doYield(IRBlockBody.java:182)
        at org.jruby.runtime.BlockBody.yield(BlockBody.java:111)
        at org.jruby.runtime.Block.yield(Block.java:167)
        at org.jruby.RubyContinuation.enter(RubyContinuation.java:107)
        at org.jruby.RubyKernel.rbCatch19Common(RubyKernel.java:1127)
        at org.jruby.RubyKernel.rbCatch19(RubyKernel.java:1120)
        at org.jruby.RubyKernel$INVOKER$s$rbCatch19.call(RubyKernel$INVOKER$s$rbCatch19.gen)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:348)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:173)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:177)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:332)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:109)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:95)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:298)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:79)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:83)
        at org.jruby.ir.instructions.CallBase.interpret(CallBase.java:428)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:355)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:77)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:144)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:130)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:192)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:318)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:131)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:339)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.ir.interpreter.Interpreter.INTERPRET_BLOCK(Interpreter.java:132)
        at org.jruby.runtime.MixedModeIRBlockBody.commonYieldPath(MixedModeIRBlockBody.java:148)
        at org.jruby.runtime.IRBlockBody.doYield(IRBlockBody.java:182)
        at org.jruby.runtime.BlockBody.yield(BlockBody.java:111)
        at org.jruby.runtime.Block.yield(Block.java:167)
        at org.jruby.RubyContinuation.enter(RubyContinuation.java:107)
        at org.jruby.RubyKernel.rbCatch19Common(RubyKernel.java:1127)
        at org.jruby.RubyKernel.rbCatch19(RubyKernel.java:1120)
        at org.jruby.RubyKernel$INVOKER$s$rbCatch19.call(RubyKernel$INVOKER$s$rbCatch19.gen)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:348)
        at org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:173)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:177)
        at org.jruby.ir.interpreter.InterpreterEngine.processCall(InterpreterEngine.java:332)
        at org.jruby.ir.interpreter.StartupInterpreterEngine.interpret(StartupInterpreterEngine.java:73)
        at org.jruby.ir.interpreter.InterpreterEngine.interpret(InterpreterEngine.java:83)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.INTERPRET_METHOD(MixedModeIRMethod.java:179)
        at org.jruby.internal.runtime.methods.MixedModeIRMethod.call(MixedModeIRMethod.java:165)
        at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:200)
        at org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:338)
        at org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:163)
        at C_3a_.jruby_minus_9_dot_1_dot_8_dot_0.bin.jirb.invokeOther13:start(C:/jruby-9.1.8.0/bin/jirb:13)
        at C_3a_.jruby_minus_9_dot_1_dot_8_dot_0.bin.jirb.RUBY$script(C:/jruby-9.1.8.0/bin/jirb:13)
        at java.lang.invoke.MethodHandle.invokeWithArguments(Unknown Source)
        at org.jruby.ir.Compiler$1.load(Compiler.java:95)
        at org.jruby.Ruby.runScript(Ruby.java:827)
        at org.jruby.Ruby.runNormally(Ruby.java:746)
        at org.jruby.Ruby.runNormally(Ruby.java:764)
        at org.jruby.Ruby.runFromMain(Ruby.java:577)
        at org.jruby.Main.doRunFromMain(Main.java:417)
        at org.jruby.Main.internalRun(Main.java:305)
        at org.jruby.Main.run(Main.java:232)
        at org.jruby.Main.main(Main.java:204)
Caused by: java.lang.ClassCastException: org.bouncycastle.asn1.DLSequence cannot be cast to org.bouncycastle.asn1.ASN1Integer
        at org.jruby.ext.openssl.impl.PKey.readPrivateKey(PKey.java:88)
        at org.jruby.ext.openssl.x509store.PEMInputOutput.readKeyPair(PEMInputOutput.java:1242)
        at org.jruby.ext.openssl.x509store.PEMInputOutput.readPrivateKey(PEMInputOutput.java:325)
        ... 136 more
PKeyRSA could not read private key java.io.IOException: unknown tag 13 encountered
PKeyRSA could not read public key java.io.IOException: unknown tag 13 encountered
PKeyRSA could not generate (PKCS8) private key org.bouncycastle.jcajce.provider.asymmetric.util.ExtendedInvalidKeySpecException: u
nable to process key spec: java.lang.IllegalArgumentException: failed to construct sequence from byte[]: unknown tag 13 encountere
d
PKeyRSA could not generate (X509) public key java.security.spec.InvalidKeySpecException: encoded key spec not recognized: failed t
o construct sequence from byte[]: unknown tag 13 encountered
OpenSSL::PKey::RSAError: Neither PUB key nor PRIV key:
from org/jruby/ext/openssl/PKeyRSA.java:309:in `initialize'
from (irb):3:in `<eval>'
from org/jruby/RubyKernel.java:1000:in `eval'
from org/jruby/RubyKernel.java:1298:in `loop'
from org/jruby/RubyKernel.java:1120:in `catch'
from org/jruby/RubyKernel.java:1120:in `catch'

Then i experimenting with private key, i've removed -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- from PEM file, and after this i'm able to execute this on Jruby:

irb(main):005:0> require "base64"
=> true
irb(main):006:0> k = OpenSSL::PKey::RSA.new(Base64.decode64(File.read("private.pem")))
=> #<OpenSSL::PKey::RSA:0x1cff11c>
irb(main):007:0> k.private?
=> true

Is any way to enable Jruby OpenSSL automatically detect PEM file format and load Base64 private keys? This is frustrating public keys in Base64 format opened successfully, private key don't.

1

There are 1 best solutions below

0
On

Well, i decided to do this workaround, not best looking code, but it works:

class RSA_Base64_Key
  def self.rsa_new key_text
    if key_text.is_a?(String) && key_text.include?('-----BEGIN RSA PRIVATE KEY-----')
      key_text.gsub!('-----BEGIN RSA PRIVATE KEY-----', '')
      key_text.gsub!('-----END RSA PRIVATE KEY-----', '')
      key_text = Base64.decode64(key_text)
    end
    OpenSSL::PKey::RSA.new(key_text)
  end
end

if RUBY_ENGINE == 'jruby'
  RSA_Base64_Key.rsa_new(File.read(key_file))
else
  OpenSSL::PKey::RSA.new(File.read(key_file))
end