Esistono un paio di soluzioni comunemente citate a questo problema. Sfortunatamente nessuno di questi è del tutto soddisfacente:
- Installa i file dei criteri di resistenza illimitati . Sebbene questa sia probabilmente la soluzione giusta per la tua workstation di sviluppo, diventa rapidamente una seccatura maggiore (se non un blocco stradale) avere utenti non tecnici che installano i file su ogni computer. Non è possibile distribuire i file con il programma; devono essere installati nella directory JRE (che potrebbe anche essere di sola lettura a causa delle autorizzazioni).
- Salta l'API JCE e utilizza un'altra libreria di crittografia come Bouncy Castle . Questo approccio richiede una libreria aggiuntiva da 1 MB, che può essere un onere significativo a seconda dell'applicazione. È anche stupido duplicare le funzionalità incluse nelle librerie standard. Ovviamente, anche l'API è completamente diversa dalla solita interfaccia JCE. (BC implementa un provider JCE, ma ciò non aiuta perché le restrizioni sulla forza chiave vengono applicate prima di passare all'implementazione.) Questa soluzione non consente inoltre di utilizzare suite di crittografia TLS (SSL) a 256 bit, poiché le librerie TLS standard chiamano JCE internamente per determinare eventuali restrizioni.
Ma poi c'è la riflessione. C'è qualcosa che non puoi fare usando la riflessione?
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"));
}
È sufficiente chiamare removeCryptographyRestrictions()
da un inizializzatore statico o simile prima di eseguire qualsiasi operazione crittografica.
La JceSecurity.isRestricted = false
parte è tutto ciò che serve per usare direttamente le cifre a 256 bit; tuttavia, senza le altre due operazioni, Cipher.getMaxAllowedKeyLength()
continuerà a generare report a 128 e le suite di crittografia TLS a 256 bit non funzioneranno.
Questo codice funziona su Oracle Java 7 e 8 e salta automaticamente il processo su Java 9 e OpenJDK dove non è necessario. Dopotutto, essendo un brutto hack, probabilmente non funziona con le macchine virtuali di altri fornitori.
Inoltre non funziona su Oracle Java 6, perché le classi JCE private sono offuscate lì. Tuttavia, l'offuscamento non cambia da versione a versione, quindi è ancora tecnicamente possibile supportare Java 6.