gcrypto_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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.ruby-version +1 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +60 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gcrypto_jce.gemspec +47 -0
- data/jars/bcmail-jdk15on-157.jar +0 -0
- data/jars/bcpg-jdk15on-157.jar +0 -0
- data/jars/bcpkix-jdk15on-157.jar +0 -0
- data/jars/bcprov-ext-jdk15on-157.jar +0 -0
- data/jars/bcprov-jdk15on-157.jar +0 -0
- data/lib/gcrypto_jce.rb +49 -0
- data/lib/gcrypto_jce/converter.rb +29 -0
- data/lib/gcrypto_jce/digest.rb +56 -0
- data/lib/gcrypto_jce/error.rb +6 -0
- data/lib/gcrypto_jce/global.rb +26 -0
- data/lib/gcrypto_jce/hkdf.rb +143 -0
- data/lib/gcrypto_jce/io_utils.rb +121 -0
- data/lib/gcrypto_jce/keypair_crypto.rb +278 -0
- data/lib/gcrypto_jce/pbkdf2.rb +95 -0
- data/lib/gcrypto_jce/provider.rb +66 -0
- data/lib/gcrypto_jce/secretkey.rb +159 -0
- data/lib/gcrypto_jce/secretkey_crypto.rb +412 -0
- data/lib/gcrypto_jce/secure_random.rb +43 -0
- data/lib/gcrypto_jce/version.rb +3 -0
- data/release.job +52 -0
- metadata +158 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
|
2
|
+
require_relative 'global'
|
3
|
+
require 'active_support'
|
4
|
+
class NumberHelper
|
5
|
+
extend ActiveSupport::NumberHelper
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
module GcryptoJce
|
10
|
+
module IoUtils
|
11
|
+
|
12
|
+
def IoUtils.load_input(opts = { })
|
13
|
+
if opts.nil? or opts.empty?
|
14
|
+
raise GcryptoJce::Error, "No input given to load"
|
15
|
+
else
|
16
|
+
file = opts[:file]
|
17
|
+
bin = opts[:bin]
|
18
|
+
|
19
|
+
if not (file.nil? or file.empty?)
|
20
|
+
fis = java.io.FileInputStream.new(file)
|
21
|
+
fis
|
22
|
+
elsif not bin.nil?
|
23
|
+
begin
|
24
|
+
bais = java.io.ByteArrayInputStream.new(bin.to_java_bytes)
|
25
|
+
rescue NoMethodError
|
26
|
+
bais = java.io.ByteArrayInputStream.new(bin)
|
27
|
+
end
|
28
|
+
bais
|
29
|
+
else
|
30
|
+
if not (opts[:input_optional].nil? and opts[:input_optional])
|
31
|
+
raise GcryptoJce::Error, "Neither file nor bin given to load input"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
#
|
37
|
+
# end load_input()
|
38
|
+
#
|
39
|
+
|
40
|
+
def IoUtils.prep_output(opts = { })
|
41
|
+
|
42
|
+
if opts.nil? or opts.empty?
|
43
|
+
raise GcryptoJce::Error, "No opts given to prep output"
|
44
|
+
else
|
45
|
+
file = opts[:outFile]
|
46
|
+
|
47
|
+
if not (file.nil? or file.empty?)
|
48
|
+
fos = java.io.FileOutputStream.new(file)
|
49
|
+
fos
|
50
|
+
else
|
51
|
+
baos = java.io.ByteArrayOutputStream.new
|
52
|
+
baos
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def IoUtils.return_if_buffer(os)
|
59
|
+
if os.java_kind_of?(java.io.ByteArrayOutputStream)
|
60
|
+
os.toByteArray
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def IoUtils.read_chunk(is, opts = { },&block)
|
65
|
+
|
66
|
+
raise GcryptoJce::Error, "Cannot read chunk from nil input stream" if is.nil?
|
67
|
+
raise GcryptoJce::Error, "Block required for read_chunk" if not block
|
68
|
+
|
69
|
+
if not is.java_kind_of?(java.io.InputStream)
|
70
|
+
raise GcryptoJce::Error, "Cannot read from '#{is.class}' object"
|
71
|
+
end
|
72
|
+
|
73
|
+
bufLen = opts[:buffer_size] || 1024*1024
|
74
|
+
GcryptoJce::GConf.instance.glog.debug "read_chunk buffer size is #{NumberHelper.number_to_human_size(bufLen)}. Modifiable by caller app via key :in_buf -> :buffer_size"
|
75
|
+
|
76
|
+
b = Java::byte[bufLen].new
|
77
|
+
while((read = is.read(b,0,b.length)) != -1)
|
78
|
+
block.call(b,0,read)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
#
|
82
|
+
# end read_chunk
|
83
|
+
#
|
84
|
+
|
85
|
+
def IoUtils.file_to_memory_byte_array(path)
|
86
|
+
if path.nil? or path.empty?
|
87
|
+
raise PkernelJce::Error, "Given path '#{path}' to load to memory is nil or empty"
|
88
|
+
else
|
89
|
+
f = java.io.File.new(path)
|
90
|
+
b = Java::byte[f.length].new
|
91
|
+
dis = java.io.DataInputStream.new(java.io.FileInputStream.new(f))
|
92
|
+
dis.readFully(b)
|
93
|
+
dis.close
|
94
|
+
|
95
|
+
b
|
96
|
+
end
|
97
|
+
end
|
98
|
+
# end file_to_memory_byte_array
|
99
|
+
#
|
100
|
+
|
101
|
+
def IoUtils.ensure_java_bytes(bin)
|
102
|
+
if not bin.java_kind_of?(Java::byte[])
|
103
|
+
bin.to_java_bytes
|
104
|
+
else
|
105
|
+
bin
|
106
|
+
end
|
107
|
+
end
|
108
|
+
# end ensure_java_bytes
|
109
|
+
#
|
110
|
+
|
111
|
+
def IoUtils.attempt_zeroise(var)
|
112
|
+
java.util.Arrays.fill(var, 0)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
# end IoUtils
|
116
|
+
#
|
117
|
+
|
118
|
+
end
|
119
|
+
# end GcryptoJce
|
120
|
+
#
|
121
|
+
|
@@ -0,0 +1,278 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
require 'pkernel'
|
4
|
+
require 'pkernel_jce'
|
5
|
+
|
6
|
+
require_relative 'error'
|
7
|
+
require_relative 'io_utils'
|
8
|
+
require_relative 'provider'
|
9
|
+
require_relative 'global'
|
10
|
+
|
11
|
+
module GcryptoJce
|
12
|
+
|
13
|
+
module KeyPairCrypto
|
14
|
+
|
15
|
+
def sign(opts = { })
|
16
|
+
raise GcryptoJce::Error, "Insufficient parameters for signature generation" if opts.nil? or opts.empty?
|
17
|
+
|
18
|
+
id = opts[:identity]
|
19
|
+
if id.nil?
|
20
|
+
raise GcryptoJce::Error, "Identity to sign the data is not available"
|
21
|
+
end
|
22
|
+
|
23
|
+
prov = GcryptoJce::Provider.handle_options(opts, nil)
|
24
|
+
|
25
|
+
is = IoUtils.load_input(opts)
|
26
|
+
|
27
|
+
if prov.nil?
|
28
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is nil"
|
29
|
+
sign = java.security.Signature.getInstance(KeyPairCrypto.derive_signing_algo(id.privKey))
|
30
|
+
else
|
31
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is '#{prov.name}'"
|
32
|
+
sign = java.security.Signature.getInstance(KeyPairCrypto.derive_signing_algo(id.privKey), prov)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
GcryptoJce::GConf.instance.glog.debug "Private key is #{id.privKey.class}"
|
37
|
+
sign.initSign(id.privKey)
|
38
|
+
|
39
|
+
begin
|
40
|
+
IoUtils.read_chunk(is) do |buf, from, len|
|
41
|
+
sign.update(buf, from, len)
|
42
|
+
end
|
43
|
+
rescue Exception => ex
|
44
|
+
GcryptoJce::GConf.instance.glog.error ex.message
|
45
|
+
GcryptoJce::GConf.instance.glog.error ex.backtrace.join("\n")
|
46
|
+
ensure
|
47
|
+
begin
|
48
|
+
is.close
|
49
|
+
rescue Exception
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
sign.sign
|
54
|
+
|
55
|
+
end
|
56
|
+
#
|
57
|
+
# end method sign()
|
58
|
+
#
|
59
|
+
|
60
|
+
def KeyPairCrypto.derive_signing_algo(kp, hash = "SHA256")
|
61
|
+
PkernelJce::KeyPair.derive_signing_algo(kp,hash)
|
62
|
+
end
|
63
|
+
|
64
|
+
def verify(opts = { })
|
65
|
+
|
66
|
+
if opts.nil? or opts.empty?
|
67
|
+
raise GcryptoJce::Error, "Insufficient parameters for signature verification"
|
68
|
+
end
|
69
|
+
|
70
|
+
file = opts[:sign_file]
|
71
|
+
bin = opts[:sign_bin]
|
72
|
+
if not (file.nil? or file.empty?)
|
73
|
+
signBin = IoUtils.file_to_memory_byte_array(file)
|
74
|
+
elsif not bin.nil?
|
75
|
+
signBin = IoUtils.ensure_java_bytes(bin)
|
76
|
+
else
|
77
|
+
raise GcryptoJce::Error, "Neither signature in file or memory buffer given for signatur verification"
|
78
|
+
end
|
79
|
+
|
80
|
+
is = IoUtils.load_input(opts)
|
81
|
+
prov = GcryptoJce::Provider.handle_options(opts, nil)
|
82
|
+
|
83
|
+
cert = opts[:certificate]
|
84
|
+
pubKey = opts[:pubKey]
|
85
|
+
if not cert.nil?
|
86
|
+
|
87
|
+
if prov.nil?
|
88
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is nil"
|
89
|
+
sign = java.security.Signature.getInstance(KeyPairCrypto.derive_signing_algo(cert))
|
90
|
+
else
|
91
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is '#{prov.name}'"
|
92
|
+
sign = java.security.Signature.getInstance(KeyPairCrypto.derive_signing_algo(cert), prov)
|
93
|
+
end
|
94
|
+
|
95
|
+
sign.initVerify(cert)
|
96
|
+
|
97
|
+
elsif not pubKey.nil?
|
98
|
+
|
99
|
+
if prov.nil?
|
100
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is nil"
|
101
|
+
sign = java.security.Signature.getInstance(KeyPairCrypto.derive_signing_algo(pubKey))
|
102
|
+
else
|
103
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is '#{prov.name}'"
|
104
|
+
sign = java.security.Signature.getInstance(KeyPairCrypto.derive_signing_algo(pubKey), prov)
|
105
|
+
end
|
106
|
+
|
107
|
+
sign.initVerify(pubKey)
|
108
|
+
|
109
|
+
else
|
110
|
+
raise GcryptoJce::Error, "No public key or certificate during signature verification"
|
111
|
+
end
|
112
|
+
|
113
|
+
begin
|
114
|
+
IoUtils.read_chunk(is) do |buf, from, len|
|
115
|
+
sign.update(buf, from, len)
|
116
|
+
end
|
117
|
+
rescue Exception => ex
|
118
|
+
GcryptoJce::GConf.instance.glog.error ex.message
|
119
|
+
GcryptoJce::GConf.instance.glog.error ex.backtrace.join("\n")
|
120
|
+
ensure
|
121
|
+
begin
|
122
|
+
is.close
|
123
|
+
rescue Exception
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
sign.verify(signBin)
|
128
|
+
|
129
|
+
end
|
130
|
+
#
|
131
|
+
# end method verify()
|
132
|
+
#
|
133
|
+
|
134
|
+
|
135
|
+
def encrypt(opts = { })
|
136
|
+
|
137
|
+
file = opts[:file]
|
138
|
+
bin = opts[:bin]
|
139
|
+
|
140
|
+
# assumption:
|
141
|
+
# Data size is small (~kb)
|
142
|
+
# Because for RSA the max data = public key size
|
143
|
+
if not (file.nil? or file.empty?)
|
144
|
+
data = IoUtils.file_to_memory_byte_array(file)
|
145
|
+
elsif not bin.nil?
|
146
|
+
data = IoUtils.ensure_java_bytes(bin)
|
147
|
+
else
|
148
|
+
raise GcryptoJce::Error, "No input given for keypair encryption"
|
149
|
+
end
|
150
|
+
|
151
|
+
recp = opts[:recipient]
|
152
|
+
if recp.nil?
|
153
|
+
raise GcryptoJce::Error, "No recipient given for keypair encryption"
|
154
|
+
end
|
155
|
+
|
156
|
+
if Pkernel::Certificate.is_cert_object?(recp)
|
157
|
+
pubKey = Pkernel::Certificate.public_key(recp)
|
158
|
+
elsif Pkernel::KeyPair.is_public_key?(recp)
|
159
|
+
pubKey = recp
|
160
|
+
else
|
161
|
+
raise GcryptoJce::Error, "Given recipient is neither certificate nor public key."
|
162
|
+
end
|
163
|
+
|
164
|
+
prov = GcryptoJce::Provider.handle_options(opts, nil)
|
165
|
+
|
166
|
+
cipherSpec = opts[:cipher_spec] || "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
|
167
|
+
type = Pkernel::KeyPair.pub_key_type(pubKey)
|
168
|
+
case type
|
169
|
+
when Pkernel::KeyPair::RSA_KEY_NAME
|
170
|
+
if prov.nil?
|
171
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is nil"
|
172
|
+
if cipherSpec =~ /PKCS5Padding/
|
173
|
+
GcryptoJce::GConf.instance.glog.warn "PKCS#1 1.5 may be vulnerable to Bleichenbacher's CCA attack"
|
174
|
+
end
|
175
|
+
GcryptoJce::GConf.instance.glog.debug "Using cipher spec '#{cipherSpec}'"
|
176
|
+
cipher = javax.crypto.Cipher.getInstance(cipherSpec)
|
177
|
+
else
|
178
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is '#{prov.name}'"
|
179
|
+
cipher = javax.crypto.Cipher.getInstance(cipherSpec, prov)
|
180
|
+
end
|
181
|
+
else
|
182
|
+
raise GcryptoJce::Error, "Unsupported key type '#{pubKey}' for keypair data encryption operation"
|
183
|
+
end
|
184
|
+
|
185
|
+
cipher.init(javax.crypto.Cipher::ENCRYPT_MODE, pubKey)
|
186
|
+
|
187
|
+
outStream = opts[:outStream]
|
188
|
+
if not outStream.nil?
|
189
|
+
outStream.write(cipher.update(data))
|
190
|
+
outStream.write(cipher.doFinal)
|
191
|
+
outStream.flush
|
192
|
+
else
|
193
|
+
baos = java.io.ByteArrayOutputStream.new
|
194
|
+
baos.write(cipher.update(data))
|
195
|
+
baos.write(cipher.doFinal)
|
196
|
+
baos.toByteArray
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
#
|
201
|
+
# end encrypt()
|
202
|
+
#
|
203
|
+
|
204
|
+
def decrypt(opts = { })
|
205
|
+
|
206
|
+
file = opts[:enc_file]
|
207
|
+
bin = opts[:enc_bin]
|
208
|
+
|
209
|
+
# assumption:
|
210
|
+
# Data size is small (~kb)
|
211
|
+
# Because for RSA the max data = public key size
|
212
|
+
if not (file.nil? or file.empty?)
|
213
|
+
data = IoUtils.file_to_memory_byte_array(file)
|
214
|
+
elsif not bin.nil?
|
215
|
+
data = IoUtils.ensure_java_bytes(bin)
|
216
|
+
else
|
217
|
+
raise GcryptoJce::Error, "No input given for decrypt"
|
218
|
+
end
|
219
|
+
|
220
|
+
id = opts[:identity]
|
221
|
+
if id.nil?
|
222
|
+
raise GcryptoJce::Error, "No identity given for decrypt"
|
223
|
+
end
|
224
|
+
|
225
|
+
if id.privKey.nil?
|
226
|
+
raise GcryptoJce::Error, "Private key is nil for data decryption"
|
227
|
+
end
|
228
|
+
|
229
|
+
prov = GcryptoJce::Provider.handle_options(opts, nil)
|
230
|
+
|
231
|
+
cipherSpec = opts[:cipher_spec] || "RSA/ECB/OAEPWithSHA-256AndMGF1Padding"
|
232
|
+
type = Pkernel::KeyPair.key_type(id.privKey)
|
233
|
+
case type
|
234
|
+
when Pkernel::KeyPair::RSA_KEY_NAME
|
235
|
+
if prov.nil?
|
236
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is nil"
|
237
|
+
if cipherSpec =~ /PKCS5Padding/
|
238
|
+
GcryptoJce::GConf.instance.glog.warn "PKCS#1 1.5 may be vulnerable to Bleichenbacher's CCA attack"
|
239
|
+
end
|
240
|
+
GcryptoJce::GConf.instance.glog.debug "Using cipher spec '#{cipherSpec}'"
|
241
|
+
cipher = javax.crypto.Cipher.getInstance(cipherSpec)
|
242
|
+
else
|
243
|
+
GcryptoJce::GConf.instance.glog.debug "Provider is '#{prov.name}'"
|
244
|
+
cipher = javax.crypto.Cipher.getInstance(cipherSpec, prov)
|
245
|
+
end
|
246
|
+
else
|
247
|
+
raise GcryptoJce::Error, "Unsupported key type '#{id.privKey}' for data decryption operation"
|
248
|
+
end
|
249
|
+
|
250
|
+
cipher.init(javax.crypto.Cipher::DECRYPT_MODE, id.privKey)
|
251
|
+
|
252
|
+
outStream = opts[:outStream]
|
253
|
+
if not outStream.nil?
|
254
|
+
outStream.write(cipher.update(data))
|
255
|
+
outStream.write(cipher.doFinal)
|
256
|
+
outStream.flush
|
257
|
+
else
|
258
|
+
baos = java.io.ByteArrayOutputStream.new
|
259
|
+
baos.write(cipher.update(data))
|
260
|
+
baos.write(cipher.doFinal)
|
261
|
+
baos.toByteArray
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
#
|
266
|
+
# end decrypt()
|
267
|
+
#
|
268
|
+
|
269
|
+
end
|
270
|
+
# end KeyPairCrypto
|
271
|
+
#
|
272
|
+
|
273
|
+
class KeyPairCryptoEngine
|
274
|
+
extend KeyPairCrypto
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
# end gcrypto
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
require 'gcrypto'
|
3
|
+
require_relative 'secretkey'
|
4
|
+
require_relative 'error'
|
5
|
+
require_relative 'io_utils'
|
6
|
+
require_relative 'provider'
|
7
|
+
require_relative 'global'
|
8
|
+
require_relative 'converter'
|
9
|
+
|
10
|
+
module GcryptoJce
|
11
|
+
module KDF
|
12
|
+
module PBKDF2
|
13
|
+
|
14
|
+
def derive(pass, opts = { })
|
15
|
+
|
16
|
+
if pass.nil? or pass.empty?
|
17
|
+
raise GcryptoJce::Error, "Password to derive is empty"
|
18
|
+
else
|
19
|
+
if pass.is_a?(String)
|
20
|
+
pa = pass.to_java.toCharArray
|
21
|
+
elsif pass.java_kind_of?(Java::byte[])
|
22
|
+
pa = String.from_java_bytes(pass).to_java.toCharArray
|
23
|
+
elsif pass.java_kind_of?(Java::char[])
|
24
|
+
pa = pass
|
25
|
+
else
|
26
|
+
raise GcryptoJce::Error, "Unknown password type '#{pass.class}'"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
iter = opts[:iteration]
|
31
|
+
if iter.nil? or iter.to_i == 0
|
32
|
+
iter = rand(10000...20000)
|
33
|
+
end
|
34
|
+
|
35
|
+
keyLen = opts[:outKeyLen]
|
36
|
+
case keyLen
|
37
|
+
when 128
|
38
|
+
outKeyLen = 128
|
39
|
+
engSpec = "PBKDF2WithHMACSHA1"
|
40
|
+
when 256, 192
|
41
|
+
outKeyLen = 256
|
42
|
+
engSpec = "PBKDF2WithHMACSHA256"
|
43
|
+
when 512
|
44
|
+
outKeyLen = 512
|
45
|
+
engSpec = "PBKDF2WithHMACSHA512"
|
46
|
+
else
|
47
|
+
outKeyLen = 256
|
48
|
+
engSpec = "PBKDF2WithHMACSHA256"
|
49
|
+
end
|
50
|
+
|
51
|
+
prov = GcryptoJce::Provider.handle_options(opts, nil)
|
52
|
+
|
53
|
+
salt = opts[:salt]
|
54
|
+
if salt.nil?
|
55
|
+
salt = Java::byte[outKeyLen/8].new
|
56
|
+
java.security.SecureRandom.new.nextBytes(salt)
|
57
|
+
end
|
58
|
+
|
59
|
+
GcryptoJce::GConf.instance.glog.debug "PBKDF2 using config salt : #{Gcrypto::Converter.to_hex(salt)} / iteration : #{iter} / output hash length : #{outKeyLen} / Eng : #{engSpec}"
|
60
|
+
spec = javax.crypto.spec.PBEKeySpec.new(pa, salt, iter, outKeyLen)
|
61
|
+
if prov.nil?
|
62
|
+
fact = javax.crypto.SecretKeyFactory.getInstance(engSpec)
|
63
|
+
else
|
64
|
+
fact = javax.crypto.SecretKeyFactory.getInstance(engSpec, prov)
|
65
|
+
end
|
66
|
+
|
67
|
+
secKey = fact.generateSecret(spec)
|
68
|
+
key = javax.crypto.spec.SecretKeySpec.new(secKey.encoded,"AES")
|
69
|
+
|
70
|
+
res = { }
|
71
|
+
res[:key] = key
|
72
|
+
res[:hash] = secKey.getEncoded
|
73
|
+
res[:salt] = salt
|
74
|
+
res[:iteration] = iter
|
75
|
+
res[:hashLen] = outKeyLen
|
76
|
+
res[:eng] = :pbkdf2
|
77
|
+
|
78
|
+
res
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
# end PBKDF2
|
83
|
+
#
|
84
|
+
|
85
|
+
class PBKDF2Engine
|
86
|
+
extend PBKDF2
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
# end KDF
|
91
|
+
#
|
92
|
+
|
93
|
+
end
|
94
|
+
# end GcryptoJce
|
95
|
+
#
|