8

次の Java コードは、メモリ内の秘密鍵をクリアする (すべてのバイト値を 0 に設定する) のに十分ですか?

zerorize(SecretKey key)
{
    byte[] rawKey = key.getEncoded();
    Arrays.fill(rawKey, (byte) 0);
}

つまり、getEncodedメソッドは実際のキーへのコピーまたは参照を返しますか? コピーが返された場合、セキュリティ対策として秘密鍵をクリアするにはどうすればよいですか?

4

8 に答える 8

4

getEncoded()主にキーのクローンを返すようです(インスタンスのOracle 1.6ソースからjavax.security.auth.kerberos):

public final byte[] getEncoded() {
  if (destroyed)
    throw new IllegalStateException("This key is no longer valid");
  return (byte[])keyBytes.clone();
}

したがって、返されたデータを消去しても、キーのすべてのコピーがメモリから消去されるわけではありません。

からキーをワイプする唯一の方法は、インターフェイスを実装してメソッドを呼び出す場合SecretKeyにキャストすることです。javax.security.auth.Destroyabledestroy()

public void destroy() throws DestroyFailedException {
  if (!destroyed) {
    destroyed = true;
    Arrays.fill(keyBytes, (byte) 0);
  }
}

奇妙なことに、すべての Key 実装が を実装していないようjavax.security.auth.Destroyableです。AES には使用されcom.sun.crypto.provider.DESedeKeyません。javax.crypto.spec.SecretKeySpecこれらのキーの実装はどちらも、getEncodedメソッド内でキーを複製します。これらの非常に一般的なアルゴリズムの 3DES と AES では、秘密鍵のメモリを消去する方法がないように見えますか?

于 2012-12-07T23:38:05.697 に答える
2

GetEncoded は秘密鍵のコピーを返し (したがって、クリアしても秘密鍵データには影響しません)、destroy はデフォルトで DestroyFailedException をスローします。また、1.8以降でしか利用できないため、Androidは運が悪い. これは、イントロスペクションを使用して、(1) 利用可能な場合は destroy を呼び出し、例外をスローしない、そうでない場合は (2) キー データをゼロにし、参照を null に設定するハックです。

package kiss.cipher;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

import javax.crypto.spec.SecretKeySpec;

/**
 * Created by wmacevoy on 10/12/16.
 */
public class CloseableKey implements AutoCloseable {

    // forward portable to JDK 1.8 to destroy keys
    // but usable in older JDK's
    static final Method DESTROY;
    static final Field KEY;

    static {
        Method _destroy = null;

        Field _key = null;
        try {
            Method destroy = SecretKeySpec.class.getMethod("destroy");
            SecretKeySpec key = new SecretKeySpec(new byte[16], "AES");
            destroy.invoke(key);
            _destroy = destroy;
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
        }

        try {
            _key = SecretKeySpec.class.getDeclaredField("key");
            _key.setAccessible(true);
        } catch (NoSuchFieldException | SecurityException ex) {
        }

        DESTROY = _destroy;
        KEY = _key;
    }

    static void close(SecretKeySpec secretKeySpec) {
        if (secretKeySpec != null) {
            if (DESTROY != null) {
                try {
                    DESTROY.invoke(secretKeySpec);
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                    throw new IllegalStateException("inconceivable: " + ex);
                }
            } else if (KEY != null) {
                try {
                    byte[] key = (byte[]) KEY.get(secretKeySpec);
                    Arrays.fill(key, (byte) 0);
                    KEY.set(secretKeySpec, null);
                } catch (IllegalAccessException | IllegalArgumentException ex) {
                    throw new IllegalStateException("inconceivable: " + ex);
                }
            }
        }
    }

    public final SecretKeySpec secretKeySpec;

    CloseableKey(SecretKeySpec _secretKeySpec) {

        secretKeySpec = _secretKeySpec;
    }

    @Override
    public void close() {
        close(secretKeySpec);
    }
}

使い方はこんな感じ

try (CloseableKey key = 
       new CloseableKey(new SecretKeySpec(data, 0, 16, "AES"))) {
  aesecb.init(Cipher.ENCRYPT_MODE, key.secretKeySpec);
}

Destroyable は 1.8 以降の機能のみであるため、Closeable インターフェイスを使用します。このバージョンは 1.7 以降で動作し、非常に効率的です (1 つのキーを試行的に破棄して、もう一度使用することを決定します)。

于 2017-03-25T05:07:45.513 に答える
1

rawKeyクリアしてものデータには影響しないと確信していますkey

SecretKeyのデータをクリアする方法は一般的にないと思います。特定の実装クラスがそれを提供するかもしれませんが、私はそれを提供するものを知りません。Androidでは、データをクリアしないままにするリスクは非常に低いです。各アプリは独自のプロセスで実行され、そのメモリは外部からは見えません。

ルート権限のあるプロセスがメモリのスナップショットを取得し、誰かの秘密鍵を発見することを期待して、分析のためにそれらをどこかのスーパーコンピューターに送信できる攻撃シナリオがあると思います。しかし、私はそのような攻撃について聞いたことがなく、システムにアクセスするための他の方法と競合しないと私は思います。この特定の仮想的な脆弱性について心配している理由はありますか?

于 2011-08-25T14:27:51.573 に答える
1

ガベージ コレクタを強化しているテクノロジによっては、任意の単一のオブジェクトがいつでも物理メモリ内で移動 (つまり、コピー) される可能性があるため、配列をゼロにすることによってキーを実際に破棄することを確信することはできません。キーを保持する"配列であり、そのコピーではありません。

簡単に言うと、セキュリティ モデルとコンテキストでキーのゼロ化が必要な場合は、Java をまったく使用しないでください (または、C とアセンブリ以外のほとんどすべてを使用する必要があります)。

于 2011-08-26T02:08:39.287 に答える
0

つまり、getEncoded メソッドは実際のキーへのコピーまたは参照を返しますか?

key.getEncoded()配列への参照を返します。

Array.fill を実行するときにキーの内容が破棄される場合は、キーが返された配列によってサポートされているかどうかによって異なります。ドキュメントを考えると、キーのエンコーディングがキーの別の表現であるかのように思えます。つまり、キーは返された配列によってサポートされていません。

それを見つけるのは簡単です。次のことを試してください。

byte[] rawKey = key.getEncoded();
Arrays.fill(rawKey, (byte) 0);

byte[] again = key.getEncoded();
Log.d(Arrays.equals(rawKey, again));

出力がfalseの場合、キーがまだ に保存されていることがわかりますSecretKey

于 2011-08-25T14:16:17.657 に答える
-1

プリミティブ値を除いて、配列を含む Java の他のすべては常に参照によって渡されるため、指定されたバイト配列を正しくクリアしています。

ただし、SecretKey クラスには、そのバイト配列を生成するために必要なデータが保持されている可能性があり、最終的には指定されたバイト配列の別のコピーが含まれるため、そのデータをクリアする方法も調査する必要があります。

于 2011-08-25T14:16:55.567 に答える
-4

少し違った方法で、上書きするメモリの正しい領域を特定したら、それを複数回実行したい場合があります。

zerorize(SecretKey key)
{
    byte[] rawKey = key.getEncoded();
    Arrays.fill(rawKey, (byte) 0xFF);
    Arrays.fill(rawKey, (byte) 0xAA);
    Arrays.fill(rawKey, (byte) 0x55);
    Arrays.fill(rawKey, (byte) 0x00);
}
于 2011-08-25T14:59:48.697 に答える