ccrypto-java 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.java-version +1 -1
  3. data/.release_history.yml +4 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +68 -53
  7. data/Rakefile +2 -1
  8. data/bin/console +14 -0
  9. data/jars/bcjmail-jdk18on-172.jar +0 -0
  10. data/jars/bcmail-jdk18on-172.jar +0 -0
  11. data/jars/bcpg-jdk18on-172.1.jar +0 -0
  12. data/jars/bcpkix-jdk18on-172.jar +0 -0
  13. data/jars/bcprov-ext-jdk18on-172.jar +0 -0
  14. data/jars/bcprov-jdk18on-172.jar +0 -0
  15. data/jars/bctls-jdk18on-172.jar +0 -0
  16. data/jars/bcutil-jdk18on-172.jar +0 -0
  17. data/lib/ccrypto/java/bc_const_mapping.rb +42 -0
  18. data/lib/ccrypto/java/data_conversion.rb +23 -2
  19. data/lib/ccrypto/java/engines/argon2_engine.rb +95 -0
  20. data/lib/ccrypto/java/engines/asn1_engine.rb +2 -1
  21. data/lib/ccrypto/java/engines/bcrypt_engine.rb +56 -0
  22. data/lib/ccrypto/java/engines/cipher_engine.rb +462 -130
  23. data/lib/ccrypto/java/engines/compression_engine.rb +7 -28
  24. data/lib/ccrypto/java/engines/crystal_dilithium_engine.rb +226 -0
  25. data/lib/ccrypto/java/engines/crystal_kyber_engine.rb +260 -0
  26. data/lib/ccrypto/java/engines/decompression_engine.rb +5 -4
  27. data/lib/ccrypto/java/engines/digest_engine.rb +221 -139
  28. data/lib/ccrypto/java/engines/ecc_engine.rb +249 -96
  29. data/lib/ccrypto/java/engines/ed25519_engine.rb +211 -0
  30. data/lib/ccrypto/java/engines/hkdf_engine.rb +82 -23
  31. data/lib/ccrypto/java/engines/hmac_engine.rb +98 -23
  32. data/lib/ccrypto/java/engines/pbkdf2_engine.rb +82 -33
  33. data/lib/ccrypto/java/engines/pkcs7_engine.rb +44 -33
  34. data/lib/ccrypto/java/engines/rsa_engine.rb +85 -31
  35. data/lib/ccrypto/java/engines/scrypt_engine.rb +12 -3
  36. data/lib/ccrypto/java/engines/secret_key_engine.rb +77 -12
  37. data/lib/ccrypto/java/engines/secret_sharing_engine.rb +17 -2
  38. data/lib/ccrypto/java/engines/x25519_engine.rb +249 -0
  39. data/lib/ccrypto/java/engines/x509_csr_engine.rb +141 -0
  40. data/lib/ccrypto/java/engines/x509_engine.rb +365 -71
  41. data/lib/ccrypto/java/ext/secret_key.rb +37 -25
  42. data/lib/ccrypto/java/ext/x509_cert.rb +429 -5
  43. data/lib/ccrypto/java/ext/x509_csr.rb +151 -0
  44. data/lib/ccrypto/java/jce_provider.rb +0 -11
  45. data/lib/ccrypto/java/keystore/jce_keystore.rb +205 -0
  46. data/lib/ccrypto/java/keystore/jks_keystore.rb +52 -0
  47. data/lib/ccrypto/java/keystore/keystore.rb +97 -0
  48. data/lib/ccrypto/java/keystore/pem_keystore.rb +147 -0
  49. data/lib/ccrypto/java/keystore/pkcs12_keystore.rb +56 -0
  50. data/lib/ccrypto/java/utils/comparator.rb +25 -2
  51. data/lib/ccrypto/java/version.rb +1 -1
  52. data/lib/ccrypto/java.rb +46 -0
  53. data/lib/ccrypto/provider.rb +139 -3
  54. metadata +40 -24
  55. data/ccrypto-java.gemspec +0 -44
  56. data/jars/bcmail-jdk15on-165.jar +0 -0
  57. data/jars/bcpg-jdk15on-165.jar +0 -0
  58. data/jars/bcpkix-jdk15on-165.jar +0 -0
  59. data/jars/bcprov-ext-jdk15on-165.jar +0 -0
  60. data/jars/bcprov-jdk15on-165.jar +0 -0
  61. data/jars/bctls-jdk15on-165.jar +0 -0
  62. data/lib/ccrypto/java/keybundle_store/pkcs12.rb +0 -125
@@ -0,0 +1,151 @@
1
+
2
+ #require 'pry'
3
+
4
+ require_relative '../data_conversion'
5
+
6
+ module Ccrypto
7
+ class X509CSR
8
+ include TR::CondUtils
9
+
10
+ include Java::DataConversion
11
+
12
+ include TeLogger::TeLogHelper
13
+ teLogger_tag :j_x509csr
14
+
15
+ attr_reader :request_subject_full
16
+
17
+ def initialize(csr)
18
+ @nativeCSR = csr
19
+ end
20
+
21
+ def to_bin
22
+ @nativeCSR.encoded
23
+ end
24
+
25
+ def to_pem
26
+
27
+ baos = java.io.ByteArrayOutputStream.new
28
+
29
+ writer = org.bouncycastle.openssl.jcajce.JcaPEMWriter.new(java.io.OutputStreamWriter.new(baos))
30
+
31
+ begin
32
+ writer.writeObject(@nativeCSR)
33
+ ensure
34
+ writer.flush
35
+ writer.close
36
+ end
37
+
38
+ baos.toByteArray
39
+
40
+ end
41
+
42
+ def csr_info
43
+ if @csrInfo.nil?
44
+ @csrInfo = parseCSR(@nativeCSR)
45
+ end
46
+ @csrInfo
47
+ end
48
+
49
+ def parseCSR(csrBin)
50
+
51
+ case csrBin
52
+ when ::Java::byte[]
53
+ csr = org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest.new(csrBin)
54
+
55
+ when String
56
+ # this assumed input is a PEM formatted content
57
+ reader = org.bouncycastle.openssl.PEMParser.new(java.io.InputStreamReader.new(java.io.ByteArrayInputStream.new(to_java_bytes(csrBin))))
58
+ csr = reader.readObject
59
+ raise X509CSRException, "Given CSR string to load does not encapsulate a CSR structure. It is read as '#{csr.class.name}'" if not csr.is_a?(org.bouncycastle.pkcs::PKCS10CertificationRequest)
60
+
61
+ when Ccrypto::X509CSR
62
+ csr = csrBin.nativeCSR
63
+
64
+ else
65
+ raise X509CSRException, "Unknown how to handle CSR of format #{csrBin.class}"
66
+
67
+ end
68
+
69
+
70
+ cvProv = org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder.new.build(csr.getSubjectPublicKeyInfo)
71
+ # 18 Apr 2023
72
+ # Not all curves are supported under X509 I think. At least those not well known curve such as B-163 from BC
73
+ # not able to verify the signature
74
+ raise X509CSRSignatureInvalid, "CSR signature is not valid" if not csr.isSignatureValid(cvProv)
75
+
76
+ certProfile = Ccrypto::X509::CertProfile.new
77
+
78
+ @request_subject_full = csr.getSubject.to_s
79
+ subj = csr.getSubject.to_s
80
+ subj.split(",").each do |e|
81
+ ee = e.split("=")
82
+ case ee[0]
83
+ when "CN"
84
+ certProfile.owner_name = ee[1]
85
+ when "O"
86
+ certProfile.org = ee[1]
87
+ when "OU"
88
+ certProfile.org_unit = ee[1]
89
+ when "E"
90
+ certProfile.email = ee[1]
91
+ end
92
+ end
93
+
94
+
95
+ pubKeyParam = org.bouncycastle.crypto.util.PublicKeyFactory.createKey(csr.subject_public_key_info)
96
+ curveName = org.bouncycastle.asn1.x9.ECNamedCurveTable.get_name(pubKeyParam.parameters.name)
97
+
98
+ spec = org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util.convertToSpec(pubKeyParam.getParameters)
99
+ point= org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util.convertPoint(pubKeyParam.getQ)
100
+ pubKeySpec = java.security.spec.ECPublicKeySpec.new(point, spec)
101
+ pubKey = java.security.KeyFactory.getInstance("EC", Ccrypto::Java::JCEProvider::DEFProv).generatePublic(pubKeySpec)
102
+ certProfile.public_key = Ccrypto::Java::ECCPublicKey.new(pubKey, curveName)
103
+
104
+ csr.attributes.each do |att|
105
+
106
+ ext = org.bouncycastle.asn1.x509.Extensions.getInstance(att.getAttrValues.getObjectAt(0))
107
+
108
+ gns = org.bouncycastle.asn1.x509.GeneralNames.fromExtensions(ext,org.bouncycastle.asn1.x509.Extension.subjectAlternativeName)
109
+ gns.getNames.each do |n|
110
+ #p n.getTagNo
111
+ case n.getTagNo
112
+ when org.bouncycastle.asn1.x509.GeneralName.dNSName
113
+ certProfile.dns_name = n.getName.to_s
114
+ when org.bouncycastle.asn1.x509.GeneralName.iPAddress
115
+ val = org.bouncycastle.asn1.DEROctetString.getInstance(n.getName.toASN1Primitive).getOctets
116
+ begin
117
+ certProfile.ip_addr = java.net.InetAddress.getByAddress(val).getHostAddress
118
+ rescue java.net.UnknownHostException => ex
119
+ certProfile.ip_addr = "Error decoding IP address : #{ex.message}"
120
+ teLogger.error "Failed to decode IP address from CSR"
121
+ teLogger.error ex.message
122
+ teLogger.error ex.backtrace.join("\n")
123
+ end
124
+ when org.bouncycastle.asn1.x509.GeneralName.uniformResourceIdentifier
125
+ certProfile.uri = n.getName.to_s
126
+ when org.bouncycastle.asn1.x509.GeneralName.rfc822Name
127
+ certProfile.email = n.getName.to_s
128
+ when org.bouncycastle.asn1.x509.GeneralName.otherName
129
+ ext = org.bouncycastle.asn1.x509.Extension.getInstance(n.getName)
130
+ certProfile.custom_extension[ext.extnId.to_s] = { value: ext.extnValue.octets.to_s, critical: ext.critical?, type: :string }
131
+ else
132
+ teLogger.debug "Unknown field tag no #{n.getTagNo}"
133
+ end
134
+ end
135
+
136
+ ext.oids.each do |o|
137
+ v = ext.getExtension(o)
138
+ next if v.extnId.to_s == org.bouncycastle.asn1.x509.Extension.subjectAlternativeName.to_s
139
+
140
+ certProfile.custom_extension[v.extnId.to_s] = { value: v.extnValue.octets.to_s, critical: v.critical?, type: :string }
141
+ end
142
+
143
+
144
+ end
145
+
146
+ certProfile
147
+
148
+ end
149
+
150
+ end
151
+ end
@@ -36,17 +36,6 @@ module Ccrypto
36
36
  add_provider
37
37
  end
38
38
 
39
- #def set_default_provider(prov)
40
-
41
- # case prov
42
- # when String
43
- # when java.security.Provider
44
- # add_provider(prov) if not is_provider_registered?(prov)
45
- # @defProvider = prov
46
- # end
47
-
48
- #end
49
-
50
39
  end
51
40
  end
52
41
  end
@@ -0,0 +1,205 @@
1
+
2
+ require_relative 'keystore'
3
+
4
+ module Ccrypto
5
+ module Java
6
+ module Keystore
7
+
8
+ class JCEKeystore
9
+ include TR::CondUtils
10
+ include DataConversion
11
+
12
+ class KeystoreException < StandardError; end
13
+
14
+ def self.from_keystore(bin, storeType, &block)
15
+
16
+ raise KeystoreException, "block is required" if not block
17
+
18
+ logger = block.call(:logger) || TeLogger::TLogger.new(STDOUT)
19
+
20
+ prov = block.call(:jce_provider)
21
+
22
+ if not_empty?(prov)
23
+ logger.debug "Keystore type '#{storeType}' with provider #{prov}"
24
+ ks = java.security.KeyStore.getInstance(storeType, prov)
25
+ else
26
+ logger.debug "Keystore type '#{storeType}' with nil provider"
27
+ ks = java.security.KeyStore.getInstance(storeType)
28
+ end
29
+
30
+ pass = block.call(:store_pass)
31
+ name = block.call(:key_name)
32
+
33
+ inForm = block.call(:in_format)
34
+ case inForm
35
+ when :b64
36
+ inp = from_b64(bin)
37
+ when :hex
38
+ inp = from_hex(bin)
39
+ else
40
+ inp = bin
41
+ end
42
+
43
+ bbin = to_java_bytes(inp)
44
+
45
+ ks.load(java.io.ByteArrayInputStream.new(bbin),pass.to_java.toCharArray)
46
+
47
+
48
+ logger.debug "Aliases found from keystore : #{ks.aliases.to_a.join(", ")}"
49
+ logger.debug "Given key name : #{name}"
50
+
51
+ # on situation there are multiple aliases, which is an error condition
52
+ # all subsequent chain and userCert shall be nil
53
+ name = ks.aliases.to_a.first if is_empty?(name)
54
+
55
+ logger.debug "Alias used to select cert and key : #{name}"
56
+
57
+ userCert = Ccrypto::X509Cert.new(ks.getCertificate(name))
58
+ raise KeystoreException, "User certificate is nil. Alias used to retrieve user certificate is '#{name}'. Aliases detected from the keystore are: #{ks.aliases.to_a.join(", ")}" if userCert.nil?
59
+ chain = ks.get_certificate_chain(name)
60
+ if not chain.nil?
61
+ chain = chain.collect { |c| Ccrypto::X509Cert.new(c) }
62
+ else
63
+ chain = []
64
+ end
65
+
66
+ key = ks.getKey(name, pass.to_java.toCharArray)
67
+ raise KeystoreException, "Private key is nil. Alias used to retrieve private key is '#{name}'. Aliases detected from the keystore are: #{ks.aliases.to_a.join(", ")}" if key.nil?
68
+ kp = java.security.KeyPair.new(userCert.getPublicKey, key)
69
+ case key
70
+ when java.security.interfaces.ECPrivateKey
71
+ param = java.security.AlgorithmParameters.getInstance("EC")
72
+ param.init(key.params)
73
+ oid = param.getParameterSpec(java.security.spec.ECGenParameterSpec.java_class).name
74
+ curve = org.bouncycastle.asn1.x9.ECNamedCurveTable.getName(org.bouncycastle.asn1.ASN1ObjectIdentifier.new(oid))
75
+ logger.debug "Recover curve info : #{curve}"
76
+ conf = Ccrypto::Java::ECCEngine.find_curve(curve)
77
+ logger.debug "Found config : #{conf}"
78
+ [Ccrypto::Java::ECCKeyBundle.new(kp, conf), userCert, chain]
79
+
80
+ when org.bouncycastle.jcajce.provider.asymmetric.ec::BCECPrivateKey
81
+ curve = key.params.name
82
+ logger.debug "Recover curve info : #{curve}"
83
+ conf = Ccrypto::Java::ECCEngine.find_curve(curve)
84
+ logger.debug "Found config : #{conf}"
85
+ [Ccrypto::Java::ECCKeyBundle.new(kp, conf), userCert, chain]
86
+ when java.security.interfaces.RSAPrivateKey
87
+ [Ccrypto::Java::RSAKeyBundle.new(kp), userCert, chain]
88
+ else
89
+ raise KeystoreException, "Unknown key type #{key}"
90
+ end
91
+
92
+ end # from_keystore
93
+
94
+ def self.to_keystore(storeType, &block)
95
+ raise KeystoreException, "Block is required" if not block
96
+
97
+ logger = block.call(:logger) || TeLogger::TLogger.new(STDOUT)
98
+
99
+ logger.debug "storetype #{storeType}"
100
+
101
+ prov = block.call(:jce_provider)
102
+
103
+ pkcs12NewCipher = false
104
+ if storeType == "PKCS12"
105
+ pkcs12NewCipher = block.call(:pkcs12_new_cipher?)
106
+ if pkcs12NewCipher == true
107
+ storeType = "JKS"
108
+ prov = nil
109
+ logger.debug "PKCS12 new cipher is on. Write to JKS first."
110
+ end
111
+ end
112
+
113
+ if not_empty?(prov)
114
+ logger.debug "Generating keystore of #{storeType} from provider #{prov}"
115
+ ks = java.security.KeyStore.getInstance(storeType, prov)
116
+ else
117
+ logger.debug "Generating keystore of #{storeType} using SUN provider"
118
+ ks = java.security.KeyStore.getInstance(storeType)
119
+ end
120
+
121
+ ks.load(nil,nil)
122
+
123
+ gcert = block.call(:cert)
124
+ raise KeystoreException, "#{storeType.upcase} requires the owner's X.509 certificate" if is_empty?(gcert)
125
+
126
+ ca = block.call(:cert_chain) || block.call(:certchain) || [gcert]
127
+ ca = [gcert] if ca.nil?
128
+ ca = ca.unshift(gcert) if not ca.first.equal?(gcert)
129
+ ca = ca.collect { |c|
130
+ Ccrypto::X509Cert.to_java_cert(c)
131
+ }
132
+
133
+ logger.debug "Cert Chain : #{ca.inspect}"
134
+
135
+ pass = block.call(:store_pass)
136
+ raise KeystoreException, "Password is required" if is_empty?(pass)
137
+
138
+ name = block.call(:key_name)
139
+ name = "Keystore for #{gcert.owner.name}" if is_empty?(name)
140
+
141
+ keypair = block.call(:keypair)
142
+ raise KeystoreException, "Keypair is required" if is_empty?(keypair)
143
+
144
+ logger.debug "Setting key entry with name : #{name}"
145
+ ks.setKeyEntry(name, keypair.private, pass.to_java.toCharArray, ca.to_java(java.security.cert.Certificate))
146
+
147
+ baos = java.io.ByteArrayOutputStream.new
148
+ ks.store(baos, pass.to_java.toCharArray)
149
+ res = baos.toByteArray
150
+
151
+ output = block.call(:output)
152
+ if not_empty?(output)
153
+ if pkcs12NewCipher
154
+ randName = SecureRandom.uuid
155
+ File.open(randName,"wb") do |f|
156
+ f.write res
157
+ end
158
+
159
+ # temporary until BC or JCE has way to completely support this to be able to load from OpenSSL
160
+ `keytool -importkeystore -alias "#{name}" -destalias "#{name}" -srckeystore #{randName} -destkeystore "#{output}" -srcstoretype JKS -deststoretype PKCS12 -srcstorepass "#{pass}" -deststorepass "#{pass}" -noprompt`
161
+
162
+ FileUtils.rm(randName)
163
+ else
164
+ File.open(output,"wb") do |f|
165
+ f.write res
166
+ end
167
+ end
168
+ else
169
+ outForm = block.call(:out_format)
170
+ case outForm
171
+ when :b64
172
+ bres = to_b64(res)
173
+ when :hex
174
+ bres = to_hex(res)
175
+ else
176
+ bres = res
177
+ end
178
+
179
+ if pkcs12NewCipher
180
+ randName = SecureRandom.uuid
181
+ File.open(randName,"wb") do |f|
182
+ f.write bres
183
+ end
184
+
185
+ # temporary until BC or JCE has way to completely support this to be able to load from OpenSSL
186
+ `keytool -importkeystore -alias "#{name}" -destalias "#{name}" -srckeystore #{randName} -destkeystore "#{output}" -srcstoretype JKS -deststoretype PKCS12 -srcstorepass "#{pass}" -deststorepass "#{pass}" -noprompt`
187
+
188
+ bcres = File.read(randName)
189
+ FileUtils.rm(randName)
190
+
191
+ bcres
192
+ else
193
+ bres
194
+ end
195
+
196
+ end
197
+
198
+ end # to_keystore
199
+
200
+
201
+ end # class JCEKeystore
202
+
203
+ end # module Keystore
204
+ end # module Java
205
+ end # module Ccrypto
@@ -0,0 +1,52 @@
1
+
2
+ require_relative 'jce_keystore'
3
+
4
+ module Ccrypto
5
+ module Java
6
+ module Keystore
7
+
8
+ class JKSKeystore
9
+ include TR::CondUtils
10
+ include DataConversion
11
+
12
+ def self.from_jks(bin, &block)
13
+
14
+ raise Ccrypto::Keystore::KeystoreException, "block is required" if not block
15
+
16
+ JCEKeystore.from_keystore(bin, "JKS") do |k|
17
+ case k
18
+ when :logger
19
+ logger
20
+ else
21
+ block.call(k)
22
+ end
23
+ end
24
+
25
+ end # from_jks
26
+
27
+ def self.to_jks(&block)
28
+
29
+ raise Ccrypto::Keystore::KeystoreException, "Block is required" if not block
30
+
31
+ JCEKeystore.to_keystore("JKS") do |k|
32
+ case k
33
+ when :logger
34
+ logger
35
+ else
36
+ block.call(k)
37
+ end
38
+ end
39
+
40
+ end # to_jks
41
+
42
+
43
+ private
44
+ def self.logger
45
+ Ccrypto::Java.logger(:jks)
46
+ end
47
+
48
+ end # class PKCS12Keystore
49
+
50
+ end # module Keystore
51
+ end # module Java
52
+ end # module Ccrypto
@@ -0,0 +1,97 @@
1
+
2
+ require 'ccrypto/keystore'
3
+ require_relative 'pkcs12_keystore'
4
+ require_relative 'jks_keystore'
5
+
6
+ module Ccrypto
7
+ module Java
8
+ module Keystore
9
+
10
+ def self.map_keystore_type(key)
11
+ case key.to_sym
12
+ when :pkcs12, :p12, :PKCS12, :P12, :Pkcs12
13
+ :pkcs12
14
+ when :jks, :Jks, :JKS, :java
15
+ :jks
16
+ else
17
+ raise Ccrypto::Keystore::KeystoreException, "Unsupported keystore type '#{key}'"
18
+ end
19
+ end
20
+
21
+ def self.load_keystore(bin, eng, &block)
22
+ mEng = map_keystore_type(eng)
23
+ case mEng
24
+ when :pkcs12
25
+ PKCS12Keystore.from_p12(bin, &block)
26
+ when :jks
27
+ JKSKeystore.from_jks(bin, &block)
28
+ else
29
+ raise Ccrypto::Keystore::KeystoreException, "Unsupported keystore type '#{eng}'"
30
+ end
31
+ end
32
+
33
+ def self.load_keystore_file(path, eng, &block)
34
+ raise Ccrypto::Keystore::KeystoreException, "Given keystore path to load '#{path}' does not exist" if not File.exist?(path)
35
+ raise Ccrypto::Keystore::KeystoreException, "Given keystore path to load '#{path}' cannot be read" if not File.readable?(path)
36
+ load_keystore(File.read(path), eng, &block)
37
+ end
38
+
39
+ def self.convert_keystore(bin, fromEng, toEng, &block)
40
+ srcEng = map_keystore_type(fromEng)
41
+ mtEng = map_keystore_type(toEng)
42
+ kp, cert, chain = load_keystore(bin, srcEng) do |opt|
43
+ case opt
44
+ when :store_pass
45
+ block.call(:source_store_pass)
46
+ else
47
+ block.call(opt)
48
+ end
49
+ end
50
+
51
+ outEng = map_keystore_type(mtEng)
52
+ case outEng
53
+ when :pkcs12
54
+ PKCS12Keystore.to_p12 do |k|
55
+ case k
56
+ when :keypair
57
+ kp
58
+ when :cert
59
+ cert
60
+ when :cert_chain
61
+ chain
62
+ when :store_pass
63
+ block.call(:dest_store_pass)
64
+ else
65
+ block.call(k)
66
+ end
67
+ end
68
+ when :jks
69
+ JKSKeystore.to_jks do |k|
70
+ case k
71
+ when :keypair
72
+ kp
73
+ when :cert
74
+ cert
75
+ when :cert_chain
76
+ chain
77
+ when :store_pass
78
+ block.call(:dest_store_pass)
79
+ else
80
+ block.call(k)
81
+ end
82
+ end
83
+
84
+ else
85
+ raise Ccrypto::Keystore::KeystoreException, "Unsupported keystore type '#{eng}'"
86
+ end
87
+ end
88
+
89
+ def self.convert_keystore_file(path, fromEng, toEng, &block)
90
+ raise Ccrypto::Keystore::KeystoreException, "Given keystore path to load '#{path}' does not exist" if not File.exist?(path)
91
+ raise Ccrypto::Keystore::KeystoreException, "Given keystore path to load '#{path}' cannot be read" if not File.readable?(path)
92
+ convert_keystore(File.read(path), fromEng, toEng, &block)
93
+ end
94
+
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,147 @@
1
+
2
+
3
+ module Ccrypto
4
+ module Java
5
+ module Keystore
6
+
7
+ class PEMKeystore
8
+ include TR::CondUtils
9
+ include DataConversion
10
+
11
+ def self.to_pem(&block)
12
+
13
+ raise KeystoreException, "Block is required" if not block
14
+
15
+ pass = block.call(:store_pass)
16
+ raise KeystoreException, "Password is required" if is_empty?(pass)
17
+
18
+ keypair = block.call(:keypair)
19
+ raise KeystoreException, "Keypair is required" if is_empty?(keypair)
20
+
21
+ output = block.call(:output)
22
+ if not_empty?(output)
23
+ ext = File.extname(output)
24
+ bname = File.basename(output, ext)
25
+ privPath = File.join("#{bname}_priv#{ext}")
26
+ pubPath = File.join("#{bname}_pub#{ext}")
27
+
28
+ privHead, privFoot, pubHead, pubFoot = marker(keypair)
29
+ File.open(privPath,"wb") do |f|
30
+ f.write privHead
31
+ f.write to_b64(keypair.private.to_bin)
32
+ f.write privFoot
33
+ end
34
+
35
+ File.open(pubPath,"wb") do |f|
36
+ f.write pubHead
37
+ f.write to_b64(keypair.public.to_bin)
38
+ f.write pubFoot
39
+ end
40
+
41
+ else
42
+ [to_b64(keypair.private.to_bin),to_b64(keypair.public.to_bin)]
43
+
44
+ end
45
+
46
+ end # def to_pem
47
+
48
+ def self.from_pem(str)
49
+
50
+ raise KeystoreException, "block is required" if not block
51
+
52
+ case check_keytype(str)
53
+ when :ecc
54
+ cont = str.lines[1..-2].join
55
+ Ccrypto::Java::ECCPrivateKey.to_key(from_b64(cont))
56
+ when :rsa
57
+ when :ed25519
58
+ when :x25519
59
+ when :crystal_dilithium
60
+ when :crystal_kyber
61
+ else
62
+ raise KeystoreException, "Unable to derive keytype from input : '#{str}'"
63
+ end
64
+
65
+ case key
66
+ when java.security.interfaces.ECPrivateKey
67
+ param = java.security.AlgorithmParameters.getInstance("EC")
68
+ param.init(key.params)
69
+ oid = param.getParameterSpec(java.security.spec.ECGenParameterSpec.java_class).name
70
+ curve = org.bouncycastle.asn1.x9.ECNamedCurveTable.getName(org.bouncycastle.asn1.ASN1ObjectIdentifier.new(oid))
71
+ logger.debug "Recover curve info : #{curve}"
72
+ conf = Ccrypto::Java::ECCEngine.find_curve(curve)
73
+ logger.debug "Found config : #{conf}"
74
+ [Ccrypto::Java::ECCKeyBundle.new(kp, conf), userCert, chain]
75
+
76
+ when org.bouncycastle.jcajce.provider.asymmetric.ec::BCECPrivateKey
77
+ curve = key.params.name
78
+ logger.debug "Recover curve info : #{curve}"
79
+ conf = Ccrypto::Java::ECCEngine.find_curve(curve)
80
+ logger.debug "Found config : #{conf}"
81
+ [Ccrypto::Java::ECCKeyBundle.new(kp, conf), userCert, chain]
82
+ when java.security.interfaces.RSAPrivateKey
83
+ [Ccrypto::Java::RSAKeyBundle.new(kp), userCert, chain]
84
+ else
85
+ raise KeystoreException, "Unknown key type #{key}"
86
+ end
87
+
88
+
89
+ end
90
+
91
+ private
92
+ def self.logger
93
+ Ccrypto::Java.logger(:pem_ks)
94
+ end
95
+
96
+ def self.marker(keypair)
97
+ case keypair
98
+ when ECCKeybundle
99
+ keytype = "ECC"
100
+ when RSAKeybundle
101
+ keytype = "RSA"
102
+ when ED25519KeyBundle
103
+ keytype = "ED25519"
104
+ when X25519KeyBundle
105
+ keytype = "X25519"
106
+ when CrystalDilithiumKeyBundle
107
+ keytype = "CRYSTAL DILITHIUM"
108
+ when CrystalKyberKeyBundle
109
+ keytype = "CRYSTAL KYBER"
110
+ else
111
+ raise Error, "Unsupported keypair type '#{keypair.class.name}'"
112
+ end
113
+
114
+ [
115
+ "-----BEGIN #{keytype} PRIVATE KEY-----\n",
116
+ "\n-----END #{keytype} PRIVATE KEY-----\n",
117
+ "-----BEGIN #{keytype} PUBLIC KEY-----\n",
118
+ "\n-----END #{keytype} PUBLIC KEY-----\n",
119
+ ]
120
+ end
121
+
122
+ def self.check_keytype(cont)
123
+
124
+ case cont
125
+ when /ECC/
126
+ :ecc
127
+ when /RSA/
128
+ :rsa
129
+ when /ED25519/
130
+ :ed25519
131
+ when /X25519/
132
+ :x25519
133
+ when /DILITHIUM/
134
+ :crystal_dilithium
135
+ when /KYBER/
136
+ :crystal_kyber
137
+ else
138
+ nil
139
+ end
140
+
141
+ end # def check_keytype
142
+
143
+ end
144
+
145
+ end
146
+ end
147
+ end