protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec) throws InvalidKeySpecException { try { // convert key to one of our keys // this also verifies that the key is a valid RSA key and ensures // that the encoding is X.509/PKCS#8 for public/private keys key = engineTranslateKey(key); } catch (InvalidKeyException e) { throw new InvalidKeySpecException(e); } if (key instanceof RSAPublicKey) { RSAPublicKey rsaKey = (RSAPublicKey) key; if (rsaPublicKeySpecClass.isAssignableFrom(keySpec)) { return keySpec.cast(new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent())); } else if (x509KeySpecClass.isAssignableFrom(keySpec)) { return keySpec.cast(new X509EncodedKeySpec(key.getEncoded())); } else { throw new InvalidKeySpecException( "KeySpec must be RSAPublicKeySpec or " + "X509EncodedKeySpec for RSA public keys"); } } else if (key instanceof RSAPrivateKey) { if (pkcs8KeySpecClass.isAssignableFrom(keySpec)) { return keySpec.cast(new PKCS8EncodedKeySpec(key.getEncoded())); } else if (rsaPrivateCrtKeySpecClass.isAssignableFrom(keySpec)) { if (key instanceof RSAPrivateCrtKey) { RSAPrivateCrtKey crtKey = (RSAPrivateCrtKey) key; return keySpec.cast( new RSAPrivateCrtKeySpec( crtKey.getModulus(), crtKey.getPublicExponent(), crtKey.getPrivateExponent(), crtKey.getPrimeP(), crtKey.getPrimeQ(), crtKey.getPrimeExponentP(), crtKey.getPrimeExponentQ(), crtKey.getCrtCoefficient())); } else { throw new InvalidKeySpecException("RSAPrivateCrtKeySpec can only be used with CRT keys"); } } else if (rsaPrivateKeySpecClass.isAssignableFrom(keySpec)) { RSAPrivateKey rsaKey = (RSAPrivateKey) key; return keySpec.cast( new RSAPrivateKeySpec(rsaKey.getModulus(), rsaKey.getPrivateExponent())); } else { throw new InvalidKeySpecException( "KeySpec must be RSAPrivate(Crt)KeySpec or " + "PKCS8EncodedKeySpec for RSA private keys"); } } else { // should not occur, caught in engineTranslateKey() throw new InvalidKeySpecException("Neither public nor private key"); } }
/** * Creates a {@link javax.crypto.Cipher} instance from a {@link javax.crypto.CipherSpi}. * * @param cipherSpi the {@link javax.cyrpto.CipherSpi} instance * @param transformation the transformation cipherSpi was obtained for * @return a {@link javax.crypto.Cipher} instance * @throws Throwable in case of an error */ private static Cipher getCipher(final CipherSpi cipherSpi, final String transformation) throws Throwable { /* This API isn't public - usually you're expected to simply use Cipher.getInstance(). * Due to the signed-jar restriction for JCE providers that is not an option, so we * use one of the private constructors of Cipher. */ final Class<Cipher> cipherClass = Cipher.class; final Constructor<Cipher> cipherConstructor = cipherClass.getDeclaredConstructor(CipherSpi.class, String.class); cipherConstructor.setAccessible(true); try { return cipherConstructor.newInstance(cipherSpi, transformation); } catch (final InvocationTargetException e) { throw e.getCause(); } }
/** * Get the reflection of a private and protected method. * * Required since {@link java.lang.Class#getMethod(String, Class...) * only find public method * * @param clazz the class to search in * @param name the method name * @param parameterTypes the method's parameter types * @return the method reflection * @throws NoSuchMethodException */ private static Method getMethod( final Class<?> clazz, final String name, final Class<?>... parameterTypes) throws NoSuchMethodException { try { /* Try to fetch the method reflection */ return clazz.getDeclaredMethod(name, parameterTypes); } catch (final NoSuchMethodException e1) { /* No such method. Search base class if there is one, * otherwise complain */ if (clazz.getSuperclass() != null) { try { /* Try to fetch the method from the base class */ return getMethod(clazz.getSuperclass(), name, parameterTypes); } catch (final NoSuchMethodException e2) { /* We don't want the exception to indicate that the * *base* class didn't contain a matching method, but * rather that the subclass didn't. */ final StringBuilder s = new StringBuilder(); s.append(clazz.getName()); s.append("("); boolean first = true; for (final Class<?> parameterType : parameterTypes) { if (!first) s.append(", "); first = false; s.append(parameterType); } s.append(")"); throw new NoSuchMethodException(s.toString()); } } else { /* No base class, complain */ throw e1; } } }
/** * Replacement for JCA/JCE's {@link javax.crypto.Cipher#getInstance}. The original method only * accepts JCE providers from signed jars, which prevents us from bundling our cryptography * provider Bouncy Caster with the application. * * @param transformation the transformation to find an implementation for */ public static Cipher getCipher(final String transformation) { try { /* Split the transformation into algorithm, mode and padding */ final Matcher transformation_matcher = s_transformation_pattern.matcher(transformation.toUpperCase()); if (!transformation_matcher.matches()) throw new RuntimeException("Transformation " + transformation + " is invalid"); final String algorithm = transformation_matcher.group(1); final String mode = transformation_matcher.group(3); final String padding = transformation_matcher.group(4); final boolean isBareAlgorithm = (mode == null) && (padding == null); /* Build the property values we need to search for. */ final String algorithmModePadding = !isBareAlgorithm ? algorithm + "/" + mode + "/" + padding : null; final String algorithmMode = !isBareAlgorithm ? algorithm + "/" + mode : null; final String algorithmPadding = !isBareAlgorithm ? algorithm + "//" + padding : null; /* Search the provider for implementations. We ask for more specific (i.e matching * the requested mode and or padding) implementation first, then fall back to more * generals ones which we then must configure for the mode and padding. */ final CipherSpi cipherSpi; if (!isBareAlgorithm && (resolveProperty(Provider, "Cipher", algorithmModePadding) != null)) { @SuppressWarnings("unchecked") final Class<? extends CipherSpi> cipherSpiClass = (Class<? extends CipherSpi>) Class.forName(resolveProperty(Provider, "Cipher", algorithmModePadding)); cipherSpi = cipherSpiClass.newInstance(); } else if (!isBareAlgorithm && (resolveProperty(Provider, "Cipher", algorithmMode) != null)) { @SuppressWarnings("unchecked") final Class<? extends CipherSpi> cipherSpiClass = (Class<? extends CipherSpi>) Class.forName(resolveProperty(Provider, "Cipher", algorithmMode)); cipherSpi = cipherSpiClass.newInstance(); if (!isBareAlgorithm) cipherSpiSetPadding(cipherSpi, padding); } else if (!isBareAlgorithm && (resolveProperty(Provider, "Cipher", algorithmPadding) != null)) { @SuppressWarnings("unchecked") final Class<? extends CipherSpi> cipherSpiClass = (Class<? extends CipherSpi>) Class.forName(resolveProperty(Provider, "Cipher", algorithmPadding)); cipherSpi = cipherSpiClass.newInstance(); if (!isBareAlgorithm) cipherSpiSetMode(cipherSpi, mode); } else if (resolveProperty(Provider, "Cipher", algorithm) != null) { @SuppressWarnings("unchecked") final Class<? extends CipherSpi> cipherSpiClass = (Class<? extends CipherSpi>) Class.forName(resolveProperty(Provider, "Cipher", algorithm)); cipherSpi = cipherSpiClass.newInstance(); if (!isBareAlgorithm) { cipherSpiSetMode(cipherSpi, mode); cipherSpiSetPadding(cipherSpi, padding); } } else { throw new RuntimeException( "Provider " + Provider.getName() + " (" + Provider.getClass() + ") does not implement " + transformation); } /* Create a {@link javax.crypto.Cipher} instance from the {@link javax.crypto.CipherSpi} the provider gave us */ s_logger.info("Using SPI " + cipherSpi.getClass() + " for " + transformation); return getCipher(cipherSpi, transformation.toUpperCase()); } catch (final RuntimeException e) { throw e; } catch (final Error e) { throw e; } catch (final Throwable e) { throw new RuntimeException( "Provider " + Provider.getName() + " (" + Provider.getClass() + ") failed to instanciate " + transformation, e); } }