アプリケーションをデプロイするときに「Unlimited Strength」JCEポリシーファイルのインストールを回避するにはどうすればよいですか?


169

Javaでそのままではサポートされていない256ビットAES暗号化を使用するアプリがあります。これを正しく機能させるために、JCE無制限の強度のjarファイルをセキュリティフォルダーにインストールします。これは私が開発者である場合は問題ありません。インストールできます。

私の質問は、このアプリが配布されるため、エンドユーザーはこれらのポリシーファイルをインストールしない可能性が高いです。エンドユーザーにアプリを機能させるためだけにこれらをダウンロードさせることは、魅力的なソリューションではありません。

エンドユーザーのマシンのファイルを上書きせずにアプリを実行する方法はありますか?ポリシーファイルをインストールせずにそれを処理できるサードパーティのソフトウェア?または、JAR内からこれらのポリシーファイルを参照する方法は?




11
NSAが接続をスヌーピングできるように、クライアントが安全性の低い暗号を使用することがSun / Oracleの意図だったと思います。私は冗談や偏執狂ではありませんが、暗号化は武器として扱われ、暗号化の共有輸出禁止になっています
そり2015

回答:


175

この問題には、よく引用されるいくつかの解決策があります。残念ながら、これらはどちらも完全に満足できるものではありません。

  • 無制限の強度ポリシーファイルをインストールしますこれはおそらく開発ワークステーションに適したソリューションですが、技術者以外のユーザーがすべてのコンピュータにファイルをインストールするのは、(ロードブロッキングではないとしても)大きな手間になります。プログラムでファイルを配布する方法はありません。これらは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 = false256ビットの暗号を直接使用するために必要なのはこの部分だけです。ただし、他の2つの操作を行わなくても、Cipher.getMaxAllowedKeyLength()128が報告され続け、256ビットTLS暗号スイートは機能しません。

このコードはOracle Java 7および8で動作し、不要なJava 9およびOpenJDKのプロセスを自動的にスキップします。結局のところ、醜いハックなので、他のベンダーのVMでは動作しない可能性があります。

また、プライベートJCEクラスが難読化されているため、Oracle Java 6では機能しません。ただし、難読化はバージョンごとに変更されないため、技術的にはJava 6をサポートできます。


23
リフレクションソリューションは、Javaライセンス契約に違反する可能性があります。「F。JAVA TECHNOLOGY RESTRICTIONS。あなたは...の動作を変更できません...何らかの方法で「java」、「javax」として識別されるクラス、インターフェース、またはサブパッケージ、「太陽」、「オラクル」または同様の慣習...」
M.ダドリー

14
@M。ダドリーかもしれない。懸念がある場合は、このコードを含む製品を発送する前に弁護士に確認してください。
ntoskrnl 2014

3
@peabodyプログラムに100MBのJREを含めることは、確かに場合によってはオプションです。ただし、そうでない場合は、プログラムに含めても(ファイルのアクセス許可などのさまざまな理由により)、ポリシーファイルを手動でインストールする必要があります。私の経験では、多くのユーザーはそれができません。
ntoskrnl 2015年

8
1.8.0_112でリフレクションソリューションが機能しなくなったようです。それは1.8.0_111で動作しますが、いない112
ジョン・L

3
@JohnLこれをアプリケーションで使用します。final8u111でフィールドに問題が発生した後、この回答に従って、最終フィールドを変更できるように変更しました。結果は、ntoskrnlの新しいバージョンとほぼ同じですが、として宣言modifiersFieldしなかった点が異なりfinalます。ユーザーの1人が、8u112でも機能すると報告しています。
Arjan 2016年

87

これは、Java 9Java 6、7、8の最近のリリースでは不要になりました。:)

パーJDK-8170157、無制限の暗号化ポリシーがデフォルトで有効になりました。

JIRA課題の特定のバージョン:

  • Java 9(10、11など):任意の公式リリース!
  • Java 8u161以降(現在入手可能)
  • Java 7u171以降(「My Oracle Support」からのみ利用可能)
  • Java 6u181以降(「My Oracle Support」からのみ利用可能)

なんらかの理由でJava 9で古い動作が必要な場合は、次のように設定できます。

Security.setProperty("crypto.policy", "limited");

4
実際、このポリシーはデフォルトなので、Java 9ではアクションは必要ありません。
ntoskrnl 2017

2018/01/14(最新のOracle JDKは8u151 / 152)の時点で、Java 8ではデフォルトでまだ有効になっていません。この回答が最初に書かれてから1年以上経っています...しかし、java.com / en / jreによると-jdk-cryptoroadmap.htmlこれは、2018
Alex

私の場合、そして私がこのサイトでAマークを取得するには:ssllabs.com/ssltest ...次のように設定する必要があります:Security.setProperty( "crypto.policy"、 "unlimited"); 次に...この記事で示した256ベースのアルゴリズムを使用して、applications.propertiesにserver.ssl.ciphersを設定します-> weakdh.org/sysadmin.html
Artanis Zeratul

OpenJDK 8インストールにも関連します。参照:stackoverlow-記事:JCEポリシーはopenjdk 8にバンドルされていますか?
leole

22

ここに解決策があります: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) {
    }
}

これは、「defaultPolicy」の部分がないことを除いて、私のソリューションと同じです。ブログの投稿は私の回答の後に日付が付けられています。
ntoskrnl

1
しかし、これは正しいことですか?このコードはリアルタイムでアプリケーションのセキュリティに挑戦できますか?影響を理解するのを手伝ってください。
ディッシュ

1
私はこれを実行した後、このエラーを取得する:java.security.InvalidKeyException: Wrong algorithm: AES or Rijndael required
アンディ

3
Java 8ビルド111の時点では、isRestrictedフィールドが最終となっているため、このソリューションでは不十分です(bugs.openjdk.java.net/browse/JDK-8149417)。@ntoskrnlの回答は、「最終」修飾子の可能なすべての包含を処理します。Javaライセンス契約に関する@ M.Dudleyのコメントも引き続き適用されます。
MPelletier 2016年


13

JDK 8u102以降、リフレクションに依存する投稿されたソリューションは機能しなくなります。これらのソリューションが設定するフィールドは現在finalhttps://bugs.openjdk.java.net/browse/JDK-8149417)です。

(a)Bouncy Castleの使用、または(b)JCEポリシーファイルのインストールに戻ったようです。


7
常により多くの反射を使用できます。stackoverflow.com/questions/3301635/…
Universal Electricity

はい、@ M.DudleyのソリューションはisRestricted、「最終」修飾子の追加の可能性を処理するため、フィールドで引き続き機能します。
MPelletier 2016年

1
新しいリリースJDK 8u151には、「暗号化ポリシーを制御する新しいセキュリティプロパティ」があります。結論:「lib \ security \ java.security」の行「#crypto.policy = unlimited」から「#」を削除:oracle.com/technetwork/java/javase/8u151-relnotes-3850493.html
hemisphire

8

別の暗号ライブラリについては、Bouncy Castleご覧ください。AESと多くの追加機能があります。それはリベラルなオープンソースライブラリです。ただし、これを機能させるには、軽量で独自仕様のBouncy Castle APIを使用する必要があります。


19
それらは優れた暗号プロバイダーですが、大きな鍵を操作するには無制限の強度のJCEファイルが必要です。
John Meagher、

16
Bouncy Castle APIを直接使用する場合、無制限の筋力ファイルは必要ありません。
2009

4

あなたは方法を使うことができました

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

利用可能なキーの長さをテストするには、それを使用して、何が起こっているかをユーザーに通知します。たとえば、ポリシーファイルがインストールされていないために、アプリケーションが128ビットキーにフォールバックしていることを示す何か。セキュリティを意識したユーザーはポリシーファイルをインストールし、他のユーザーはより弱いキーを使用し続けます。


3

このアプリケーションでは、クライアントサーバーアーキテクチャがあり、サーバーレベルでのみデータの復号化/暗号化を許可しました。したがって、JCEファイルはそこでのみ必要です。

クライアントマシンのセキュリティjarを更新する必要があるという別の問題がありました。JNLPを使用する${java.home}/lib/security/と、最初の実行時にライブラリとJVMが上書きされます。

それでうまくいきました。


2

これがntoskrnl回答の更新バージョンです。さらに、コメントに記載されているArjanのような最終修飾子を削除する関数が含まれています。

このバージョンは、JRE 8u111以降で動作します。

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"));
}

うまく((Map<?, ?>) perms.get(defaultPolicy)).clear();動作しますが、この行はコンパイラエラーを生成します。コメントアウトしても機能に影響はないようです。この線は必要ですか?
Andreas Unterweger 2017

2

@ntoskrnl のコードの変更バージョンは、次のようにisRestrictedCryptography実際のCipher.getMaxAllowedKeyLengthチェック、slf4jロギング、およびアプリケーションのブートストラップからのシングルトン初期化のサポートを特徴としています。

static {
    UnlimitedKeyStrengthJurisdictionPolicy.ensure();
}

@cranphinの回答が予測するように、Java 8u162でデフォルトで無制限のポリシーが使用可能になると、このコードはリフレクションによるマングルを正しく停止します。


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;

// /programming/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
    }
}

-1

プログラムのインストール中に、ユーザーにプロンプ​​トを出し、DOSバッチスクリプトまたはBashシェルスクリプトをダウンロードして、JCEを適切なシステムの場所にコピーします。

以前はサーバーWebサービスでこれを行う必要があり、正式なインストーラーではなく、ユーザーが実行する前にアプリをセットアップするスクリプトを提供しました。セットアップスクリプトを実行するまで、アプリを実行不可にすることができます。また、JCEが見つからないことをアプリに訴え、アプリをダウンロードして再起動するように依頼することもできますか?


7
「エンドユーザーのマシンでファイルを上書きせずにアプリを実行する」
エリクソン09

最初の回答が間違っていたので、私は私の回答を完全に編集しました。
djangofan 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.