ccrypto-java 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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