Istnieje kilka często cytowanych rozwiązań tego problemu. Niestety żaden z nich nie jest w pełni zadowalający:
- Zainstaluj nieograniczone pliki zasad siły . Chociaż jest to prawdopodobnie właściwe rozwiązanie dla twojej programistycznej stacji roboczej, szybko staje się dużym kłopotem (jeśli nie przeszkodą), aby użytkownicy nietechniczni instalowali pliki na każdym komputerze. Nie ma możliwości rozpowszechniania plików wraz z programem; muszą być zainstalowane w katalogu JRE (który może być nawet tylko do odczytu ze względu na uprawnienia).
- Pomiń interfejs JCE API i użyj innej biblioteki kryptograficznej, takiej jak Bouncy Castle . Takie podejście wymaga dodatkowej biblioteki 1 MB, co może być znacznym obciążeniem w zależności od aplikacji. Głupio jest również powielać funkcje zawarte w standardowych bibliotekach. Oczywiście API jest również zupełnie inne niż zwykły interfejs JCE. (BC implementuje dostawcę JCE, ale to nie pomaga, ponieważ ograniczenia siły klucza są stosowane przed przekazaniem do implementacji). standardowe biblioteki TLS wywołują wewnętrznie JCE w celu określenia wszelkich ograniczeń.
Ale jest też refleksja. Czy jest coś, czego nie możesz zrobić za pomocą odbicia?
private static void removeCryptographyRestrictions() {
if (!isRestrictedCryptography()) {
logger.fine("Cryptography restrictions removal not needed");
return;
}
try {
/*
* Do the following, but with reflection to bypass access checks:
*
* JceSecurity.isRestricted = false;
* JceSecurity.defaultPolicy.perms.clear();
* JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
*/
final Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
final Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
final Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");
final Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
isRestrictedField.setAccessible(true);
final Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
isRestrictedField.set(null, false);
final Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
defaultPolicyField.setAccessible(true);
final PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);
final Field perms = cryptoPermissions.getDeclaredField("perms");
perms.setAccessible(true);
((Map<?, ?>) perms.get(defaultPolicy)).clear();
final Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
instance.setAccessible(true);
defaultPolicy.add((Permission) instance.get(null));
logger.fine("Successfully removed cryptography restrictions");
} catch (final Exception e) {
logger.log(Level.WARNING, "Failed to remove cryptography restrictions", e);
}
}
private static boolean isRestrictedCryptography() {
// This matches Oracle Java 7 and 8, but not Java 9 or OpenJDK.
final String name = System.getProperty("java.runtime.name");
final String ver = System.getProperty("java.version");
return name != null && name.equals("Java(TM) SE Runtime Environment")
&& ver != null && (ver.startsWith("1.7") || ver.startsWith("1.8"));
}
Po prostu wywołaj removeCryptographyRestrictions()
ze statycznego inicjatora lub takiego przed wykonaniem jakichkolwiek operacji kryptograficznych.
Ta JceSecurity.isRestricted = false
część jest wszystkim, czego potrzeba do bezpośredniego używania 256-bitowych szyfrów; jednak bez dwóch pozostałych operacji Cipher.getMaxAllowedKeyLength()
nadal będzie raportować 128, a 256-bitowe zestawy szyfrów TLS nie będą działać.
Ten kod działa na Oracle Java 7 i 8 i automatycznie pomija proces w Javie 9 i OpenJDK, gdzie nie jest potrzebny. W końcu jest to brzydki hack, ale prawdopodobnie nie działa na maszynach wirtualnych innych dostawców.
Nie działa również na Oracle Java 6, ponieważ prywatne klasy JCE są tam zaciemnione. Obfuskacja nie zmienia się jednak z wersji na wersję, więc nadal jest technicznie możliwe wsparcie Java 6.