pkernel_jce 0.1

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.
@@ -0,0 +1,389 @@
1
+
2
+
3
+ require 'pkernel'
4
+
5
+ require_relative "io_utils"
6
+ require_relative "identity"
7
+ require_relative "bc_helpers"
8
+
9
+ #require_relative '../pkernel_jce'
10
+
11
+ module PkernelJce
12
+
13
+ # RFC3161 is timestamping backed by PKI
14
+ module Rfc3161
15
+ module Response
16
+
17
+ # generate rfc3161 timestamp token
18
+ def generate(opts = { })
19
+ bin = opts[:bin]
20
+ file = opts[:file]
21
+
22
+ if not (file.nil? or file.empty?)
23
+ breq = IoUtils.file_to_memory_byte_array(file)
24
+ elsif not bin.nil?
25
+ breq = IoUtils.ensure_java_bytes(bin)
26
+ else
27
+ raise PkernelJce::Error, "No request file or memory is given to generate timestamp response"
28
+ end
29
+
30
+
31
+ id = opts[:identity]
32
+ if id.nil?
33
+ raise PkernelJce::Error, "Identity is not given to generate timestamping token"
34
+ end
35
+
36
+ req = org.bouncycastle.tsp.TimeStampRequest.new(breq)
37
+ #p req.messageImprintAlgOID
38
+
39
+ begin
40
+ dgstEng = BcHelpers.find_digest_calculator(req.messageImprintAlgOID)
41
+ PkernelJce::GConf.instance.glog.debug "Timestamp request using hash '#{dgstEng}'"
42
+ rescue PkernelJce::Error => ex
43
+ opts[:status] = Pkernel::Rfc3161::RESP_REJECTION
44
+ opts[:reason] = Pkernel::Rfc3161::REASON_BAD_ALG
45
+ opts[:reasonMsg] = "Digest not supported"
46
+ end
47
+
48
+ chain = id.chain
49
+ list = java.util.ArrayList.new
50
+ chain.each do |c|
51
+ list.add(c)
52
+ end
53
+ store = org.bouncycastle.cert.jcajce.JcaCertStore.new(list)
54
+
55
+ # this one seems useless since the actual algo shall be taken from request anyway...
56
+ signHashAlgo = opts[:signHash] || "SHA256"
57
+ policyId = opts[:policy_id] || "1.2.3.4.1"
58
+ signInfo = org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder.new.setProvider(id.provider).build(PkernelJce::KeyPair.derive_signing_algo(id.privKey,signHashAlgo), id.privKey, id.certificate)
59
+
60
+ # policy 1.2.3.4.1 = tsa_policy1
61
+ gen = org.bouncycastle.tsp.TimeStampTokenGenerator.new(signInfo, BcHelpers.find_digest_calculator(signHashAlgo), org.bouncycastle.asn1.ASN1ObjectIdentifier.new(policyId))
62
+ # policy 1.2 = ISO Member body
63
+ #gen = org.bouncycastle.tsp.TimeStampTokenGenerator.new(signInfo, SHA256DigestCalculator.new, org.bouncycastle.asn1.ASN1ObjectIdentifier.new("1.2"))
64
+
65
+ gen.addCertificates(store)
66
+
67
+ if req.cert_req?
68
+ PkernelJce::GConf.instance.glog.debug "Client requested to include certificate for timestamping"
69
+ # this option requires the request to reqCert set first if not error shall be thrown at the verification end
70
+ gen.setTSA(org.bouncycastle.asn1.x509.GeneralName.new(PkernelJce::Certificate.ensure_bc_cert(id.certificate).subject_to_x500))
71
+ end
72
+
73
+ status = opts[:status] || Pkernel::Rfc3161::RESP_GRANTED
74
+ reason = opts[:reason] || Pkernel::Rfc3161::REASON_SYS_FAILURE
75
+ reasonMsg = opts[:reasonMsg] || ""
76
+
77
+ respGen = org.bouncycastle.tsp.TimeStampResponseGenerator.new(gen, org.bouncycastle.tsp.TSPAlgorithms::ALLOWED)
78
+
79
+ if status == Pkernel::Rfc3161::RESP_GRANTED or status == Pkernel::Rfc3161::RESP_GRANTED_WITH_MODS
80
+
81
+ # etsi want min 1 sec...:)
82
+ accuracy = opts[:accuracy] || { seconds: 1 }
83
+ if not accuracy[:seconds].nil?
84
+ PkernelJce::GConf.instance.glog.debug "Timestamping setting accuracy #{accuracy[:seconds]} seconds"
85
+ gen.setAccuracySeconds(accuracy[:seconds].to_i)
86
+ end
87
+
88
+ if not accuracy[:milis].nil?
89
+ PkernelJce::GConf.instance.glog.debug "Timestamping setting accuracy #{accuracy[:milis]} mili seconds"
90
+ gen.setAccuracyMillis(accuracy[:milis].to_i)
91
+ end
92
+
93
+ if not accuracy[:micros].nil?
94
+ PkernelJce::GConf.instance.glog.debug "Timestamping setting accuracy #{accuracy[:micros]} micro seconds"
95
+ gen.setAccuracyMicros(accuracy[:micros].to_i)
96
+ end
97
+
98
+ tsResp = respGen.generateGrantedResponse(req, java.math.BigInteger.new(SecureRandom.uuid.gsub("-",""),16), java.util.Date.new)
99
+
100
+ else
101
+
102
+ tsResp = respGen.generateFailResponse(status, reason, reasonMsg)
103
+ end
104
+ #resp = org.bouncycastle.tsp.TimeStampResponse.new(tsResp.getEncoded())
105
+
106
+ #resp
107
+ tsResp
108
+ end
109
+ #
110
+ # end generate()
111
+ #
112
+
113
+ def parse(opts = {})
114
+
115
+ file = opts[:file]
116
+ bin = opts[:bin]
117
+
118
+ if not (file.nil? or file.empty?)
119
+ bresp = IoUtils.file_to_memory_byte_array(file)
120
+ elsif not bin.nil?
121
+ bresp = IoUtils.ensure_java_bytes(bin)
122
+ else
123
+ raise PkernelJce::Error, "No file or memory is given to parse timestamp response"
124
+ end
125
+
126
+ result = { }
127
+ verifyResp = opts[:verifyResp] || true
128
+
129
+ resp = org.bouncycastle.tsp.TimeStampResponse.new(bresp)
130
+
131
+ result[:status] = resp.status
132
+
133
+ if resp.status == Pkernel::Rfc3161::RESP_GRANTED || resp.status == Pkernel::Rfc3161::RESP_GRANTED_WITH_MODS
134
+
135
+ token = resp.getTimeStampToken
136
+
137
+ if not token.nil?
138
+ info = token.getTimeStampInfo
139
+ else
140
+ PkernelJce::GConf.instance.glog.warn "Timestamp does not contain token!"
141
+ end
142
+
143
+
144
+
145
+ if not token.nil? and verifyResp
146
+
147
+ tsaCert = opts[:tsaCert]
148
+
149
+ signingCert = nil
150
+ token.getCertificates.to_a.each do |c|
151
+ if c.subject_to_x500.equals(info.getTsa.name)
152
+ signingCert = c
153
+ break
154
+ end
155
+ end
156
+
157
+ if not tsaCert.nil?
158
+ # check if tsaCert and signingCert is same
159
+ if not tsaCert.equals(signingCert)
160
+ raise PkernelJce::Error, "Given TSA cert and signing cert obtained from the timestamp token is not the same"
161
+ end
162
+ end
163
+
164
+ prov = PkernelJce::Provider.add_default
165
+ begin
166
+ token.validate(org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder.new.setProvider(prov).build(signingCert))
167
+ result[:signature_status] = :verified
168
+ PkernelJce::GConf.instance.glog.debug "Timestamp verified against signing cert [#{signingCert.subject}]"
169
+
170
+ result[:signer] = { }
171
+ result[:signer][:cert] = signingCert
172
+ result[:signer][:chain] = token.getCertificates.to_a
173
+
174
+ rescue Exception => ex
175
+ result[:signature_status] = :failed
176
+ result[:signature_status_details] = ex.message
177
+ end
178
+ end
179
+ # end verifyResp
180
+
181
+ req = opts[:request]
182
+ if not token.nil? and not req.nil?
183
+
184
+ source = opts[:data]
185
+ if not source.nil?
186
+ sfile = source[:file]
187
+ sbin = source[:bin]
188
+ end
189
+
190
+ if not (sfile.nil? or sfile.empty?) or not sbin.nil?
191
+
192
+ dgst = PkernelJce::BcHelpers.find_digest_calculator(req.getMessageImprintAlgOID)
193
+ if not dgst.nil?
194
+ if not (sfile.nil? or sfile.empty?)
195
+ PkernelJce::GConf.instance.glog.debug "Calculating hash for source '#{sfile}'"
196
+ b = Java::byte[10240].new
197
+ fis = java.io.FileInputStream.new(sfile)
198
+ while((read = fis.read(b,0,b.length)) != -1)
199
+ dgst.getOutputStream.write(b,0,read)
200
+ end
201
+ dgst.getOutputStream.close
202
+
203
+ elsif not sbin.nil?
204
+ PkernelJce::GConf.instance.glog.debug "Calculating hash for source from memory"
205
+ dgst.getOutputStream.write(sbin.to_java_bytes)
206
+ dgst.getOutputStream.close
207
+ end
208
+
209
+ if not java.util.Arrays.equals(dgst.digest, req.getMessageImprintDigest)
210
+ raise PkernelJce::Error, "Source digest is different from request! Source file is not the original data sent for timestamp"
211
+ else
212
+ PkernelJce::GConf.instance.glog.debug "Source file hash matched request digest."
213
+ end
214
+ else
215
+ PkernelJce::GConf.instance.glog.warn "Failed to initialize the hashing algo for source verification. Source verification shall be skipped."
216
+ end
217
+ end
218
+ # end not source.nil?
219
+
220
+ begin
221
+ resp.validate(req)
222
+ result[:request_validation] = :verified
223
+ result[:digest] = info.getMessageImprintDigest
224
+ result[:digest_algo] = info.getMessageImprintAlgOID
225
+ PkernelJce::GConf.instance.glog.debug "Timestamp response verified against timestamp request. Checked [Nonce, Status, Digest, Hash Engine, Policy, Signing Certificates]"
226
+ rescue Exception => ex
227
+ result[:request_validation] = :failed
228
+ result[:request_validation_details] = ex.message
229
+ end
230
+ end
231
+
232
+
233
+ if not info.nil?
234
+ acc = info.getAccuracy
235
+ result[:accuracy] = {}
236
+ result[:accuracy][:seconds] = acc.getSeconds.nil? ? 0 : acc.getSeconds.value
237
+ result[:accuracy][:milis] = acc.getMillis.nil? ? 0 : acc.getMillis.value
238
+ result[:accuracy][:micros] = acc.getMicros.nil? ? 0 : acc.getMicros.value
239
+ time = info.getGenTime
240
+ result[:time] = time
241
+ nonce = info.getNonce
242
+ result[:nonce] = nonce.to_s(16)
243
+ serial = info.getSerialNumber
244
+ result[:serial] = serial.to_s(16)
245
+ policy = info.getPolicy
246
+ result[:policy] = policy
247
+ tsa = info.getTsa
248
+ result[:tsa_name] = tsa.name.to_s
249
+ ordered = info.isOrdered
250
+ result[:ordered] = ordered
251
+ end
252
+
253
+ else
254
+ # status is NOT granted or granted with mods!
255
+ result[:reason] = resp.getFailInfo
256
+ result[:reasonMsg] = resp.getStatusString
257
+ end
258
+ # if status is granted or granted with mods
259
+
260
+ result
261
+
262
+ end
263
+ # end parse()
264
+ #
265
+
266
+ end
267
+ # end module response
268
+
269
+ module Request
270
+ def generate(opts = { })
271
+
272
+ targetFile = opts[:tbts_file]
273
+ targetBin = opts[:tbts_bin]
274
+
275
+ reqGen = org.bouncycastle.tsp.TimeStampRequestGenerator.new
276
+
277
+ reqCert = opts[:inc_certs] || true
278
+ if reqCert
279
+ reqGen.setCertReq(true)
280
+ end
281
+
282
+ reqPolicy = opts[:req_policy]
283
+ if not (reqPolicy.nil? or reqPolicy.empty?)
284
+ reqGen.setReqPolicy(org.bouncycastle.asn1.x509.AlgorithmIdentifier.new(reqPolicy))
285
+ end
286
+
287
+ nonce = Java::byte[16].new
288
+ java.util.Random.new.nextBytes(nonce)
289
+
290
+ hashAlgo = opts[:hashAlgo] || "SHA256"
291
+ if not hashAlgo.nil?
292
+ hashAlgo = hashAlgo.upcase
293
+ case hashAlgo
294
+ when "SHA224","SHA256","SHA384","SHA512","RIPEMD128","RIPEMD160","RIPEMD256"
295
+ dgstCal = PkernelJce::BcHelpers.find_digest_calculator(hashAlgo)
296
+
297
+ if not (targetFile.nil? or targetFile.empty?)
298
+ b = Java::byte[10240].new
299
+ fis = java.io.FileInputStream.new(targetFile)
300
+ while((cont = fis.read(b,0,b.length)) != -1)
301
+ dgstCal.getOutputStream.write(b,0,cont)
302
+ end
303
+ dgstCal.getOutputStream.close
304
+
305
+ elsif not targetBin.nil?
306
+ bin = PkernelJce::IoUtils.ensure_java_bytes(targetBin)
307
+ dgstCal.getOutputStream.write(bin)
308
+ dgstCal.getOutputStream.close
309
+
310
+ else
311
+ raise PkernelJce::Error, "No to-be-timestamped file or memory input given to timestamp"
312
+ end
313
+
314
+ else
315
+ raise PkernelJce::Error, "Unsupported hash algo '#{hashAlgo}'"
316
+ end
317
+ end
318
+
319
+ digest = dgstCal.digest
320
+ tspHash = PkernelJce::Rfc3161::Request.find_tsp_algo(dgstCal)
321
+ reqGen.generate(tspHash, digest.to_s.to_java_bytes, java.math.BigInteger.new(nonce).abs)
322
+ end
323
+ # end generate()
324
+ #
325
+
326
+ def parse(opts = { })
327
+
328
+ bin = opts[:bin]
329
+ file = opts[:file]
330
+
331
+ if not (file.nil? or file.empty?)
332
+ breq = IoUtils.file_to_memory_byte_array(file)
333
+ elsif not bin.nil?
334
+ breq = IoUtils.ensure_java_bytes(bin)
335
+ else
336
+ raise PkernelJce::Error, "No request file or memory is given to parse"
337
+ end
338
+
339
+ result = { }
340
+ req = org.bouncycastle.tsp.TimeStampRequest.new(breq)
341
+
342
+ result[:cert_req] = req.cert_req?
343
+ result[:digest_algo] = req.getMessageImprintAlgOID
344
+ result[:digest] = req.getMessageImprintDigest
345
+ result[:nonce] = req.getNonce.to_s(16)
346
+ result[:policy] = req.req_policy
347
+
348
+ result
349
+ end
350
+
351
+ def Request.find_tsp_algo(dgstCal)
352
+ if dgstCal.nil?
353
+ raise PkernelJce::Error, "Digest calculator not given to derive tsp algo"
354
+ end
355
+
356
+ tspHash = nil
357
+ org.bouncycastle.tsp.TSPAlgorithms.constants.each do |al|
358
+ tspAlgo = org.bouncycastle.tsp.TSPAlgorithms.send(al)
359
+ next if not tspAlgo.java_kind_of?(Java::OrgBouncycastleAsn1::ASN1ObjectIdentifier)
360
+ if tspAlgo.toASN1Object.equals(dgstCal.algorithm_identifier.algorithm)
361
+ tspHash = tspAlgo
362
+ break
363
+ end
364
+ end
365
+
366
+ tspHash
367
+ end
368
+ # end find_tsp_algo
369
+
370
+ end
371
+ # end module Request
372
+
373
+ end
374
+ #
375
+ # end Rfc3161
376
+ #
377
+
378
+ class Rfc3161RequestEngine
379
+ extend PkernelJce::Rfc3161::Request
380
+
381
+ end
382
+
383
+ class Rfc3161ResponseEngine
384
+ extend PkernelJce::Rfc3161::Response
385
+ end
386
+ # end class Rfc3161Engine
387
+
388
+ end
389
+
@@ -0,0 +1,59 @@
1
+
2
+ require_relative 'provider'
3
+
4
+ class Array
5
+ def to_vector
6
+ v = Java::JavaUtil::Vector.new
7
+ self.each do |rec|
8
+ v.add_element rec
9
+ end
10
+ v
11
+ end
12
+ end
13
+
14
+ class Java::JavaUtil::Date
15
+ def to_ruby_date
16
+ Time.at(self.getTime/1000)
17
+ end
18
+ end
19
+
20
+ class Time
21
+ def to_java_date
22
+ java.util.Date.new(self.to_i*1000)
23
+ end
24
+ end
25
+
26
+ #class DateTime
27
+ # def to_java_date
28
+ # java.util.Date.new(self.to_i*1000)
29
+ # end
30
+ #end
31
+
32
+
33
+ # bouncycastle has those annoying intermediary object which translation
34
+ # is PITA
35
+ class Java::OrgBouncycastleCert::X509CertificateHolder
36
+ def to_java_cert
37
+ org.bouncycastle.cert.jcajce.JcaX509CertificateConverter.new.setProvider(PkernelJce::Provider::DefProvider).getCertificate(self)
38
+ end
39
+
40
+ def subject_to_x500
41
+ org.bouncycastle.asn1.x500.X500Name.getInstance(self.subject.encoded)
42
+ end
43
+ end
44
+
45
+ class Java::JavaSecurityCert::Certificate
46
+ def to_bc_cert_holder
47
+ org.bouncycastle.cert.X509CertificateHolder.new(self.encoded)
48
+ end
49
+ alias_method :to_bc_cert, :to_bc_cert_holder
50
+ end
51
+
52
+
53
+ class Converter
54
+ def self.java_bytes_to_hex(bytes)
55
+ String.from_java_bytes(org.bouncycastle.util.encoders.Hex.encode(bytes))
56
+ end
57
+ end
58
+
59
+
@@ -0,0 +1,3 @@
1
+ module PkernelJce
2
+ VERSION = "0.1"
3
+ end
@@ -0,0 +1,102 @@
1
+ require "pkernel_jce/version"
2
+
3
+ require 'pkernel'
4
+
5
+ require_relative 'pkernel_jce/keypair'
6
+ require_relative 'pkernel_jce/certificate_owner'
7
+ require_relative 'pkernel_jce/certificate'
8
+ require_relative 'pkernel_jce/identity'
9
+ require_relative 'pkernel_jce/crl'
10
+ require_relative 'pkernel_jce/csr'
11
+ require_relative 'pkernel_jce/ocsp'
12
+ require_relative 'pkernel_jce/rfc3161'
13
+ require_relative 'pkernel_jce/converter'
14
+
15
+ module PkernelJce
16
+ #class Error < StandardError; end
17
+ # Your code goes here...
18
+
19
+ class Pkernel::KeyPair
20
+ extend PkernelJce::KeyPair
21
+
22
+ def self.method_missing(mtd,*args,&block)
23
+ if PkernelJce::KeyPair.respond_to?(mtd)
24
+ PkernelJce::KeyPair.send(mtd,*args,&block)
25
+ else
26
+ super
27
+ end
28
+ end
29
+ end
30
+
31
+ class Pkernel::Certificate
32
+ extend PkernelJce::Certificate
33
+
34
+ class KeyUsage
35
+ include PkernelJce::Certificate::KeyUsage
36
+ end
37
+
38
+ class ExtKeyUsage
39
+ include PkernelJce::Certificate::ExtKeyUsage
40
+ end
41
+
42
+ def self.method_missing(mtd,*args,&block)
43
+ if PkernelJce::Certificate.respond_to?(mtd)
44
+ PkernelJce::Certificate.send(mtd,*args,&block)
45
+ else
46
+ super
47
+ end
48
+ end
49
+ end
50
+
51
+ class Pkernel::CSR
52
+ extend PkernelJce::CSR
53
+
54
+ def self.method_missing(mtd,*args,&block)
55
+ if PkernelJce::CSR.respond_to?(mtd)
56
+ PkernelJce::CSR.send(mtd,*args,&block)
57
+ else
58
+ super
59
+ end
60
+ end
61
+ end
62
+ # end class Pkernel::CSR
63
+ #
64
+
65
+ class Pkernel::IdentityFactory
66
+ extend PkernelJce::IdentityFactory
67
+ include PkernelJce::IdentityFactory
68
+
69
+ def self.method_missing(mtd,*args,&block)
70
+ if PkernelJce::IdentityFactory.respond_to?(mtd)
71
+ PkernelJce::IdentityFactory.send(mtd,*args,&block)
72
+ else
73
+ super
74
+ end
75
+ end
76
+ end
77
+ # end class Pkernel::IdentityFactory
78
+ #
79
+
80
+
81
+ class Pkernel::CRL
82
+ extend PkernelJce::CRL
83
+ end
84
+
85
+ class Pkernel::Rfc3161
86
+ class Response
87
+ extend PkernelJce::Rfc3161::Response
88
+ end
89
+
90
+ class Request
91
+ extend PkernelJce::Rfc3161::Request
92
+ end
93
+ end
94
+
95
+ class Pkernel::OCSP
96
+ extend PkernelJce::OCSP
97
+ end
98
+
99
+ class Pkernel::Converter
100
+ extend PkernelJce::Converter
101
+ end
102
+ end
@@ -0,0 +1,45 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "pkernel_jce/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pkernel_jce"
8
+ spec.version = PkernelJce::VERSION
9
+ spec.authors = ["Chris Liaw"]
10
+ spec.email = ["chrisliaw@antrapol.com"]
11
+
12
+ spec.summary = "PKI Kernel - Java and BouncyCastle implementation"
13
+ spec.description = "Collection of fundamental functions for PKI core engine implemented in Java and/or BouncyCastle library."
14
+ spec.homepage = ""
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ #if spec.respond_to?(:metadata)
20
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
21
+
22
+ # spec.metadata["homepage_uri"] = spec.homepage
23
+ # spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
24
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
25
+ #else
26
+ # raise "RubyGems 2.0 or newer is required to protect against " \
27
+ # "public gem pushes."
28
+ #end
29
+
30
+ # Specify which files should be added to the gem when it is released.
31
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
32
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
33
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
34
+ end
35
+ spec.bindir = "exe"
36
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
+ spec.require_paths = ["lib"]
38
+
39
+ spec.add_development_dependency "bundler", "~> 2.0"
40
+ spec.add_development_dependency "rake", "~> 10.0"
41
+ spec.add_development_dependency 'pry-debugger-jruby'
42
+
43
+ spec.add_dependency 'tlogger'
44
+ spec.add_dependency 'activesupport'
45
+ end