J'ai une application qui utilise le cryptage AES 256 bits qui n'est pas pris en charge par Java prêt à l'emploi. Je sais que cela fonctionne correctement, j'installe les jars JCE à puissance illimitée dans le dossier de sécurité. C’est bien pour moi d’être développeur, je peux les installer.
Ma question est que, puisque cette application sera distribuée, les fichiers de stratégie ne seront probablement pas installés par les utilisateurs finaux. Demander à l’utilisateur final de les télécharger uniquement pour que la fonction de l’application fonctionne n’est pas une solution attrayante.
Existe-t-il un moyen de faire fonctionner mon application sans écraser les fichiers sur la machine de l'utilisateur final? Un logiciel tiers capable de le gérer sans les fichiers de stratégie installés? Ou un moyen de simplement référencer ces fichiers de règles à partir d'un fichier JAR?
Il existe plusieurs solutions couramment citées à ce problème. Malheureusement, ni l'un ni l'autre ne sont entièrement satisfaisants:
Mais alors il y a réflexion. Y a-t-il quelque chose que vous ne pouvez pas faire en utilisant la réflexion?
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"));
}
Appelez simplement removeCryptographyRestrictions()
à partir d'un initialiseur statique ou autre avant d'effectuer toute opération cryptographique.
La partie JceSecurity.isRestricted = false
Est tout ce qui est nécessaire pour utiliser directement des chiffrements 256 bits; cependant, sans les deux autres opérations, Cipher.getMaxAllowedKeyLength()
continuera à en signaler 128, et les suites de chiffrement TLS 256 bits ne fonctionneront pas.
Ce code fonctionne sur Oracle Java 7 et 8 et ignore automatiquement le processus sur Java 9 et sur OpenJDK là où il n'est pas nécessaire. Après tout, étant un bidule laid, cela ne fonctionnera probablement pas sur les ordinateurs virtuels d'autres fournisseurs.
De plus, cela ne fonctionne pas sur Oracle Java 6, car les classes JCE privées y sont masquées. L'obfuscation ne change pas d'une version à l'autre, il est donc toujours techniquement possible de prendre en charge Java 6.
Ceci n’est plus nécessaire pour Java 9 , ni pour les versions récentes de Java 6, 7 ou 8 . Finalement! :)
Per JDK-8170157 , la stratégie cryptographique illimitée est maintenant activée par défaut.
Versions spécifiques du numéro JIRA:
Notez que si, pour une raison quelconque, l'ancien comportement est requis dans Java 9, il peut être défini à l'aide de:
Security.setProperty("crypto.policy", "limited");
Voici la solution: http://middlesphere-1.blogspot.ru/2014/06/this-code-allows-to-break-limit-if.html
//this code allows to break limit if client jdk/jre has no unlimited policy files for JCE.
//it should be run once. So this static section is always execute during the class loading process.
//this code is useful when working with Bouncycastle library.
static {
try {
Field field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
field.setAccessible(true);
field.set(null, Java.lang.Boolean.FALSE);
} catch (Exception ex) {
}
}
À partir du JDK 8u102, les solutions publiées reposant sur la réflexion ne fonctionneront plus: le champ défini par ces solutions est désormais final
( https://bugs.openjdk.Java.net/browse/JDK -8149417 ).
On dirait que cela revient à (a) utiliser Bouncy Castle ou (b) à installer les fichiers de règles JCE.
Bouncy Castle nécessite toujours des bocaux installés pour autant que je sache.
J'ai fait un petit test et cela a semblé confirmer ceci:
http://www.bouncycastle.org/wiki/display/JA1/Frequent+Asked+Questions
Pour une bibliothèque de cryptographie alternative, consultez Bouncy Castle . Il a AES et beaucoup de fonctionnalités ajoutées. C'est une librairie libérale open source. Pour que cela fonctionne, vous devrez utiliser l’API propriétaire légère et légendaire de Bouncy Castle.
Vous pouvez utiliser la méthode
javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)
pour tester la longueur de clé disponible, utilisez-la et informez l'utilisateur de ce qui se passe. Quelque chose indiquant que votre application retombe sur des clés 128 bits en raison de fichiers non installés, par exemple. Les utilisateurs soucieux de la sécurité installeront les fichiers de règles, d’autres continueront à utiliser des clés plus faibles.
Pour notre application, nous avions une architecture client/serveur et nous ne permettions que le décryptage/cryptage des données au niveau du serveur. Par conséquent, les fichiers JCE ne sont nécessaires qu’à cet endroit.
Un autre problème nous a obligés à mettre à jour un fichier jar de sécurité sur les ordinateurs clients. JNLP écrase les bibliothèques in${Java.home}/lib/security/
et la JVM lors de la première exécution.
Cela a fonctionné.
Voici une version mise à jour de ntoskrnl answer. Il contient en outre une fonction pour supprimer le modificateur final comme Arjan mentionné dans les commentaires.
Cette version fonctionne avec JRE 8u111 ou une version plus récente.
private static void removeCryptographyRestrictions() {
if (!isRestrictedCryptography()) {
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");
Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
isRestrictedField.setAccessible(true);
setFinalStatic(isRestrictedField, true);
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));
}
catch (final Exception e) {
e.printStackTrace();
}
}
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
private static boolean isRestrictedCryptography() {
// This simply matches the Oracle JRE, but not OpenJDK.
return "Java(TM) SE Runtime Environment".equals(System.getProperty("Java.runtime.name"));
}
Voici une version modifiée du code de @ ntoskrnl avec isRestrictedCryptography
check by real Cipher.getMaxAllowedKeyLength
, enregistrement de slf4j et support de l’initialisation de singleton à partir de l’application bootstrap comme ceci:
static {
UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}
Ce code cesserait correctement de perturber la réflexion lorsque la stratégie illimitée devient disponible par défaut dans Java 8u162, comme le prédit la réponse de @ cranphin.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import Java.lang.reflect.Field;
import Java.lang.reflect.Modifier;
import Java.security.NoSuchAlgorithmException;
import Java.security.Permission;
import Java.security.PermissionCollection;
import Java.util.Map;
// https://stackoverflow.com/questions/1179672/how-to-avoid-installing-unlimited-strength-jce-policy-files-when-deploying-an
public class UnlimitedKeyStrengthJurisdictionPolicy {
private static final Logger log = LoggerFactory.getLogger(UnlimitedKeyStrengthJurisdictionPolicy.class);
private static boolean isRestrictedCryptography() throws NoSuchAlgorithmException {
return Cipher.getMaxAllowedKeyLength("AES/ECB/NoPadding") <= 128;
}
private static void removeCryptographyRestrictions() {
try {
if (!isRestrictedCryptography()) {
log.debug("Cryptography restrictions removal not needed");
return;
}
/*
* Do the following, but with reflection to bypass access checks:
*
* JceSecurity.isRestricted = false;
* JceSecurity.defaultPolicy.perms.clear();
* JceSecurity.defaultPolicy.add(CryptoAllPermission.INSTANCE);
*/
Class<?> jceSecurity = Class.forName("javax.crypto.JceSecurity");
Class<?> cryptoPermissions = Class.forName("javax.crypto.CryptoPermissions");
Class<?> cryptoAllPermission = Class.forName("javax.crypto.CryptoAllPermission");
Field isRestrictedField = jceSecurity.getDeclaredField("isRestricted");
isRestrictedField.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(isRestrictedField, isRestrictedField.getModifiers() & ~Modifier.FINAL);
isRestrictedField.set(null, false);
Field defaultPolicyField = jceSecurity.getDeclaredField("defaultPolicy");
defaultPolicyField.setAccessible(true);
PermissionCollection defaultPolicy = (PermissionCollection) defaultPolicyField.get(null);
Field perms = cryptoPermissions.getDeclaredField("perms");
perms.setAccessible(true);
((Map<?, ?>) perms.get(defaultPolicy)).clear();
Field instance = cryptoAllPermission.getDeclaredField("INSTANCE");
instance.setAccessible(true);
defaultPolicy.add((Permission) instance.get(null));
log.info("Successfully removed cryptography restrictions");
} catch (Exception e) {
log.warn("Failed to remove cryptography restrictions", e);
}
}
static {
removeCryptographyRestrictions();
}
public static void ensure() {
// just force loading of this class
}
}
Lors de l'installation de votre programme, il suffit d'inviter l'utilisateur et de télécharger un script Batch DOS ou Bash Shell et de copier le JCE à l'emplacement système approprié.
J'avais l'habitude de faire cela pour un service Web de serveur et au lieu d'un programme d'installation officiel, je venais de fournir des scripts pour configurer l'application avant que l'utilisateur ne puisse l'exécuter. Vous pouvez rendre l'application non exécutable jusqu'à ce qu'ils exécutent le script d'installation. Vous pouvez également demander à l'application de se plaindre de l'absence de l'entreprise criminelle commune, puis de demander de télécharger et de redémarrer l'application?