この問題には、よく引用されるいくつかの解決策があります。残念ながら、これらはどちらも完全に満足できるものではありません。
- 無制限の強度ポリシーファイルをインストールします。これはおそらく開発ワークステーションに適したソリューションですが、技術者以外のユーザーがすべてのコンピュータにファイルをインストールするのは、(ロードブロッキングではないとしても)大きな手間になります。プログラムでファイルを配布する方法はありません。これらはJREディレクトリにインストールする必要があります(アクセス許可のため、読み取り専用の場合もあります)。
- JCE APIをスキップして、Bouncy Castleなどの別の暗号化ライブラリを使用します。このアプローチでは、追加の1MBライブラリが必要です。これは、アプリケーションによっては大きな負担になる場合があります。また、標準ライブラリに含まれている機能を複製するのはばかげています。もちろん、APIも通常のJCEインターフェースとはまったく異なります。(BCはJCEプロバイダーを実装しますが、実装に引き渡す前にキー強度の制限が適用されるため、これは役に立ちません。)また、このソリューションでは、256ビットTLS(SSL)暗号スイートを使用できません。標準のTLSライブラリは、JCEを内部的に呼び出して制限を決定します。
しかし、反射があります。リフレクションではできないことはありますか?
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"));
}
removeCryptographyRestrictions()
暗号化操作を実行する前に、静的初期化子などから呼び出すだけです。
JceSecurity.isRestricted = false
256ビットの暗号を直接使用するために必要なのはこの部分だけです。ただし、他の2つの操作を行わなくても、Cipher.getMaxAllowedKeyLength()
128が報告され続け、256ビットTLS暗号スイートは機能しません。
このコードはOracle Java 7および8で動作し、不要なJava 9およびOpenJDKのプロセスを自動的にスキップします。結局のところ、醜いハックなので、他のベンダーのVMでは動作しない可能性があります。
また、プライベートJCEクラスが難読化されているため、Oracle Java 6では機能しません。ただし、難読化はバージョンごとに変更されないため、技術的にはJava 6をサポートできます。