web-dev-qa-db-ja.com

アプリケーションを展開するときに「無制限の強度」のJCEポリシーファイルをインストールしないようにする方法

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

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

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

166
Ken

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

  • 無制限の強度ポリシーファイルをインストールしますこれはおそらく開発ワークステーションに適したソリューションですが、すぐに大きな面倒になります(ロードブロッキングではない場合)、非技術ユーザーがすべてのコンピューターにファイルをインストールするようにします。プログラムでファイルを配布する方法はありません。これらはJREディレクトリにインストールする必要があります(権限により読み取り専用になる場合もあります)。
  • JCE APIをスキップし、 Bouncy Castle などの別の暗号化ライブラリを使用します。このアプローチには追加の1MBライブラリが必要であり、アプリケーションによっては大きな負担になる場合があります。また、標準ライブラリに含まれる機能を複製するのは愚かなことです。明らかに、APIも通常のJCEインターフェイスとはまったく異なります。 (BCはJCEプロバイダーを実装しますが、キー強度の制限が適用されるため、これは役に立ちませんbefore実装に引き渡します。)このソリューションでは、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をサポートすることは依然として技術的に可能です。

171
ntoskrnl

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

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

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

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

何らかの奇妙な理由でJava 9で古い動作が必要な場合、以下を使用して設定できることに注意してください。

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

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

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

(a)Bouncy Castleを使用するか、(b)JCEポリシーファイルをインストールするかのいずれかに戻ったようです。

13
Sam Roberton

Bouncy Castleでは、私が知る限り、jarファイルをインストールする必要があります。

私は少しテストをしましたが、これはこれを確認しているようです:

http://www.bouncycastle.org/wiki/display/JA1/Frequently+Asked+Questions

13
timothyjc

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

8
user150924

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

javax.crypto.Cipher.getMaxAllowedKeyLength(String transformation)

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

4

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

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

それが機能しました。

3
Mohamed Mansour

ntoskrnl answerの更新バージョンを次に示します。さらに、コメントで言及されている 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"));
}
2
xoned

以下は、 実際のCipher.getMaxAllowedKeyLength によるisRestrictedCryptographyチェック、slf4jロギング、およびアプリケーションbootstrapからのシングルトン初期化のサポートを特徴とする@ntoskrnlのコードの修正バージョンです。

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;

// 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
    }
}
1
Vadzim

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

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

0
djangofan