ccrypto-java 0.1.0
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 +7 -0
- data/.java-version +1 -0
- data/.rspec +3 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +94 -0
- data/README.md +150 -0
- data/Rakefile +10 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/ccrypto-java.gemspec +44 -0
- data/jars/bcmail-jdk15on-165.jar +0 -0
- data/jars/bcpg-jdk15on-165.jar +0 -0
- data/jars/bcpkix-jdk15on-165.jar +0 -0
- data/jars/bcprov-ext-jdk15on-165.jar +0 -0
- data/jars/bcprov-jdk15on-165.jar +0 -0
- data/jars/bctls-jdk15on-165.jar +0 -0
- data/jars/shamir-0.6.1-p.jar +0 -0
- data/lib/ccrypto/java/data_conversion.rb +80 -0
- data/lib/ccrypto/java/engines/asn1_engine.rb +161 -0
- data/lib/ccrypto/java/engines/asn1_object.rb +12 -0
- data/lib/ccrypto/java/engines/cipher_engine.rb +255 -0
- data/lib/ccrypto/java/engines/compression_engine.rb +92 -0
- data/lib/ccrypto/java/engines/data_conversion_engine.rb +9 -0
- data/lib/ccrypto/java/engines/decompression_engine.rb +48 -0
- data/lib/ccrypto/java/engines/digest_engine.rb +208 -0
- data/lib/ccrypto/java/engines/ecc_engine.rb +263 -0
- data/lib/ccrypto/java/engines/hkdf_engine.rb +72 -0
- data/lib/ccrypto/java/engines/hmac_engine.rb +75 -0
- data/lib/ccrypto/java/engines/pbkdf2_engine.rb +87 -0
- data/lib/ccrypto/java/engines/pkcs7_engine.rb +558 -0
- data/lib/ccrypto/java/engines/rsa_engine.rb +572 -0
- data/lib/ccrypto/java/engines/scrypt_engine.rb +35 -0
- data/lib/ccrypto/java/engines/secret_key_engine.rb +44 -0
- data/lib/ccrypto/java/engines/secret_sharing_engine.rb +59 -0
- data/lib/ccrypto/java/engines/secure_random_engine.rb +76 -0
- data/lib/ccrypto/java/engines/x509_engine.rb +311 -0
- data/lib/ccrypto/java/ext/secret_key.rb +75 -0
- data/lib/ccrypto/java/ext/x509_cert.rb +48 -0
- data/lib/ccrypto/java/jce_provider.rb +52 -0
- data/lib/ccrypto/java/keybundle_store/pkcs12.rb +125 -0
- data/lib/ccrypto/java/utils/comparator.rb +20 -0
- data/lib/ccrypto/java/utils/memory_buffer.rb +77 -0
- data/lib/ccrypto/java/utils/native_helper.rb +19 -0
- data/lib/ccrypto/java/version.rb +7 -0
- data/lib/ccrypto/java.rb +30 -0
- data/lib/ccrypto/provider.rb +132 -0
- metadata +144 -0
@@ -0,0 +1,572 @@
|
|
1
|
+
|
2
|
+
require_relative '../data_conversion'
|
3
|
+
require_relative '../keybundle_store/pkcs12'
|
4
|
+
#require_relative '../keybundle_store/pem_store'
|
5
|
+
|
6
|
+
module Ccrypto
|
7
|
+
module Java
|
8
|
+
|
9
|
+
class RSAPublicKey < Ccrypto::RSAPublicKey
|
10
|
+
|
11
|
+
def to_bin
|
12
|
+
@native_pubKey.encoded
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.to_key(bin)
|
16
|
+
pubKey = java.security.KeyFactory.getInstance("RSA", "BC").generatePublic(java.security.spec.X509EncodedKeySpec.new(bin))
|
17
|
+
RSAPublicKey.new(pubKey)
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(mtd, *args, &block)
|
21
|
+
@native_pubKey.send(mtd, *args, &block)
|
22
|
+
end
|
23
|
+
|
24
|
+
end # RSAPublicKey
|
25
|
+
|
26
|
+
class RSAKeyBundle
|
27
|
+
include Ccrypto::RSAKeyBundle
|
28
|
+
include TR::CondUtils
|
29
|
+
|
30
|
+
include PKCS12
|
31
|
+
#include PEMStore
|
32
|
+
|
33
|
+
include TeLogger::TeLogHelper
|
34
|
+
|
35
|
+
teLogger_tag :j_rsa_keybundle
|
36
|
+
|
37
|
+
def initialize(kp)
|
38
|
+
@nativeKeypair = kp
|
39
|
+
end
|
40
|
+
|
41
|
+
def public_key
|
42
|
+
if @pubKey.nil?
|
43
|
+
@pubKey = RSAPublicKey.new(@nativeKeypair.public)
|
44
|
+
end
|
45
|
+
@pubKey
|
46
|
+
end
|
47
|
+
|
48
|
+
def private_key
|
49
|
+
if @privKey.nil?
|
50
|
+
@privKey = RSAPrivateKey.new(@nativeKeypair.private)
|
51
|
+
end
|
52
|
+
@privKey
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_storage(type, &block)
|
56
|
+
|
57
|
+
case type
|
58
|
+
when :p12, :pkcs12
|
59
|
+
to_pkcs12 do |key|
|
60
|
+
case key
|
61
|
+
when :keypair
|
62
|
+
@nativeKeypair
|
63
|
+
else
|
64
|
+
block.call(key) if block
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
when :jks
|
69
|
+
to_pkcs12 do |key|
|
70
|
+
case key
|
71
|
+
when :storeType
|
72
|
+
"JKS"
|
73
|
+
when :keypair
|
74
|
+
@nativeKeypair
|
75
|
+
else
|
76
|
+
block.call(key) if block
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.from_storage(bin, &block)
|
84
|
+
|
85
|
+
if is_pem?(bin)
|
86
|
+
else
|
87
|
+
from_pkcs12(bin, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.is_pem?(bin)
|
93
|
+
begin
|
94
|
+
(bin =~ /BEGIN/) != nil
|
95
|
+
rescue ArgumentError => ex
|
96
|
+
false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def equal?(kp)
|
101
|
+
case kp
|
102
|
+
when Ccrypto::RSAKeyBundle
|
103
|
+
@nativeKeypair.encoded == kp.private.encoded
|
104
|
+
else
|
105
|
+
false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def method_missing(mtd, *args, &block)
|
110
|
+
teLogger.debug "Sending to native #{mtd}"
|
111
|
+
@nativeKeypair.send(mtd, *args, &block)
|
112
|
+
end
|
113
|
+
|
114
|
+
def respond_to_missing?(mtd, incPriv = false)
|
115
|
+
teLogger.debug "Respond to missing #{mtd}"
|
116
|
+
@nativeKeypair.respond_to?(mtd)
|
117
|
+
end
|
118
|
+
|
119
|
+
end # RSAKeyBundle
|
120
|
+
|
121
|
+
class RSAEngine
|
122
|
+
include TR::CondUtils
|
123
|
+
include DataConversion
|
124
|
+
|
125
|
+
include TeLogger::TeLogHelper
|
126
|
+
|
127
|
+
teLogger_tag :j_rsa
|
128
|
+
|
129
|
+
def initialize(*args, &block)
|
130
|
+
@config = args.first
|
131
|
+
raise KeypairEngineException, "1st parameter must be a #{Ccrypto::KeypairConfig.class} object" if not @config.is_a?(Ccrypto::KeypairConfig)
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
def generate_keypair(&block)
|
136
|
+
prov = Ccrypto::Java::JCEProvider::DEFProv
|
137
|
+
if block
|
138
|
+
prov = block.call(:jce_provider)
|
139
|
+
end
|
140
|
+
prov = Ccrypto::Java::JCEProvider::DEFProv if is_empty?(prov)
|
141
|
+
|
142
|
+
kpg = java.security.KeyPairGenerator.getInstance("RSA", prov)
|
143
|
+
kpg.java_send :initialize, [::Java::int], @config.keysize
|
144
|
+
kp = kpg.generate_key_pair
|
145
|
+
|
146
|
+
RSAKeyBundle.new(kp)
|
147
|
+
end
|
148
|
+
|
149
|
+
def sign(val, &block)
|
150
|
+
if block
|
151
|
+
pss = block.call(:pss_mode)
|
152
|
+
pss = false if is_empty?(pss) or not is_bool?(pss)
|
153
|
+
|
154
|
+
if pss
|
155
|
+
sign_pss(val, &block)
|
156
|
+
else
|
157
|
+
sign_typical(val, &block)
|
158
|
+
end
|
159
|
+
else
|
160
|
+
sign_typical(val,&block)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.verify(pubKey, val, ssign, &block)
|
165
|
+
if block
|
166
|
+
pss = block.call(:pss_mode)
|
167
|
+
pss = false if is_empty?(pss) or not is_bool?(pss)
|
168
|
+
|
169
|
+
if pss
|
170
|
+
verify_pss(pubKey, val, ssign, &block)
|
171
|
+
else
|
172
|
+
verify_typical()
|
173
|
+
end
|
174
|
+
|
175
|
+
else
|
176
|
+
verify_typical(pubKey, val, ssign, &block)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.encrypt(pubKey, val, &block)
|
181
|
+
|
182
|
+
raise KeypairEngineException, "Public key is required" if is_empty?(pubKey)
|
183
|
+
|
184
|
+
prov = nil
|
185
|
+
if block
|
186
|
+
prov = block.call(:jce_provider)
|
187
|
+
padding = block.call(:padding)
|
188
|
+
digAlgo = block.call(:oaep_digest)
|
189
|
+
mode = block.call(:mode)
|
190
|
+
end
|
191
|
+
padding = :oaep if is_empty?(padding)
|
192
|
+
digAlgo = :sha256 if is_empty?(digAlgo)
|
193
|
+
mode = :none if is_empty?(mode)
|
194
|
+
|
195
|
+
case padding
|
196
|
+
when :pkcs1
|
197
|
+
teLogger.owarn "RSA with PKCS1Padding mode is vulnerable. :oeap mode recommended"
|
198
|
+
transform = "RSA/#{mode.to_s.upcase}/PKCS1Padding"
|
199
|
+
|
200
|
+
when :oaep
|
201
|
+
transform = "RSA/None/OAEPWith#{digAlgo.to_s.upcase}AndMGF1Padding"
|
202
|
+
|
203
|
+
# standardize BC vs Oracle defaults
|
204
|
+
# https://stackoverflow.com/a/50299291/3625825
|
205
|
+
case digAlgo
|
206
|
+
when :sha1
|
207
|
+
oaepSpec = javax.crypto.spec.OAEPParameterSpec.new(digAlgo.to_s.upcase, "MGF1", java.security.spec.MGF1ParameterSpec::SHA1, javax.crypto.spec.PSource::PSpecified::DEFAULT)
|
208
|
+
when :sha224
|
209
|
+
oaepSpec = javax.crypto.spec.OAEPParameterSpec.new(digAlgo.to_s.upcase, "MGF1", java.security.spec.MGF1ParameterSpec::SHA224, javax.crypto.spec.PSource::PSpecified::DEFAULT)
|
210
|
+
when :sha256
|
211
|
+
oaepSpec = javax.crypto.spec.OAEPParameterSpec.new(digAlgo.to_s.upcase, "MGF1", java.security.spec.MGF1ParameterSpec::SHA256, javax.crypto.spec.PSource::PSpecified::DEFAULT)
|
212
|
+
when :sha384
|
213
|
+
oaepSpec = javax.crypto.spec.OAEPParameterSpec.new(digAlgo.to_s.upcase, "MGF1", java.security.spec.MGF1ParameterSpec::SHA384, javax.crypto.spec.PSource::PSpecified::DEFAULT)
|
214
|
+
when :sha512
|
215
|
+
oaepSpec = javax.crypto.spec.OAEPParameterSpec.new(digAlgo.to_s.upcase, "MGF1", java.security.spec.MGF1ParameterSpec::SHA512, javax.crypto.spec.PSource::PSpecified::DEFAULT)
|
216
|
+
else
|
217
|
+
raise KeypairEngineException, "Unknown #{digAlgo} digest for OAEP mode"
|
218
|
+
end
|
219
|
+
|
220
|
+
when :no_padding
|
221
|
+
teLogger.owarn "RSA with NoPadding mode is vulnerable. :oeap mode recommended"
|
222
|
+
transform = "RSA/#{mode.to_s.upcase}/NoPadding"
|
223
|
+
|
224
|
+
else
|
225
|
+
raise KeypairEngineException, "Padding requires either :pkcs1, :no_padding or :oaep. Default is :oaep"
|
226
|
+
end
|
227
|
+
|
228
|
+
begin
|
229
|
+
|
230
|
+
if prov.nil?
|
231
|
+
teLogger.debug "Encrypt transformation #{transform} with nil provider"
|
232
|
+
cipher = javax.crypto.Cipher.getInstance(transform)
|
233
|
+
else
|
234
|
+
teLogger.debug "Encrypt transformation #{transform} with provider #{prov.is_a?(String) ? prov : prov.name}"
|
235
|
+
cipher = javax.crypto.Cipher.getInstance(transform, prov)
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
if oaepSpec.nil?
|
240
|
+
teLogger.debug "Init cipher with default parameter spec"
|
241
|
+
cipher.init(javax.crypto.Cipher::ENCRYPT_MODE, pubKey.native_pubKey)
|
242
|
+
else
|
243
|
+
teLogger.debug "Init cipher with parameter spec #{oaepSpec}"
|
244
|
+
cipher.init(javax.crypto.Cipher::ENCRYPT_MODE, pubKey.native_pubKey, oaepSpec)
|
245
|
+
end
|
246
|
+
|
247
|
+
if block
|
248
|
+
# this is share with caller to ensure input data should not be longer then this size
|
249
|
+
block.call(:max_data_size, cipher.getBlockSize)
|
250
|
+
end
|
251
|
+
|
252
|
+
out = java.io.ByteArrayOutputStream.new
|
253
|
+
case val
|
254
|
+
when java.io.InputStream
|
255
|
+
buf = ::Java::byte[102400].new
|
256
|
+
while((read = val.read(buf, 0, buf.length)) != nil)
|
257
|
+
out.write(cipher.update(buf, 0, read))
|
258
|
+
end
|
259
|
+
else
|
260
|
+
inDat = to_java_bytes(val)
|
261
|
+
teLogger.debug "Encrypting #{inDat.length} bytes"
|
262
|
+
ed = cipher.update(inDat)
|
263
|
+
out.write(ed) if not_empty?(ed)
|
264
|
+
end
|
265
|
+
|
266
|
+
last = cipher.doFinal
|
267
|
+
out.write(last) if not_empty?(last)
|
268
|
+
#out.write(cipher.doFinal)
|
269
|
+
|
270
|
+
out.toByteArray
|
271
|
+
|
272
|
+
rescue Exception => ex
|
273
|
+
raise KeypairEngineException, ex
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
def decrypt(enc, &block)
|
279
|
+
|
280
|
+
raise KeypairEngineException, "Private key is required" if not @config.has_private_key?
|
281
|
+
raise KeypairEngineException, "RSA private key is required. Given #{@config.private_key}" if not @config.private_key.is_a?(RSAPrivateKey)
|
282
|
+
|
283
|
+
prov = nil
|
284
|
+
if block
|
285
|
+
prov = block.call(:jce_provider)
|
286
|
+
padding = block.call(:padding)
|
287
|
+
digAlgo = block.call(:oaep_digest)
|
288
|
+
mode = block.call(:mode)
|
289
|
+
end
|
290
|
+
padding = :oaep if is_empty?(padding)
|
291
|
+
digAlgo = :sha256 if is_empty?(digAlgo)
|
292
|
+
mode = :none if is_empty?(mode)
|
293
|
+
|
294
|
+
case padding
|
295
|
+
when :pkcs1
|
296
|
+
transform = "RSA/#{mode.to_s.upcase}/PKCS1Padding"
|
297
|
+
when :oaep
|
298
|
+
transform = "RSA/None/OAEPWith#{digAlgo.to_s.upcase}AndMGF1Padding"
|
299
|
+
when :no_padding
|
300
|
+
transform = "RSA/#{mode.to_s.upcase}/NoPadding"
|
301
|
+
else
|
302
|
+
raise KeypairEngineException, "Padding requires either :pkcs1, :no_padding or :oaep. Default is :oaep"
|
303
|
+
end
|
304
|
+
|
305
|
+
begin
|
306
|
+
|
307
|
+
if prov.nil?
|
308
|
+
cipher = javax.crypto.Cipher.getInstance(transform)
|
309
|
+
else
|
310
|
+
cipher = javax.crypto.Cipher.getInstance(transform, prov)
|
311
|
+
end
|
312
|
+
|
313
|
+
cipher.init(javax.crypto.Cipher::DECRYPT_MODE, @config.private_key.native_privKey)
|
314
|
+
|
315
|
+
out = java.io.ByteArrayOutputStream.new
|
316
|
+
case enc
|
317
|
+
when java.io.InputStream
|
318
|
+
buf = ::Java::byte[102400].new
|
319
|
+
while((read = enc.read(buf, 0, buf.length)) != nil)
|
320
|
+
out.write(cipher.update(buf,0, read))
|
321
|
+
end
|
322
|
+
else
|
323
|
+
inDat = to_java_bytes(enc)
|
324
|
+
teLogger.debug "Decrypting #{inDat.length} bytes"
|
325
|
+
pd = cipher.update(inDat)
|
326
|
+
out.write(pd) if not_empty?(pd)
|
327
|
+
end
|
328
|
+
|
329
|
+
last = cipher.doFinal
|
330
|
+
out.write(last) if not_empty?(last)
|
331
|
+
|
332
|
+
out.toByteArray
|
333
|
+
|
334
|
+
rescue Exception => ex
|
335
|
+
raise KeypairEngineException, ex
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
##############################################
|
342
|
+
## Private section
|
343
|
+
###
|
344
|
+
private
|
345
|
+
def sign_typical(val, &block)
|
346
|
+
|
347
|
+
prov = block.call(:jce_provider) if block
|
348
|
+
|
349
|
+
signHash = block.call(:sign_hash) if block
|
350
|
+
signHash = :sha256 if is_empty?(signHash)
|
351
|
+
|
352
|
+
signAlgo = "#{signHash.to_s.upcase}WithRSA"
|
353
|
+
|
354
|
+
begin
|
355
|
+
|
356
|
+
if is_empty?(prov)
|
357
|
+
teLogger.debug "Provider is nil"
|
358
|
+
sign = java.security.Signature.getInstance(signAlgo)
|
359
|
+
else
|
360
|
+
teLogger.debug "Provider is '#{prov.name}'"
|
361
|
+
sign = java.security.Signature.getInstance(signAlgo, prov)
|
362
|
+
end
|
363
|
+
|
364
|
+
teLogger.debug "Private key is #{@config.private_key.native_privKey}"
|
365
|
+
sign.initSign(@config.private_key.native_privKey)
|
366
|
+
|
367
|
+
algoSpec = block.call(:signAlgoSpec) if block
|
368
|
+
|
369
|
+
if not_empty?(algoSpec) and algoSpec.is_a?(java.security.spec.AlgorithmParameterSpec)
|
370
|
+
sign.setParameter(algoSpec)
|
371
|
+
teLogger.debug "Sign Algo Parameter : '#{algoSpec}'"
|
372
|
+
end
|
373
|
+
|
374
|
+
case val
|
375
|
+
when java.io.InputStream
|
376
|
+
buf = ::Java::byte[102400].new
|
377
|
+
while((read = val.read(buf, 0, buf.length)) != nil)
|
378
|
+
sign.update(buf, 0, read)
|
379
|
+
end
|
380
|
+
else
|
381
|
+
sign.update(to_java_bytes(val))
|
382
|
+
end
|
383
|
+
|
384
|
+
sign.sign
|
385
|
+
|
386
|
+
rescue Exception => ex
|
387
|
+
raise KeypairEngineException, ex
|
388
|
+
end
|
389
|
+
|
390
|
+
end
|
391
|
+
|
392
|
+
def sign_pss(val, &block)
|
393
|
+
|
394
|
+
raise KeypairEngineException, "Private key is required" if not @config.has_private_key?
|
395
|
+
raise KeypairEngineException, "RSA private key is required." if not @config.private_key.is_a?(RSAPrivateKey)
|
396
|
+
|
397
|
+
privKey = @config.private_key
|
398
|
+
|
399
|
+
if block
|
400
|
+
signHash = block.call(:sign_hash)
|
401
|
+
mgf1Hash = block.call(:mgf1_hash)
|
402
|
+
saltLen = block.call(:salt_length)
|
403
|
+
prov = block.call(:jce_provider)
|
404
|
+
trailer = block.call(:trailer_field)
|
405
|
+
end
|
406
|
+
|
407
|
+
mgf1Hash = :sha256 if is_empty?(mgf1Hash)
|
408
|
+
# Comment under post https://stackoverflow.com/a/48854106/3625825
|
409
|
+
# indicated 20 is the value when use with OpenSSL
|
410
|
+
#saltLen = 20 if is_empty?(saltLen)
|
411
|
+
saltLen = 32 if is_empty?(saltLen)
|
412
|
+
signHash = "sha256" if is_empty?(signHash)
|
413
|
+
# there is post on StackOverflow indicated to verify with OpenSSL
|
414
|
+
# trailer = 0xBC
|
415
|
+
#trailer = 0xBC if is_empty?(trailer)
|
416
|
+
trailer = 1 if is_empty?(trailer)
|
417
|
+
|
418
|
+
case mgf1Hash.to_sym
|
419
|
+
when :sha1
|
420
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA1
|
421
|
+
when :sha224
|
422
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA224
|
423
|
+
when :sha256
|
424
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA256
|
425
|
+
when :sha384
|
426
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA384
|
427
|
+
when :sha512
|
428
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA512
|
429
|
+
when :sha512_224
|
430
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA512_224
|
431
|
+
when :sha512_256
|
432
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA512_256
|
433
|
+
else
|
434
|
+
raise KeypairEngineException, "Not supported mgf1Hash value #{mgf1Hash}. Supported value including: :sha1, :sha224, :sha256, :sha384, :sha512, :sha512_224 and :sha512_256"
|
435
|
+
end
|
436
|
+
|
437
|
+
if prov.nil?
|
438
|
+
sign = java.security.Signature.getInstance("#{signHash.to_s.strip.upcase}WithRSA/PSS")
|
439
|
+
else
|
440
|
+
sign = java.security.Signature.getInstance("#{signHash.to_s.strip.upcase}WithRSA/PSS", prov)
|
441
|
+
end
|
442
|
+
|
443
|
+
sign.setParameter(java.security.spec.PSSParameterSpec.new(signHash.to_s.strip.upcase,"MGF1", mgf1Spec, saltLen, trailer))
|
444
|
+
|
445
|
+
sign.initSign(privKey.native_privKey)
|
446
|
+
|
447
|
+
case val
|
448
|
+
when java.io.InputStream
|
449
|
+
buf = ::Java::byte[102400].new
|
450
|
+
while((read = val.read(buf, 0, buf.length)) != nil)
|
451
|
+
sign.update(buf, 0, read)
|
452
|
+
end
|
453
|
+
else
|
454
|
+
sign.update(to_java_bytes(val))
|
455
|
+
end
|
456
|
+
|
457
|
+
sign.sign
|
458
|
+
|
459
|
+
end
|
460
|
+
|
461
|
+
def self.verify_typical(pubKey, val, ssign, &block)
|
462
|
+
|
463
|
+
#raise KeypairEngineException, "block is required" if not block
|
464
|
+
|
465
|
+
prov = block.call(:jce_provider) if block
|
466
|
+
|
467
|
+
signAlgo = block.call(:signAlgo) if block
|
468
|
+
signAlgo = "SHA256WithRSA" if is_empty?(signAlgo)
|
469
|
+
|
470
|
+
case pubKey.native_pubKey
|
471
|
+
when java.security.cert.Certificate, java.security.PublicKey
|
472
|
+
|
473
|
+
if is_empty?(prov)
|
474
|
+
teLogger.debug "Provider is nil"
|
475
|
+
sign = java.security.Signature.getInstance(signAlgo)
|
476
|
+
else
|
477
|
+
teLogger.debug "Provider is '#{prov.name}'"
|
478
|
+
sign = java.security.Signature.getInstance(signAlgo, prov)
|
479
|
+
end
|
480
|
+
|
481
|
+
sign.initVerify(pubKey.native_pubKey)
|
482
|
+
|
483
|
+
else
|
484
|
+
raise KeypairEngineException, "Unknown pubKey type #{pubKey}"
|
485
|
+
end
|
486
|
+
|
487
|
+
case val
|
488
|
+
when java.io.InputStream
|
489
|
+
buf = ::Java::byte[102400].new
|
490
|
+
while((read = val.read(buf, 0, buf.length)) != nil)
|
491
|
+
sign.update(buf, 0, read)
|
492
|
+
end
|
493
|
+
else
|
494
|
+
sign.update(to_java_bytes(val))
|
495
|
+
end
|
496
|
+
|
497
|
+
sign.verify(to_java_bytes(ssign))
|
498
|
+
|
499
|
+
end
|
500
|
+
|
501
|
+
def self.verify_pss(pubKey, val, ssign, &block)
|
502
|
+
|
503
|
+
raise KeypairEngineException, "Public key is required" if pubKey.nil?
|
504
|
+
raise KeypairEngineException, "RSA public key is required. Given #{pubKey}" if not pubKey.is_a?(RSAPublicKey)
|
505
|
+
|
506
|
+
if block
|
507
|
+
signHash = block.call(:sign_hash)
|
508
|
+
mgf1Hash = block.call(:mgf1_hash)
|
509
|
+
saltLen = block.call(:salt_length)
|
510
|
+
prov = block.call(:jce_provider)
|
511
|
+
trailer = block.call(:trailer_field)
|
512
|
+
end
|
513
|
+
|
514
|
+
mgf1Hash = :sha256 if is_empty?(mgf1Hash)
|
515
|
+
# Comment under post https://stackoverflow.com/a/48854106/3625825
|
516
|
+
# indicated 20 is the value when use with OpenSSL
|
517
|
+
#saltLen = 20 if is_empty?(saltLen)
|
518
|
+
saltLen = 32 if is_empty?(saltLen)
|
519
|
+
signHash = "sha256" if is_empty?(signHash)
|
520
|
+
# there is post on StackOverflow indicated to verify with OpenSSL
|
521
|
+
# trailer = 0xBC
|
522
|
+
#trailer = 0xBC if is_empty?(trailer)
|
523
|
+
trailer = 1 if is_empty?(trailer)
|
524
|
+
|
525
|
+
case mgf1Hash.to_sym
|
526
|
+
when :sha1
|
527
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA1
|
528
|
+
when :sha224
|
529
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA224
|
530
|
+
when :sha256
|
531
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA256
|
532
|
+
when :sha384
|
533
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA384
|
534
|
+
when :sha512
|
535
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA512
|
536
|
+
when :sha512_224
|
537
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA512_224
|
538
|
+
when :sha512_256
|
539
|
+
mgf1Spec = java.security.spec.MGF1ParameterSpec::SHA512_256
|
540
|
+
else
|
541
|
+
raise KeypairEngineException, "Not supported mgf1Hash value #{mgf1Hash}. Supported value including: :sha1, :sha224, :sha256, :sha384, :sha512, :sha512_224 and :sha512_256"
|
542
|
+
end
|
543
|
+
|
544
|
+
if prov.nil?
|
545
|
+
sign = java.security.Signature.getInstance("#{signHash.to_s.strip.upcase}WithRSA/PSS")
|
546
|
+
else
|
547
|
+
sign = java.security.Signature.getInstance("#{signHash.to_s.strip.upcase}WithRSA/PSS", prov)
|
548
|
+
end
|
549
|
+
|
550
|
+
sign.setParameter(java.security.spec.PSSParameterSpec.new(signHash.to_s.strip.upcase,"MGF1", mgf1Spec, saltLen, trailer))
|
551
|
+
|
552
|
+
sign.initVerify(pubKey.native_pubKey)
|
553
|
+
|
554
|
+
case val
|
555
|
+
when java.io.InputStream
|
556
|
+
buf = ::Java::byte[102400].new
|
557
|
+
while((read = val.read(buf, 0, buf.length)) != nil)
|
558
|
+
sign.update(buf, 0, read)
|
559
|
+
end
|
560
|
+
else
|
561
|
+
sign.update(to_java_bytes(val))
|
562
|
+
end
|
563
|
+
|
564
|
+
sign.verify(ssign)
|
565
|
+
|
566
|
+
end
|
567
|
+
|
568
|
+
|
569
|
+
end
|
570
|
+
|
571
|
+
end
|
572
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
require_relative '../data_conversion'
|
3
|
+
|
4
|
+
module Ccrypto
|
5
|
+
module Java
|
6
|
+
class ScryptEngine
|
7
|
+
include DataConversion
|
8
|
+
include TR::CondUtils
|
9
|
+
|
10
|
+
def initialize(conf, &block)
|
11
|
+
raise KDFEngineException, "KDF config is expected" if not conf.is_a?(Ccrypto::KDFConfig)
|
12
|
+
raise KDFEngineException, "Output bit length (outBitLength) value is not given or not a positive value (#{conf.outBitLength})" if is_empty?(conf.outBitLength) or conf.outBitLength <= 0
|
13
|
+
@config = conf
|
14
|
+
|
15
|
+
if is_empty?(@config.salt)
|
16
|
+
@config.salt = Java::byte[16].new
|
17
|
+
java.security.SecureRandom.getInstance("NativePRNG").random_bytes(@config.salt)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def derive(input, output = :binary)
|
22
|
+
res = org.bouncycastle.crypto.generators.SCrypt.generate(to_java_bytes(input), to_java_bytes(@config.salt),@config.cost, @config.blockSize, @config.parallel, @config.outBitLength/8)
|
23
|
+
case output
|
24
|
+
when :hex
|
25
|
+
to_hex(res)
|
26
|
+
when :b64
|
27
|
+
to_b64(res)
|
28
|
+
else
|
29
|
+
res
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Ccrypto
|
4
|
+
module Java
|
5
|
+
class SecretKeyEngine
|
6
|
+
|
7
|
+
include TeLogger::TeLogHelper
|
8
|
+
teLogger_tag :j_secretkey
|
9
|
+
|
10
|
+
def self.generate(*args, &block)
|
11
|
+
config = args.first
|
12
|
+
|
13
|
+
raise SecretKeyEngineException, "KeyConfig is expected" if not config.is_a?(Ccrypto::KeyConfig)
|
14
|
+
|
15
|
+
if block
|
16
|
+
kgProv = block.call(:keygen_jceProvider)
|
17
|
+
ranProv = block.call(:random_jceProvider)
|
18
|
+
end
|
19
|
+
|
20
|
+
if kgProv.nil?
|
21
|
+
teLogger.debug "KeyGen using algo #{config.algo.to_s} with null provider"
|
22
|
+
keyGen = javax.crypto.KeyGenerator.getInstance(config.algo.to_s)
|
23
|
+
else
|
24
|
+
teLogger.debug "KeyGen using algo #{config.algo.to_s} with provider #{kgProv.is_a?(String) ? kgProv : kgProv.name}"
|
25
|
+
keyGen = javax.crypto.KeyGenerator.getInstance(config.algo.to_s, kgProv)
|
26
|
+
end
|
27
|
+
|
28
|
+
if ranProv.nil?
|
29
|
+
teLogger.debug "Init KeyGen with keysize #{config.keysize.to_i}"
|
30
|
+
keyGen.init(config.keysize.to_i)
|
31
|
+
else
|
32
|
+
teLogger.debug "Init KeyGen with keysize #{config.keysize.to_i} with provider #{ranProv.is_a?(String) ? ranProv : ranProv.name}"
|
33
|
+
keyGen.init(config.keysize.to_i, ranProv)
|
34
|
+
end
|
35
|
+
|
36
|
+
key = keyGen.generateKey
|
37
|
+
teLogger.debug "Secret key #{config} generated"
|
38
|
+
Ccrypto::SecretKey.new(config.algo, key)
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
require_relative '../data_conversion'
|
3
|
+
|
4
|
+
module Ccrypto
|
5
|
+
module Java
|
6
|
+
class SecretSharingEngine
|
7
|
+
include DataConversion
|
8
|
+
|
9
|
+
def initialize(*args, &block)
|
10
|
+
@config = args.first
|
11
|
+
raise SecretSharingException, "SecretSharingConfig is required" if not @config.is_a?(Ccrypto::SecretSharingConfig)
|
12
|
+
raise SecretSharingException, "split_into value must be more than 1" if not @config.split_into.to_i > 1
|
13
|
+
raise SecretSharingException, "required_parts value (#{@config.required_parts}) must be less than or equal split_into value (#{@config.split_into})." if not @config.required_parts.to_i < @config.split_into.to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
def split(secVal)
|
17
|
+
eng = com.codahale.shamir.Scheme.new(java.security.SecureRandom.new, @config.split_into.to_i, @config.required_parts.to_i)
|
18
|
+
case secVal
|
19
|
+
when Ccrypto::SecretKey
|
20
|
+
val = secVal.to_bin
|
21
|
+
when ::Java::byte[]
|
22
|
+
val = secVal
|
23
|
+
when String
|
24
|
+
val = to_java_bytes(secVal)
|
25
|
+
else
|
26
|
+
raise SecretSharingException, "Unknown secret value to split (#{secVal.class})"
|
27
|
+
end
|
28
|
+
eng.split(val)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.combine(req, parts)
|
32
|
+
jhash = java.util.HashMap.new
|
33
|
+
case parts
|
34
|
+
when Hash
|
35
|
+
# need to lock the key to java.lang.Integer
|
36
|
+
# as the automated conversion of JRuby will turn the key into
|
37
|
+
# java.lang.Long instead of java.lang.Integer
|
38
|
+
# Using Map with parameterize auto conversion will failed inside the Java
|
39
|
+
parts.each do |k,v|
|
40
|
+
if not v.is_a?(::Java::byte[])
|
41
|
+
vv = to_java_bytes(v)
|
42
|
+
else
|
43
|
+
vv = v
|
44
|
+
end
|
45
|
+
|
46
|
+
jhash.put(java.lang.Integer.new(k),vv)
|
47
|
+
end
|
48
|
+
when java.util.Map
|
49
|
+
jhash = parts
|
50
|
+
else
|
51
|
+
raise SecretSharingException, "Unsupported parts of #{parts.class}"
|
52
|
+
end
|
53
|
+
|
54
|
+
com.codahale.shamir.Scheme.join(jhash)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|