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,415 @@
1
+
2
+ require 'pkernel'
3
+ require_relative "utils"
4
+ require_relative "provider"
5
+ require_relative "io_utils"
6
+
7
+ class Pkernel::OCSP
8
+ GOOD_CERT = org.bouncycastle.cert.ocsp.CertificateStatus::GOOD
9
+ REVOKED_CERT = lambda do |dt,reason|
10
+ revokedOn = dt
11
+ revokedOn = revokedOn.to_java_date
12
+ org.bouncycastle.cert.ocsp.RevokedStatus.new(revokedOn, reason)
13
+ end
14
+ UNKNOWN_CERT = lambda { org.bouncycastle.cert.ocsp.UnknownStatus.new }
15
+
16
+ def self.request_to_bin(req)
17
+ PkernelJce::OCSPRequestEngine.to_bin(req)
18
+ end
19
+
20
+ def self.response_to_bin(resp)
21
+ PkernelJce::OCSPResponseEngine.to_bin(resp)
22
+ end
23
+
24
+ def self.to_cert_id(cert, issuer = nil, opts = { })
25
+
26
+ digest = org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder.new.setProvider(PkernelJce::Provider::DefProvider).build
27
+ signHash = opts[:signHash] || :sha1
28
+ case signHash
29
+ when :sha1, "SHA1"
30
+ else
31
+ PkernelJce::GConf.instance.glog.warn "Hashing algo '#{signHash}' not yet supported by library. Adjusted to SHA1 as default"
32
+ end
33
+
34
+ # for this version of BC (157) this is the only option
35
+ d = digest.get(org.bouncycastle.cert.ocsp.CertificateID::HASH_SHA1)
36
+
37
+ org.bouncycastle.cert.ocsp.CertificateID.new(d,PkernelJce::Certificate.ensure_bc_cert(cert),PkernelJce::Certificate.ensure_java_cert(cert).serial_number)
38
+ end
39
+ end
40
+
41
+
42
+
43
+ module PkernelJce
44
+ module OCSP
45
+
46
+ # module Response
47
+ module Response
48
+
49
+ ST_SUCCESSFUL = org.bouncycastle.cert.ocsp.OCSPRespBuilder::SUCCESSFUL
50
+ ST_MALFORM_REQ = org.bouncycastle.cert.ocsp.OCSPRespBuilder::MALFORMED_REQUEST
51
+ ST_ERROR = org.bouncycastle.cert.ocsp.OCSPRespBuilder::INTERNAL_ERROR
52
+ ST_TRY_LATER = org.bouncycastle.cert.ocsp.OCSPRespBuilder::TRY_LATER
53
+ ST_SIG_REQUIRED = org.bouncycastle.cert.ocsp.OCSPRespBuilder::SIG_REQUIRED
54
+ ST_UNAUTHORIZED = org.bouncycastle.cert.ocsp.OCSPRespBuilder::UNAUTHORIZED
55
+
56
+ #
57
+ # used by OCSP responder
58
+ #
59
+ def generate(identity, opts = { }, &block)
60
+
61
+ if identity.nil?
62
+ raise PkernelJce::Error, "Identity is nil in generate OCSP response"
63
+ end
64
+
65
+ provider = opts[:provider]
66
+ if provider.nil?
67
+ prov = PkernelJce::Provider.add_default
68
+ else
69
+ prov = PkernelJce::Provider.add_provider(provider)
70
+ end
71
+
72
+ digest = org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder.new.setProvider(PkernelJce::Provider::DefProvider).build
73
+ # for this version of BC (157) this is the only option
74
+ #d = digest.get(org.bouncycastle.cert.ocsp.CertificateID::HASH_SHA1)
75
+
76
+ respBuilder = org.bouncycastle.cert.ocsp.jcajce.JcaBasicOCSPRespBuilder.new(identity.pubKey, digest.get(org.bouncycastle.cert.ocsp.RespID::HASH_SHA1))
77
+
78
+ reqBin = opts[:request]
79
+
80
+ reqRes = OCSPRequestEngine.parse({ bin: reqBin }) do |info|
81
+ if block
82
+ block.call(respBuilder, info)
83
+ else
84
+ v_cert_status_unknown(respBuilder, info[:cid])
85
+ end
86
+ end
87
+
88
+ req = reqRes[:req]
89
+
90
+ nonceField = req.getExtension(org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers::id_pkix_ocsp_nonce)
91
+ if not nonceField.nil?
92
+ extGen = org.bouncycastle.asn1.x509.ExtensionsGenerator.new
93
+ extGen.addExtension(org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers::id_pkix_ocsp_nonce, false, org.bouncycastle.asn1.DEROctetString.new(nonceField.parsed_value.getOctets))
94
+ respBuilder.setResponseExtensions(extGen.generate)
95
+ end
96
+
97
+ signHash = opts[:signHash] || "SHA256"
98
+ resp = respBuilder.build(org.bouncycastle.operator.jcajce.JcaContentSignerBuilder.new(PkernelJce::KeyPair.derive_signing_algo(identity.privKey,signHash)).setProvider(prov).build(identity.privKey), identity.chain, java.util.Date.new)
99
+
100
+ #public class OCSPRespBuilder
101
+ #{
102
+ # public static final int SUCCESSFUL = 0; // Response has valid confirmations
103
+ # public static final int MALFORMED_REQUEST = 1; // Illegal confirmation request
104
+ # public static final int INTERNAL_ERROR = 2; // Internal error in issuer
105
+ # public static final int TRY_LATER = 3; // Try again later
106
+ # // (4) is not used
107
+ # public static final int SIG_REQUIRED = 5; // Must sign the request
108
+ # public static final int UNAUTHORIZED = 6; // Request unauthorized
109
+ #
110
+ # this response should be a step higher
111
+ #org.bouncycastle.cert.ocsp.OCSPRespBuilder.new.build(org.bouncycastle.cert.ocsp.OCSPRespBuilder::SUCCESSFUL, resp).encoded.to_s
112
+ to_response_asn1(ST_SUCCESSFUL, resp)
113
+ end
114
+ #
115
+ # end generate
116
+ #
117
+
118
+ def to_response_asn1(st, resp = nil)
119
+ org.bouncycastle.cert.ocsp.OCSPRespBuilder.new.build(st, resp)
120
+ end
121
+
122
+ #
123
+ # invoke by client side to read the result
124
+ #
125
+ def parse(opts = {})
126
+ file = opts[:file]
127
+ bin = opts[:bin]
128
+
129
+ if not file.nil?
130
+ bresp = IoUtils.file_to_memory_byte_array(file)
131
+ elsif not bin.nil?
132
+ bresp = IoUtils.ensure_java_bytes(bin)
133
+ else
134
+ raise PkernelJce::Error, "No OCSP response input available for parsing"
135
+ end
136
+
137
+ resp = org.bouncycastle.cert.ocsp.OCSPResp.new(bresp)
138
+ if resp.status == ST_SUCCESSFUL
139
+ respObj = resp.response_object
140
+ #
141
+ #nonceField = respObj.getExtension(org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers::id_pkix_ocsp_nonce)
142
+ #if not nonceField.nil?
143
+ # result[:nonce] = nonceField.parsed_value.getOctets
144
+ #end
145
+
146
+ provider = opts[:provider]
147
+ if provider.nil?
148
+ prov = PkernelJce::Provider.add_default
149
+ else
150
+ prov = PkernelJce::Provider.add_provider(provider)
151
+ end
152
+
153
+ if respObj.is_signature_valid?(org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder.new.setProvider(prov).build(respObj.certs[0]))
154
+ #vres = { }
155
+ #respObj.responses.each do |re|
156
+ # vres[re.cert_id] = re.cert_status
157
+ #end
158
+ #result[:result] = vres
159
+ resp
160
+ else
161
+ raise PkernelJce::Error, "OCSP response digital signature failed to be verified. Result discarded."
162
+ end
163
+ else
164
+ raise PkernelJce::Error, "OCSP response unsuccessful. Message was : #{resp.status}"
165
+ end
166
+
167
+ #result
168
+ end
169
+ # end parse()
170
+ #
171
+
172
+ def is_cert_good?(resp, cert_id, opts = { })
173
+ respObj = resp.response_object
174
+ respObj.responses.each do |re|
175
+ if re.cert_id.equals(cert_id)
176
+ return re.cert_status.nil?
177
+ end
178
+ end
179
+
180
+ false
181
+ end
182
+
183
+ def is_cert_revoked?(resp, cert_id, opts = { })
184
+ respObj = resp.response_object
185
+ respObj.responses.each do |re|
186
+ if re.cert_id.equals(cert_id)
187
+ if (not re.cert_status.nil? and re.cert_status.java_kind_of?(org.bouncycastle.cert.ocsp.RevokedStatus))
188
+ return [true, re.cert_status.revocation_reason, re.cert_status.revocation_time]
189
+ end
190
+ end
191
+ end
192
+
193
+ [false]
194
+ end
195
+
196
+ def is_cert_unknown?(resp, cert_id, opts = { })
197
+ respObj = resp.response_object
198
+ respObj.responses.each do |re|
199
+ if re.cert_id.equals(cert_id)
200
+ return (not re.cert_status.nil? and re.cert_status.java_kind_of?(org.bouncycastle.cert.ocsp.UnknownStatus))
201
+ end
202
+ end
203
+
204
+ false
205
+ end
206
+
207
+ def v_cert_good(resp, cid, opts = { })
208
+ resp.addResponse(cid, org.bouncycastle.cert.ocsp.CertificateStatus::GOOD)
209
+ end
210
+
211
+ def v_cert_revoked(resp, cid, reason = Pkernel::CRLReason::UNSPECIFIED, revokedOn = java.util.Date.new, opts = { })
212
+ resp.addResponse(cid, org.bouncycastle.cert.ocsp.RevokedStatus.new(revokedOn, reason))
213
+ end
214
+
215
+ def v_cert_status_unknown(resp, cid, opts= { })
216
+ resp.addResponse(cid, org.bouncycastle.cert.ocsp.UnknownStatus.new)
217
+ end
218
+
219
+ def to_bin(resp)
220
+ if resp.nil?
221
+ raise PkernelJce::Error, "Response object is nil to convert to bin"
222
+ end
223
+ resp.encoded
224
+ end
225
+
226
+ end
227
+ #
228
+ # end module Response
229
+ #
230
+
231
+ module Request
232
+
233
+ #
234
+ # invoked by server side during response
235
+ #
236
+ def parse(opts = {},&block)
237
+ file = opts[:file]
238
+ bin = opts[:bin]
239
+
240
+ if not block
241
+ raise PkernelJce::Error, "Block must be given for OCSP request parse operation"
242
+ end
243
+
244
+ if not file.nil?
245
+ breq = PkernelJce::IoUtils.file_to_memory_byte_array(file)
246
+ #f = java.io.File.new(file)
247
+ #if f.exists?
248
+ # breq = Java::byte[f.length].new
249
+ # dis = java.io.DataInputStream.new(java.io.FileInputStream.new(f))
250
+ # dis.readFully(breq)
251
+ # dis.close
252
+ #else
253
+ # raise PkernelJce::Error, "Given OCSP request in file '#{f.absolute_path}' does not exist"
254
+ #end
255
+ elsif not bin.nil?
256
+ breq = PkernelJce::IoUtils.ensure_java_bytes(bin)
257
+ else
258
+ raise PkernelJce::Error, "No OCSP request input available for parsing"
259
+ end
260
+
261
+ res = {}
262
+ req = org.bouncycastle.cert.ocsp.OCSPReq.new(breq)
263
+
264
+ res[:req] = req
265
+
266
+ verifySign = opts[:verify_sign] || true
267
+ if verifySign and req.isSigned
268
+
269
+ provider = opts[:provider]
270
+ if provider.nil?
271
+ prov = PkernelJce::Provider.add_default
272
+ else
273
+ prov = PkernelJce::Provider.add_provider(provider)
274
+ end
275
+
276
+ if not req.isSignatureValid(org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder.new.setProvider(prov).build(req.getCerts[0]))
277
+ if block
278
+ res = block.call(:ocsp_verify_failed, { request: req, signer_cert: req.getCerts[0] })
279
+ if not res
280
+ raise PkernelOpenssl::Error, "OCSP request verification failed"
281
+ end
282
+ else
283
+ raise PkernelJce::Error, "Request signature is invalid. Request parsing is aborted."
284
+ end
285
+ end
286
+ end
287
+
288
+ nonceField = req.getExtension(org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers::id_pkix_ocsp_nonce)
289
+ if not nonceField.nil?
290
+ res[:nonce] = nonceField.parsed_value.getOctets
291
+ end
292
+
293
+ #certs = {}
294
+ req.getRequestList.each do |qc|
295
+ cid = qc.getCertID
296
+ info = { }
297
+ info[:serial] = cid.serial_number
298
+ info[:issuer_key_hash] = cid.issuer_key_hash
299
+ info[:issuer_name_hash] = cid.issuer_name_hash
300
+ info[:cid] = cid
301
+ # let block decide what is the status and mechanism
302
+ block.call(info)
303
+ end
304
+
305
+ #res[:result] = certs
306
+
307
+ res
308
+ end
309
+ # end parse()
310
+ #
311
+
312
+ def gen_nonce(len = 16)
313
+ nonce = Java::byte[len].new
314
+ java.util.Random.new.nextBytes(nonce)
315
+ nonce
316
+ end
317
+
318
+ # initiate by client
319
+ def generate(certs = [], opts = {})
320
+
321
+ if certs.nil?
322
+ raise PkernelJce::Error, "Given certificates to generate OCSP request is nil"
323
+ elsif not certs.is_a?(Array)
324
+ certs = [certs]
325
+ end
326
+
327
+ #digest = org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder.new.setProvider(PkernelJce::Provider::DefProvider).build
328
+ ## for this version of BC (157) this is the only option
329
+ #d = digest.get(org.bouncycastle.cert.ocsp.CertificateID::HASH_SHA1)
330
+
331
+ gen = org.bouncycastle.cert.ocsp.OCSPReqBuilder.new
332
+
333
+ result = {}
334
+
335
+ nonce = opts[:nonce]
336
+ genNonce = opts[:gen_nonce] || true
337
+ if genNonce
338
+ nonce = Java::byte[16].new
339
+ java.util.Random.new.nextBytes(nonce)
340
+ extGen = org.bouncycastle.asn1.x509.ExtensionsGenerator.new
341
+ extGen.addExtension(org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers::id_pkix_ocsp_nonce, false, org.bouncycastle.asn1.DEROctetString.new(nonce))
342
+ gen.setRequestExtensions(extGen.generate)
343
+ result[:nonce] = nonce
344
+ elsif not nonce.nil?
345
+ extGen = org.bouncycastle.asn1.x509.ExtensionsGenerator.new
346
+ extGen.addExtension(org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers::id_pkix_ocsp_nonce, false, org.bouncycastle.asn1.DEROctetString.new(nonce))
347
+ gen.setRequestExtensions(extGen.generate)
348
+ end
349
+
350
+ certMap = { }
351
+ certs.each do |c|
352
+ #id = org.bouncycastle.cert.ocsp.CertificateID.new(d,PkernelJce::Certificate.ensure_bc_cert(c),PkernelJce::Certificate.ensure_java_cert(c).serial_number)
353
+ #certMap[id] = c
354
+ gen.addRequest(c)
355
+ end
356
+ result[:cert_id] = certMap
357
+
358
+ id = opts[:identity]
359
+ provider = opts[:provider]
360
+ if provider.nil?
361
+ prov = PkernelJce::Provider.add_default
362
+ else
363
+ prov = PkernelJce::Provider.add_provider(provider)
364
+ end
365
+
366
+ if id.nil?
367
+ result[:req] = gen.build
368
+ else
369
+ name = opts[:requestor_name]
370
+ x500Name = opts[:requestor_x500name]
371
+
372
+ if not (name.nil? or name.empty?)
373
+ gen.setRequestorName(org.bouncycastle.asn1.x500.X500Name.new("CN=#{name}"))
374
+ elsif not (x500Name.nil? or x500Name.empty?)
375
+ gen.setRequestorName(org.bouncycastle.asn1.x500.X500Name.new(x500Name))
376
+ elsif not id.certificate.nil?
377
+ bcCert = PkernelJce::Certificate.ensure_bc_cert(id.certificate)
378
+ gen.setRequestorName(bcCert.subject_to_x500)
379
+ else
380
+ raise PkernelJce::Error, "Cannot sign content as requestor name/certificate is not given"
381
+ end
382
+
383
+ signer = org.bouncycastle.operator.jcajce.JcaContentSignerBuilder.new(PkernelJce::KeyPair.derive_signing_algo(id.privKey,"SHA256")).setProvider(prov).build(id.privKey)
384
+ result[:req] = gen.build(signer, PkernelJce::Certificate.ensure_bc_cert(id.chain).to_java(Java::OrgBouncycastleCert::X509CertificateHolder))
385
+ end
386
+
387
+ result[:req]
388
+ end
389
+ # end generate
390
+ #
391
+
392
+
393
+ def to_bin(req)
394
+ if req.nil?
395
+ raise PkernelJce::Error, "Request object cannot be nil to convert to binary"
396
+ end
397
+ req.encoded
398
+ end
399
+
400
+ end
401
+ #
402
+ # end module Request
403
+ #
404
+
405
+ end
406
+
407
+ class OCSPRequestEngine
408
+ extend OCSP::Request
409
+ end
410
+
411
+ class OCSPResponseEngine
412
+ extend OCSP::Response
413
+ end
414
+
415
+ end
@@ -0,0 +1,40 @@
1
+
2
+
3
+ Dir.glob(File.join(File.dirname(__FILE__),'../../jars/*.jar')).each do |f|
4
+ require_relative f
5
+ end
6
+
7
+ module PkernelJce
8
+ module Provider
9
+
10
+ DefProvider = org.bouncycastle.jce.provider.BouncyCastleProvider.new
11
+
12
+ def Provider::add_default
13
+ add_provider(DefProvider)
14
+ DefProvider
15
+ end
16
+
17
+ def Provider::add_provider(prov)
18
+ if prov != nil
19
+ if prov.is_a?(String) and not prov.empty?
20
+ PkernelJce::GConf.instance.glog.error "Unknown provider by string '#{prov}'. Please use provider object."
21
+ raise Exception, "Unknown provider by string '#{prov}'. Please use provider object."
22
+ elsif prov.is_a?(java.security.Provider)
23
+ if prov != nil
24
+ if not java.security.Security.get_providers.to_a.include?(prov)
25
+ PkernelJce::GConf.instance.glog.debug "Adding security provider '#{prov.name}'"
26
+ java.security.Security.add_provider(prov)
27
+ end
28
+ prov
29
+ else
30
+ raise Exception, "Unknown provider object #{prov.inspect}"
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
40
+