web-dev-qa-db-ja.com

AES-256パスワードベースの暗号化/復号化Java

JavaでAES暗号化/復号化を実装するためのガイドを見つけ、それを自分のソリューションに入れるときに各行を理解しようとしました。しかし、私はそれを完全に理解せず、最終的な目標は、パスフレーズベースの暗号化/復号化を行うことです。これについて他の記事/ stackoverflowの投稿を読みましたが、ほとんどは十分な説明を提供していません(Javaの暗号化は非常に新しいです)

私の現在の主な問題は、byte[] saltBytes = "Hello".getBytes();を設定しても、最終的には異なるBase64結果が得られることです(_char[] password_は毎回ランダムですが、パスワードを残しておくほうが安全だと読みました_char[]_フォーム。他の問題は、プログラムがdecrypt()に到達すると、byte[] saltBytes = salt.getBytes("UTF-8");でNullPointerExceptionが発生することです。

あなたが私に与えることができる助け/アドバイスを前もってありがとう。

問題のコード:

_import Java.security.AlgorithmParameters;
import Java.security.NoSuchAlgorithmException;
import Java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class EncryptionDecryption {

    private static String salt;
    private static int iterations = 65536  ;
    private static int keySize = 256;
    private static byte[] ivBytes;

    public static void main(String []args) throws Exception {

        char[] message = "PasswordToEncrypt".toCharArray();
        System.out.println("Message: " + message.toString());
        System.out.println("Encrypted: " + encrypt(message));
        System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
    }

    public static String encrypt(char[] plaintext) throws Exception {

        salt = getSalt();
        byte[] saltBytes = salt.getBytes();

        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
        SecretKey secretKey = skf.generateSecret(spec);
        SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
        AlgorithmParameters params = cipher.getParameters();
        ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
        byte[] encryptedTextBytes = cipher.doFinal(plaintext.toString().getBytes("UTF-8"));

        return DatatypeConverter.printBase64Binary(encryptedTextBytes);
    }

    public static String decrypt(char[] encryptedText) throws Exception {

        byte[] saltBytes = salt.getBytes("UTF-8");
        byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(encryptedText.toString());

        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(encryptedText, saltBytes, iterations, keySize);
        SecretKey secretkey = skf.generateSecret(spec);
        SecretKeySpec secretSpec = new SecretKeySpec(secretkey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));

        byte[] decryptedTextBytes = null;

        try {
            decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        }   catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }   catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return decryptedTextBytes.toString();

    }

    public static String getSalt() throws Exception {

        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[20];
        sr.nextBytes(salt);
        return salt.toString();
    }
}
_
9
volo_java

私はあなたが2つの間違いを犯していると思います:)

動作するようにサンプルコードを修正しました。

import Java.security.AlgorithmParameters;
import Java.security.NoSuchAlgorithmException;
import Java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

public class EncryptionDecryption {

    private static String salt;
    private static int iterations = 65536  ;
    private static int keySize = 256;
    private static byte[] ivBytes;

    private static SecretKey secretKey;

    public static void main(String []args) throws Exception {

        salt = getSalt();

        char[] message = "PasswordToEncrypt".toCharArray();
        System.out.println("Message: " + String.valueOf(message));
        System.out.println("Encrypted: " + encrypt(message));
        System.out.println("Decrypted: " + decrypt(encrypt(message).toCharArray()));
    }

    public static String encrypt(char[] plaintext) throws Exception {
        byte[] saltBytes = salt.getBytes();

        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        PBEKeySpec spec = new PBEKeySpec(plaintext, saltBytes, iterations, keySize);
        secretKey = skf.generateSecret(spec);
        SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretSpec);
        AlgorithmParameters params = cipher.getParameters();
        ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
        byte[] encryptedTextBytes = cipher.doFinal(String.valueOf(plaintext).getBytes("UTF-8"));

        return DatatypeConverter.printBase64Binary(encryptedTextBytes);
    }

    public static String decrypt(char[] encryptedText) throws Exception {

        System.out.println(encryptedText);

        byte[] encryptedTextBytes = DatatypeConverter.parseBase64Binary(new String(encryptedText));
        SecretKeySpec secretSpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, secretSpec, new IvParameterSpec(ivBytes));

        byte[] decryptedTextBytes = null;

        try {
            decryptedTextBytes = cipher.doFinal(encryptedTextBytes);
        }   catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        }   catch (BadPaddingException e) {
            e.printStackTrace();
        }

        return new String(decryptedTextBytes);

    }

    public static String getSalt() throws Exception {

        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[20];
        sr.nextBytes(salt);
        return new String(salt);
    }
}

最初の間違いは、暗号化方式を使用するときに2つの異なるソルトを生成するため、暗号化/復号化されたログが異なることです(論理的ですが、暗号化の直後に復号化を呼び出すため、復号化は引き続き機能します)。

2番目の間違いは、秘密鍵に関するものでした。暗号化するときは秘密キーを生成する必要がありますが、復号化はしません。もっと簡単に言えば、「暗号化」というパスワードで暗号化しており、「復号化」というパスワードで復号化しようとしているようです。

すべてのランダムなもの(プライベートキー、ソルトなどの起動時)を生成することをお勧めします。ただし、アプリを停止すると、まったく同じランダムなものを取得しない限り、古いものを復号化できないことに注意してください。

私が助けたことを願っています:)

よろしく、

19
user4584346