ccrypto-ruby 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.release_history.yml +6 -0
- data/Dockerfile.dockerun +29 -0
- data/Gemfile.lock +60 -33
- data/ccrypto-ruby.gemspec +4 -1
- data/gen_ecc_const.rb +13 -0
- data/lib/ccrypto/provider.rb +52 -44
- data/lib/ccrypto/ruby/ecc_const.rb +82 -0
- data/lib/ccrypto/ruby/engines/cipher_engine.rb +214 -55
- data/lib/ccrypto/ruby/engines/digest_engine.rb +39 -17
- data/lib/ccrypto/ruby/engines/ecc_engine.rb +89 -14
- data/lib/ccrypto/ruby/engines/ed25519_engine.rb +84 -0
- data/lib/ccrypto/ruby/engines/pkcs7_engine.rb +1 -0
- data/lib/ccrypto/ruby/engines/rsa_engine.rb +9 -0
- data/lib/ccrypto/ruby/engines/x25519_engine.rb +65 -0
- data/lib/ccrypto/ruby/engines/x509_csr_engine.rb +128 -0
- data/lib/ccrypto/ruby/engines/x509_engine.rb +208 -8
- data/lib/ccrypto/ruby/ext/x509_csr.rb +153 -0
- data/lib/ccrypto/ruby/keybundle_store/pem_store.rb +3 -3
- data/lib/ccrypto/ruby/keybundle_store/pkcs12.rb +6 -5
- data/lib/ccrypto/ruby/version.rb +1 -1
- data/lib/ccrypto/ruby.rb +1 -0
- metadata +39 -3
@@ -204,6 +204,15 @@ module Ccrypto
|
|
204
204
|
@config.private_key.private_decrypt(enc, padVal)
|
205
205
|
end
|
206
206
|
|
207
|
+
def self.supported_keysizes
|
208
|
+
[
|
209
|
+
Ccrypto::RSAConfig.new(1024, Ccrypto::KeypairConfig::Algo_NotRecommended),
|
210
|
+
Ccrypto::RSAConfig.new(2048, Ccrypto::KeypairConfig::Algo_Active, true),
|
211
|
+
Ccrypto::RSAConfig.new(4096),
|
212
|
+
Ccrypto::RSAConfig.new(8192)
|
213
|
+
]
|
214
|
+
end
|
215
|
+
|
207
216
|
|
208
217
|
|
209
218
|
#####################
|
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
require 'x25519'
|
3
|
+
|
4
|
+
module Ccrypto
|
5
|
+
module Ruby
|
6
|
+
|
7
|
+
class X25519PublicKey < Ccrypto::X25519PublicKey
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class X25519KeyBundle
|
12
|
+
include Ccrypto::X25519KeyBundle
|
13
|
+
|
14
|
+
include TR::CondUtils
|
15
|
+
|
16
|
+
include TeLogger::TeLogHelper
|
17
|
+
teLogger_tag :x25519_kb
|
18
|
+
|
19
|
+
def initialize(kp)
|
20
|
+
@nativeKeypair = kp
|
21
|
+
end
|
22
|
+
|
23
|
+
def public_key
|
24
|
+
if @pubKey.nil?
|
25
|
+
@pubKey = X25519PublicKey.new(@nativeKeypair.public_key)
|
26
|
+
end
|
27
|
+
@pubKey
|
28
|
+
end
|
29
|
+
|
30
|
+
def private_key
|
31
|
+
X25519PrivateKey.new(@nativeKeypair)
|
32
|
+
end
|
33
|
+
|
34
|
+
def derive_dh_shared_secret(pubKey)
|
35
|
+
|
36
|
+
case pubKey
|
37
|
+
when Ccrypto::X25519PublicKey
|
38
|
+
uPubKey = pubKey.native_pubKey
|
39
|
+
else
|
40
|
+
raise KeypairEngineException, "Unknown X25519 public key type '#{pubKey.class}'"
|
41
|
+
end
|
42
|
+
|
43
|
+
@nativeKeypair.diffie_hellman(uPubKey).to_bytes
|
44
|
+
end
|
45
|
+
|
46
|
+
end # X25519KeyBundle
|
47
|
+
|
48
|
+
class X25519Engine
|
49
|
+
include TeLogger::TeLogHelper
|
50
|
+
teLogger_tag :x25519_eng
|
51
|
+
|
52
|
+
def initialize(*args, &block)
|
53
|
+
@config = args.first
|
54
|
+
teLogger.debug "Config : #{@config}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_keypair(&block)
|
58
|
+
teLogger.debug "Generating X25519 keypair"
|
59
|
+
X25519KeyBundle.new(X25519::Scalar.generate)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
|
2
|
+
require 'openssl'
|
3
|
+
if OpenSSL::VERSION < "3.0.0"
|
4
|
+
|
5
|
+
module PKeyPatch
|
6
|
+
def to_pem; public_key.to_pem end
|
7
|
+
def to_der; public_key.to_der end
|
8
|
+
|
9
|
+
#private
|
10
|
+
def public_key
|
11
|
+
key = ::OpenSSL::PKey::EC.new group
|
12
|
+
key.public_key = self
|
13
|
+
key
|
14
|
+
end
|
15
|
+
end
|
16
|
+
OpenSSL::PKey::EC::Point.prepend PKeyPatch
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
module Ccrypto
|
21
|
+
module Ruby
|
22
|
+
class X509CSREngine
|
23
|
+
include TR::CondUtils
|
24
|
+
|
25
|
+
include TeLogger::TeLogHelper
|
26
|
+
teLogger_tag :r_csr
|
27
|
+
|
28
|
+
def initialize(csrProfile)
|
29
|
+
@csrProfile = csrProfile
|
30
|
+
end
|
31
|
+
|
32
|
+
def generate(privKey, &block)
|
33
|
+
|
34
|
+
cp = @csrProfile
|
35
|
+
csr = OpenSSL::X509::Request.new
|
36
|
+
csr.version = 0
|
37
|
+
csr.subject = to_subject(cp)
|
38
|
+
|
39
|
+
case cp.public_key
|
40
|
+
when Ccrypto::PublicKey
|
41
|
+
pubKey = cp.public_key.native_pubKey
|
42
|
+
else
|
43
|
+
raise X509CSREngineException, "Public key type '#{cp.public_key.class}' is not supported"
|
44
|
+
end
|
45
|
+
|
46
|
+
if pubKey.is_a?(OpenSSL::PKey::EC::Point)
|
47
|
+
# ECC patch
|
48
|
+
pub = OpenSSL::PKey::EC.new(pubKey.group)
|
49
|
+
pub.public_key = pubKey
|
50
|
+
csr.public_key = pub
|
51
|
+
elsif pubKey.is_a?(String)
|
52
|
+
pub = OpenSSL::PKey::EC.new(pubKey)
|
53
|
+
csr.public_key = pub
|
54
|
+
else
|
55
|
+
csr.public_key = pubKey
|
56
|
+
end
|
57
|
+
|
58
|
+
exts = []
|
59
|
+
exts << OpenSSL::X509::ExtensionFactory.new.create_extension('subjectAltName', "email:#{cp.email.join(",email:")}") if not_empty?(cp.email)
|
60
|
+
exts << OpenSSL::X509::ExtensionFactory.new.create_extension('subjectAltName', "IP:#{cp.ip_addr.join(",IP:")}") if not_empty?(cp.ip_addr)
|
61
|
+
exts << OpenSSL::X509::ExtensionFactory.new.create_extension('subjectAltName', "DNS:#{cp.dns_name.join(",DNS:")}") if not_empty?(cp.dns_name)
|
62
|
+
exts << OpenSSL::X509::ExtensionFactory.new.create_extension('subjectAltName', "URI:#{cp.uri.join(",URI:")}") if not_empty?(cp.uri)
|
63
|
+
|
64
|
+
if not_empty?(cp.custom_extension) and cp.custom_extension.is_a?(Hash)
|
65
|
+
teLogger.debug "custom extension"
|
66
|
+
cp.custom_extension.each do |k,v|
|
67
|
+
case v[:type]
|
68
|
+
when :string
|
69
|
+
exts << OpenSSL::X509::Extension.new(k, OpenSSL::ASN1::OctetString.new(v[:value]), v[:critical])
|
70
|
+
else
|
71
|
+
raise X509CSREngineException, "Unsupported custom extension type #{v[:type]}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
attrVal = OpenSSL::ASN1::Set [OpenSSL::ASN1::Sequence(exts)]
|
78
|
+
csr.add_attribute OpenSSL::X509::Attribute.new('extReq', attrVal)
|
79
|
+
csr.add_attribute OpenSSL::X509::Attribute.new('msExtReq', attrVal)
|
80
|
+
|
81
|
+
if not_empty?(cp.additional_attributes) and cp.additional_attributes.is_a?(Hash)
|
82
|
+
teLogger.debug "addtinal attributes"
|
83
|
+
cp.additional_attributes.each do |k,v|
|
84
|
+
case v[:type]
|
85
|
+
when :string
|
86
|
+
csr.add_attribute OpenSSL::X509::Attribute.new(k, OpenSSL::ASN1::Set.new([OpenSSL::ASN1::OctetString.new(v[:value])]))
|
87
|
+
else
|
88
|
+
raise X509CSREngineException, "Unknown additional attribute type #{v[:type]}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
case privKey
|
95
|
+
when Ccrypto::KeyBundle
|
96
|
+
pkey = privKey.private_key.native_privKey
|
97
|
+
when Ccrypto::PrivateKey
|
98
|
+
pkey = privKey.native_privKey
|
99
|
+
else
|
100
|
+
raise X509CSREngineException, "Unsupported signing key #{privKey}"
|
101
|
+
end
|
102
|
+
|
103
|
+
gcsr = csr.sign(pkey, DigestEngine.instance(cp.hashAlgo).native_instance)
|
104
|
+
|
105
|
+
Ccrypto::X509CSR.new(gcsr)
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
def to_subject(csrProf)
|
111
|
+
res = []
|
112
|
+
res << ["CN", csrProf.owner_name]
|
113
|
+
res << ["O", csrProf.org] if not_empty?(csrProf.org)
|
114
|
+
csrProf.org_unit.each do |ou|
|
115
|
+
res << ["OU", ou]
|
116
|
+
end
|
117
|
+
|
118
|
+
e = csrProf.email.first
|
119
|
+
if not_empty?(e)
|
120
|
+
res << ["emailAddress", e]
|
121
|
+
end
|
122
|
+
|
123
|
+
OpenSSL::X509::Name.new(res)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -15,10 +15,29 @@ module Ccrypto
|
|
15
15
|
def generate(issuerKey, &block)
|
16
16
|
|
17
17
|
cp = @certProfile
|
18
|
+
|
19
|
+
if not_empty?(cp.csr)
|
20
|
+
teLogger.debug "Given cert profile with CSR"
|
21
|
+
generate_from_csr(cp, issuerKey, &block)
|
22
|
+
else
|
23
|
+
teLogger.debug "Given cert profile with user values"
|
24
|
+
generate_from_cert_profile(cp, issuerKey, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_from_cert_profile(cp, issuerKey, &block)
|
30
|
+
|
18
31
|
cert = OpenSSL::X509::Certificate.new
|
19
32
|
cert.version = 2
|
20
|
-
|
21
|
-
|
33
|
+
if is_empty?(cert.serial)
|
34
|
+
raise X509EngineException, "Certificate serial no and block is both not given. " if not block
|
35
|
+
serial = block.call(:cert_serial)
|
36
|
+
cert.serial = OpenSSL::BN.new(serial, 16)
|
37
|
+
else
|
38
|
+
cert.serial = OpenSSL::BN.new(cp.serial, 16)
|
39
|
+
end
|
40
|
+
cert.subject = to_cert_subject(cp)
|
22
41
|
|
23
42
|
ext = OpenSSL::X509::ExtensionFactory.new
|
24
43
|
ext.subject_certificate = cert
|
@@ -55,11 +74,24 @@ module Ccrypto
|
|
55
74
|
pub = OpenSSL::PKey::EC.new(pubKey.group)
|
56
75
|
pub.public_key = pubKey
|
57
76
|
cert.public_key = pub
|
77
|
+
|
78
|
+
elsif pubKey.is_a?(String)
|
79
|
+
# Changes for OpenSSL v3/Ruby v3
|
80
|
+
# native_pubKey is no longer object, will be a binary string instead
|
81
|
+
pub = OpenSSL::PKey::EC.new(pubKey)
|
82
|
+
cert.public_key = pub
|
83
|
+
|
58
84
|
else
|
59
85
|
cert.public_key = pubKey
|
60
86
|
end
|
61
87
|
|
62
|
-
|
88
|
+
if cp.gen_issuer_cert?
|
89
|
+
spec = []
|
90
|
+
spec << "CA:TRUE"
|
91
|
+
spec << "pathlen:#{cp.issuer_path_len}" if not_empty?(cp.issuer_path_len)
|
92
|
+
cert.add_extension(ext.create_extension("basicConstraints",spec.join(","),true))
|
93
|
+
end
|
94
|
+
|
63
95
|
cert.add_extension(ext.create_extension("subjectKeyIdentifier","hash")) if cp.gen_subj_key_id?
|
64
96
|
cert.add_extension(ext.create_extension("authorityKeyIdentifier","keyid:always,issuer:always")) if cp.gen_auth_key_id?
|
65
97
|
|
@@ -112,6 +144,11 @@ module Ccrypto
|
|
112
144
|
cert.add_extension(ext.create_extension("subjectAltName","IP:#{cp.ip_addr.join(",IP:")}",false)) if not_empty?(cp.ip_addr)
|
113
145
|
cert.add_extension(ext.create_extension("subjectAltName","URI:#{cp.uri.join(",URI:")}",false)) if not_empty?(cp.uri)
|
114
146
|
|
147
|
+
cp.custom_extension.each do |k,v|
|
148
|
+
cert.add_extension(OpenSSL::X509::Extension.new(k, v[:value], v[:critical]))
|
149
|
+
end
|
150
|
+
|
151
|
+
|
115
152
|
# try to sync the structure with Java BC output
|
116
153
|
# whereby single name = multiple URI however failed
|
117
154
|
# If single format is required need more R&D
|
@@ -134,6 +171,17 @@ module Ccrypto
|
|
134
171
|
aia << "caIssuers;URI:#{cp.issuer_url.join(",caIssuers;URI:")}" if not_empty?(cp.issuer_url)
|
135
172
|
cert.add_extension(ext.create_extension("authorityInfoAccess",aia.join(","),false)) if not_empty?(aia)
|
136
173
|
|
174
|
+
if not_empty?(cp.custom_extension) and cp.custom_extension.is_a?(Hash)
|
175
|
+
teLogger.debug "custom extension"
|
176
|
+
cp.custom_extension.each do |k,v|
|
177
|
+
case v[:type]
|
178
|
+
when :string
|
179
|
+
cert.add_extension OpenSSL::X509::Extension.new(k, OpenSSL::ASN1::OctetString.new(v[:value]), v[:critical])
|
180
|
+
else
|
181
|
+
raise X509CSREngineException, "Unsupported custom extension type #{v[:type]}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
137
185
|
|
138
186
|
case issuerKey
|
139
187
|
when Ccrypto::KeyBundle
|
@@ -147,19 +195,171 @@ module Ccrypto
|
|
147
195
|
res = cert.sign(privKey, DigestEngine.instance(cp.hashAlgo).native_instance)
|
148
196
|
|
149
197
|
Ccrypto::X509Cert.new(res)
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
def generate_from_csr(cp, issuerKey, &block)
|
202
|
+
|
203
|
+
csrObj = Ccrypto::X509CSR.new(cp.csr)
|
204
|
+
csrCp = csrObj.csr_info
|
205
|
+
|
206
|
+
cp.public_key = csrCp.public_key
|
207
|
+
|
208
|
+
cert = OpenSSL::X509::Certificate.new
|
209
|
+
cert.version = 2
|
210
|
+
if is_empty?(cert.serial)
|
211
|
+
serial = block.call(:cert_serial) if block
|
212
|
+
raise X509EngineException, "No serial number is given for the certificate" if is_empty?(serial)
|
213
|
+
cert.serial = OpenSSL::BN.new(serial, 16)
|
214
|
+
else
|
215
|
+
cert.serial = OpenSSL::BN.new(cp.serial, 16)
|
216
|
+
end
|
217
|
+
|
218
|
+
# allow external to add or edit parsed info before convert into actual certificate
|
219
|
+
csrCp = block.call(:verify_csr_info, csrCp) if block
|
220
|
+
|
221
|
+
cert.subject = to_cert_subject(csrCp)
|
222
|
+
|
223
|
+
ext = OpenSSL::X509::ExtensionFactory.new
|
224
|
+
ext.subject_certificate = cert
|
225
|
+
|
226
|
+
iss = cp.issuer_cert
|
227
|
+
iss = iss.nativeX509 if iss.is_a?(Ccrypto::X509Cert)
|
228
|
+
|
229
|
+
if not_empty?(iss)
|
230
|
+
raise X509EngineException, "Issuer certificate must be X509 Certificate object" if not iss.is_a?(OpenSSL::X509::Certificate)
|
231
|
+
cert.issuer = iss.subject
|
232
|
+
ext.issuer_certificate = iss
|
233
|
+
|
234
|
+
cp.match_issuer_not_before(iss.not_before)
|
235
|
+
cp.match_issuer_not_after(iss.not_after)
|
236
|
+
|
237
|
+
else
|
238
|
+
cert.issuer = cert.subject
|
239
|
+
ext.issuer_certificate = cert
|
240
|
+
end
|
241
|
+
|
242
|
+
cert.not_before = cp.not_before
|
243
|
+
cert.not_after = cp.not_after
|
244
|
+
|
245
|
+
case csrCp.public_key
|
246
|
+
when Ccrypto::PublicKey
|
247
|
+
pubKey = csrCp.public_key.native_pubKey
|
248
|
+
when OpenSSL::PKey::EC, OpenSSL::PKey::RSA
|
249
|
+
pubKey = csrCp.public_key
|
250
|
+
else
|
251
|
+
raise X509EngineException, "Public key type '#{csrCp.public_key.class}' is not supported"
|
252
|
+
end
|
253
|
+
|
254
|
+
if pubKey.is_a?(OpenSSL::PKey::EC::Point)
|
255
|
+
# ECC patch
|
256
|
+
pub = OpenSSL::PKey::EC.new(pubKey.group)
|
257
|
+
pub.public_key = pubKey
|
258
|
+
cert.public_key = pub
|
259
|
+
else
|
260
|
+
cert.public_key = pubKey
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
if cp.gen_issuer_cert?
|
265
|
+
spec = []
|
266
|
+
spec << "CA:TRUE"
|
267
|
+
spec << "pathlen:#{cp.issuer_path_len}" if not_empty?(cp.issuer_path_len)
|
268
|
+
cert.add_extension(ext.create_extension("basicConstraints",spec.join(","),true))
|
269
|
+
end
|
270
|
+
|
271
|
+
#cert.add_extension(ext.create_extension("basicConstraints","CA:TRUE,pathlen:0",true)) if cp.gen_issuer_cert?
|
272
|
+
cert.add_extension(ext.create_extension("subjectKeyIdentifier","hash")) if cp.gen_subj_key_id?
|
273
|
+
cert.add_extension(ext.create_extension("authorityKeyIdentifier","keyid:always,issuer:always")) if cp.gen_auth_key_id?
|
274
|
+
|
275
|
+
#cert.add_extension(ext.create_extension("keyUsage",to_keyusage,true))
|
276
|
+
cp.key_usage.selected.each do |ku,critical|
|
277
|
+
teLogger.debug "Setting KeyUsage : #{ku} (#{critical})"
|
278
|
+
case ku
|
279
|
+
when :crlSign
|
280
|
+
cert.add_extension(ext.create_extension("keyUsage","cRLSign",critical))
|
281
|
+
else
|
282
|
+
cert.add_extension(ext.create_extension("keyUsage",ku.to_s,critical))
|
283
|
+
end
|
284
|
+
end
|
150
285
|
|
286
|
+
|
287
|
+
#extKeyUsage = to_extkeyusage
|
288
|
+
extKeyUsage = []
|
289
|
+
cp.ext_key_usage.selected.each do |ku,critical|
|
290
|
+
case ku
|
291
|
+
when :allPurpose
|
292
|
+
#kur << :anyExtendedKeyUsage
|
293
|
+
cert.add_extension(ext.create_extension("extendedKeyUsage","anyExtendedKeyUsage",critical))
|
294
|
+
when :timestamping
|
295
|
+
#kur << :timeStamping
|
296
|
+
cert.add_extension(ext.create_extension("extendedKeyUsage","timeStamping",critical))
|
297
|
+
when :ocspSigning
|
298
|
+
#kur << :oCSPSigning
|
299
|
+
cert.add_extension(ext.create_extension("extendedKeyUsage","oCSPSigning",critical))
|
300
|
+
when :ipSecIKE
|
301
|
+
#kur << :ipsecIKE
|
302
|
+
cert.add_extension(ext.create_extension("extendedKeyUsage","ipsecIKE",critical))
|
303
|
+
when :msCtlsign
|
304
|
+
#kur << :msCTLSign
|
305
|
+
cert.add_extension(ext.create_extension("extendedKeyUsage","msCTLSign",critical))
|
306
|
+
when :msEFS
|
307
|
+
#kur << :msEfs
|
308
|
+
cert.add_extension(ext.create_extension("extendedKeyUsage","msEfs",critical))
|
309
|
+
else
|
310
|
+
#kur << ku
|
311
|
+
cert.add_extension(ext.create_extension("extendedKeyUsage",ku.to_s,critical))
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
cp.domain_key_usage.each do |dku, critical|
|
316
|
+
cert.add_extension(ext.create_extension("extendedKeyUsage",dku.to_s,critical))
|
317
|
+
end
|
318
|
+
|
319
|
+
cert.add_extension(ext.create_extension("subjectAltName","email:#{csrCp.email.uniq.join(",email:")}",false)) if not_empty?(csrCp.email)
|
320
|
+
cert.add_extension(ext.create_extension("subjectAltName","DNS:#{csrCp.dns_name.uniq.join(",DNS:")}",false)) if not_empty?(csrCp.dns_name)
|
321
|
+
cert.add_extension(ext.create_extension("subjectAltName","IP:#{csrCp.ip_addr.uniq.join(",IP:")}",false)) if not_empty?(csrCp.ip_addr)
|
322
|
+
cert.add_extension(ext.create_extension("subjectAltName","URI:#{csrCp.uri.uniq.join(",URI:")}",false)) if not_empty?(csrCp.uri)
|
323
|
+
|
324
|
+
csrCp.custom_extension.each do |k,v|
|
325
|
+
cert.add_extension(OpenSSL::X509::Extension.new(k, v[:value], v[:critical]))
|
326
|
+
end
|
327
|
+
|
328
|
+
cert.add_extension(ext.create_extension("crlDistributionPoints","URI:#{cp.crl_dist_point.join(",URI:")}",false)) if not_empty?(cp.crl_dist_point)
|
329
|
+
|
330
|
+
aia = []
|
331
|
+
aia << "OCSP;URI:#{cp.ocsp_url.join(",OCSP;URI:")}" if not_empty?(cp.ocsp_url)
|
332
|
+
aia << "caIssuers;URI:#{cp.issuer_url.join(",caIssuers;URI:")}" if not_empty?(cp.issuer_url)
|
333
|
+
cert.add_extension(ext.create_extension("authorityInfoAccess",aia.join(","),false)) if not_empty?(aia)
|
334
|
+
|
335
|
+
|
336
|
+
case issuerKey
|
337
|
+
when Ccrypto::KeyBundle
|
338
|
+
privKey = issuerKey.private_key.native_privKey
|
339
|
+
when Ccrypto::PrivateKey
|
340
|
+
privKey = issuerKey.native_privKey
|
341
|
+
else
|
342
|
+
raise X509EngineException, "Unsupported issuer key #{issuerKey}"
|
343
|
+
end
|
344
|
+
|
345
|
+
res = cert.sign(privKey, DigestEngine.instance(cp.hashAlgo).native_instance)
|
346
|
+
|
347
|
+
Ccrypto::X509Cert.new(res)
|
348
|
+
|
151
349
|
end
|
152
350
|
|
153
351
|
private
|
154
|
-
def to_cert_subject
|
352
|
+
def to_cert_subject(cp)
|
155
353
|
res = []
|
156
|
-
res << ["CN",
|
157
|
-
res << ["O",
|
158
|
-
|
354
|
+
res << ["CN", cp.owner_name]
|
355
|
+
res << ["O", cp.org] if not_empty?(cp.org)
|
356
|
+
cp.org_unit.each do |ou|
|
159
357
|
res << ["OU", ou]
|
160
358
|
end
|
359
|
+
res << ["L", cp.locality] if not_empty?(cp.locality)
|
360
|
+
res << ["C", cp.country] if not_empty?(cp.country)
|
161
361
|
|
162
|
-
e =
|
362
|
+
e = cp.email.first
|
163
363
|
if not_empty?(e)
|
164
364
|
res << ["emailAddress", e]
|
165
365
|
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Ccrypto
|
4
|
+
class X509CSR
|
5
|
+
include TR::CondUtils
|
6
|
+
|
7
|
+
include TeLogger::TeLogHelper
|
8
|
+
teLogger_tag :r_csr
|
9
|
+
|
10
|
+
def initialize(csr)
|
11
|
+
@nativeCSR = csr
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_bin
|
15
|
+
@nativeCSR.to_der
|
16
|
+
end
|
17
|
+
|
18
|
+
def equal?(csr)
|
19
|
+
if not_empty?(csr)
|
20
|
+
case csr
|
21
|
+
when String
|
22
|
+
@nativeCSR.to_der == csr
|
23
|
+
when OpenSSL::X509::Request
|
24
|
+
@nativeCSR.to_der == csr.to_der
|
25
|
+
when Ccrypto::X509CSR
|
26
|
+
@nativeCSR.to_der == csr.to_bin
|
27
|
+
else
|
28
|
+
raise X509CSRException, "Unknown CSR type #{csr.class}"
|
29
|
+
end
|
30
|
+
else
|
31
|
+
@nativeCSR == csr
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(mtd, *args, &block)
|
36
|
+
@nativeCSR.send(mtd, *args, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def csr_info
|
40
|
+
if @csrInfo.nil?
|
41
|
+
@csrInfo = parseCSR(@nativeCSR)
|
42
|
+
end
|
43
|
+
@csrInfo
|
44
|
+
end
|
45
|
+
|
46
|
+
def parseCSR(csrBin)
|
47
|
+
|
48
|
+
case csrBin
|
49
|
+
when String
|
50
|
+
csr = OpenSSL::X509::Request.new(csrBin)
|
51
|
+
when Ccrypto::X509CSR
|
52
|
+
csr = csrBin.nativeCSR
|
53
|
+
else
|
54
|
+
raise X509CSREngineException, "Unknown CSR to parse #{csrBin}"
|
55
|
+
end
|
56
|
+
|
57
|
+
raise X509CSRSignatureInvalid, "CSR signature is not valid!" if not csr.verify(csr.public_key)
|
58
|
+
|
59
|
+
certProf = Ccrypto::X509::CertProfile.new
|
60
|
+
|
61
|
+
csr.subject.to_a.each do |k,v,a|
|
62
|
+
case k
|
63
|
+
when "CN"
|
64
|
+
certProf.owner_name = v
|
65
|
+
when "O"
|
66
|
+
certProf.org = v
|
67
|
+
when "OU"
|
68
|
+
certProf.org_unit = v
|
69
|
+
when "emailAddress"
|
70
|
+
certProf.email = v
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
certProf.public_key = csr.public_key
|
75
|
+
csr.attributes.each do |att|
|
76
|
+
teLogger.debug "Processing attribute ID #{att.oid}"
|
77
|
+
#p att.oid
|
78
|
+
#p att.value
|
79
|
+
|
80
|
+
att.value.each do |v|
|
81
|
+
case v
|
82
|
+
when OpenSSL::ASN1::Sequence
|
83
|
+
v.value.each do |vv|
|
84
|
+
#p vv.value[0]
|
85
|
+
#p vv.value[1]
|
86
|
+
tv = OpenSSL::ASN1.decode(vv.value[1].value)
|
87
|
+
case tv
|
88
|
+
when OpenSSL::ASN1::Sequence
|
89
|
+
tvv = tv.to_a
|
90
|
+
tvv.each do |tt|
|
91
|
+
case tt.tag
|
92
|
+
when 1
|
93
|
+
# email
|
94
|
+
certProf.email = tt.value
|
95
|
+
when 2
|
96
|
+
# dns
|
97
|
+
certProf.dns_name = tt.value
|
98
|
+
when 6
|
99
|
+
# uri
|
100
|
+
certProf.uri = tt.value
|
101
|
+
when 7
|
102
|
+
# ip address
|
103
|
+
v = tt.value
|
104
|
+
case v.size
|
105
|
+
when 4
|
106
|
+
ip = v.unpack('C*').join('.')
|
107
|
+
when 6
|
108
|
+
ip = v.unpack('n*').map { |o| sprintf("%X", o) }.join(':')
|
109
|
+
else
|
110
|
+
raise X509EngineException, "Neither IPv4 or IPv6 is given as IP address attributes"
|
111
|
+
end
|
112
|
+
certProf.ip_addr = ip
|
113
|
+
|
114
|
+
else
|
115
|
+
raise X509EngineException, "Unsupported CSR attributes value #{tt.tag}"
|
116
|
+
end
|
117
|
+
#p tt.tag
|
118
|
+
#p tt.value
|
119
|
+
end
|
120
|
+
|
121
|
+
when OpenSSL::ASN1::OctetString
|
122
|
+
## custom extension
|
123
|
+
|
124
|
+
certProf.custom_extension[vv.value[0].value] = { value: vv.value[1].value, type: :string, critical: false }
|
125
|
+
#cert.add_extension(OpenSSL::X509::Extension.new(vv.value[0].value,vv.value[1].value, false))
|
126
|
+
|
127
|
+
else
|
128
|
+
teLogger.error "Unsupported extension type #{tv.class} in target CSR"
|
129
|
+
#raise X509EngineException, "Unknown extension type #{tv.class}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
when OpenSSL::ASN1::OctetString
|
134
|
+
# ## custom attributes
|
135
|
+
# cert.add_extension(OpenSSL::X509::Extension.new(att.oid,v.value, false))
|
136
|
+
|
137
|
+
# certProf.custom_attributes[att.oid] = { value: v.value, type: :string }
|
138
|
+
certProf.custom_extension[att.oid] = { value: v.value, type: :string, critical: false }
|
139
|
+
|
140
|
+
else
|
141
|
+
#raise X509EngineException, "Given attribute #{att.oid} has value of type #{v.class}. Not able to handle"
|
142
|
+
teLogger.error "Given attribute #{att.oid} has value of type #{v.class}. Not able to handle"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
certProf
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
@@ -34,7 +34,7 @@ module Ccrypto
|
|
34
34
|
ECCKeyBundle.new(pKey)
|
35
35
|
rescue OpenSSL::PKey::PKeyError => ex
|
36
36
|
raise PEMStoreException, "block is required" if not block
|
37
|
-
pass = block.call(:
|
37
|
+
pass = block.call(:store_pass)
|
38
38
|
begin
|
39
39
|
pKey = OpenSSL::PKey.read(input, pass)
|
40
40
|
ECCKeyBundle.new(pKey)
|
@@ -51,8 +51,8 @@ module Ccrypto
|
|
51
51
|
|
52
52
|
def to_pem(&block)
|
53
53
|
raise PEMStoreException, "Block is required" if not block
|
54
|
-
kcipher = block.call(:
|
55
|
-
kpass = block.call(:
|
54
|
+
kcipher = block.call(:store_cipher)
|
55
|
+
kpass = block.call(:store_pass)
|
56
56
|
|
57
57
|
kcipher = "AES-256-GCM" if is_empty?(kcipher)
|
58
58
|
|
@@ -11,6 +11,7 @@ module Ccrypto
|
|
11
11
|
class PKCS12StoreException < KeyBundleStorageException; end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
+
|
14
15
|
def from_pkcs12(input, &block)
|
15
16
|
raise PKCS12StoreException, "Input cannot be empty" if is_empty?(input)
|
16
17
|
|
@@ -19,14 +20,14 @@ module Ccrypto
|
|
19
20
|
inForm = block.call(:in_format)
|
20
21
|
case inForm
|
21
22
|
when :b64
|
22
|
-
inp = from_b64(
|
23
|
+
inp = from_b64(input)
|
23
24
|
when :hex
|
24
|
-
inp = from_hex(
|
25
|
+
inp = from_hex(input)
|
25
26
|
else
|
26
27
|
inp = input
|
27
28
|
end
|
28
29
|
|
29
|
-
pass = block.call(:
|
30
|
+
pass = block.call(:store_pass)
|
30
31
|
raise PKCS12StoreException, "Password cannot be empty" if is_empty?(pass)
|
31
32
|
|
32
33
|
begin
|
@@ -80,10 +81,10 @@ module Ccrypto
|
|
80
81
|
end
|
81
82
|
end
|
82
83
|
|
83
|
-
pass = block.call(:
|
84
|
+
pass = block.call(:store_pass)
|
84
85
|
raise PKCS12StoreException, "Password is required" if is_empty?(pass)
|
85
86
|
|
86
|
-
name = block.call(:
|
87
|
+
name = block.call(:key_name)
|
87
88
|
name = "Ccrypto KeyBundle" if is_empty?(name)
|
88
89
|
|
89
90
|
keypair = block.call(:keypair)
|