Java AES 및 자체 키 사용


88

자체 키로 AES를 사용하여 문자열을 암호화하고 싶습니다. 하지만 키의 비트 길이에 문제가 있습니다. 내 코드를 검토하고 수정 / 변경해야 할 사항을 확인할 수 있습니까?

public static void main(String[] args) throws Exception {
    String username = "bob@google.org";
    String password = "Password1";
    String secretID = "BlahBlahBlah";
    String SALT2 = "deliciously salty";

    // Get the Key
    byte[] key = (SALT2 + username + password).getBytes();
    System.out.println((SALT2 + username + password).getBytes().length);

    // Need to pad key for AES
    // TODO: Best way?

    // Generate the secret key specs.
    SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

    // Instantiate the cipher
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

    byte[] encrypted = cipher.doFinal((secrectID).getBytes());
    System.out.println("encrypted string: " + asHex(encrypted));

    cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
    byte[] original = cipher.doFinal(encrypted);
    String originalString = new String(original);
    System.out.println("Original string: " + originalString + "\nOriginal string (Hex): " + asHex(original));
}

지금은 " 잘못된 AES 키 길이 : 86 바이트 " 예외가 발생 합니다. 키를 패딩해야합니까? 어떻게해야합니까?

또한 ECB 또는 CBC에 대한 설정이 필요합니까?

감사



16
하하, 웃기다. 나는 실제로 무작위 솔트를 가지고 있지만 내 질문을 더 명확하게하기 위해 코드를 정리했습니다. 이것이 변수 이름이 SALT2 인 이유입니다. 그러나 이와 동일한 문제를 겪고 코드 복사 / 붙여 넣기를 좋아하는 다른 사람들에게는 좋은 참조입니다.
Bernie Perez

답변:


125

편집하다:

주석에 쓰여진 것처럼 이전 코드는 "모범 사례"가 아닙니다. 반복 횟수가 높은 PBKDF2와 같은 키 생성 알고리즘을 사용해야합니다. 또한 적어도 부분적으로 비 정적 (각 "ID"배타적 의미) 솔트를 사용해야합니다. 가능한 경우 무작위로 생성되어 암호문과 함께 저장됩니다.

    SecureRandom sr = SecureRandom.getInstanceStrong();
    byte[] salt = new byte[16];
    sr.nextBytes(salt);

    PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 1000, 128 * 8);
    SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(spec);
    Cipher aes = Cipher.getInstance("AES");
    aes.init(Cipher.ENCRYPT_MODE, key);

===========

이전 답변

SHA-1을 사용하여 키에서 해시를 생성하고 결과를 128 비트 (16 바이트)로 잘라야합니다.

또한 getBytes ()를 통해 Strings에서 바이트 배열을 생성하지 마십시오 . 플랫폼 기본 Charset을 사용합니다. 따라서 암호 "blaöä"는 다른 플랫폼에서 다른 바이트 배열을 생성합니다.

byte[] key = (SALT2 + username + password).getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit

SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

편집 : 키 크기로 256 비트가 필요한 경우 "JCE (Java Cryptography Extension) Unlimited Strength Jurisdiction Policy Files" Oracle 다운로드 링크를 다운로드하고 SHA-256을 해시로 사용하고 Arrays.copyOf 행을 제거 해야 합니다. "ECB"는 기본 암호 모드이고 "PKCS5Padding"은 기본 패딩입니다. 다음 형식을 사용하여 Cipher.getInstance 문자열을 통해 다른 암호화 모드 및 패딩 모드를 사용할 수 있습니다 . "Cipher / Mode / Padding"

CTS 및 PKCS5Padding을 사용하는 AES의 경우 문자열은 "AES / CTS / PKCS5Padding"입니다.


이것은 작동하지만 내 암호를 해싱 한 다음 처음 몇 비트 만 사용합니다. 이 작업을 수행하는 더 좋은 방법이 없습니까?
Bernie Perez

4
키를 생성하는 더 좋은 방법은 없습니다. AES에 128/192/256 비트 키가 필요하기 때문입니다. 키를 해시하지 않고 입력 만 자르면 처음 16/24/32 바이트 만 사용합니다. 따라서 해시를 생성하는 것이 유일한 합리적인 방법입니다.
mknjc

13
이 답변은 좋은 키 유도 기능을 사용하지 않으므로 안전하지 않습니다 . 약간 오래된 키 파생 함수에 대한 다른 답변 을 참조하십시오 -불행히도 여전히 정적 솔트입니다.
Maarten Bodewes

2
이 답변은 매우 나쁜 습관 이므로 삭제하는 것이 좋습니다 . 적절한 키 유도 기능을 사용해야합니다-최소한 PBKDF2.
보리스 스파이더

1
예, Maarten이 몇 년 전에 말했듯이 대답은 매우 나쁩니다. 에서이 답변을 확인하시기 바랍니다 암호화키 유도 기능
kelalaka

14

KeyGenerator를 사용하여 키를 생성해야합니다.

AES 키 길이는 사용하려는 암호에 따라 128, 192 및 256 비트입니다.

여기 에서 튜토리얼을 살펴보십시오.

다음은 암호 기반 암호화에 대한 코드입니다. System.in을 통해 입력 한 암호를 원하는 경우 저장된 암호를 사용하도록 변경할 수 있습니다.

        PBEKeySpec pbeKeySpec;
        PBEParameterSpec pbeParamSpec;
        SecretKeyFactory keyFac;

        // Salt
        byte[] salt = {
            (byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c,
            (byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99
        };

        // Iteration count
        int count = 20;

        // Create PBE parameter set
        pbeParamSpec = new PBEParameterSpec(salt, count);

        // Prompt user for encryption password.
        // Collect user password as char array (using the
        // "readPassword" method from above), and convert
        // it into a SecretKey object, using a PBE key
        // factory.
        System.out.print("Enter encryption password:  ");
        System.out.flush();
        pbeKeySpec = new PBEKeySpec(readPassword(System.in));
        keyFac = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

        // Create PBE Cipher
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");

        // Initialize PBE Cipher with key and parameters
        pbeCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);

        // Our cleartext
        byte[] cleartext = "This is another example".getBytes();

        // Encrypt the cleartext
        byte[] ciphertext = pbeCipher.doFinal(cleartext);

3
KeyGenerator를 사용하여 비밀번호로 키를 생성하려면 어떻게해야합니까? 비밀번호를 기반으로 동일한 키를 생성하고 싶습니다. 그래서 나중에 문자열을 해독 할 수 있습니다.
Bernie Perez

당신이 말하는 것은 AES가 아닌 암호 기반 암호화입니다. PBE
Keibosh

5
SecretKeyFactory최신 암호화를 위해 문자열 "PBKDF2WithHmacSHA1"을 사용하는 대신 PBEKDF2 키 생성기를 사용해보십시오 .
Maarten Bodewes 2013 년

12
실제로이 답변에서 사용되는 모든 암호화 기본 형식은 확실히 오래된 , MD5 및 DES입니다. 주의하십시오.
Maarten Bodewes

MD5 및 DES는 약한 암호 모음이며 피해야합니다.
atom88

6
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;
import java.io.BufferedReader;
import java.io.FileReader;

public class AESFile 
{
private static String algorithm = "AES";
private static byte[] keyValue=new byte[] {'0','2','3','4','5','6','7','8','9','1','2','3','4','5','6','7'};// your key

    // Performs Encryption
    public static String encrypt(String plainText) throws Exception 
    {
            Key key = generateKey();
            Cipher chiper = Cipher.getInstance(algorithm);
            chiper.init(Cipher.ENCRYPT_MODE, key);
            byte[] encVal = chiper.doFinal(plainText.getBytes());
            String encryptedValue = new BASE64Encoder().encode(encVal);
            return encryptedValue;
    }

    // Performs decryption
    public static String decrypt(String encryptedText) throws Exception 
    {
            // generate key 
            Key key = generateKey();
            Cipher chiper = Cipher.getInstance(algorithm);
            chiper.init(Cipher.DECRYPT_MODE, key);
            byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedText);
            byte[] decValue = chiper.doFinal(decordedValue);
            String decryptedValue = new String(decValue);
            return decryptedValue;
    }

//generateKey() is used to generate a secret key for AES algorithm
    private static Key generateKey() throws Exception 
    {
            Key key = new SecretKeySpec(keyValue, algorithm);
            return key;
    }

    // performs encryption & decryption 
    public static void main(String[] args) throws Exception 
    {
        FileReader file = new FileReader("C://myprograms//plaintext.txt");
        BufferedReader reader = new BufferedReader(file);
        String text = "";
        String line = reader.readLine();
    while(line!= null)
        {
            text += line;
    line = reader.readLine();
        }
        reader.close();
    System.out.println(text);

            String plainText = text;
            String encryptedText = AESFile.encrypt(plainText);
            String decryptedText = AESFile.decrypt(encryptedText);

            System.out.println("Plain Text : " + plainText);
            System.out.println("Encrypted Text : " + encryptedText);
            System.out.println("Decrypted Text : " + decryptedText);
    }
}

5
설명 텍스트를 더 추가 할 수 있습니다.
ChrisG

질문, keyValue바이트 배열과 함께 갖는 요점은 무엇 입니까? 열쇠를 만드는 데 사용되는 이유는 무엇입니까? SecretKey대신 like 를 사용하여 수행 할 수 있습니까 ? 그렇다면 어떻게?
Austin

@Mandrek, "plaintext.txt"파일의 내용이 암호화됩니다. 위의 논리는 FileReader 생성자에서 인수로 읽은 파일의 데이터 / 메시지를 암호화합니다.
Shankar Murthy

2

이것은 작동합니다.

public class CryptoUtils {

    private  final String TRANSFORMATION = "AES";
    private  final String encodekey = "1234543444555666";
    public  String encrypt(String inputFile)
            throws CryptoException {
        return doEncrypt(encodekey, inputFile);
    }


    public  String decrypt(String input)
            throws CryptoException {
    // return  doCrypto(Cipher.DECRYPT_MODE, key, inputFile);
    return doDecrypt(encodekey,input);
    }

    private  String doEncrypt(String encodekey, String inputStr)   throws CryptoException {
        try {

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);

            byte[] key = encodekey.getBytes("UTF-8");
            MessageDigest sha = MessageDigest.getInstance("SHA-1");
            key = sha.digest(key);
            key = Arrays.copyOf(key, 16); // use only first 128 bit

            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);

            byte[] inputBytes = inputStr.getBytes();     
            byte[] outputBytes = cipher.doFinal(inputBytes);

            return Base64Utils.encodeToString(outputBytes);

        } catch (NoSuchPaddingException | NoSuchAlgorithmException
                | InvalidKeyException | BadPaddingException
                | IllegalBlockSizeException | IOException ex) {
            throw new CryptoException("Error encrypting/decrypting file", ex);
       }
     }


    public  String doDecrypt(String encodekey,String encrptedStr) { 
          try {     

              Cipher dcipher = Cipher.getInstance(TRANSFORMATION);
              dcipher = Cipher.getInstance("AES");
              byte[] key = encodekey.getBytes("UTF-8");
              MessageDigest sha = MessageDigest.getInstance("SHA-1");
              key = sha.digest(key);
              key = Arrays.copyOf(key, 16); // use only first 128 bit

              SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");

              dcipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            // decode with base64 to get bytes

              byte[] dec = Base64Utils.decode(encrptedStr.getBytes());  
              byte[] utf8 = dcipher.doFinal(dec);

              // create new string based on the specified charset
              return new String(utf8, "UTF8");

          } catch (Exception e) {

            e.printStackTrace();

          }
      return null;
      }
 }

2

MD5, AES, 패딩 없음

import static javax.crypto.Cipher.DECRYPT_MODE;
import static javax.crypto.Cipher.ENCRYPT_MODE;
import static org.apache.commons.io.Charsets.UTF_8;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class PasswordUtils {

    private PasswordUtils() {}

    public static String encrypt(String text, String pass) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(ENCRYPT_MODE, key);

            byte[] encrypted = cipher.doFinal(text.getBytes(UTF_8));
            byte[] encoded = Base64.getEncoder().encode(encrypted);
            return new String(encoded, UTF_8);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot encrypt", e);
        }
    }

    public static String decrypt(String text, String pass) {
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            Key key = new SecretKeySpec(messageDigest.digest(pass.getBytes(UTF_8)), "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(DECRYPT_MODE, key);

            byte[] decoded = Base64.getDecoder().decode(text.getBytes(UTF_8));
            byte[] decrypted = cipher.doFinal(decoded);
            return new String(decrypted, UTF_8);

        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            throw new RuntimeException("Cannot decrypt", e);
        }
    }
}

각도 (ionic 4)에서 SecretKeySpec과 같은 보안 키를 만드는 방법
Nitin Karale

0
    byte[] seed = (SALT2 + username + password).getBytes();
    SecureRandom random = new SecureRandom(seed);
    KeyGenerator generator;
    generator = KeyGenerator.getInstance("AES");
    generator.init(random);
    generator.init(256);
    Key keyObj = generator.generateKey();
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.