ccrypto-ruby 0.1.0 → 0.1.2
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.
- 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)
|