fast-rsa-engine 0.2.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/.gitignore +4 -0
- data/.travis.yml +18 -0
- data/Gemfile +4 -0
- data/Gemfile096 +7 -0
- data/Gemfile098 +6 -0
- data/LICENSE +22 -0
- data/README.md +50 -0
- data/Rakefile +28 -0
- data/benchmark/benchmark-with-builtin-rsa.rb +3 -0
- data/benchmark/benchmark-with-fast-rsa.rb +7 -0
- data/benchmark/benchmark.rb +54 -0
- data/benchmark/foo.pem +51 -0
- data/benchmark/foo_cert.pem +29 -0
- data/fast-rsa-engine.gemspec +36 -0
- data/lib/fast-rsa-engine.rb +32 -0
- data/lib/fast-rsa-engine_jars.rb +6 -0
- data/spec/cipher_spec.rb +45 -0
- data/spec/foo.pem +51 -0
- data/spec/foo_cert.pem +29 -0
- data/spec/security_helper_spec.rb +15 -0
- data/spec/setup.rb +15 -0
- data/spec/signature_spec.rb +38 -0
- data/src/main/java/com/github/lookout/fastrsa/FastCipherSpi.java +194 -0
- data/src/main/java/com/github/lookout/fastrsa/FastDigestSignatureSpi.java +133 -0
- data/src/main/java/org/jruby/ext/openssl/SecurityHelper.java +751 -0
- metadata +102 -0
data/spec/foo_cert.pem
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIFAjCCAuoCBFWkCjYwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCQVUxEzAR
|
3
|
+
BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5
|
4
|
+
IEx0ZDAeFw0xNTA3MTMxODU3NThaFw0xNzA3MTIxODU3NThaMEYxCzAJBgNVBAYT
|
5
|
+
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYDVQQHDAtTYW5GcmFuY2lzbzEM
|
6
|
+
MAoGA1UEAwwDZm9vMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtPhH
|
7
|
+
vStOVd29e7ElR3+uUeFYDdmVJ9OW1XQOs2QPpxSnpWbe++NNoqroCGCliRbXHYoX
|
8
|
+
+WpnbtdMDmLhSq4w4jmvL9CotFhfwhOtNY87GrEtMi2k+//OHz7hBZ5FqxEgNuC/
|
9
|
+
vCZoGFZvnGRLmtU0Q2B/7VV4zG9CiEumBdvZ6K40471XL1c2W/AOyXXcGHVAO8PY
|
10
|
+
F91CHu0gi6qB3tJ+kOrieX2cIs1W8NVc5Cz4SGwzlI8J9td9XE0Yef2aLxT021Dx
|
11
|
+
xuRubZI57/zrbR0MUoa0dKG6GTYMGw3g3gzjkcHjFLhn7FJgqxubFz2xEQWO03SI
|
12
|
+
aQj5lxF9tW9u8PKJwhiNrnIMa4JreNELlGCfqTd1eYJgvC2f6pkiNODbAqwhlALC
|
13
|
+
XKOSVBdT5ZzkP99O8vq3+mLfjYH9/0bheXKe/eeXcNoedH5xtnglQOUqGw5BwkRk
|
14
|
+
cApv/+rcs2GvhlWjCc9SmBQlu2lR7gPkUMvemHlzVtid3VKOt45/pGTkcD8iK/6Q
|
15
|
+
YmwlqmiU3Wq0NOcF5UcVqyQRCl9Vyzu280OsA8jpNRbTm/QLIxVKRwNSlSa3Et17
|
16
|
+
omk2Xi7nLQybrpEpIZ6NsTaps23Auxg3cPHNjcPVOWbUpiRsQh8JBQMAyJY/7eQ4
|
17
|
+
xtaKOS8VHrwIJVIuJWbLiXW9oBCjk6eA/kHM/FMCAwEAATANBgkqhkiG9w0BAQsF
|
18
|
+
AAOCAgEAdcnH4rVynCQWGYfR/n2XC0j34d058kfTqFuIYuTwEkY0jkIZqRBZuzwm
|
19
|
+
F88BKlRPk4qkkMXjRmvBisdcyFQiyvlf+fJlDuNKk21Dae+4P3HMz6xPK6vathKS
|
20
|
+
pAzQr9KDOp628c/8rGoxCV2hAkSyzHBC5WUwP8yBxWVG6QzqFrnUkTEo6x9S6RkG
|
21
|
+
10g7YWi594L3RoJ/gFWKuqn3sGCetrqhQfrdskS5FaCEGLrs7/h74AJnjs6aVJCZ
|
22
|
+
LklsjPJpadd+WSFIRCGQhTWQUKVc8bil9vDxfIPW5Kj/DJvirbeJinGEIc7gR9WI
|
23
|
+
bYdje5UCphWlJr5ePGreyZvyDUElgTjhiiqWlUAgG5eJYJ2NEvH4g70laIDJZYt2
|
24
|
+
PZA0egp29+EKG3U3CXJkOoW8/z4mFgReYpWh/pqZesLePh4dbBrK/WCdAprKImZc
|
25
|
+
ymOD6K5IY9A0CeN6uW9VBnI0ed8KEGagfKCpCEBgbkhq27y7FVke1/oVXkWrXIFt
|
26
|
+
WfDd1hAJwUbxAeQXEwyAqDoCyKIvDqksGW+NL2o0N9DCP5cTMgho34WeUVdAuezM
|
27
|
+
6wznqkkK9TB8mOtiJhPomlO2YL5/ShVVX0FoyQQ+ox2WMYIurbwguRVUAUNnfIdf
|
28
|
+
EqvJtVogFDekF8bxVA5RoiON8Dr8cDxSMU5eoH0Ixp3id5zsxQI=
|
29
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'setup'
|
2
|
+
|
3
|
+
describe 'SecurityHelper' do
|
4
|
+
|
5
|
+
it 'registers signatures with SecurityHelper' do
|
6
|
+
skip( 'jruby too old' ) if too_old
|
7
|
+
# clear the fast engines
|
8
|
+
engines.clear
|
9
|
+
# setup the fast engines - this creates a warning
|
10
|
+
load( "${this}/../lib/fast-rsa-engine.rb" )
|
11
|
+
|
12
|
+
expect( engines.size ).to eq( 12 )
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/spec/setup.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$: << File.expand_path( '../../lib', __FILE__ )
|
2
|
+
require 'fast-rsa-engine'
|
3
|
+
|
4
|
+
def too_old
|
5
|
+
if defined? Jopenssl::Version
|
6
|
+
Jopenssl::Version::VERSION < '0.9.6'
|
7
|
+
else
|
8
|
+
true # we are in MRI
|
9
|
+
end
|
10
|
+
end
|
11
|
+
def engines
|
12
|
+
engines = Java::OrgJrubyExtOpenssl::SecurityHelper.java_class.declared_field 'implEngines'
|
13
|
+
engines.accessible = true
|
14
|
+
engines.value( Java::OrgJrubyExtOpenssl::SecurityHelper )
|
15
|
+
end unless too_old
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative 'setup'
|
2
|
+
|
3
|
+
describe 'Signature' do
|
4
|
+
|
5
|
+
let( :this ) { File.expand_path( '..', __FILE__) }
|
6
|
+
|
7
|
+
let( :private_key ) { OpenSSL::PKey::RSA.new(File.read("#{this}/foo.pem")) }
|
8
|
+
|
9
|
+
let( :msg ) { "THIS IS A TEST" }
|
10
|
+
|
11
|
+
let( :rounds ) { 10 }
|
12
|
+
|
13
|
+
it 'is faster the regular signature' do
|
14
|
+
skip( 'jruby too old' ) if too_old
|
15
|
+
# clear the fast engines
|
16
|
+
engines.clear
|
17
|
+
|
18
|
+
start = Time.new.to_f
|
19
|
+
rounds.times do
|
20
|
+
private_key.sign(OpenSSL::Digest.new('sha512'), msg)
|
21
|
+
end
|
22
|
+
delta1 = Time.new.to_f - start
|
23
|
+
|
24
|
+
# setup the fast engines
|
25
|
+
engines.clear
|
26
|
+
# this creates a warning
|
27
|
+
load( "${this}/../lib/fast-rsa-engine.rb" )
|
28
|
+
|
29
|
+
start = Time.new.to_f
|
30
|
+
rounds.times do
|
31
|
+
private_key.sign(OpenSSL::Digest.new('sha512'), msg)
|
32
|
+
end
|
33
|
+
delta2 = Time.new.to_f - start
|
34
|
+
|
35
|
+
expect( delta1 ).to be > 2 * delta2
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
package com.github.lookout.fastrsa;
|
2
|
+
|
3
|
+
import com.squareup.crypto.rsa.NativeRSAEngine;
|
4
|
+
|
5
|
+
import java.lang.reflect.Field;
|
6
|
+
import java.security.spec.MGF1ParameterSpec;
|
7
|
+
|
8
|
+
import javax.crypto.NoSuchPaddingException;
|
9
|
+
import javax.crypto.spec.OAEPParameterSpec;
|
10
|
+
import javax.crypto.spec.PSource;
|
11
|
+
|
12
|
+
import org.bouncycastle.crypto.Digest;
|
13
|
+
import org.bouncycastle.jcajce.provider.util.DigestFactory;
|
14
|
+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
15
|
+
import org.bouncycastle.crypto.AsymmetricBlockCipher;
|
16
|
+
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
|
17
|
+
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
|
18
|
+
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
19
|
+
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
|
20
|
+
import org.bouncycastle.crypto.encodings.ISO9796d1Encoding;
|
21
|
+
import org.bouncycastle.crypto.encodings.OAEPEncoding;
|
22
|
+
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
|
23
|
+
import org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi;
|
24
|
+
import org.bouncycastle.util.Strings;
|
25
|
+
|
26
|
+
public class FastCipherSpi extends CipherSpi {
|
27
|
+
|
28
|
+
private FastCipherSpi(AsymmetricBlockCipher cipher) {
|
29
|
+
super( cipher );
|
30
|
+
}
|
31
|
+
|
32
|
+
private void initFromSpec(OAEPParameterSpec pSpec)
|
33
|
+
throws NoSuchPaddingException, NoSuchFieldException, IllegalAccessException
|
34
|
+
{
|
35
|
+
MGF1ParameterSpec mgfParams = (MGF1ParameterSpec)pSpec.getMGFParameters();
|
36
|
+
Digest digest = DigestFactory.getDigest(mgfParams.getDigestAlgorithm());
|
37
|
+
|
38
|
+
if (digest == null)
|
39
|
+
{
|
40
|
+
throw new NoSuchPaddingException("no match on OAEP constructor for digest algorithm: "+ mgfParams.getDigestAlgorithm());
|
41
|
+
}
|
42
|
+
|
43
|
+
cipher(new OAEPEncoding(new NativeRSAEngine(), digest, ((PSource.PSpecified)pSpec.getPSource()).getValue()));
|
44
|
+
set( pSpec, "paramSpec" );
|
45
|
+
}
|
46
|
+
|
47
|
+
private void cipher(AsymmetricBlockCipher cipher)
|
48
|
+
throws NoSuchFieldException, IllegalAccessException
|
49
|
+
{
|
50
|
+
set( cipher, "cipher" );
|
51
|
+
}
|
52
|
+
|
53
|
+
private void set(Object object, String name)
|
54
|
+
throws NoSuchFieldException, IllegalAccessException
|
55
|
+
{
|
56
|
+
Field field = getClass().getSuperclass().getSuperclass().getDeclaredField(name);
|
57
|
+
field.setAccessible(true);
|
58
|
+
field.set(this, object);
|
59
|
+
}
|
60
|
+
|
61
|
+
protected void engineSetPadding(
|
62
|
+
String padding)
|
63
|
+
throws NoSuchPaddingException
|
64
|
+
{
|
65
|
+
try {
|
66
|
+
String pad = Strings.toUpperCase(padding);
|
67
|
+
if (pad.equals("NOPADDING"))
|
68
|
+
{
|
69
|
+
cipher(new NativeRSAEngine());
|
70
|
+
}
|
71
|
+
else if (pad.equals("PKCS1PADDING"))
|
72
|
+
{
|
73
|
+
cipher(new PKCS1Encoding(new NativeRSAEngine()));
|
74
|
+
}
|
75
|
+
else if (pad.equals("ISO9796-1PADDING"))
|
76
|
+
{
|
77
|
+
cipher(new ISO9796d1Encoding(new NativeRSAEngine()));
|
78
|
+
}
|
79
|
+
else if (pad.equals("OAEPWITHMD5ANDMGF1PADDING"))
|
80
|
+
{
|
81
|
+
initFromSpec(new OAEPParameterSpec("MD5", "MGF1", new MGF1ParameterSpec("MD5"), PSource.PSpecified.DEFAULT));
|
82
|
+
}
|
83
|
+
else if (pad.equals("OAEPPADDING"))
|
84
|
+
{
|
85
|
+
initFromSpec(OAEPParameterSpec.DEFAULT);
|
86
|
+
}
|
87
|
+
else if (pad.equals("OAEPWITHSHA1ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-1ANDMGF1PADDING"))
|
88
|
+
{
|
89
|
+
initFromSpec(OAEPParameterSpec.DEFAULT);
|
90
|
+
}
|
91
|
+
else if (pad.equals("OAEPWITHSHA224ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-224ANDMGF1PADDING"))
|
92
|
+
{
|
93
|
+
initFromSpec(new OAEPParameterSpec("SHA-224", "MGF1", new MGF1ParameterSpec("SHA-224"), PSource.PSpecified.DEFAULT));
|
94
|
+
}
|
95
|
+
else if (pad.equals("OAEPWITHSHA256ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-256ANDMGF1PADDING"))
|
96
|
+
{
|
97
|
+
initFromSpec(new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
|
98
|
+
}
|
99
|
+
else if (pad.equals("OAEPWITHSHA384ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-384ANDMGF1PADDING"))
|
100
|
+
{
|
101
|
+
initFromSpec(new OAEPParameterSpec("SHA-384", "MGF1", MGF1ParameterSpec.SHA384, PSource.PSpecified.DEFAULT));
|
102
|
+
}
|
103
|
+
else if (pad.equals("OAEPWITHSHA512ANDMGF1PADDING") || pad.equals("OAEPWITHSHA-512ANDMGF1PADDING"))
|
104
|
+
{
|
105
|
+
initFromSpec(new OAEPParameterSpec("SHA-512", "MGF1", MGF1ParameterSpec.SHA512, PSource.PSpecified.DEFAULT));
|
106
|
+
}
|
107
|
+
else
|
108
|
+
{
|
109
|
+
throw new NoSuchPaddingException(padding + " unavailable with RSA.");
|
110
|
+
}
|
111
|
+
}
|
112
|
+
catch(NoSuchFieldException e){
|
113
|
+
System.err.println("fall back to slow engine: " + e.getMessage());
|
114
|
+
super.engineSetPadding(padding);
|
115
|
+
}
|
116
|
+
catch(IllegalAccessException e){
|
117
|
+
System.err.println("fall back to slow engine: " + e.getMessage());
|
118
|
+
super.engineSetPadding(padding);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
static public class NoPadding
|
123
|
+
extends FastCipherSpi
|
124
|
+
{
|
125
|
+
public NoPadding()
|
126
|
+
{
|
127
|
+
super(new NativeRSAEngine());
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
static public class PKCS1v1_5Padding
|
132
|
+
extends FastCipherSpi
|
133
|
+
{
|
134
|
+
public PKCS1v1_5Padding()
|
135
|
+
{
|
136
|
+
super(new PKCS1Encoding(new NativeRSAEngine()));
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
static public class PKCS1v1_5Padding_PrivateOnly
|
141
|
+
extends FastCipherSpi
|
142
|
+
{
|
143
|
+
public PKCS1v1_5Padding_PrivateOnly()
|
144
|
+
{
|
145
|
+
super(new PKCS1Encoding(new NativeRSAEngine()));
|
146
|
+
try {
|
147
|
+
engineSetMode("1");// private key only
|
148
|
+
}
|
149
|
+
catch(Exception e) {
|
150
|
+
throw new RuntimeException( "bug", e );
|
151
|
+
}
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
static public class PKCS1v1_5Padding_PublicOnly
|
156
|
+
extends FastCipherSpi
|
157
|
+
{
|
158
|
+
public PKCS1v1_5Padding_PublicOnly()
|
159
|
+
{
|
160
|
+
super(new PKCS1Encoding(new NativeRSAEngine()));
|
161
|
+
try {
|
162
|
+
engineSetMode("2");// public key only
|
163
|
+
}
|
164
|
+
catch(Exception e) {
|
165
|
+
throw new RuntimeException( "bug", e );
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
static public class OAEPPadding
|
171
|
+
extends FastCipherSpi
|
172
|
+
{
|
173
|
+
public OAEPPadding()
|
174
|
+
{
|
175
|
+
super(new NativeRSAEngine());
|
176
|
+
//super(OAEPParameterSpec.DEFAULT);
|
177
|
+
try {
|
178
|
+
engineSetPadding("OAEPPADDING");
|
179
|
+
}
|
180
|
+
catch(Exception e) {
|
181
|
+
throw new RuntimeException( "bug", e );
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
static public class ISO9796d1Padding
|
187
|
+
extends FastCipherSpi
|
188
|
+
{
|
189
|
+
public ISO9796d1Padding()
|
190
|
+
{
|
191
|
+
super(new ISO9796d1Encoding(new NativeRSAEngine()));
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
@@ -0,0 +1,133 @@
|
|
1
|
+
package com.github.lookout.fastrsa;
|
2
|
+
|
3
|
+
import com.squareup.crypto.rsa.NativeRSAEngine;
|
4
|
+
|
5
|
+
import java.lang.reflect.Field;
|
6
|
+
|
7
|
+
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
|
8
|
+
import org.bouncycastle.crypto.Digest;
|
9
|
+
import org.bouncycastle.crypto.digests.MD2Digest;
|
10
|
+
import org.bouncycastle.crypto.digests.MD4Digest;
|
11
|
+
import org.bouncycastle.crypto.digests.MD5Digest;
|
12
|
+
import org.bouncycastle.crypto.digests.NullDigest;
|
13
|
+
import org.bouncycastle.crypto.digests.RIPEMD128Digest;
|
14
|
+
import org.bouncycastle.crypto.digests.RIPEMD160Digest;
|
15
|
+
import org.bouncycastle.crypto.digests.RIPEMD256Digest;
|
16
|
+
import org.bouncycastle.crypto.digests.SHA1Digest;
|
17
|
+
import org.bouncycastle.crypto.digests.SHA224Digest;
|
18
|
+
import org.bouncycastle.crypto.digests.SHA256Digest;
|
19
|
+
import org.bouncycastle.crypto.digests.SHA384Digest;
|
20
|
+
import org.bouncycastle.crypto.digests.SHA512Digest;
|
21
|
+
import org.bouncycastle.crypto.AsymmetricBlockCipher;
|
22
|
+
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
|
23
|
+
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
|
24
|
+
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
|
25
|
+
import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
|
26
|
+
import org.bouncycastle.crypto.encodings.PKCS1Encoding;
|
27
|
+
import org.bouncycastle.jcajce.provider.asymmetric.rsa.DigestSignatureSpi;
|
28
|
+
|
29
|
+
public class FastDigestSignatureSpi extends DigestSignatureSpi {
|
30
|
+
|
31
|
+
private FastDigestSignatureSpi(ASN1ObjectIdentifier objId, Digest digest, AsymmetricBlockCipher cipher) {
|
32
|
+
super( objId, digest, cipher);
|
33
|
+
}
|
34
|
+
|
35
|
+
static public class SHA1
|
36
|
+
extends DigestSignatureSpi
|
37
|
+
{
|
38
|
+
public SHA1()
|
39
|
+
{
|
40
|
+
super(OIWObjectIdentifiers.idSHA1, new SHA1Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
static public class SHA224
|
45
|
+
extends DigestSignatureSpi
|
46
|
+
{
|
47
|
+
public SHA224()
|
48
|
+
{
|
49
|
+
super(NISTObjectIdentifiers.id_sha224, new SHA224Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
static public class SHA256
|
54
|
+
extends DigestSignatureSpi
|
55
|
+
{
|
56
|
+
public SHA256()
|
57
|
+
{
|
58
|
+
super(NISTObjectIdentifiers.id_sha256, new SHA256Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
static public class SHA384
|
63
|
+
extends DigestSignatureSpi
|
64
|
+
{
|
65
|
+
public SHA384()
|
66
|
+
{
|
67
|
+
super(NISTObjectIdentifiers.id_sha384, new SHA384Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
static public class SHA512
|
72
|
+
extends DigestSignatureSpi
|
73
|
+
{
|
74
|
+
public SHA512()
|
75
|
+
{
|
76
|
+
super(NISTObjectIdentifiers.id_sha512, new SHA512Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
static public class MD2
|
81
|
+
extends DigestSignatureSpi
|
82
|
+
{
|
83
|
+
public MD2()
|
84
|
+
{
|
85
|
+
super(PKCSObjectIdentifiers.md2, new MD2Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
static public class MD4
|
90
|
+
extends DigestSignatureSpi
|
91
|
+
{
|
92
|
+
public MD4()
|
93
|
+
{
|
94
|
+
super(PKCSObjectIdentifiers.md4, new MD4Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
static public class MD5
|
99
|
+
extends DigestSignatureSpi
|
100
|
+
{
|
101
|
+
public MD5()
|
102
|
+
{
|
103
|
+
super(PKCSObjectIdentifiers.md5, new MD5Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
static public class RIPEMD160
|
108
|
+
extends DigestSignatureSpi
|
109
|
+
{
|
110
|
+
public RIPEMD160()
|
111
|
+
{
|
112
|
+
super(TeleTrusTObjectIdentifiers.ripemd160, new RIPEMD160Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
static public class RIPEMD128
|
117
|
+
extends DigestSignatureSpi
|
118
|
+
{
|
119
|
+
public RIPEMD128()
|
120
|
+
{
|
121
|
+
super(TeleTrusTObjectIdentifiers.ripemd128, new RIPEMD128Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
static public class RIPEMD256
|
126
|
+
extends DigestSignatureSpi
|
127
|
+
{
|
128
|
+
public RIPEMD256()
|
129
|
+
{
|
130
|
+
super(TeleTrusTObjectIdentifiers.ripemd256, new RIPEMD256Digest(), new PKCS1Encoding(new NativeRSAEngine()));
|
131
|
+
}
|
132
|
+
}
|
133
|
+
}
|
@@ -0,0 +1,751 @@
|
|
1
|
+
/*
|
2
|
+
* The MIT License
|
3
|
+
*
|
4
|
+
* Copyright 2014 Karol Bucek.
|
5
|
+
*
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
11
|
+
* furnished to do so, subject to the following conditions:
|
12
|
+
*
|
13
|
+
* The above copyright notice and this permission notice shall be included in
|
14
|
+
* all copies or substantial portions of the Software.
|
15
|
+
*
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
* THE SOFTWARE.
|
23
|
+
*/
|
24
|
+
package org.jruby.ext.openssl;
|
25
|
+
|
26
|
+
import java.io.IOException;
|
27
|
+
import java.lang.reflect.Constructor;
|
28
|
+
import java.lang.reflect.Field;
|
29
|
+
import java.lang.reflect.InvocationTargetException;
|
30
|
+
import java.lang.reflect.Method;
|
31
|
+
import java.math.BigInteger;
|
32
|
+
import java.security.InvalidKeyException;
|
33
|
+
import java.security.KeyFactory;
|
34
|
+
import java.security.KeyFactorySpi;
|
35
|
+
import java.security.KeyPairGenerator;
|
36
|
+
import java.security.KeyPairGeneratorSpi;
|
37
|
+
import java.security.KeyStore;
|
38
|
+
import java.security.KeyStoreException;
|
39
|
+
import java.security.MessageDigest;
|
40
|
+
import java.security.MessageDigestSpi;
|
41
|
+
import java.security.NoSuchAlgorithmException;
|
42
|
+
import java.security.Provider;
|
43
|
+
import java.security.PublicKey;
|
44
|
+
import java.security.SecureRandom;
|
45
|
+
import java.security.SecureRandomSpi;
|
46
|
+
import java.security.Security;
|
47
|
+
import java.security.Signature;
|
48
|
+
import java.security.SignatureException;
|
49
|
+
import java.security.SignatureSpi;
|
50
|
+
import java.security.cert.CRLException;
|
51
|
+
import java.security.cert.CertificateException;
|
52
|
+
import java.security.cert.CertificateFactory;
|
53
|
+
import java.security.cert.CertificateFactorySpi;
|
54
|
+
import java.security.cert.X509CRL;
|
55
|
+
import java.security.interfaces.DSAParams;
|
56
|
+
import java.security.interfaces.DSAPublicKey;
|
57
|
+
import java.security.interfaces.RSAPublicKey;
|
58
|
+
import java.util.Locale;
|
59
|
+
import java.util.Map;
|
60
|
+
import java.util.StringTokenizer;
|
61
|
+
import java.util.concurrent.ConcurrentHashMap;
|
62
|
+
|
63
|
+
import javax.crypto.Cipher;
|
64
|
+
import javax.crypto.CipherSpi;
|
65
|
+
import javax.crypto.KeyGenerator;
|
66
|
+
import javax.crypto.KeyGeneratorSpi;
|
67
|
+
import javax.crypto.Mac;
|
68
|
+
import javax.crypto.MacSpi;
|
69
|
+
import javax.crypto.NoSuchPaddingException;
|
70
|
+
import javax.crypto.SecretKeyFactory;
|
71
|
+
import javax.crypto.SecretKeyFactorySpi;
|
72
|
+
import javax.net.ssl.SSLContext;
|
73
|
+
|
74
|
+
import com.sun.crypto.provider.CipherWithWrappingSpi;
|
75
|
+
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
|
76
|
+
import org.bouncycastle.asn1.x509.CertificateList;
|
77
|
+
import org.bouncycastle.cert.CertException;
|
78
|
+
import org.bouncycastle.cert.X509CRLHolder;
|
79
|
+
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
|
80
|
+
import org.bouncycastle.crypto.params.DSAParameters;
|
81
|
+
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
|
82
|
+
import org.bouncycastle.crypto.params.RSAKeyParameters;
|
83
|
+
import org.bouncycastle.jce.provider.X509CRLObject;
|
84
|
+
import org.bouncycastle.operator.ContentVerifierProvider;
|
85
|
+
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
|
86
|
+
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
|
87
|
+
import org.bouncycastle.operator.OperatorException;
|
88
|
+
import org.bouncycastle.operator.bc.BcDSAContentVerifierProviderBuilder;
|
89
|
+
import org.bouncycastle.operator.bc.BcRSAContentVerifierProviderBuilder;
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Java Security (and JCE) helpers.
|
93
|
+
*
|
94
|
+
* @author kares
|
95
|
+
*/
|
96
|
+
public abstract class SecurityHelper {
|
97
|
+
|
98
|
+
private static String BC_PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider";
|
99
|
+
static boolean setBouncyCastleProvider = true; // (package access for tests)
|
100
|
+
static Provider securityProvider; // 'BC' provider (package access for tests)
|
101
|
+
private static Boolean registerProvider = null;
|
102
|
+
private static final Map<String, Class> implEngines = new ConcurrentHashMap<String, Class>(16, 0.75f, 1);
|
103
|
+
|
104
|
+
public static void addCipher(String name, Class<? extends CipherSpi> clazz) {
|
105
|
+
implEngines.put("Cipher:" + name, clazz);
|
106
|
+
tryCipherInternal = true;
|
107
|
+
}
|
108
|
+
|
109
|
+
public static void addSignature(String name, Class<? extends SignatureSpi> clazz) {
|
110
|
+
implEngines.put("Signature:" + name, clazz);
|
111
|
+
}
|
112
|
+
|
113
|
+
public static Provider getSecurityProvider() {
|
114
|
+
if ( setBouncyCastleProvider && securityProvider == null ) {
|
115
|
+
synchronized(SecurityHelper.class) {
|
116
|
+
if ( setBouncyCastleProvider && securityProvider == null ) {
|
117
|
+
setBouncyCastleProvider(); setBouncyCastleProvider = false;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
doRegisterProvider();
|
122
|
+
return securityProvider;
|
123
|
+
}
|
124
|
+
|
125
|
+
public static synchronized void setSecurityProvider(final Provider provider) {
|
126
|
+
securityProvider = provider;
|
127
|
+
}
|
128
|
+
|
129
|
+
static synchronized void setBouncyCastleProvider() {
|
130
|
+
setSecurityProvider( newBouncyCastleProvider() );
|
131
|
+
}
|
132
|
+
|
133
|
+
private static Provider newBouncyCastleProvider() {
|
134
|
+
try {
|
135
|
+
return (Provider) Class.forName(BC_PROVIDER_CLASS).newInstance();
|
136
|
+
}
|
137
|
+
catch (Throwable ignored) { /* no bouncy castle available */ }
|
138
|
+
return null;
|
139
|
+
}
|
140
|
+
|
141
|
+
public static synchronized void setRegisterProvider(boolean register) {
|
142
|
+
registerProvider = Boolean.valueOf(register); doRegisterProvider();
|
143
|
+
}
|
144
|
+
|
145
|
+
static boolean isProviderAvailable(final String name) {
|
146
|
+
return Security.getProvider(name) != null;
|
147
|
+
}
|
148
|
+
|
149
|
+
static boolean isProviderRegistered() {
|
150
|
+
if ( securityProvider == null ) return false;
|
151
|
+
return Security.getProvider(securityProvider.getName()) != null;
|
152
|
+
}
|
153
|
+
|
154
|
+
private static void doRegisterProvider() {
|
155
|
+
if ( registerProvider != null ) {
|
156
|
+
synchronized(SecurityHelper.class) {
|
157
|
+
if ( registerProvider != null && registerProvider.booleanValue() ) {
|
158
|
+
if ( securityProvider != null ) {
|
159
|
+
Security.addProvider(securityProvider);
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
registerProvider = null;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
/**
|
168
|
+
* @note code calling this should not assume BC provider internals !
|
169
|
+
*/
|
170
|
+
public static CertificateFactory getCertificateFactory(final String type)
|
171
|
+
throws CertificateException {
|
172
|
+
try {
|
173
|
+
final Provider provider = getSecurityProvider();
|
174
|
+
if ( provider != null ) return getCertificateFactory(type, provider);
|
175
|
+
}
|
176
|
+
catch (CertificateException e) { }
|
177
|
+
return CertificateFactory.getInstance(type);
|
178
|
+
}
|
179
|
+
|
180
|
+
static CertificateFactory getCertificateFactory(final String type, final Provider provider)
|
181
|
+
throws CertificateException {
|
182
|
+
final CertificateFactorySpi spi = (CertificateFactorySpi) getImplEngine("CertificateFactory", type);
|
183
|
+
if ( spi == null ) throw new CertificateException(type + " not found");
|
184
|
+
return newInstance(CertificateFactory.class,
|
185
|
+
new Class[]{ CertificateFactorySpi.class, Provider.class, String.class },
|
186
|
+
new Object[]{ spi, provider, type }
|
187
|
+
);
|
188
|
+
}
|
189
|
+
|
190
|
+
/**
|
191
|
+
* @note code calling this should not assume BC provider internals !
|
192
|
+
*/
|
193
|
+
public static KeyFactory getKeyFactory(final String algorithm)
|
194
|
+
throws NoSuchAlgorithmException {
|
195
|
+
try {
|
196
|
+
final Provider provider = getSecurityProvider();
|
197
|
+
if ( provider != null ) return getKeyFactory(algorithm, provider);
|
198
|
+
}
|
199
|
+
catch (NoSuchAlgorithmException e) { }
|
200
|
+
return KeyFactory.getInstance(algorithm);
|
201
|
+
}
|
202
|
+
|
203
|
+
static KeyFactory getKeyFactory(final String algorithm, final Provider provider)
|
204
|
+
throws NoSuchAlgorithmException {
|
205
|
+
KeyFactorySpi spi = (KeyFactorySpi) getImplEngine("KeyFactory", algorithm);
|
206
|
+
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
|
207
|
+
return newInstance(KeyFactory.class,
|
208
|
+
new Class[] { KeyFactorySpi.class, Provider.class, String.class },
|
209
|
+
new Object[] { spi, provider, algorithm }
|
210
|
+
);
|
211
|
+
}
|
212
|
+
|
213
|
+
/**
|
214
|
+
* @note code calling this should not assume BC provider internals !
|
215
|
+
*/
|
216
|
+
public static KeyPairGenerator getKeyPairGenerator(final String algorithm)
|
217
|
+
throws NoSuchAlgorithmException {
|
218
|
+
try {
|
219
|
+
final Provider provider = getSecurityProvider();
|
220
|
+
if ( provider != null ) return getKeyPairGenerator(algorithm, provider);
|
221
|
+
}
|
222
|
+
catch (NoSuchAlgorithmException e) { }
|
223
|
+
return KeyPairGenerator.getInstance(algorithm);
|
224
|
+
}
|
225
|
+
|
226
|
+
@SuppressWarnings("unchecked")
|
227
|
+
static KeyPairGenerator getKeyPairGenerator(final String algorithm, final Provider provider)
|
228
|
+
throws NoSuchAlgorithmException {
|
229
|
+
final Object spi = getImplEngine("KeyPairGenerator", algorithm);
|
230
|
+
if ( spi == null ) {
|
231
|
+
throw new NoSuchAlgorithmException(algorithm + " KeyPairGenerator not available");
|
232
|
+
}
|
233
|
+
|
234
|
+
final KeyPairGenerator keyPairGenerator;
|
235
|
+
if ( spi instanceof KeyPairGenerator ) {
|
236
|
+
keyPairGenerator = (KeyPairGenerator) spi;
|
237
|
+
}
|
238
|
+
else {
|
239
|
+
final Class<? extends KeyPairGenerator> delegate;
|
240
|
+
try {
|
241
|
+
delegate = (Class<? extends KeyPairGenerator>)
|
242
|
+
Class.forName(KeyPairGenerator.class.getName() + "$Delegate");
|
243
|
+
} catch (ClassNotFoundException e) { throw new RuntimeException(e); }
|
244
|
+
|
245
|
+
keyPairGenerator = newInstance(delegate,
|
246
|
+
new Class[] { KeyPairGeneratorSpi.class, String.class }, spi, algorithm
|
247
|
+
);
|
248
|
+
}
|
249
|
+
setField(keyPairGenerator, KeyPairGenerator.class, "provider", provider);
|
250
|
+
return keyPairGenerator;
|
251
|
+
}
|
252
|
+
|
253
|
+
/**
|
254
|
+
* @note code calling this should not assume BC provider internals !
|
255
|
+
*/
|
256
|
+
public static KeyStore getKeyStore(final String type)
|
257
|
+
throws KeyStoreException {
|
258
|
+
try {
|
259
|
+
final Provider provider = getSecurityProvider();
|
260
|
+
if ( provider != null ) return getKeyStore(type, provider);
|
261
|
+
}
|
262
|
+
catch (KeyStoreException e) { }
|
263
|
+
return KeyStore.getInstance(type);
|
264
|
+
}
|
265
|
+
|
266
|
+
static KeyStore getKeyStore(final String type, final Provider provider)
|
267
|
+
throws KeyStoreException {
|
268
|
+
return KeyStore.getInstance(type, provider);
|
269
|
+
}
|
270
|
+
|
271
|
+
/**
|
272
|
+
* @note code calling this should not assume BC provider internals !
|
273
|
+
*/
|
274
|
+
public static MessageDigest getMessageDigest(final String algorithm) throws NoSuchAlgorithmException {
|
275
|
+
try {
|
276
|
+
final Provider provider = getSecurityProvider();
|
277
|
+
if ( provider != null ) return getMessageDigest(algorithm, provider);
|
278
|
+
}
|
279
|
+
catch (NoSuchAlgorithmException e) { }
|
280
|
+
return MessageDigest.getInstance(algorithm);
|
281
|
+
}
|
282
|
+
|
283
|
+
@SuppressWarnings("unchecked")
|
284
|
+
static MessageDigest getMessageDigest(final String algorithm, final Provider provider)
|
285
|
+
throws NoSuchAlgorithmException {
|
286
|
+
final Object spi = getImplEngine("MessageDigest", algorithm);
|
287
|
+
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
|
288
|
+
|
289
|
+
final MessageDigest messageDigest;
|
290
|
+
if ( spi instanceof MessageDigest ) {
|
291
|
+
messageDigest = (MessageDigest) spi;
|
292
|
+
}
|
293
|
+
else {
|
294
|
+
final Class<? extends MessageDigest> delegate;
|
295
|
+
try {
|
296
|
+
delegate = (Class<? extends MessageDigest>)
|
297
|
+
Class.forName(MessageDigest.class.getName() + "$Delegate");
|
298
|
+
} catch (ClassNotFoundException e) { throw new RuntimeException(e); }
|
299
|
+
|
300
|
+
messageDigest = newInstance(delegate,
|
301
|
+
new Class[] { MessageDigestSpi.class, String.class }, spi, algorithm
|
302
|
+
);
|
303
|
+
}
|
304
|
+
setField(messageDigest, MessageDigest.class, "provider", provider);
|
305
|
+
return messageDigest;
|
306
|
+
}
|
307
|
+
|
308
|
+
public static SecureRandom getSecureRandom() {
|
309
|
+
try {
|
310
|
+
final Provider provider = getSecurityProvider();
|
311
|
+
if ( provider != null ) {
|
312
|
+
final String algorithm = getSecureRandomAlgorithm(provider);
|
313
|
+
if ( algorithm != null ) {
|
314
|
+
return getSecureRandom(algorithm, provider);
|
315
|
+
}
|
316
|
+
}
|
317
|
+
}
|
318
|
+
catch (NoSuchAlgorithmException e) { }
|
319
|
+
return new SecureRandom(); // likely "SHA1PRNG" from SPI sun.security.provider.SecureRandom
|
320
|
+
}
|
321
|
+
|
322
|
+
private static SecureRandom getSecureRandom(final String algorithm, final Provider provider)
|
323
|
+
throws NoSuchAlgorithmException {
|
324
|
+
final SecureRandomSpi spi = (SecureRandomSpi) getImplEngine("SecureRandom", algorithm);
|
325
|
+
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
|
326
|
+
|
327
|
+
return newInstance(SecureRandom.class,
|
328
|
+
new Class[] { SecureRandomSpi.class, Provider.class, String.class },
|
329
|
+
new Object[] { spi, provider, algorithm }
|
330
|
+
);
|
331
|
+
}
|
332
|
+
|
333
|
+
// NOTE: none (at least for BC 1.47)
|
334
|
+
private static String getSecureRandomAlgorithm(final Provider provider) {
|
335
|
+
for ( Provider.Service service : provider.getServices() ) {
|
336
|
+
if ( "SecureRandom".equals( service.getType() ) ) {
|
337
|
+
return service.getAlgorithm();
|
338
|
+
}
|
339
|
+
}
|
340
|
+
return null;
|
341
|
+
}
|
342
|
+
|
343
|
+
static void debugStackTrace(Exception e){
|
344
|
+
e.printStackTrace();
|
345
|
+
}
|
346
|
+
private static Boolean tryCipherInternal = Boolean.FALSE;
|
347
|
+
|
348
|
+
/**
|
349
|
+
* @note code calling this should not assume BC provider internals !
|
350
|
+
*/
|
351
|
+
public static Cipher getCipher(final String transformation)
|
352
|
+
throws NoSuchAlgorithmException, NoSuchPaddingException {
|
353
|
+
try {
|
354
|
+
if ( tryCipherInternal == Boolean.FALSE ) {
|
355
|
+
final Provider provider = getSecurityProvider();
|
356
|
+
if ( provider != null ) {
|
357
|
+
return getCipher(transformation, provider);
|
358
|
+
}
|
359
|
+
}
|
360
|
+
}
|
361
|
+
catch (NoSuchAlgorithmException e) { }
|
362
|
+
catch (NoSuchPaddingException e) { }
|
363
|
+
catch (SecurityException e) {
|
364
|
+
// java.lang.SecurityException: JCE cannot authenticate the provider BC
|
365
|
+
if ( tryCipherInternal != null ) tryCipherInternal = Boolean.TRUE;
|
366
|
+
debugStackTrace(e);
|
367
|
+
}
|
368
|
+
if ( tryCipherInternal == Boolean.TRUE ) {
|
369
|
+
try {
|
370
|
+
final Provider provider = getSecurityProvider();
|
371
|
+
if ( provider != null ) {
|
372
|
+
return getCipherInternal(transformation, provider);
|
373
|
+
}
|
374
|
+
}
|
375
|
+
catch (NoSuchAlgorithmException e) { }
|
376
|
+
catch (RuntimeException e) {
|
377
|
+
// likely javax.crypto.JceSecurityManager.isCallerTrusted gets
|
378
|
+
// us a NPE from javax.crypto.Cipher.<init>(Cipher.java:264)
|
379
|
+
tryCipherInternal = null; // do not try BC at all
|
380
|
+
debugStackTrace(e);
|
381
|
+
}
|
382
|
+
}
|
383
|
+
return Cipher.getInstance(transformation);
|
384
|
+
}
|
385
|
+
|
386
|
+
static Cipher getCipher(final String transformation, final Provider provider)
|
387
|
+
throws NoSuchAlgorithmException, NoSuchPaddingException {
|
388
|
+
return Cipher.getInstance(transformation, provider);
|
389
|
+
}
|
390
|
+
|
391
|
+
private static final Class<?>[] STRING_PARAM = { String.class };
|
392
|
+
|
393
|
+
private static Cipher getCipherInternal(String transformation, final Provider provider)
|
394
|
+
throws NoSuchAlgorithmException {
|
395
|
+
CipherSpi spi = (CipherSpi) getImplEngine("Cipher", transformation);
|
396
|
+
if ( spi == null ) {
|
397
|
+
//
|
398
|
+
// try the long way
|
399
|
+
//
|
400
|
+
StringTokenizer tok = new StringTokenizer(transformation, "/");
|
401
|
+
final String algorithm = tok.nextToken();
|
402
|
+
|
403
|
+
spi = (CipherSpi) getImplEngine("Cipher", algorithm);
|
404
|
+
if ( spi == null ) {
|
405
|
+
// if ( silent ) return null;
|
406
|
+
throw new NoSuchAlgorithmException(transformation + " not found");
|
407
|
+
}
|
408
|
+
|
409
|
+
//
|
410
|
+
// make sure we don't get fooled by a "//" in the string
|
411
|
+
//
|
412
|
+
if ( tok.hasMoreTokens() && ! transformation.regionMatches(algorithm.length(), "//", 0, 2) ) {
|
413
|
+
// spi.engineSetMode(tok.nextToken()) :
|
414
|
+
invoke(spi, CipherSpi.class, "engineSetMode", STRING_PARAM, tok.nextToken());
|
415
|
+
}
|
416
|
+
if ( tok.hasMoreTokens() ) {
|
417
|
+
// spi.engineSetPadding(tok.nextToken()) :
|
418
|
+
invoke(spi, CipherSpi.class, "engineSetPadding", STRING_PARAM, tok.nextToken());
|
419
|
+
}
|
420
|
+
|
421
|
+
}
|
422
|
+
try {
|
423
|
+
return newInstance(Cipher.class,
|
424
|
+
new Class[] { CipherSpi.class, Provider.class, String.class },
|
425
|
+
new Object[] { spi, provider, transformation }
|
426
|
+
);
|
427
|
+
}
|
428
|
+
catch( IllegalStateException e ) {
|
429
|
+
// this can be due to trusted check in Cipher constructor
|
430
|
+
if (e.getCause().getClass() == NullPointerException.class) {
|
431
|
+
Cipher cipher = newInstance(Cipher.class,
|
432
|
+
new Class[] { CipherSpi.class, String.class },
|
433
|
+
new Object[] { spi, transformation }
|
434
|
+
);
|
435
|
+
setField(cipher, Cipher.class, "provider", provider);
|
436
|
+
return cipher;
|
437
|
+
}
|
438
|
+
throw e;
|
439
|
+
}
|
440
|
+
}
|
441
|
+
|
442
|
+
/**
|
443
|
+
* @note code calling this should not assume BC provider internals !
|
444
|
+
*/
|
445
|
+
public static Signature getSignature(final String algorithm) throws NoSuchAlgorithmException {
|
446
|
+
try {
|
447
|
+
final Provider provider = getSecurityProvider();
|
448
|
+
if ( provider != null ) return getSignature(algorithm, provider);
|
449
|
+
}
|
450
|
+
catch (NoSuchAlgorithmException e) { }
|
451
|
+
return Signature.getInstance(algorithm);
|
452
|
+
}
|
453
|
+
|
454
|
+
@SuppressWarnings("unchecked")
|
455
|
+
static Signature getSignature(final String algorithm, final Provider provider)
|
456
|
+
throws NoSuchAlgorithmException {
|
457
|
+
final Object spi = getImplEngine("Signature", algorithm);
|
458
|
+
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " Signature not available");
|
459
|
+
|
460
|
+
final Signature signature;
|
461
|
+
if ( spi instanceof Signature ) {
|
462
|
+
signature = (Signature) spi;
|
463
|
+
} else {
|
464
|
+
final Class<? extends Signature> delegate;
|
465
|
+
try {
|
466
|
+
delegate = (Class<? extends Signature>)
|
467
|
+
Class.forName(Signature.class.getName() + "$Delegate");
|
468
|
+
} catch (ClassNotFoundException e) { throw new RuntimeException(e); }
|
469
|
+
|
470
|
+
signature = newInstance(delegate,
|
471
|
+
new Class[] { SignatureSpi.class, String.class }, spi, algorithm
|
472
|
+
);
|
473
|
+
}
|
474
|
+
setField(signature, Signature.class, "provider", provider);
|
475
|
+
return signature;
|
476
|
+
}
|
477
|
+
|
478
|
+
/**
|
479
|
+
* @note code calling this should not assume BC provider internals !
|
480
|
+
*/
|
481
|
+
public static Mac getMac(final String algorithm) throws NoSuchAlgorithmException {
|
482
|
+
Mac mac = null;
|
483
|
+
final Provider provider = getSecurityProvider();
|
484
|
+
if ( provider != null ) {
|
485
|
+
mac = getMac(algorithm, provider, true);
|
486
|
+
}
|
487
|
+
if ( mac == null ) mac = Mac.getInstance(algorithm);
|
488
|
+
return mac;
|
489
|
+
}
|
490
|
+
|
491
|
+
static Mac getMac(final String algorithm, final Provider provider)
|
492
|
+
throws NoSuchAlgorithmException {
|
493
|
+
return getMac(algorithm, provider, false);
|
494
|
+
}
|
495
|
+
|
496
|
+
private static Mac getMac(final String algorithm, final Provider provider, boolean silent)
|
497
|
+
throws NoSuchAlgorithmException {
|
498
|
+
MacSpi spi = (MacSpi) getImplEngine("Mac", algorithm);
|
499
|
+
if ( spi == null ) {
|
500
|
+
if ( silent ) return null;
|
501
|
+
throw new NoSuchAlgorithmException(algorithm + " not found");
|
502
|
+
}
|
503
|
+
return newInstance(Mac.class,
|
504
|
+
new Class[] { MacSpi.class, Provider.class, String.class },
|
505
|
+
new Object[] { spi, provider, algorithm }
|
506
|
+
);
|
507
|
+
}
|
508
|
+
|
509
|
+
/**
|
510
|
+
* @note code calling this should not assume BC provider internals !
|
511
|
+
*/
|
512
|
+
public static KeyGenerator getKeyGenerator(final String algorithm) throws NoSuchAlgorithmException {
|
513
|
+
try {
|
514
|
+
final Provider provider = getSecurityProvider();
|
515
|
+
if ( provider != null ) return getKeyGenerator(algorithm, provider);
|
516
|
+
}
|
517
|
+
catch (NoSuchAlgorithmException e) { }
|
518
|
+
catch (SecurityException e) { debugStackTrace(e); }
|
519
|
+
return KeyGenerator.getInstance(algorithm);
|
520
|
+
}
|
521
|
+
|
522
|
+
static KeyGenerator getKeyGenerator(final String algorithm, final Provider provider)
|
523
|
+
throws NoSuchAlgorithmException {
|
524
|
+
final KeyGeneratorSpi spi = (KeyGeneratorSpi) getImplEngine("KeyGenerator", algorithm);
|
525
|
+
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
|
526
|
+
|
527
|
+
return newInstance(KeyGenerator.class,
|
528
|
+
new Class[] { KeyGeneratorSpi.class, Provider.class, String.class },
|
529
|
+
new Object[] { spi, provider, algorithm }
|
530
|
+
);
|
531
|
+
}
|
532
|
+
|
533
|
+
/**
|
534
|
+
* @note code calling this should not assume BC provider internals !
|
535
|
+
*/
|
536
|
+
public static SecretKeyFactory getSecretKeyFactory(final String algorithm) throws NoSuchAlgorithmException {
|
537
|
+
try {
|
538
|
+
final Provider provider = getSecurityProvider();
|
539
|
+
if ( provider != null ) return getSecretKeyFactory(algorithm, provider);
|
540
|
+
}
|
541
|
+
catch (NoSuchAlgorithmException e) { }
|
542
|
+
catch (SecurityException e) { debugStackTrace(e); }
|
543
|
+
return SecretKeyFactory.getInstance(algorithm);
|
544
|
+
}
|
545
|
+
|
546
|
+
static SecretKeyFactory getSecretKeyFactory(final String algorithm, final Provider provider)
|
547
|
+
throws NoSuchAlgorithmException {
|
548
|
+
final SecretKeyFactorySpi spi = (SecretKeyFactorySpi) getImplEngine("SecretKeyFactory", algorithm);
|
549
|
+
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
|
550
|
+
|
551
|
+
return newInstance(SecretKeyFactory.class,
|
552
|
+
new Class[] { SecretKeyFactorySpi.class, Provider.class, String.class },
|
553
|
+
new Object[] { spi, provider, algorithm }
|
554
|
+
);
|
555
|
+
}
|
556
|
+
|
557
|
+
private static boolean providerSSLContext = false; // BC does not implement + JDK default is fine
|
558
|
+
|
559
|
+
public static SSLContext getSSLContext(final String protocol)
|
560
|
+
throws NoSuchAlgorithmException {
|
561
|
+
try {
|
562
|
+
if ( providerSSLContext ) {
|
563
|
+
final Provider provider = getSecurityProvider();
|
564
|
+
if ( provider != null ) {
|
565
|
+
return getSSLContext(protocol, provider);
|
566
|
+
}
|
567
|
+
}
|
568
|
+
}
|
569
|
+
catch (NoSuchAlgorithmException e) { }
|
570
|
+
return SSLContext.getInstance(protocol);
|
571
|
+
}
|
572
|
+
|
573
|
+
private static SSLContext getSSLContext(final String protocol, final Provider provider)
|
574
|
+
throws NoSuchAlgorithmException {
|
575
|
+
return SSLContext.getInstance(protocol, provider);
|
576
|
+
}
|
577
|
+
|
578
|
+
public static boolean verify(final X509CRL crl, final PublicKey publicKey)
|
579
|
+
throws NoSuchAlgorithmException, CRLException, InvalidKeyException, SignatureException {
|
580
|
+
return verify(crl, publicKey, false);
|
581
|
+
}
|
582
|
+
|
583
|
+
static boolean verify(final X509CRL crl, final PublicKey publicKey, final boolean silent)
|
584
|
+
throws NoSuchAlgorithmException, CRLException, InvalidKeyException, SignatureException {
|
585
|
+
|
586
|
+
if ( crl instanceof X509CRLObject ) {
|
587
|
+
final CertificateList crlList = (CertificateList) getCertificateList(crl);
|
588
|
+
final AlgorithmIdentifier tbsSignatureId = crlList.getTBSCertList().getSignature();
|
589
|
+
if ( ! crlList.getSignatureAlgorithm().equals(tbsSignatureId) ) {
|
590
|
+
if ( silent ) return false;
|
591
|
+
throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
|
592
|
+
}
|
593
|
+
|
594
|
+
final Signature signature = getSignature(crl.getSigAlgName(), securityProvider);
|
595
|
+
|
596
|
+
signature.initVerify(publicKey);
|
597
|
+
signature.update(crl.getTBSCertList());
|
598
|
+
|
599
|
+
if ( ! signature.verify( crl.getSignature() ) ) {
|
600
|
+
if ( silent ) return false;
|
601
|
+
throw new SignatureException("CRL does not verify with supplied public key.");
|
602
|
+
}
|
603
|
+
return true;
|
604
|
+
}
|
605
|
+
else {
|
606
|
+
try {
|
607
|
+
final DigestAlgorithmIdentifierFinder digestAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
|
608
|
+
final ContentVerifierProvider verifierProvider;
|
609
|
+
if ( "DSA".equalsIgnoreCase( publicKey.getAlgorithm() )) {
|
610
|
+
BigInteger y = ((DSAPublicKey) publicKey).getY();
|
611
|
+
DSAParams params = ((DSAPublicKey) publicKey).getParams();
|
612
|
+
DSAParameters parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
|
613
|
+
AsymmetricKeyParameter dsaKey = new DSAPublicKeyParameters(y, parameters);
|
614
|
+
verifierProvider = new BcDSAContentVerifierProviderBuilder(digestAlgFinder).build(dsaKey);
|
615
|
+
}
|
616
|
+
else {
|
617
|
+
BigInteger mod = ((RSAPublicKey) publicKey).getModulus();
|
618
|
+
BigInteger exp = ((RSAPublicKey) publicKey).getPublicExponent();
|
619
|
+
AsymmetricKeyParameter rsaKey = new RSAKeyParameters(false, mod, exp);
|
620
|
+
verifierProvider = new BcRSAContentVerifierProviderBuilder(digestAlgFinder).build(rsaKey);
|
621
|
+
}
|
622
|
+
return new X509CRLHolder(crl.getEncoded()).isSignatureValid( verifierProvider );
|
623
|
+
}
|
624
|
+
catch (OperatorException e) {
|
625
|
+
throw new SignatureException(e);
|
626
|
+
}
|
627
|
+
catch (CertException e) {
|
628
|
+
throw new SignatureException(e);
|
629
|
+
}
|
630
|
+
// can happen if the input is DER but does not match expected strucure
|
631
|
+
catch (ClassCastException e) {
|
632
|
+
throw new SignatureException(e);
|
633
|
+
}
|
634
|
+
catch (IOException e) {
|
635
|
+
throw new SignatureException(e);
|
636
|
+
}
|
637
|
+
}
|
638
|
+
}
|
639
|
+
|
640
|
+
private static Object getCertificateList(final Object crl) { // X509CRLObject
|
641
|
+
try { // private CertificateList c;
|
642
|
+
final Field cField = X509CRLObject.class.getDeclaredField("c");
|
643
|
+
cField.setAccessible(true);
|
644
|
+
return cField.get(crl);
|
645
|
+
}
|
646
|
+
catch (NoSuchFieldException e) {
|
647
|
+
debugStackTrace(e); return null;
|
648
|
+
}
|
649
|
+
catch (IllegalAccessException e) { return null; }
|
650
|
+
catch (SecurityException e) { return null; }
|
651
|
+
}
|
652
|
+
|
653
|
+
// these are BC JCE (@see javax.crypto.JCEUtil) inspired internals :
|
654
|
+
// https://github.com/bcgit/bc-java/blob/master/jce/src/main/java/javax/crypto/JCEUtil.java
|
655
|
+
|
656
|
+
private static Object getImplEngine(String baseName, String algorithm) {
|
657
|
+
Object engine = findImplEngine(baseName, algorithm.toUpperCase(Locale.ENGLISH));
|
658
|
+
if (engine == null) {
|
659
|
+
engine = findImplEngine(baseName, algorithm);
|
660
|
+
}
|
661
|
+
return engine;
|
662
|
+
}
|
663
|
+
|
664
|
+
private static Object findImplEngine(final String baseName, String algorithm) {
|
665
|
+
Class implEngineClass = implEngines.get(baseName + ":" + algorithm);
|
666
|
+
|
667
|
+
if (implEngineClass == null) {
|
668
|
+
final Provider bcProvider = securityProvider;
|
669
|
+
String alias;
|
670
|
+
while ((alias = bcProvider.getProperty("Alg.Alias." + baseName + "." + algorithm)) != null) {
|
671
|
+
algorithm = alias;
|
672
|
+
}
|
673
|
+
final String className = bcProvider.getProperty(baseName + "." + algorithm);
|
674
|
+
if (className != null) {
|
675
|
+
try {
|
676
|
+
ClassLoader loader = bcProvider.getClass().getClassLoader();
|
677
|
+
if (loader != null) {
|
678
|
+
implEngineClass = loader.loadClass(className);
|
679
|
+
} else {
|
680
|
+
implEngineClass = Class.forName(className);
|
681
|
+
}
|
682
|
+
implEngineClass.newInstance(); // this instance is thrown away to test newInstance, but only once
|
683
|
+
} catch (ClassNotFoundException e) {
|
684
|
+
throw new IllegalStateException("algorithm " + algorithm + " in provider " + bcProvider.getName() + " but no class \"" + className + "\" found!");
|
685
|
+
} catch (Exception e) {
|
686
|
+
throw new IllegalStateException("algorithm " + algorithm + " in provider " + bcProvider.getName() + " but class \"" + className + "\" inaccessible!");
|
687
|
+
}
|
688
|
+
} else {
|
689
|
+
return null;
|
690
|
+
}
|
691
|
+
|
692
|
+
implEngines.put(baseName + ":" + algorithm, implEngineClass);
|
693
|
+
}
|
694
|
+
|
695
|
+
try {
|
696
|
+
return implEngineClass.newInstance();
|
697
|
+
} catch (Exception e) {
|
698
|
+
final Provider bcProvider = securityProvider;
|
699
|
+
String className = implEngineClass.getName();
|
700
|
+
throw new IllegalStateException("algorithm " + algorithm + " in provider " + bcProvider.getName() + " but class \"" + className + "\" inaccessible!");
|
701
|
+
}
|
702
|
+
}
|
703
|
+
|
704
|
+
// the obligratory "reflection crap" :
|
705
|
+
|
706
|
+
private static <T> T newInstance(Class<T> klass, Class<?>[] paramTypes, Object... params) {
|
707
|
+
final Constructor<T> constructor;
|
708
|
+
try {
|
709
|
+
constructor = klass.getDeclaredConstructor(paramTypes);
|
710
|
+
constructor.setAccessible(true);
|
711
|
+
return constructor.newInstance(params);
|
712
|
+
} catch (NoSuchMethodException e) {
|
713
|
+
throw new IllegalStateException(e.getMessage(), e);
|
714
|
+
} catch (InvocationTargetException e) {
|
715
|
+
throw new IllegalStateException(e.getTargetException());
|
716
|
+
} catch (InstantiationException e) {
|
717
|
+
throw new IllegalStateException(e);
|
718
|
+
} catch (IllegalAccessException e) {
|
719
|
+
throw new IllegalStateException(e);
|
720
|
+
}
|
721
|
+
}
|
722
|
+
|
723
|
+
@SuppressWarnings("unchecked")
|
724
|
+
private static <T> T invoke(Object object, Class<?> klass, String methodName, Class<?>[] paramTypes, Object... params) {
|
725
|
+
final Method method;
|
726
|
+
try {
|
727
|
+
method = klass.getDeclaredMethod(methodName, paramTypes);
|
728
|
+
method.setAccessible(true);
|
729
|
+
return (T) method.invoke(object, params);
|
730
|
+
} catch (NoSuchMethodException e) {
|
731
|
+
throw new IllegalStateException(e.getMessage(), e);
|
732
|
+
} catch (InvocationTargetException e) {
|
733
|
+
throw new IllegalStateException(e.getTargetException());
|
734
|
+
} catch (IllegalAccessException e) {
|
735
|
+
throw new IllegalStateException(e);
|
736
|
+
}
|
737
|
+
}
|
738
|
+
|
739
|
+
private static void setField(Object obj, Class<?> fieldOwner, String fieldName, Object value) {
|
740
|
+
final Field field;
|
741
|
+
try {
|
742
|
+
field = fieldOwner.getDeclaredField(fieldName);
|
743
|
+
field.setAccessible(true);
|
744
|
+
field.set(obj, value);
|
745
|
+
} catch (NoSuchFieldException e) {
|
746
|
+
throw new IllegalStateException("no field '" + fieldName + "' declared in " + fieldOwner + "", e);
|
747
|
+
} catch (IllegalAccessException e) {
|
748
|
+
throw new IllegalStateException(e);
|
749
|
+
}
|
750
|
+
}
|
751
|
+
}
|