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.
- checksums.yaml +4 -4
- data/.java-version +1 -1
- data/.release_history.yml +4 -0
- data/.ruby-version +1 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +68 -53
- data/Rakefile +2 -1
- data/bin/console +14 -0
- data/jars/bcjmail-jdk18on-172.jar +0 -0
- data/jars/bcmail-jdk18on-172.jar +0 -0
- data/jars/bcpg-jdk18on-172.1.jar +0 -0
- data/jars/bcpkix-jdk18on-172.jar +0 -0
- data/jars/bcprov-ext-jdk18on-172.jar +0 -0
- data/jars/bcprov-jdk18on-172.jar +0 -0
- data/jars/bctls-jdk18on-172.jar +0 -0
- data/jars/bcutil-jdk18on-172.jar +0 -0
- data/lib/ccrypto/java/bc_const_mapping.rb +42 -0
- data/lib/ccrypto/java/data_conversion.rb +23 -2
- data/lib/ccrypto/java/engines/argon2_engine.rb +95 -0
- data/lib/ccrypto/java/engines/asn1_engine.rb +2 -1
- data/lib/ccrypto/java/engines/bcrypt_engine.rb +56 -0
- data/lib/ccrypto/java/engines/cipher_engine.rb +462 -130
- data/lib/ccrypto/java/engines/compression_engine.rb +7 -28
- data/lib/ccrypto/java/engines/crystal_dilithium_engine.rb +226 -0
- data/lib/ccrypto/java/engines/crystal_kyber_engine.rb +260 -0
- data/lib/ccrypto/java/engines/decompression_engine.rb +5 -4
- data/lib/ccrypto/java/engines/digest_engine.rb +221 -139
- data/lib/ccrypto/java/engines/ecc_engine.rb +249 -96
- data/lib/ccrypto/java/engines/ed25519_engine.rb +211 -0
- data/lib/ccrypto/java/engines/hkdf_engine.rb +82 -23
- data/lib/ccrypto/java/engines/hmac_engine.rb +98 -23
- data/lib/ccrypto/java/engines/pbkdf2_engine.rb +82 -33
- data/lib/ccrypto/java/engines/pkcs7_engine.rb +44 -33
- data/lib/ccrypto/java/engines/rsa_engine.rb +85 -31
- data/lib/ccrypto/java/engines/scrypt_engine.rb +12 -3
- data/lib/ccrypto/java/engines/secret_key_engine.rb +77 -12
- data/lib/ccrypto/java/engines/secret_sharing_engine.rb +17 -2
- data/lib/ccrypto/java/engines/x25519_engine.rb +249 -0
- data/lib/ccrypto/java/engines/x509_csr_engine.rb +141 -0
- data/lib/ccrypto/java/engines/x509_engine.rb +365 -71
- data/lib/ccrypto/java/ext/secret_key.rb +37 -25
- data/lib/ccrypto/java/ext/x509_cert.rb +429 -5
- data/lib/ccrypto/java/ext/x509_csr.rb +151 -0
- data/lib/ccrypto/java/jce_provider.rb +0 -11
- data/lib/ccrypto/java/keystore/jce_keystore.rb +205 -0
- data/lib/ccrypto/java/keystore/jks_keystore.rb +52 -0
- data/lib/ccrypto/java/keystore/keystore.rb +97 -0
- data/lib/ccrypto/java/keystore/pem_keystore.rb +147 -0
- data/lib/ccrypto/java/keystore/pkcs12_keystore.rb +56 -0
- data/lib/ccrypto/java/utils/comparator.rb +25 -2
- data/lib/ccrypto/java/version.rb +1 -1
- data/lib/ccrypto/java.rb +46 -0
- data/lib/ccrypto/provider.rb +139 -3
- metadata +40 -24
- data/ccrypto-java.gemspec +0 -44
- data/jars/bcmail-jdk15on-165.jar +0 -0
- data/jars/bcpg-jdk15on-165.jar +0 -0
- data/jars/bcpkix-jdk15on-165.jar +0 -0
- data/jars/bcprov-ext-jdk15on-165.jar +0 -0
- data/jars/bcprov-jdk15on-165.jar +0 -0
- data/jars/bctls-jdk15on-165.jar +0 -0
- 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
|