/** * Sets the mode of a {@link javax.crypto.CipherSpi} instance. * * <p>Like {@link #getCipher(String)}, we're accessing a private API here, so me must work around * the access restrictions * * @param cipherSpi the {@link javax.crypto.CipherSpi} instance * @param mode the mode to set * @throws Throwable if {@link javax.crypto.CipherSpi#engineSetPadding} throws */ private static void cipherSpiSetMode(final CipherSpi cipherSpi, final String mode) throws Throwable { final Method engineSetMode = getMethod(cipherSpi.getClass(), "engineSetMode", String.class); engineSetMode.setAccessible(true); try { engineSetMode.invoke(cipherSpi, mode); } catch (final InvocationTargetException e) { throw e.getCause(); } }
/** * 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); } }