0

私はほとんど動作する次のコードを持っています:

AES.java

import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {

    private static final int    KEY_LENGTH              = 128;
    private static final int    ITERATIONS              = 100;

    private static final String ALGORITHM               = "AES";
    private static final String SECRET_KEY_ALGORITHM    = "PBKDF2WithHmacSHA1";
    private static final String TRANSFORMATION          = "AES/CBC/PKCS5Padding";

    private final Cipher        m_enc_cipher;
    private final Cipher        m_dec_cipher;

    private final byte[]        m_iv;

    public AES(final char[] password, final byte[] salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException,
            NoSuchPaddingException, InvalidKeyException,
            InvalidParameterSpecException, IllegalBlockSizeException,
            BadPaddingException, UnsupportedEncodingException,
            InvalidAlgorithmParameterException {

        // Derive the key, given password and salt
        final SecretKeyFactory factory = SecretKeyFactory
                .getInstance(SECRET_KEY_ALGORITHM);
        final KeySpec spec = new PBEKeySpec(password, salt, ITERATIONS,
                KEY_LENGTH);
        final SecretKey tmp = factory.generateSecret(spec);
        final SecretKey secret = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);

        // Build encryptor and get IV
        final Cipher enc_cipher = Cipher.getInstance(TRANSFORMATION);
        enc_cipher.init(Cipher.ENCRYPT_MODE, secret);
        final AlgorithmParameters params = enc_cipher.getParameters();
        final byte[] iv = params.getParameterSpec(IvParameterSpec.class)
                .getIV();

        // Build decryptor
        final Cipher dec_cipher = Cipher.getInstance(TRANSFORMATION);
        dec_cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));

        this.m_enc_cipher = enc_cipher;
        this.m_dec_cipher = dec_cipher;

        this.m_iv = iv;
    }

    public AES(final byte[] iv) throws NoSuchAlgorithmException,
            InvalidKeySpecException, NoSuchPaddingException,
            InvalidKeyException, InvalidParameterSpecException,
            IllegalBlockSizeException, BadPaddingException,
            UnsupportedEncodingException, InvalidAlgorithmParameterException {

        final AlgorithmParameterSpec aps = new IvParameterSpec(iv);

        final KeyGenerator keygen = KeyGenerator.getInstance(ALGORITHM);
        keygen.init(KEY_LENGTH);
        final SecretKey secret = keygen.generateKey();

        // Build encryptor
        final Cipher enc_cipher = Cipher.getInstance(TRANSFORMATION);
        enc_cipher.init(Cipher.ENCRYPT_MODE, secret, aps);

        // Build decryptor
        final Cipher dec_cipher = Cipher.getInstance(TRANSFORMATION);
        dec_cipher.init(Cipher.DECRYPT_MODE, secret, aps);

        this.m_enc_cipher = enc_cipher;
        this.m_dec_cipher = dec_cipher;

        this.m_iv = iv;
    }

    public byte[] get_iv() {
        return this.m_iv;
    }

    public byte[] encrypt(final byte[] data) throws NoSuchAlgorithmException,
            InvalidKeySpecException, NoSuchPaddingException,
            InvalidKeyException, InvalidParameterSpecException,
            IllegalBlockSizeException, BadPaddingException,
            UnsupportedEncodingException {
        return this.m_enc_cipher.doFinal(data);
    }

    public byte[] decrypt(final byte[] data) throws IllegalBlockSizeException,
            BadPaddingException {
        return this.m_dec_cipher.doFinal(data);
    }
}

AESTest.java

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

import org.junit.Test;
import static org.junit.Assert.*;

public class AESTest {
    @Test
    public void test() throws InvalidKeyException, NoSuchAlgorithmException,
            InvalidKeySpecException, NoSuchPaddingException,
            InvalidParameterSpecException, IllegalBlockSizeException,
            BadPaddingException, UnsupportedEncodingException,
            InvalidAlgorithmParameterException {

        final char[] password = "my_password".toCharArray();
        final byte[] salt = new byte[] { 22, 11 };

        final byte[] original_data = "Hello, World!".getBytes("UTF-8");
        final AES aesA = new AES(password, salt);
        final byte[] encrypted_data = aesA.encrypt(original_data);
        System.out.println("Encrypted:");
        System.out.println(javax.xml.bind.DatatypeConverter
                .printBase64Binary(encrypted_data));

        final AES aesB = new AES(aesA.get_iv());
        final byte[] decrypted_data_B = aesB.decrypt(encrypted_data);
        System.out.println("Decrypted B:");
        System.out.println(javax.xml.bind.DatatypeConverter
                .printBase64Binary(decrypted_data_B));
        assertTrue(Arrays.equals(original_data, decrypted_data_B));
    }
}

テスト (AESTest.java) では、次のエラーが表示されます。

javax.crypto.BadPaddingException: Given final block not properly padded

私の最終的な目的は、暗号化されたデータ ブロックを送信し、後でそれを取得できるようにすることです。
「後で」という用語は、日/週/年を指す場合があります。
次に、同じパスワードを使用して復号化します。

「後で」は 1 か月になる可能性があるため、新しいAESオブジェクトを作成する必要があります。

ここで、この新しいAESオブジェクトは、同じ固定パスワード/ソルトでデータを復号化できる必要があります。
それで全部です。

同じ IV を使用しようとしましたが、機能しません。

ここで何が間違っていますか?

編集#1:ITERATIONS同様 に注意してください。

4

2 に答える 2

3

AES を初期iv化すると、別のsecret. 間違ってない?

同じパスワードとソルトを使用していません。

于 2012-04-07T23:21:01.533 に答える
2

既に述べたように、2 番目のコンストラクターは新しい AES キーを作成するため、最初のコンストラクターで作成されたものとは一致しません。暗号化に適した AES インスタンスと復号化に適した別のインスタンスをそれぞれ作成する 2 つの異なるコンストラクターを使用する意図がわかりませんか? 暗号化するか復号化するかに応じて、その設計を再考し、単一のインスタンスのみを使用することは理にかなっています。

最初のコンストラクターで生成するキーは秘密にされますが、生成される IV は公開情報であり、安全に公開できます。これによりセキュリティが損なわれることはありません。ただし、暗号化されたメッセージごとに一意の IV を作成する必要があります。そうしないと、使用している CBC モードに対して攻撃を受ける可能性があります。

そこで、私がお勧めする方法は次のとおりです。

暗号化:

基本的に最初のコンストラクターで、復号化のために 2 番目のインスタンスを除外します。作成された IV を取得します。

復号化:

これも基本的に最初のコンストラクターで、IV パラメーターが追加されています。まったく同じパラメーター (ソルト、パスワード、反復) を使用して最初からキーを再度作成し、今回Cipherは復号化モードで開始し、さらに IV パラメーターを渡します。

それはそれを行う必要があります!

于 2012-04-08T17:23:41.637 に答える