Comment éviter d'installer des fichiers JcE "Unlimited Strength" lors du déploiement d'une application?
j'ai une application qui utilise un cryptage AES 256-bit qui n'est pas pris en charge par Java out of the box. Je sais que pour que cela fonctionne correctement, j'installe les pots de force JcE unlimited dans le dossier de sécurité. C'est très bien pour moi étant le développeur, je peux les installer.
ma question Est que puisque cette application sera distribuée, les utilisateurs finaux n'auront probablement pas ces fichiers de politique installés. Avoir l'utilisateur final télécharger ces juste pour faire la fonction app n'est pas un solution attrayante.
y a-t-il un moyen de faire tourner mon application sans écraser les fichiers sur la machine de l'utilisateur final? Un logiciel tiers qui peut le gérer sans les fichiers de police installés? Ou un moyen de référence de ces fichiers dans un BOCAL?
11 réponses
il y a quelques solutions communément citées à ce problème. Malheureusement, ni l'un ni l'autre n'est entièrement satisfaisant:
- Installer force illimitée de la politique de fichiers . bien que ce soit probablement la bonne solution pour votre poste de travail de développement, il devient rapidement un problème majeur (si pas un barrage routier) pour avoir des utilisateurs non-techniques installent les fichiers sur chaque ordinateur. Il y a Non mode pour distribuer les fichiers avec votre programme; ils doivent être installés dans le répertoire JRE (qui peut même être lu uniquement à cause des permissions).
- Sauter L'API JCE et utiliser une autre bibliothèque de cryptographie comme château gonflable . Cette approche nécessite une bibliothèque supplémentaire de 1 Mo, ce qui peut représenter un fardeau important selon l'application. Il semble également stupide de dupliquer les fonctionnalités incluses dans la norme bibliothèque. Évidemment, L'API est aussi complètement différente de l'interface JCE habituelle. (BC ne met en œuvre un fournisseur JCE, mais cela n'aide pas parce que les restrictions de force clés sont appliquées avant passer à la mise en œuvre. Cette solution ne vous permettra pas non plus d'utiliser les suites de chiffrement TLS (SSL) 256-bit, car les bibliothèques TLS standard appellent le JCE en interne pour déterminer les restrictions.
mais il y a aussi réflexion. y a-t-il quelque chose que vous ne pouvez pas faire avec 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"));
}
simplement appeler removeCryptographyRestrictions()
d'un initialiseur statique ou tel avant d'effectuer des opérations cryptographiques.
la partie JceSecurity.isRestricted = false
est tout ce qui est nécessaire pour utiliser des encodeurs 256-bits directement; cependant, sans les deux autres opérations, Cipher.getMaxAllowedKeyLength()
continuera à rapporter 128, et les suites de chiffrement 256-bit TLS ne fonctionneront pas.
ce code fonctionne sur Oracle Java 7 et 8, et saute automatiquement le processus sur Java 9 et OpenJDK où il n'est pas nécessaire. Être un vilain pirate après tout, il ne fonctionne probablement pas sur les Vm des autres vendeurs.
il ne fonctionne pas non plus sur Oracle Java 6, car les classes JcE privées y sont obscurcies. La confusion ne change pas d'une version à l'autre, il est donc techniquement possible de prendre Java 6 en charge.
ce n'est plus nécessaire pour Java 9 , ni pour toute publication récente de Java 6, 7, ou 8. Enfin!!! :)
Per JDK-8170157 , la Politique cryptographique illimitée est maintenant activée par défaut.
des versions Spécifiques de l'JIRA question:
- Java 9: Toute version officielle!
- Java 8u161 ou plus tard (Disponible maintenant )
- Java 7u171 ou une version plus récente (uniquement disponible via 'My Oracle Support')
- Java 6u181 or later (seulement disponible via 'My Oracle Support')
notez que si pour une raison étrange L'ancien comportement est nécessaire dans Java 9, Il peut être défini en utilisant:
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) {
}
}
château gonflable nécessite toujours des pots installés autant que je peux dire.
j'ai fait un petit test et il semble le confirmer:
http://www.bouncycastle.org/wiki/display/JA1/Frequently+questions posées + Questions
à partir de JDK 8u102, les solutions postées s'appuyant sur la réflexion ne fonctionneront plus: le champ que ces solutions placent est maintenant final
( https://bugs.openjdk.java.net/browse/JDK-8149417 ).
semble revenir soit (a) en utilisant le château gonflable, ou (B) en installant les fichiers de la Politique JCE.
une alternative à la bibliothèque de cryptographie, jetez un oeil à Château Gonflable . Il a un SEA et beaucoup de fonctionnalités supplémentaires. C'est un libéral de la bibliothèque open source. Vous devrez utiliser l'API de Château gonflable légère et propriétaire pour que cela fonctionne.
vous pouvez utiliser la méthode
javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)
pour tester la longueur de la clé disponible, utilisez-la et informez l'utilisateur de ce qui se passe. Quelque chose indiquant que votre application tombe en arrière à 128 bits clés en raison des fichiers de politique n'étant pas installé, par exemple. Les utilisateurs soucieux de la sécurité installeront les fichiers de la Politique, d'autres continueront à utiliser des clés plus faibles.
pour notre application, nous avions une architecture de serveur client et nous n'avons autorisé le décryptage/chiffrement des données qu'au niveau du serveur. Par conséquent, les fichiers JCE ne sont nécessaires que là-bas.
nous avons eu un autre problème où nous avons eu besoin de mettre à jour un bocal de sécurité sur les machines clientes, par L'intermédiaire de JNLP, il écrase les bibliothèques dans ${java.home}/lib/security/
et la JVM à la première exécution.
ça a marché.
Voici une version mise à jour de ntoskrnl réponse. Il contient en outre une fonction pour supprimer le modificateur final comme Arjan mentionné dans les commentaires.
cette version Fonctionne avec JRE 8u111 ou plus récent.
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"));
}
pendant l'installation de votre programme, il suffit de demander à l'utilisateur et avoir un script par lot DOS ou un script shell Bash télécharger et copier le JCE dans le bon emplacement du système.
j'avais l'habitude de faire cela pour un serveur web et au lieu d'un installateur formel, j'ai juste fourni des scripts pour configurer l'application avant que l'utilisateur puisse l'exécuter. Vous pouvez désactiver l'application jusqu'à ce qu'ils exécutent le script de configuration. Vous pouvez également faire l'application se plaindre que le JCE est manquant et ensuite, demandez à télécharger et redémarrez l'application?
Voici une version modifiée du code de @ntoskrnl avec isRestrictedCryptography
check by actual Cipher.getMaxAllowedKeyLength
151960920", logging slf4j et support de l'initialisation de singleton à partir de l'application bootstrap comme ceci:
static {
UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}
ce code arrêterait correctement de se déformer lorsque la Politique 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;
// /q/how-to-avoid-installing-unlimited-strength-jce-policy-files-when-deploying-an-application-25075/"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
}
}