pkernel_jce 0.1

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