pkernel_jce 0.1

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