/*
* The MIT License
*
* Copyright 2014 Karol Bucek.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.jruby.ext.openssl;
import static org.jruby.ext.openssl.OpenSSL.debugStackTrace;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyFactorySpi;
import java.security.KeyPairGenerator;
import java.security.KeyPairGeneratorSpi;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.MessageDigestSpi;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.SignatureSpi;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateFactorySpi;
import java.security.cert.X509CRL;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.KeyAgreement;
import javax.crypto.KeyAgreementSpi;
import javax.crypto.KeyGenerator;
import javax.crypto.KeyGeneratorSpi;
import javax.crypto.Mac;
import javax.crypto.MacSpi;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKeyFactorySpi;
import javax.net.ssl.SSLContext;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.CertificateList;
import org.bouncycastle.cert.CertException;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.jce.provider.X509CRLObject;
import org.bouncycastle.operator.ContentVerifierProvider;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorException;
import org.bouncycastle.operator.bc.BcDSAContentVerifierProviderBuilder;
import org.bouncycastle.operator.bc.BcRSAContentVerifierProviderBuilder;
/**
* Java Security (and JCE) helpers.
*
* @author kares
*/
public abstract class SecurityHelper {
private static String BC_PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider";
private static String BC_PROVIDER_NAME = "BC";
static boolean setBouncyCastleProvider = true; // (package access for tests)
static Provider securityProvider; // 'BC' provider (package access for tests)
private static volatile Boolean registerProvider = null;
static final Map<String, Class> implEngines = new ConcurrentHashMap<String, Class>(16, 0.75f, 1);
/**
* inject under a given name a cipher. also ensures that the registered
* classes are getting used.
*
* @param name the name under which the class gets registered
* @param clazz the CipherSpi class
*/
public static void addCipher(String name, Class<? extends CipherSpi> clazz) {
implEngines.put("Cipher:" + name, clazz);
tryCipherInternal = true;
}
/**
* inject under a given name a signature
*
* @param name the name under which the class gets registered
* @param clazz the SignaturSpi class
*/
public static void addSignature(String name, Class<? extends SignatureSpi> clazz) {
implEngines.put("Signature:" + name, clazz);
}
public static Provider getSecurityProvider() {
if ( setBouncyCastleProvider && securityProvider == null ) {
synchronized(SecurityHelper.class) {
if ( setBouncyCastleProvider && securityProvider == null ) {
setBouncyCastleProvider(); setBouncyCastleProvider = false;
}
}
}
doRegisterProvider();
return securityProvider;
}
static final boolean SPI_ACCESSIBLE;
static {
boolean canSetAccessible = true;
if ( OpenSSL.javaVersion9(true) ) {
final Provider provider = getSecurityProvider();
if ( provider != null ) {
try {
// NOTE: some getXxx pieces might still work
// where SPI are returned directly + there's a public <init> e.g. MessageDigest(...)
getCertificateFactory("X.509", provider); // !!! disables EVERYTHING :(
}
catch (CertificateException ex) {
debugStackTrace(ex);
canSetAccessible = false;
}
catch (RuntimeException ex) {
debugStackTrace(ex);
// java.lang.reflect.InaccessibleObjectException (extends RuntimeException)
canSetAccessible = false;
}
}
}
SPI_ACCESSIBLE = canSetAccessible;
}
static Provider getSecurityProviderIfAccessible() {
return SPI_ACCESSIBLE ? getSecurityProvider() : null;
}
public static synchronized void setSecurityProvider(final Provider provider) {
if ( provider != null ) OpenSSL.debug("using provider: " + provider);
securityProvider = provider;
}
static synchronized void setBouncyCastleProvider() {
setSecurityProvider( newBouncyCastleProvider() );
}
private static Provider newBouncyCastleProvider() {
try {
return (Provider) Class.forName(BC_PROVIDER_CLASS).newInstance();
}
catch (Throwable ignored) { /* no bouncy castle available */ }
return null;
}
public static synchronized void setRegisterProvider(final boolean register) {
registerProvider = Boolean.valueOf(register);
if ( register ) getSecurityProvider(); // so that securityProvider != null
// getSecurityProvider does doRegisterProvider();
}
static boolean isProviderAvailable(final String name) {
return Security.getProvider(name) != null;
}
public static boolean isProviderRegistered() {
if ( securityProvider == null ) return false;
return Security.getProvider(securityProvider.getName()) != null;
}
private static void doRegisterProvider() {
if ( registerProvider != null ) {
synchronized(SecurityHelper.class) {
final Boolean register = registerProvider;
if ( register != null && register.booleanValue() ) {
if ( securityProvider != null ) {
Security.addProvider(securityProvider);
registerProvider = null;
}
}
}
}
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static CertificateFactory getCertificateFactory(final String type)
throws CertificateException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getCertificateFactory(type, provider);
}
catch (CertificateException e) { debugStackTrace(e); }
return CertificateFactory.getInstance(type);
}
static CertificateFactory getCertificateFactory(final String type, final Provider provider)
throws CertificateException {
final CertificateFactorySpi spi;
boolean addedBC = false;
synchronized(SecurityHelper.class) {
try { // TODO fixed since BC 1.55 (only needed on 1.54) and should be removed eventually ...
if (provider.getName().equals(BC_PROVIDER_NAME) && Security.getProvider(BC_PROVIDER_NAME) == null) {
Security.addProvider(provider);
addedBC = true;
}
spi = (CertificateFactorySpi) getImplEngine("CertificateFactory", type);
} finally {
if (addedBC) {
Security.removeProvider(BC_PROVIDER_NAME);
}
}
}
if ( spi == null ) throw new CertificateException(type + " not found");
return newInstance(CertificateFactory.class,
new Class[]{ CertificateFactorySpi.class, Provider.class, String.class },
new Object[]{ spi, provider, type }
);
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static KeyFactory getKeyFactory(final String algorithm)
throws NoSuchAlgorithmException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getKeyFactory(algorithm, provider);
}
catch (NoSuchAlgorithmException e) { }
return KeyFactory.getInstance(algorithm);
}
static KeyFactory getKeyFactory(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
KeyFactorySpi spi = (KeyFactorySpi) getImplEngine("KeyFactory", algorithm);
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
return newInstance(KeyFactory.class,
new Class[] { KeyFactorySpi.class, Provider.class, String.class },
new Object[] { spi, provider, algorithm }
);
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static KeyPairGenerator getKeyPairGenerator(final String algorithm)
throws NoSuchAlgorithmException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getKeyPairGenerator(algorithm, provider);
}
catch (NoSuchAlgorithmException e) { }
return KeyPairGenerator.getInstance(algorithm);
}
@SuppressWarnings("unchecked")
static KeyPairGenerator getKeyPairGenerator(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
final Object spi = getImplEngine("KeyPairGenerator", algorithm);
if ( spi == null ) {
throw new NoSuchAlgorithmException(algorithm + " KeyPairGenerator not available");
}
final KeyPairGenerator keyPairGenerator;
if ( spi instanceof KeyPairGenerator ) {
keyPairGenerator = (KeyPairGenerator) spi;
}
else {
final Class<? extends KeyPairGenerator> delegate;
try {
delegate = (Class<? extends KeyPairGenerator>)
Class.forName(KeyPairGenerator.class.getName() + "$Delegate");
} catch (ClassNotFoundException e) { throw new RuntimeException(e); }
keyPairGenerator = newInstance(delegate,
new Class[] { KeyPairGeneratorSpi.class, String.class }, spi, algorithm
);
}
setField(keyPairGenerator, KeyPairGenerator.class, "provider", provider);
return keyPairGenerator;
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static KeyStore getKeyStore(final String type)
throws KeyStoreException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getKeyStore(type, provider);
}
catch (KeyStoreException e) { }
return KeyStore.getInstance(type);
}
static KeyStore getKeyStore(final String type, final Provider provider)
throws KeyStoreException {
return KeyStore.getInstance(type, provider);
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static MessageDigest getMessageDigest(final String algorithm) throws NoSuchAlgorithmException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getMessageDigest(algorithm, provider);
}
catch (NoSuchAlgorithmException e) { }
return MessageDigest.getInstance(algorithm);
}
@SuppressWarnings("unchecked")
static MessageDigest getMessageDigest(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
final Object spi = getImplEngine("MessageDigest", algorithm);
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
final MessageDigest messageDigest;
if ( spi instanceof MessageDigest ) {
messageDigest = (MessageDigest) spi;
}
else {
final Class<? extends MessageDigest> delegate;
try {
delegate = (Class<? extends MessageDigest>)
Class.forName(MessageDigest.class.getName() + "$Delegate");
} catch (ClassNotFoundException e) { throw new RuntimeException(e); }
messageDigest = newInstance(delegate,
new Class[] { MessageDigestSpi.class, String.class }, spi, algorithm
);
}
setField(messageDigest, MessageDigest.class, "provider", provider);
return messageDigest;
}
public static SecureRandom getSecureRandom() {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) {
final String algorithm = getSecureRandomAlgorithm(provider);
if ( algorithm != null ) {
return getSecureRandom(algorithm, provider);
}
}
}
catch (NoSuchAlgorithmException e) { }
return new SecureRandom(); // likely "SHA1PRNG" from SPI sun.security.provider.SecureRandom
}
private static SecureRandom getSecureRandom(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
final SecureRandomSpi spi = (SecureRandomSpi) getImplEngine("SecureRandom", algorithm);
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
return newInstance(SecureRandom.class,
new Class[] { SecureRandomSpi.class, Provider.class, String.class },
new Object[] { spi, provider, algorithm }
);
}
// NOTE: none (at least for BC 1.47)
private static String getSecureRandomAlgorithm(final Provider provider) {
for ( Provider.Service service : provider.getServices() ) {
if ( "SecureRandom".equals( service.getType() ) ) {
return service.getAlgorithm();
}
}
return null;
}
private static Boolean tryCipherInternal = Boolean.FALSE;
/**
* @note code calling this should not assume BC provider internals !
*/
public static Cipher getCipher(final String transformation)
throws NoSuchAlgorithmException, NoSuchPaddingException {
try {
if ( tryCipherInternal == Boolean.FALSE ) {
final Provider provider = getSecurityProvider();
if ( provider != null ) {
return getCipher(transformation, provider);
}
}
}
catch (NoSuchAlgorithmException e) { }
catch (NoSuchPaddingException e) { }
catch (SecurityException e) {
// java.lang.SecurityException: JCE cannot authenticate the provider BC
if ( tryCipherInternal != null ) tryCipherInternal = Boolean.TRUE;
debugStackTrace(e);
}
if ( tryCipherInternal == Boolean.TRUE ) {
try {
final Provider provider = getSecurityProvider();
if ( provider != null ) {
return getCipherInternal(transformation, provider);
}
}
catch (NoSuchAlgorithmException e) { }
catch (RuntimeException e) {
// likely javax.crypto.JceSecurityManager.isCallerTrusted gets
// us a NPE from javax.crypto.Cipher.<init>(Cipher.java:264)
tryCipherInternal = null; // do not try BC at all
debugStackTrace(e);
}
}
return Cipher.getInstance(transformation);
}
static Cipher getCipher(final String transformation, final Provider provider)
throws NoSuchAlgorithmException, NoSuchPaddingException {
return Cipher.getInstance(transformation, provider);
}
private static final Class<?>[] STRING_PARAM = { String.class };
private static Cipher getCipherInternal(String transformation, final Provider provider)
throws NoSuchAlgorithmException {
CipherSpi spi = (CipherSpi) getImplEngine("Cipher", transformation);
if ( spi == null ) {
//
// try the long way
//
StringTokenizer tok = new StringTokenizer(transformation, "/");
final String algorithm = tok.nextToken();
spi = (CipherSpi) getImplEngine("Cipher", algorithm);
if ( spi == null ) {
// if ( silent ) return null;
throw new NoSuchAlgorithmException(transformation + " not found");
}
//
// make sure we don't get fooled by a "//" in the string
//
if ( tok.hasMoreTokens() && ! transformation.regionMatches(algorithm.length(), "//", 0, 2) ) {
// spi.engineSetMode(tok.nextToken()) :
invoke(spi, CipherSpi.class, "engineSetMode", STRING_PARAM, tok.nextToken());
}
if ( tok.hasMoreTokens() ) {
// spi.engineSetPadding(tok.nextToken()) :
invoke(spi, CipherSpi.class, "engineSetPadding", STRING_PARAM, tok.nextToken());
}
}
try {
// this constructor does not verify the provider
Cipher cipher = newInstance(Cipher.class,
new Class[] { CipherSpi.class, String.class },
new Object[] { spi, transformation }
);
setField(cipher, Cipher.class, "provider", provider);
return cipher;
}
catch( Exception e ) {
// this constructor does verify the provider which might fail
return newInstance(Cipher.class,
new Class[] { CipherSpi.class, Provider.class, String.class },
new Object[] { spi, provider, transformation }
);
}
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static Signature getSignature(final String algorithm) throws NoSuchAlgorithmException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getSignature(algorithm, provider);
}
catch (NoSuchAlgorithmException e) { }
return Signature.getInstance(algorithm);
}
@SuppressWarnings("unchecked")
static Signature getSignature(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
final Object spi = getImplEngine("Signature", algorithm);
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " Signature not available");
final Signature signature;
if ( spi instanceof Signature ) {
signature = (Signature) spi;
} else {
final Class<? extends Signature> delegate;
try {
delegate = (Class<? extends Signature>)
Class.forName(Signature.class.getName() + "$Delegate");
} catch (ClassNotFoundException e) { throw new RuntimeException(e); }
signature = newInstance(delegate,
new Class[] { SignatureSpi.class, String.class }, spi, algorithm
);
}
setField(signature, Signature.class, "provider", provider);
return signature;
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static Mac getMac(final String algorithm) throws NoSuchAlgorithmException {
Mac mac = null;
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) {
mac = getMac(algorithm, provider, true);
}
if ( mac == null ) mac = Mac.getInstance(algorithm);
return mac;
}
static Mac getMac(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
return getMac(algorithm, provider, false);
}
private static Mac getMac(final String algorithm, final Provider provider, boolean silent)
throws NoSuchAlgorithmException {
MacSpi spi = (MacSpi) getImplEngine("Mac", algorithm);
if ( spi == null ) {
if ( silent ) return null;
throw new NoSuchAlgorithmException(algorithm + " not found");
}
return newInstance(Mac.class,
new Class[] { MacSpi.class, Provider.class, String.class },
new Object[] { spi, provider, algorithm }
);
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static KeyGenerator getKeyGenerator(final String algorithm) throws NoSuchAlgorithmException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getKeyGenerator(algorithm, provider);
}
catch (NoSuchAlgorithmException e) { }
catch (SecurityException e) { debugStackTrace(e); }
return KeyGenerator.getInstance(algorithm);
}
static KeyGenerator getKeyGenerator(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
final KeyGeneratorSpi spi = (KeyGeneratorSpi) getImplEngine("KeyGenerator", algorithm);
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
return newInstance(KeyGenerator.class,
new Class[] { KeyGeneratorSpi.class, Provider.class, String.class },
new Object[] { spi, provider, algorithm }
);
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static KeyAgreement getKeyAgreement(final String algorithm) throws NoSuchAlgorithmException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getKeyAgreement(algorithm, provider);
}
catch (NoSuchAlgorithmException e) { }
catch (SecurityException e) { debugStackTrace(e); }
return KeyAgreement.getInstance(algorithm);
}
static KeyAgreement getKeyAgreement(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
final KeyAgreementSpi spi = (KeyAgreementSpi) getImplEngine("KeyAgreement", algorithm);
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
return newInstance(KeyAgreement.class,
new Class[] { KeyAgreementSpi.class, Provider.class, String.class },
new Object[] { spi, provider, algorithm }
);
}
/**
* @note code calling this should not assume BC provider internals !
*/
public static SecretKeyFactory getSecretKeyFactory(final String algorithm) throws NoSuchAlgorithmException {
try {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) return getSecretKeyFactory(algorithm, provider);
}
catch (NoSuchAlgorithmException e) { }
catch (SecurityException e) { debugStackTrace(e); }
return SecretKeyFactory.getInstance(algorithm);
}
static SecretKeyFactory getSecretKeyFactory(final String algorithm, final Provider provider)
throws NoSuchAlgorithmException {
final SecretKeyFactorySpi spi = (SecretKeyFactorySpi) getImplEngine("SecretKeyFactory", algorithm);
if ( spi == null ) throw new NoSuchAlgorithmException(algorithm + " not found");
return newInstance(SecretKeyFactory.class,
new Class[] { SecretKeyFactorySpi.class, Provider.class, String.class },
new Object[] { spi, provider, algorithm }
);
}
private static boolean providerSSLContext = false; // BC does not implement + JDK default is fine
public static SSLContext getSSLContext(final String protocol)
throws NoSuchAlgorithmException {
try {
if ( providerSSLContext ) {
final Provider provider = getSecurityProviderIfAccessible();
if ( provider != null ) {
return getSSLContext(protocol, provider);
}
}
}
catch (NoSuchAlgorithmException e) { }
return SSLContext.getInstance(protocol);
}
private static SSLContext getSSLContext(final String protocol, final Provider provider)
throws NoSuchAlgorithmException {
return SSLContext.getInstance(protocol, provider);
}
public static boolean verify(final X509CRL crl, final PublicKey publicKey)
throws NoSuchAlgorithmException, CRLException, InvalidKeyException, SignatureException {
return verify(crl, publicKey, false);
}
static boolean verify(final X509CRL crl, final PublicKey publicKey, final boolean silent)
throws NoSuchAlgorithmException, CRLException, InvalidKeyException, SignatureException {
if ( crl instanceof X509CRLObject ) {
final CertificateList crlList = (CertificateList) getCertificateList(crl);
final AlgorithmIdentifier tbsSignatureId = crlList.getTBSCertList().getSignature();
if ( ! crlList.getSignatureAlgorithm().equals(tbsSignatureId) ) {
if ( silent ) return false;
throw new CRLException("Signature algorithm on CertificateList does not match TBSCertList.");
}
final Signature signature = getSignature(crl.getSigAlgName(), securityProvider);
signature.initVerify(publicKey);
signature.update(crl.getTBSCertList());
if ( ! signature.verify( crl.getSignature() ) ) {
if ( silent ) return false;
throw new SignatureException("CRL does not verify with supplied public key.");
}
return true;
}
else {
try {
final DigestAlgorithmIdentifierFinder digestAlgFinder = new DefaultDigestAlgorithmIdentifierFinder();
final ContentVerifierProvider verifierProvider;
if ( "DSA".equalsIgnoreCase( publicKey.getAlgorithm() )) {
BigInteger y = ((DSAPublicKey) publicKey).getY();
DSAParams params = ((DSAPublicKey) publicKey).getParams();
DSAParameters parameters = new DSAParameters(params.getP(), params.getQ(), params.getG());
AsymmetricKeyParameter dsaKey = new DSAPublicKeyParameters(y, parameters);
verifierProvider = new BcDSAContentVerifierProviderBuilder(digestAlgFinder).build(dsaKey);
}
else {
BigInteger mod = ((RSAPublicKey) publicKey).getModulus();
BigInteger exp = ((RSAPublicKey) publicKey).getPublicExponent();
AsymmetricKeyParameter rsaKey = new RSAKeyParameters(false, mod, exp);
verifierProvider = new BcRSAContentVerifierProviderBuilder(digestAlgFinder).build(rsaKey);
}
return new X509CRLHolder(crl.getEncoded()).isSignatureValid( verifierProvider );
}
catch (OperatorException e) {
throw new SignatureException(e);
}
catch (CertException e) {
throw new SignatureException(e);
}
// can happen if the input is DER but does not match expected strucure
catch (ClassCastException e) {
throw new SignatureException(e);
}
catch (IOException e) {
throw new SignatureException(e);
}
}
}
private static Object getCertificateList(final Object crl) { // X509CRLObject
try { // private CertificateList c;
final Field cField = X509CRLObject.class.getDeclaredField("c");
cField.setAccessible(true);
return cField.get(crl);
}
catch (NoSuchFieldException e) {
debugStackTrace(e); return null;
}
catch (IllegalAccessException e) { return null; }
catch (SecurityException e) { return null; }
}
// these are BC JCE (@see javax.crypto.JCEUtil) inspired internals :
// https://github.com/bcgit/bc-java/blob/master/jce/src/main/java/javax/crypto/JCEUtil.java
private static Object getImplEngine(String baseName, String algorithm) {
Object engine = findImplEngine(baseName, algorithm.toUpperCase(Locale.ENGLISH));
if (engine == null) {
engine = findImplEngine(baseName, algorithm);
}
return engine;
}
private static Object findImplEngine(final String baseName, String algorithm) {
Class implEngineClass = implEngines.get(baseName + ":" + algorithm);
if (implEngineClass == null) {
final Provider bcProvider = securityProvider;
String alias;
while ((alias = bcProvider.getProperty("Alg.Alias." + baseName + "." + algorithm)) != null) {
algorithm = alias;
}
final String className = bcProvider.getProperty(baseName + "." + algorithm);
if (className != null) {
try {
ClassLoader loader = bcProvider.getClass().getClassLoader();
if (loader != null) {
implEngineClass = loader.loadClass(className);
} else {
implEngineClass = Class.forName(className);
}
implEngineClass.newInstance(); // this instance is thrown away to test newInstance, but only once
} catch (ClassNotFoundException e) {
throw new IllegalStateException("algorithm " + algorithm + " in provider " + bcProvider.getName() + " but no class \"" + className + "\" found!");
} catch (Exception e) {
throw new IllegalStateException("algorithm " + algorithm + " in provider " + bcProvider.getName() + " but class \"" + className + "\" inaccessible!");
}
} else {
return null;
}
implEngines.put(baseName + ":" + algorithm, implEngineClass);
}
try {
return implEngineClass.newInstance();
} catch (Exception e) {
final Provider bcProvider = securityProvider;
String className = implEngineClass.getName();
throw new IllegalStateException("algorithm " + algorithm + " in provider " + bcProvider.getName() + " but class \"" + className + "\" inaccessible!");
}
}
// the obligratory "reflection crap" :
private static <T> T newInstance(Class<T> klass, Class<?>[] paramTypes, Object... params) {
final Constructor<T> constructor;
try {
constructor = klass.getDeclaredConstructor(paramTypes);
constructor.setAccessible(true);
return constructor.newInstance(params);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(e.getTargetException());
} catch (InstantiationException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
@SuppressWarnings("unchecked")
private static <T> T invoke(Object object, Class<?> klass, String methodName, Class<?>[] paramTypes, Object... params) {
final Method method;
try {
method = klass.getDeclaredMethod(methodName, paramTypes);
method.setAccessible(true);
return (T) method.invoke(object, params);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e.getMessage(), e);
} catch (InvocationTargetException e) {
throw new IllegalStateException(e.getTargetException());
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
private static void setField(Object obj, Class<?> fieldOwner, String fieldName, Object value) {
final Field field;
try {
field = fieldOwner.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
} catch (NoSuchFieldException e) {
throw new IllegalStateException("no field '" + fieldName + "' declared in " + fieldOwner + "", e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}