require 'pkernel' require_relative 'provider' require_relative 'global' module PkernelJce module KeyPair def generate(algo, conf = {}) log = conf[:log] if log.nil? log = GConf.instance.glog end prov = conf[:provider] #log.debug "Provider is #{prov == nil ? 'nil' : prov.name}" case algo.to_s.upcase when "RSA" len = conf[:len] || 2048 log.debug "Generating RSA #{len} bits with provider #{prov == nil ? 'default' : prov.name}" if prov.nil? kpg = java.security.KeyPairGenerator.getInstance("RSA") else PkernelJce::Provider.add_provider(prov) kpg = java.security.KeyPairGenerator.getInstance("RSA", prov) end kpg.java_send :initialize, [Java::int], len kpg.generate_key_pair when "DSA" len = conf[:len] || 2048 log.debug "Generating DSA #{len} bits with provider #{prov == nil ? 'default' : prov.name}" if prov.nil? kpg = java.security.KeyPairGenerator.getInstance("DSA") else PkernelJce::Provider.add_provider(prov) kpg = java.security.KeyPairGenerator.getInstance("DSA", prov) end kpg.java_send :initialize, [Java::int], len kpg.generate_key_pair when "ECC","EC" # this only supported by bc? curve = conf[:curve] || "prime256v1" if prov.nil? #kpg = java.security.KeyPairGenerator.getInstance("ECDSA") # default Java does not have ECDSA yet as of Java 8 prov = PkernelJce::Provider.add_default end log.debug "Generating ECC curve '#{curve}' with provider #{prov == nil ? 'default' : prov.name}" PkernelJce::Provider.add_provider(prov) kpg = java.security.KeyPairGenerator.getInstance("ECDSA", prov) kpg.java_send :initialize, [java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom], java.security.spec.ECGenParameterSpec.new(curve), java.security.SecureRandom.new kpg.generate_key_pair else log.error "Unknown '#{algo}' for java keypair" raise PkernelJce::Error, "Unknown algo '#{algo}' for java keypair" end end # end generate # def KeyPair.key_type(key) if key.java_kind_of?(java.security.KeyPair) privKey = key.getPrivate else # given is private key privKey = key end case privKey when java.security.interfaces.RSAPrivateCrtKey Pkernel::KeyPair::RSA_KEY_NAME when Java::sun.security.provider.DSAPrivateKey, org.bouncycastle.jcajce.provider.asymmetric.dsa.BCDSAPrivateKey Pkernel::KeyPair::DSA_KEY_NAME when org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey Pkernel::KeyPair::EC_KEY_NAME else raise PkernelJce::Error, "Unknown priv key type '#{privKey.class}'" end end # # end get_key_type # def KeyPair.pub_key_type(key) if key.java_kind_of?(java.security.KeyPair) pubKey = key.public elsif PkernelJce::Certificate.is_cert_object?(key) pubKey = PkernelJce::Certificate.public_key(key) else pubKey = key end case pubKey when java.security.interfaces.RSAPublicKey Pkernel::KeyPair::RSA_KEY_NAME when Java::sun.security.provider.DSAPublicKey, Java::OrgBouncycastleJcajceProviderAsymmetricDsa::BCDSAPublicKey Pkernel::KeyPair::DSA_KEY_NAME when Java::OrgBouncycastleJcajceProviderAsymmetricEc::BCECPublicKey Pkernel::KeyPair::EC_KEY_NAME else raise PkernelJce::Error, "Unknown pub key type '#{pubKey.class}'" end end def KeyPair.ensure_java_keypair(kp) raise PkernelJce::Error, "Keypair to check for Java keypair type is nil" if kp.nil? end def KeyPair.is_public_key?(key) if key.nil? false else case key when java.security.interfaces.RSAPublicKey, Java::OrgBouncycastleJcajceProviderAsymmetricDsa::BCDSAPublicKey,Java::sun.security.provider.DSAPublicKey, Java::OrgBouncycastleJcajceProviderAsymmetricEc::BCECPublicKey true else false end end end def KeyPair.is_private_key?(key) if key.nil? false else case key when java.security.interfaces.RSAPrivateCrtKey, Java::sun.security.provider.DSAPrivateKey, org.bouncycastle.jcajce.provider.asymmetric.dsa.BCDSAPrivateKey, org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey true else false end end end def KeyPair.is_keypair?(key) if key.nil? false else case key when java.security.KeyPair true else false end end end def KeyPair.public_key(priv) if priv.nil? raise PkernelJce::Error, "Cannot derive public key from nil key" else if priv.java_kind_of?(java.security.KeyPair) priv.getPublic elsif priv.java_kind_of?(java.security.PrivateKey) type = key_type(priv) case type when Pkernel::KeyPair::RSA_KEY_NAME java.security.KeyFactory.getInstance("RSA").generatePublic(java.security.spec.RSAPublicKeySpec.new(priv.modulus, priv.public_exponent)) when Pkernel::KeyPair::DSA_KEY_NAME y = priv.params.g.to_java.modPow(priv.x, priv.params.p) spec = java.security.spec.DSAPublicKeySpec.new(y, priv.params.p, priv.params.q, priv.params.g) prov = PkernelJce::Provider.add_default java.security.KeyFactory.getInstance("DSA",prov).generatePublic(spec) when Pkernel::KeyPair::EC_KEY_NAME d = priv.d; q = priv.parameters.g.to_java.multiply(d); pubSpec = org.bouncycastle.jce.spec.ECPublicKeySpec.new(q, priv.parameters); prov = PkernelJce::Provider.add_default java.security.KeyFactory.getInstance("EC",prov).generatePublic(pubSpec) else end elsif priv.java_kind_of?(java.security.PublicKey) priv elsif priv.java_kind_of?(org.bouncycastle.asn1.x509.SubjectPublicKeyInfo) org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter.new.getPublicKey(priv) elsif priv.java_kind_of?(org.bouncycastle.cert.X509CertificateHolder) org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter.new.getPublicKey(priv.subject_public_key_info) elsif priv.java_kind_of?(java.security.cert.Certificate) priv.public_key else raise PkernelJce::Error, "Unsupported key type '#{priv.class}' to convert to public key" end end end # end public_key(priv) # # harmonize the private key acquiring # There are BC and Java, keypair and private key def KeyPair.private_key(kp) if kp.nil? raise PkernelJce::Error, "Cannot derive private key from nil private key" else case kp when java.security.KeyPair kp.private when java.security.PrivateKey kp else raise PkernelJce::Error, "Unsupported key type '#{kp}' to derive private key from." end end end def KeyPair.derive_signing_algo(kp, hashAlgo = "SHA256") raise PkernelJce::Error, "Hashing algo should not be nil while deriving signing algo" if hashAlgo.nil? PkernelJce::GConf.instance.glog.debug "Derive signing algo for key '#{kp.class}'" if KeyPair.is_keypair?(kp) type = PkernelJce::KeyPair.key_type(KeyPair.private_key(kp)) elsif KeyPair.is_public_key?(kp) type = PkernelJce::KeyPair.pub_key_type(kp) elsif Certificate.is_cert_object?(kp) type = PkernelJce::KeyPair.pub_key_type(Certificate.public_key(kp)) elsif KeyPair.is_private_key?(kp) type = PkernelJce::KeyPair.key_type(kp) else raise PkernelJce::Error, "Unknown key type to derive signing key algo from" end #type = PkernelJce::KeyPair.key_type(kp) case type when Pkernel::KeyPair::RSA_KEY_NAME "#{hashAlgo}with#{type.upcase}" when Pkernel::KeyPair::DSA_KEY_NAME "#{hashAlgo}with#{type.upcase}" when Pkernel::KeyPair::EC_KEY_NAME "#{hashAlgo}with#{type.upcase}DSA" else raise PkernelJce::Error, "Unsupported type to derive signing algo '#{type}'" end end def dump(keypair, options = {}) if keypair.nil? raise PkernelJce::Error, "Keypair is nil during writing PEM" end # outputStream only make sense in Java world #os = options[:outputStream] file = options[:file] pass = options[:password] binOut = java.io.ByteArrayOutputStream.new if file.nil? PkernelJce::GConf.instance.glog.debug "Dump output to memory" # return binary to caller writer = org.bouncycastle.openssl.jcajce.JcaPEMWriter.new(java.io.OutputStreamWriter.new(binOut)); else PkernelJce::GConf.instance.glog.debug "Dump output to file '#{file}'" out = java.io.FileOutputStream.new(file) writer = org.bouncycastle.openssl.jcajce.JcaPEMWriter.new(java.io.OutputStreamWriter.new(out)); end begin if pass.nil? or pass.empty? writer.writeObject(keypair.getPrivate) else builder = org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder.new(org.bouncycastle.openssl.PKCS8Generator::AES_256_CBC).setProvider(PkernelJce::Provider::DefProvider).setPasssword(pass.to_java.toCharArray()); pkcs8 = org.bouncycastle.openssl.jcajce.JcaPKCS8Generator.new(keypair.getPrivate, builder.build); writer.writeObject(pkcs8) end ensure writer.flush writer.close end if file.nil? binOut.toByteArray end end # end dump def load(opts = {}, &block) #is = opts[:inputStream] #if is.nil? # raise PkernelJce::Error, "InputStream not given" #end file = opts[:file] bin = opts[:bin] baos = java.io.ByteArrayOutputStream.new if not file.nil? and not file.empty? PkernelJce::GConf.instance.glog.debug "Load keypair from file '#{file}'" f = java.io.File.new(file) if f.exists? b = Java::byte[f.length].new dis = java.io.DataInputStream.new(java.io.FileInputStream.new(f)) dis.readFully(b) dis.close baos.write(b) else raise PkernelJce::Error, "Given file to load '#{f.absolute_path}' does not exist" end elsif not bin.nil? PkernelJce::GConf.instance.glog.debug "Load keypair from memory" baos.write(bin) else raise PkernelJce::Error, "No 'file' or 'bin' is defined to load keypair" end prov = opts[:provider] || PkernelJce::Provider::DefProvider prov = PkernelJce::Provider.add_provider(prov) reader = org.bouncycastle.openssl.PEMParser.new(java.io.InputStreamReader.new(java.io.ByteArrayInputStream.new(baos.toByteArray))) obj = reader.readObject #p obj.inspect #p obj.methods.sort #p obj.java_kind_of?(org.bouncycastle.openssl.PEMEncryptedKeyPair) if obj.is_a?(org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo) pass = opts[:password] if pass.nil? if block pass = block.call(:password) else raise PkernelJce::Error, "PEM key is encrypted. Please provide a password to load the key." end end decProv = org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder.new.setProvider(PkernelJce::Provider::DefProvider).build(pass.to_java.toCharArray) keyinfo = obj.decryptPrivateKeyInfo(decProv) converter = org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter.new converter.getPrivateKey(keyinfo) else keySpec = java.security.spec.PKCS8EncodedKeySpec.new(obj.getPrivateKeyInfo.encoded) # Now it's in a PKCS#8 PrivateKeyInfo structure. Read its Algorithm # OID and use that to construct a KeyFactory. bIn = org.bouncycastle.asn1.ASN1InputStream.new(java.io.ByteArrayInputStream.new(keySpec.getEncoded())); pki = org.bouncycastle.asn1.pkcs.PrivateKeyInfo.getInstance(bIn.readObject()); algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId(); java.security.KeyFactory.getInstance(algOid,prov).generatePrivate(keySpec); #privKey = java.security.KeyFactory.getInstance("RSA").generatePrivate(keySpec) #privKey end end # end load() # end class KeyPairEngine extend KeyPair end end