다음 예의 문제점은 무엇입니까?
문제는 해독 된 문자열의 첫 번째 부분이 말도 안된다는 것입니다. 그러나 나머지는 괜찮습니다.
Result: `£eB6O�geS��i are you? Have a nice day.
@Test
public void testEncrypt() {
try {
String s = "Hello there. How are you? Have a nice day.";
// Generate key
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey aesKey = kgen.generateKey();
// Encrypt cipher
Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey);
// Encrypt
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, encryptCipher);
cipherOutputStream.write(s.getBytes());
cipherOutputStream.flush();
cipherOutputStream.close();
byte[] encryptedBytes = outputStream.toByteArray();
// Decrypt cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
// Decrypt
outputStream = new ByteArrayOutputStream();
ByteArrayInputStream inStream = new ByteArrayInputStream(encryptedBytes);
CipherInputStream cipherInputStream = new CipherInputStream(inStream, decryptCipher);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = cipherInputStream.read(buf)) >= 0) {
outputStream.write(buf, 0, bytesRead);
}
System.out.println("Result: " + new String(outputStream.toByteArray()));
}
catch (Exception ex) {
ex.printStackTrace();
}
}
답변
저를 포함한 많은 사람들이 Base64로 변환하는 것을 잊고 초기화 벡터, 문자 집합 등과 같은 정보가 누락되어이 작업을 수행하는 데 많은 문제에 직면합니다. 그래서 저는 완전한 기능의 코드를 만들 생각을했습니다.
이것이 여러분 모두에게 유용하길 바랍니다 : 컴파일하려면 추가 Apache Commons Codec jar가 필요합니다.
http://commons.apache.org/proper/commons-codec/download_codec.cgi
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class Encryptor {
public static String encrypt(String key, String initVector, String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
System.out.println("encrypted string: "
+ Base64.encodeBase64String(encrypted));
return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String key = "Bar12345Bar12345"; // 128 bit key
String initVector = "RandomInitVector"; // 16 bytes IV
System.out.println(decrypt(key, initVector,
encrypt(key, initVector, "Hello World")));
}
}
답변
여기에없는 솔루션 Apache Commons Codec
의 Base64
:
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AdvancedEncryptionStandard
{
private byte[] key;
private static final String ALGORITHM = "AES";
public AdvancedEncryptionStandard(byte[] key)
{
this.key = key;
}
/**
* Encrypts the given plain text
*
* @param plainText The plain text to encrypt
*/
public byte[] encrypt(byte[] plainText) throws Exception
{
SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return cipher.doFinal(plainText);
}
/**
* Decrypts the given byte array
*
* @param cipherText The data to decrypt
*/
public byte[] decrypt(byte[] cipherText) throws Exception
{
SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(cipherText);
}
}
사용 예 :
byte[] encryptionKey = "MZygpewJsCpRrfOr".getBytes(StandardCharsets.UTF_8);
byte[] plainText = "Hello world!".getBytes(StandardCharsets.UTF_8);
AdvancedEncryptionStandard advancedEncryptionStandard = new AdvancedEncryptionStandard(
encryptionKey);
byte[] cipherText = advancedEncryptionStandard.encrypt(plainText);
byte[] decryptedCipherText = advancedEncryptionStandard.decrypt(cipherText);
System.out.println(new String(plainText));
System.out.println(new String(cipherText));
System.out.println(new String(decryptedCipherText));
인쇄물:
Hello world!
դ;��LA+�ߙb*
Hello world!
답변
IV (초기화 벡터)를 제대로 처리하지 않는 것 같습니다. AES, IV 및 블록 체인에 대해 마지막으로 읽은 지 오래되었지만
IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());
괜찮지 않은 것 같습니다. AES의 경우 초기화 벡터를 암호 인스턴스의 “초기 상태”로 생각할 수 있으며이 상태는 키에서 얻을 수 없지만 암호화 암호의 실제 계산에서 얻을 수있는 약간의 정보입니다. (키에서 IV를 추출 할 수 있다면 키가 초기화 단계에서 이미 암호 인스턴스에 주어 졌기 때문에 쓸모가 없을 것이라고 주장 할 수 있습니다.)
따라서 암호화가 끝날 때 암호 인스턴스에서 IV를 byte []로 가져와야합니다.
cipherOutputStream.close();
byte[] iv = encryptCipher.getIV();
당신은 당신의 초기화 할 필요가 Cipher
있는을 DECRYPT_MODE
] [이 바이트 :
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
그러면 암호 해독이 정상입니다. 도움이 되었기를 바랍니다.
답변
암호 해독에 사용하는 IV가 잘못되었습니다. 이 코드 교체
//Decrypt cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
이 코드로
//Decrypt cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptCipher.getIV());
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);
그리고 그것은 당신의 문제를 해결해야합니다.
다음은 Java로 된 간단한 AES 클래스의 예입니다. 이 클래스는 응용 프로그램의 모든 특정 요구 사항을 설명하지 못할 수 있으므로 프로덕션 환경에서는 사용하지 않는 것이 좋습니다.
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class AES
{
public static byte[] encrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
{
return AES.transform(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, messageBytes);
}
public static byte[] decrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
{
return AES.transform(Cipher.DECRYPT_MODE, keyBytes, ivBytes, messageBytes);
}
private static byte[] transform(final int mode, final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
{
final SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
final IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
byte[] transformedBytes = null;
try
{
final Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(mode, keySpec, ivSpec);
transformedBytes = cipher.doFinal(messageBytes);
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e)
{
e.printStackTrace();
}
return transformedBytes;
}
public static void main(final String[] args) throws InvalidKeyException, InvalidAlgorithmParameterException
{
//Retrieved from a protected local file.
//Do not hard-code and do not version control.
final String base64Key = "ABEiM0RVZneImaq7zN3u/w==";
//Retrieved from a protected database.
//Do not hard-code and do not version control.
final String shadowEntry = "AAECAwQFBgcICQoLDA0ODw==:ZtrkahwcMzTu7e/WuJ3AZmF09DE=";
//Extract the iv and the ciphertext from the shadow entry.
final String[] shadowData = shadowEntry.split(":");
final String base64Iv = shadowData[0];
final String base64Ciphertext = shadowData[1];
//Convert to raw bytes.
final byte[] keyBytes = Base64.getDecoder().decode(base64Key);
final byte[] ivBytes = Base64.getDecoder().decode(base64Iv);
final byte[] encryptedBytes = Base64.getDecoder().decode(base64Ciphertext);
//Decrypt data and do something with it.
final byte[] decryptedBytes = AES.decrypt(keyBytes, ivBytes, encryptedBytes);
//Use non-blocking SecureRandom implementation for the new IV.
final SecureRandom secureRandom = new SecureRandom();
//Generate a new IV.
secureRandom.nextBytes(ivBytes);
//At this point instead of printing to the screen,
//one should replace the old shadow entry with the new one.
System.out.println("Old Shadow Entry = " + shadowEntry);
System.out.println("Decrytped Shadow Data = " + new String(decryptedBytes, StandardCharsets.UTF_8));
System.out.println("New Shadow Entry = " + Base64.getEncoder().encodeToString(ivBytes) + ":" + Base64.getEncoder().encodeToString(AES.encrypt(keyBytes, ivBytes, decryptedBytes)));
}
}
AES는 인코딩과 관련이 없으므로 타사 라이브러리없이 별도로 처리하기로 선택한 이유입니다.
답변
이 답변에서 나는 이것이 대부분의 독자에게 이익이 될 것이라고 생각하기 때문에 특정 디버깅 질문이 아닌 “Simple Java AES encrypt / decrypt example”기본 테마에 접근하기로 선택했습니다.
이것은 Java의 AES 암호화에 대한 내 블로그 게시물 의 간단한 요약입니다. 이므로 구현하기 전에 읽어 보는 것이 좋습니다. 그러나 나는 여전히 사용하는 간단한 예제를 제공하고주의해야 할 몇 가지 지침을 제공 할 것입니다.
이 예에서 나는 사용을 선택합니다 인증 암호화를 함께 갈루아 / 카운터 모드 또는 GCM의 모드. 그 이유는 대부분의 경우 기밀성과 함께 무결성과 신뢰성 을 원하기 때문입니다 ( 블로그 에서 자세히 알아보기 ).
AES-GCM 암호화 / 복호화 자습서
다음은 JCA (Java Cryptography Architecture) 를 사용하여 AES-GCM 으로 암호화 / 복호화하는 데 필요한 단계 입니다. 다른 예와 혼합하지 마십시오미묘한 차이로 인해 코드가 완전히 안전하지 않을 수 있으므로 .
1. 키 생성
사용 사례에 따라 다르므로 가장 간단한 경우 인 임의의 비밀 키를 가정하겠습니다.
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = SecretKeySpec(key, "AES");
중대한:
- 항상 강한 사용하는 의사 난수 생성기 등이
SecureRandom
- 16 바이트 / 128 비트 긴 키 사용 (또는 그 이상- 그러나 더 많은 것은 거의 필요하지 않음) )
- 사용자 암호에서 파생 된 키를 원한다면 PBKDF2 또는 bcrypt 와 같은 확장 속성이 있는 암호 해시 함수 (또는 KDF) 를 살펴보십시오.
- 다른 소스에서 파생 된 키를 원하는 경우 HKDF ( 여기에서 Java 구현 ) 와 같은 적절한 키 파생 함수 (KDF)를 사용 하십시오 . 마십시오 하지 간단한 사용하는 암호 해시를 그 (등에 SHA-256 ).
2. 초기화 벡터 생성
초기화 벡터 (IV)는 동일한 비밀 키가 다른 것이다 만들도록 사용되는 암호 텍스트를 .
byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);
중대한:
- 동일한 키로 동일한 IV 를 재사용 하지 마십시오 ( GCM / CTR 모드 에서 매우 중요 ).
- IV는 고유해야합니다 (예 : 임의 IV 또는 카운터 사용).
- IV는 비밀이 필요하지 않습니다
- 항상 강한 사용하는 의사 난수 생성기 등이
SecureRandom
- 12 바이트 IV는 AES-GCM 모드에 대한 올바른 선택입니다.
3. IV 및 키로 암호화
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] cipherText = cipher.doFinal(plainText);
중대한:
- 16 바이트 / 128 비트 인증 태그 사용 사용 (무결성 / 진위성을 확인하는 데 사용됨)
- 인증 태그가 자동으로 암호 텍스트에 추가됩니다 (JCA 구현에서).
- GCM은 스트림 암호처럼 작동하므로 패딩이 필요하지 않습니다.
- 사용하다
CipherInputStream
대량의 데이터를 암호화 할 때 - 변경된 경우 추가 (비밀) 데이터를 확인 하시겠습니까? 여기 에서 더보기 와 관련된 데이터 를 사용할 수 있습니다
cipher.updateAAD(associatedData);
.
3. 단일 메시지로 직렬화
IV와 암호문 만 추가하면됩니다. 위에서 언급했듯이 IV는 비밀 일 필요가 없습니다.
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();
문자열 표현이 필요한 경우 선택적으로 Base64로 인코딩 합니다. 어느 사용하는 안드로이드의 또는 자바 (8 개)의 내장 구현 (아파치 코 몬즈 코덱을 사용하지 마십시오 – 그것은 끔찍한 구현입니다). 인코딩은 바이트 배열을 문자열 표현으로 “변환”하여 ASCII 안전을 만드는 데 사용됩니다. 예 :
String base64CipherMessage = Base64.getEncoder().encodeToString(cipherMessage);
4. 해독 준비 : 역 직렬화
메시지를 인코딩 한 경우 먼저 바이트 배열로 디코딩합니다.
byte[] cipherMessage = Base64.getDecoder().decode(base64CipherMessage)
중대한:
- 너무 많은 메모리를 할당 하여 서비스 거부 공격 을 방지하려면 입력 매개 변수의 유효성을 검사 해야 합니다.
5. 해독
암호를 초기화하고 암호화와 동일한 매개 변수를 설정합니다.
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
//use first 12 bytes for iv
AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128, cipherMessage, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmIv);
//use everything from 12 bytes on as ciphertext
byte[] plainText = cipher.doFinal(cipherMessage, 12, cipherMessage.length - 12);
중대한:
- 암호화 중에 추가 한 경우 관련 데이터 를 추가하는 것을 잊지 마십시오
cipher.updateAAD(associatedData);
.
최신 Android (SDK 21+) 및 Java (7+) 구현에는 AES-GCM이 있어야합니다. 이전 버전에는 부족할 수 있습니다. 비슷한 모드 인 Encrypt-then-Mac (예 : AES-CBC + HMAC 사용 )에 비해 더 효율적일뿐만 아니라 구현하기가 더 쉽기 때문에 여전히이 모드를 선택합니다 . HMAC로 AES-CBC를 구현하는 방법에 대한이 문서를 참조하십시오 .
답변
온라인 편집기 실행 가능 버전 :-
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//import org.apache.commons.codec.binary.Base64;
import java.util.Base64;
public class Encryptor {
public static String encrypt(String key, String initVector, String value) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(value.getBytes());
//System.out.println("encrypted string: "
// + Base64.encodeBase64String(encrypted));
//return Base64.encodeBase64String(encrypted);
String s = new String(Base64.getEncoder().encode(encrypted));
return s;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public static void main(String[] args) {
String key = "Bar12345Bar12345"; // 128 bit key
String initVector = "RandomInitVector"; // 16 bytes IV
System.out.println(encrypt(key, initVector, "Hello World"));
System.out.println(decrypt(key, initVector, encrypt(key, initVector, "Hello World")));
}
}
답변
종종 표준 라이브러리 제공 솔루션에 의존하는 것이 좋습니다.
private static void stackOverflow15554296()
throws
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException
{
// prepare key
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecretKey aesKey = keygen.generateKey();
String aesKeyForFutureUse = Base64.getEncoder().encodeToString(
aesKey.getEncoded()
);
// cipher engine
Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
// cipher input
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] clearTextBuff = "Text to encode".getBytes();
byte[] cipherTextBuff = aesCipher.doFinal(clearTextBuff);
// recreate key
byte[] aesKeyBuff = Base64.getDecoder().decode(aesKeyForFutureUse);
SecretKey aesDecryptKey = new SecretKeySpec(aesKeyBuff, "AES");
// decipher input
aesCipher.init(Cipher.DECRYPT_MODE, aesDecryptKey);
byte[] decipheredBuff = aesCipher.doFinal(cipherTextBuff);
System.out.println(new String(decipheredBuff));
}
그러면 “인코딩 할 텍스트”가 인쇄됩니다.
솔루션은 Java Cryptography Architecture Reference Guide 및 https://stackoverflow.com/a/20591539/146745 답변을 기반으로 합니다.
