lib/ccrypto/java/engines/ecc_engine.rb in ccrypto-java-0.1.0 vs lib/ccrypto/java/engines/ecc_engine.rb in ccrypto-java-0.2.0

- old
+ new

@@ -1,191 +1,319 @@ require_relative '../data_conversion' -require_relative '../keybundle_store/pkcs12' +require_relative '../keystore/keystore' module Ccrypto module Java class ECCPublicKey < Ccrypto::ECCPublicKey include DataConversion - def to_bin - @native_pubKey.encoded + def initialize(kp, curve) + super(kp) + @curve = curve end + def curve + @curve + end + + def to_bin(enc = :bin) + res = @native_pubKey.encoded + case enc + when :b64, :base64 + to_b64(res) + when :hex + to_hex(res) + else + res + end + end + def encoded to_bin end + def to_pem + cont = ["-----BEGIN ECC PUBLIC KEY-----\n"] + cont << to_b64(to_bin) + cont << "\n-----END ECC PUBLIC KEY-----" + cont.join + end + + def self.from_pem(str) + if str =~ /ECC PUBLIC/ + cont = str.lines[1..-2].join.strip + to_key(from_b64(cont)) + else + raise KeypairEngineException, "Not an ECC public key" + end + end + + # from binary to public key object def self.to_key(bin) bin = to_java_bytes(bin) if not bin.is_a?(::Java::byte[]) pubKey = java.security.KeyFactory.getInstance("ECDSA", "BC").generatePublic(java.security.spec.X509EncodedKeySpec.new(bin)) - ECCPublicKey.new(pubKey) + #p pubKey + curve = pubKey.params.name + ECCPublicKey.new(pubKey, curve) end - end + def equals?(pubKey) + if not @native_pubKey.nil? + case pubKey + when ECCPublicKey + @native_pubKey.encoded == pubKey.to_bin + else + logger.warn "Unmatched public key : (native) #{@native_pubKey} vs. (subject) #{pubKey}" + false + end + else + logger.warn "ECCPublicKey equals? returned false because native_pubKey is nil" + false + end + end + alias_method :key_equals?, :equals? + private + def logger + Ccrypto::Java.logger(:ecc_pubKey) + end + + end # class ECCPublicKey + + class ECCPrivateKey < Ccrypto::ECCPrivateKey + include DataConversion + + def self.to_key(bin, &block) + if block + prov = block.call(:jce_provider) + else + prov = JCEProvider::BCProv + end + + kf = java.security.KeyFactory.getInstance("ECDSA",prov) + priv = kf.generate_private(java.security.spec.PKCS8EncodedKeySpec.new(bin)) + curve = priv.params.name + ECCPrivateKey.new(priv, curve) + + end + + attr_reader :curve + def initialize(privKey, curve = nil) + super(privKey) + @curve = curve + end + + def to_pem + cont = ["-----BEGIN ECC PRIVATE KEY-----\n"] + cont << to_b64(@native_privKey.encoded) + cont << "\n-----END ECC PRIVATE KEY-----" + cont.join + end + + def self.from_pem(str) + if str =~ /ECC PRIVATE/ + cont = str.lines[1..-2].join.strip + to_key(from_b64(cont)) + else + raise KeypairEngineException, "Not an ECC private key" + end + end + + def to_bin + @native_privKey.encoded + end + + def equals?(privKey) + if not @native_privKey.nil? + case privKey + when ECCPrivateKey + @native_privKey.encoded == privKey.to_bin + else + logger.warn "Unmatched private key : (native) #{@native_privKey} vs. (subject) #{privKey}" + false + end + else + logger.warn "ECCPrivateKey equals? returned false because native_privKey is nil" + false + end + end + alias_method :key_equals?, :equals? + + end # class ECCPrivateKey + class ECCKeyBundle include Ccrypto::ECCKeyBundle include TR::CondUtils include DataConversion - include PKCS12 + def initialize(kp, conf) + @nativeKeypair = kp + @config = conf + end - include TeLogger::TeLogHelper - - teLogger_tag :j_ecc_keybundle - - def initialize(kp) - @keypair = kp + def curve + @config.provider_config[:curve] end - def native_keypair - @keypair + # standardize external API + def key_param + @config end + # standardize external API def public_key if @pubKey.nil? - @pubKey = ECCPublicKey.new(@keypair.public) + @pubKey = ECCPublicKey.new(@nativeKeypair.public, @config.curve) end @pubKey end + # standardize external API def private_key - ECCPrivateKey.new(@keypair.private) + ECCPrivateKey.new(@nativeKeypair.private) end - def derive_dh_shared_secret(pubKey, &block) + # standardize external API + def derive_enc_shared_secret(*args, &block) + derive_dh_shared_secret(*args, &block) + end - JCEProvider.instance.add_bc_provider + # standardize external API + def derive_dec_shared_secret(*args, &block) + derive_dh_shared_secret(*args, &block) + end - ka = javax.crypto.KeyAgreement.getInstance("ECDH", JCEProvider::DEFProv) - ka.init(@keypair.private) + def is_public_key_equal?(pubKey) + public_key.equals?(pubKey) + end - case pubKey - when ECCPublicKey - pub = pubKey.native_pubKey - when java.security.PublicKey - pub = pubKey + def equal?(kp) + case kp + when Ccrypto::ECCKeyBundle + private_key.encoded == kp.private_key.encoded else - raise KeypairEngineException, "Unsupported public key type #{pubKey.class}" + false end - ka.doPhase(pub, true) - #ka.doPhase(pubKey.native_pubKey, true) - if block - keyType = block.call(:keytype) - else - keyType = "AES" - end - keyType = "AES" if is_empty?(keyType) - teLogger.debug "Generate secret key type #{keyType}" - ka.generateSecret(keyType).encoded end - def is_public_key_equal?(pubKey) - @keypair.public.encoded == pubKey.encoded - end - - def to_storage(type, &block) - - case type - when :p12, :pkcs12 - to_pkcs12 do |key| + def write_keystore(type, &block) + ksType = Keystore.map_keystore_type(type) + case ksType + when :pkcs12 + Keystore::PKCS12Keystore.to_p12 do |key, *val| case key when :keypair - @keypair + @nativeKeypair else block.call(key) if block end end - when :jks - to_pkcs12 do |key| + Keystore::JKSKeystore.to_jks do |key, *val| case key - when :storeType - "JKS" when :keypair - @keypair + @nativeKeypair else - block.call(key) if key + block.call(key) if block end end - - when :pem - header = "-----BEGIN EC PRIVATE KEY-----\n" - footer = "\n-----END EC PRIVATE KEY-----" - - out = StringIO.new - out.write header - out.write to_b64_mime(@keypair.private.encoded) - out.write footer - - out.string - else - raise KeypairEngineException, "Unknown storage type #{type}" + raise Ccrypto::Keystore::KeystoreException, "Unsupported keystore type '#{type}' for engine '#{self.class.name}'" end - end - def self.from_storage(bin, &block) - - if is_pem?(bin) - else - from_pkcs12(bin, &block) - end + private + def derive_dh_shared_secret(pubKey, &block) - end + JCEProvider.instance.add_bc_provider - def self.is_pem?(bin) - begin - (bin =~ /BEGIN/) != nil - rescue ArgumentError => ex - false - end - end + ka = javax.crypto.KeyAgreement.getInstance("ECDH", JCEProvider::DEFProv) + ka.init(@nativeKeypair.private) - def equal?(kp) - case kp - when Ccrypto::ECCKeyBundle - @keypair.encoded == kp.private.encoded + case pubKey + when ECCPublicKey + #teLogger.debug "pubKey instanceof ECCPublicKey" + pub = pubKey.native_pubKey + when java.security.PublicKey + #teLogger.debug "pubKey instanceof java.security.PublicKey" + pub = pubKey else - false + raise KeypairEngineException, "Unsupported public key type #{pubKey.class}" end + ka.doPhase(pub, true) + + ka.generateSecret + + #if block + # keyType = block.call(:keytype) + #else + # keyType = "AES" + #end + #keyType = "AES" if is_empty?(keyType) + #teLogger.debug "Generate secret key type #{keyType}" + #ka.generateSecret(keyType).encoded + end def method_missing(mtd, *args, &block) teLogger.debug "Sending to native #{mtd}" - @keypair.send(mtd, *args, &block) + @nativeKeypair.send(mtd, *args, &block) end def respond_to_missing?(mtd, incPriv = false) teLogger.debug "Respond to missing #{mtd}" - @keypair.respond_to?(mtd) + @nativeKeypair.respond_to?(mtd) end - end + def teLogger + Ccrypto::Java.logger(:ecc_keybundle) + end + end # class ECCKeyBundle + class ECCEngine include TR::CondUtils include DataConversion - include TeLogger::TeLogHelper - teLogger_tag :j_ecc - def self.supported_curves if @curves.nil? - @curves = org.bouncycastle.asn1.x9.ECNamedCurveTable.getNames.sort.to_a.map { |c| Ccrypto::ECCConfig.new(c) } + @curves = org.bouncycastle.asn1.x9.ECNamedCurveTable.getNames.sort.to_a.map { |c| + conf = Ccrypto::ECCConfig.new(c.downcase.to_sym) + conf.provider_config = { curve: c } + conf + } end @curves end + def self.find_curve(name) + case name + when String, Symbol + supported_curves.select { |c| c.provider_config[:curve] == name or c.curve == name.downcase.to_sym }.first + when Ccrypto::ECCKeyBundle + fname = name.key_param + supported_curves.select { |c| c.provider_config[:curve] == fname.provider_config[:curve] or c.curve == fname.algo }.first + when Ccrypto::ECCPublicKey + fname = name.curve + supported_curves.select { |c| c.provider_config[:curve] == fname or c.curve == fname.to_sym }.first + else + raise KeypairEngineException, "Not supported curve finder with '#{name.class}'" + end + end + class <<self + alias_method :supported_params, :supported_curves + alias_method :find_by_param, :find_curve + end + def initialize(*args,&block) @config = args.first raise KeypairEngineException, "1st parameter must be a #{Ccrypto::KeypairConfig.class} object" if not @config.is_a?(Ccrypto::KeypairConfig) + raise KeypairEngineException, "Keypair config must be an initialized config. Please get the initialized config from the list of supported_curves()" if is_empty?(@config.provider_config) end def generate_keypair(&block) algoName = "ECDSA" @@ -205,14 +333,14 @@ randomEngine = uRandEng if not uRandEng.nil? end kpg = java.security.KeyPairGenerator.getInstance(algoName, prov) #kpg.java_send :initialize, [java.security.spec.AlgorithmParameterSpec, java.security.SecureRandom], java.security.spec.ECGenParameterSpec.new(curve), java.security.SecureRandom.new - kpg.java_send :initialize, [java.security.spec.AlgorithmParameterSpec, randomEngine.class], java.security.spec.ECGenParameterSpec.new(@config.curve), randomEngine + kpg.java_send :initialize, [java.security.spec.AlgorithmParameterSpec, randomEngine.class], java.security.spec.ECGenParameterSpec.new(@config.provider_config[:curve]), randomEngine kp = kpg.generate_key_pair - kb = ECCKeyBundle.new(kp) + kb = ECCKeyBundle.new(kp, @config) kb end def regenerate_keypair(pubKey, privKey, &block) @@ -225,11 +353,11 @@ raise KeypairEngineException, "ECC keypair is required. Given #{@config.keypair}" if not @config.keypair.is_a?(ECCKeyBundle) kp = @config.keypair sign = java.security.Signature.getInstance("SHA256WithECDSA") sign.initSign(kp.private_key) - teLogger.debug "Signing data : #{val}" + #teLogger.debug "Signing data : #{val}" case val when java.io.InputStream buf = Java::byte[102400].new while((read = val.read(buf, 0, buf.length)) != nil) sign.update(buf,0,read) @@ -242,11 +370,11 @@ end def self.verify(pubKey, val, sign) ver = java.security.Signature.getInstance("SHA256WithECDSA") ver.initVerify(pubKey) - teLogger.debug "Verifing data : #{val}" + #teLogger.debug "Verifing data : #{val}" case val when java.io.InputStream buf = Java::byte[102400].new while((read = val.read(buf, 0 ,buf.length)) != nil) ver.update(buf,0, read) @@ -255,9 +383,34 @@ ver.update(to_java_bytes(val)) end ver.verify(to_java_bytes(sign)) end + + # + # KeyFactory keyFactory = KeyFactory.getInstance(EC.getAlgorithmName(), BouncyCastleProviderHolder.getInstance()); + # BCECPrivateKey ecPrivateKey = (BCECPrivateKey) privateKey; + # ECParameterSpec ecParameterSpec = ecPrivateKey.getParameters(); + # ECPoint ecPoint = new FixedPointCombMultiplier().multiply(ecParameterSpec.getG(), ecPrivateKey.getD()); + # ECPublicKeySpec keySpec = new ECPublicKeySpec(ecPoint, ecParameterSpec); + # return keyFactory.generatePublic(keySpec); + # + def self.ecc_public_key_from_private_key(privKey) + kf = java.security.KeyFactory.getInstance("ECDSA", JCEProvider::BCProv) + param = privKey.parameters + ecPoint = org.bouncycastle.math.ec.FixedPointCombMultiplier.new.multiply(param.g, privKey.d) + keySpec = org.bouncycastle.jce.spec.ECPublicKeySpec.new(ecPoint, param) + kf.generatePublic(keySpec) + end + + private + def self.teLogger + Ccrypto::Java.logger(:ecc_eng) + end + def teLogger + self.class.teLogger + end + end end end