256 비트 AES 암호화를 구현해야하지만 온라인에서 찾은 모든 예제는 “KeyGenerator”를 사용하여 256 비트 키를 생성하지만 고유 한 패스 키를 사용하고 싶습니다. 내 키를 어떻게 만들 수 있습니까? 256 비트로 패딩을 시도했지만 키가 너무 길다는 오류가 발생합니다. 무제한 관할 패치가 설치되어 있으므로 문제가 아닙니다. 🙂
즉. KeyGenerator는 다음과 같습니다.
// Get the KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
편집하다
실제로 암호가 비트가 아닌 256 바이트로 채워져 너무 길었습니다. 다음은 이것에 대한 경험이 많으므로 지금 사용하고있는 코드입니다.
byte[] key = null; // TODO
byte[] input = null; // TODO
byte[] output = null;
SecretKeySpec keySpec = null;
keySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
output = cipher.doFinal(input)
“TODO”비트는 스스로해야합니다 🙂
답변
받는 사람이 대역 외와 password
(a char[]
) 및 salt
( 비밀을 유지할 필요가없는 좋은 소금으로 만드는 byte[]
-8 바이트 )를 공유하십시오 SecureRandom
. 그런 다음이 정보에서 유용한 키를 도출하십시오.
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
매직 넘버 (어딘가에 상수로 정의 될 수 있음) 65536과 256은 각각 키 파생 반복 횟수와 키 크기입니다.
키 파생 함수는 상당한 계산 노력을 요구하기 위해 반복되어 공격자가 여러 가지 다른 암호를 빠르게 시도하지 못하게합니다. 사용 가능한 컴퓨팅 리소스에 따라 반복 횟수를 변경할 수 있습니다.
키 크기를 128 비트로 줄일 수 있는데, 여전히 “강력한”암호화로 간주되지만 공격이 AES를 약화시키는 것으로 밝혀지면 안전성이 크게 향상되지 않습니다.
적절한 블록 체인 모드와 함께 사용하면 동일한 파생 키를 사용하여 많은 메시지를 암호화 할 수 있습니다. 에 암호 블록 체인 (CBC) , 임의의 초기화 벡터 (IV)의 평문이 동일해도 다른 암호문을 수득 각 메시지에 대해 생성된다. CBC는 가장 안전한 모드가 아닐 수 있습니다 (아래 AEAD 참조). 보안 속성이 다른 많은 모드가 있지만 모두 유사한 임의 입력을 사용합니다. 어쨌든 각 암호화 작업의 출력은 암호 텍스트 와 초기화 벡터입니다.
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
ciphertext
및을 저장하십시오 iv
. 암호 해독시 SecretKey
암호는 동일한 소금 및 반복 매개 변수를 사용하여 정확하게 동일한 방식으로 재생성됩니다. 이 키 와 메시지와 함께 저장된 초기화 벡터로 암호를 초기화하십시오 .
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
String plaintext = new String(cipher.doFinal(ciphertext), "UTF-8");
System.out.println(plaintext);
Java 7에는 AEAD 암호 모드에 대한 API 지원이 포함되어 있으며 OpenJDK 및 Oracle 배포에 포함 된 “SunJCE”공급자는 Java 8부터 이러한 구현을 구현합니다. 이러한 모드 중 하나는 CBC 대신 권장됩니다. 데이터의 무결성과 개인 정보를 보호합니다.
java.security.InvalidKeyException
“잘못된 키 크기 또는 기본 매개 변수”메시지와 함께 A 는 암호화 강도 가 제한됨을 의미합니다 . 무제한 강도 관할 정책 파일이 올바른 위치에 있지 않습니다. JDK에서는 아래에 배치해야합니다.${jdk}/jre/lib/security
문제점 설명에 따라 정책 파일이 올바르게 설치되지 않은 것 같습니다. 시스템은 여러 Java 런타임을 쉽게 가질 수 있습니다. 올바른 위치를 사용하고 있는지 다시 확인하십시오.
답변
스프링 보안 암호화 모듈 사용을 고려하십시오
Spring Security Crypto 모듈은 대칭 암호화, 키 생성 및 비밀번호 인코딩을 지원합니다. 이 코드는 핵심 모듈의 일부로 배포되지만 다른 스프링 보안 (또는 스프링) 코드에는 의존하지 않습니다.
암호화에 대한 간단한 추상화를 제공하며 여기에 필요한 것과 일치하는 것 같습니다.
“표준”암호화 방법은 PKCS # 5의 PBKDF2 (비밀번호 기반 키 파생 함수 # 2)를 사용하는 256 비트 AES입니다. 이 방법에는 Java 6이 필요합니다. SecretKey를 생성하는 데 사용되는 비밀번호는 안전한 곳에 보관하고 공유하지 않아야합니다. 솔트는 암호화 된 데이터가 손상된 경우 키에 대한 사전 공격을 방지하는 데 사용됩니다. 16 바이트 랜덤 초기화 벡터도 적용되므로 각 암호화 된 메시지는 고유합니다.
상기 봐 내부는 유사한 구조를 보여 에릭슨의 답변을 .
질문에서 언급했듯이 이것은 또한 자바 암호화 확장 기능 (JCE) 무제한 강도 관할 정책 (다른 접하게을 InvalidKeyException: Illegal Key Size
). Java 6 , Java 7 및 Java 8 용으로 다운로드 할 수 있습니다.
사용법 예
import org.springframework.security.crypto.encrypt.Encryptors;
import org.springframework.security.crypto.encrypt.TextEncryptor;
import org.springframework.security.crypto.keygen.KeyGenerators;
public class CryptoExample {
public static void main(String[] args) {
final String password = "I AM SHERLOCKED";
final String salt = KeyGenerators.string().generateKey();
TextEncryptor encryptor = Encryptors.text(password, salt);
System.out.println("Salt: \"" + salt + "\"");
String textToEncrypt = "*royal secrets*";
System.out.println("Original text: \"" + textToEncrypt + "\"");
String encryptedText = encryptor.encrypt(textToEncrypt);
System.out.println("Encrypted text: \"" + encryptedText + "\"");
// Could reuse encryptor but wanted to show reconstructing TextEncryptor
TextEncryptor decryptor = Encryptors.text(password, salt);
String decryptedText = decryptor.decrypt(encryptedText);
System.out.println("Decrypted text: \"" + decryptedText + "\"");
if(textToEncrypt.equals(decryptedText)) {
System.out.println("Success: decrypted text matches");
} else {
System.out.println("Failed: decrypted text does not match");
}
}
}
샘플 출력
소금 : "feacbc02a3a697b0" 원문 : "* royal secrets *" 암호화 된 텍스트 : "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a" 해독 된 텍스트 : "* 왕의 비밀 *" 성공 : 해독 된 텍스트 일치
답변
erickson의 제안을 읽고 몇 가지 다른 게시물 과이 예제 에서 얻을 수있는 것을 얻은 후에 Doug의 코드를 권장 변경 사항으로 업데이트하려고 시도했습니다. 더 나은 편집을 위해 자유롭게 편집하십시오.
- 초기화 벡터가 더 이상 고정되지 않습니다
- 암호화 키는 erickson의 코드를 사용하여 파생됩니다.
- SecureRandom ()을 사용하여 setupEncrypt ()에서 8 바이트 솔트가 생성됩니다.
- 암호 해독 키는 암호화 솔트 및 암호에서 생성됩니다.
- 암호 해독 암호는 암호 해독 키와 초기화 벡터에서 생성됩니다.
- org.apache.commons 코덱 16 진수 루틴 대신 16 진수 twiddling 제거
참고 사항 : 이것은 128 비트 암호화 키를 사용합니다-java는 256 비트 암호화를 기본적으로 수행하지 않습니다. 256을 구현하려면 일부 추가 파일을 java 설치 디렉토리에 설치해야합니다.
또한 저는 암호화 된 사람이 아닙니다. 조심하십시오.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
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.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException;
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;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
public class Crypto
{
String mPassword = null;
public final static int SALT_LEN = 8;
byte [] mInitVec = null;
byte [] mSalt = null;
Cipher mEcipher = null;
Cipher mDecipher = null;
private final int KEYLEN_BITS = 128; // see notes below where this is used.
private final int ITERATIONS = 65536;
private final int MAX_FILE_BUF = 1024;
/**
* create an object with just the passphrase from the user. Don't do anything else yet
* @param password
*/
public Crypto (String password)
{
mPassword = password;
}
/**
* return the generated salt for this object
* @return
*/
public byte [] getSalt ()
{
return (mSalt);
}
/**
* return the initialization vector created from setupEncryption
* @return
*/
public byte [] getInitVec ()
{
return (mInitVec);
}
/**
* debug/print messages
* @param msg
*/
private void Db (String msg)
{
System.out.println ("** Crypt ** " + msg);
}
/**
* this must be called after creating the initial Crypto object. It creates a salt of SALT_LEN bytes
* and generates the salt bytes using secureRandom(). The encryption secret key is created
* along with the initialization vectory. The member variable mEcipher is created to be used
* by the class later on when either creating a CipherOutputStream, or encrypting a buffer
* to be written to disk.
*
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws InvalidParameterSpecException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws UnsupportedEncodingException
* @throws InvalidKeyException
*/
public void setupEncrypt () throws NoSuchAlgorithmException,
InvalidKeySpecException,
NoSuchPaddingException,
InvalidParameterSpecException,
IllegalBlockSizeException,
BadPaddingException,
UnsupportedEncodingException,
InvalidKeyException
{
SecretKeyFactory factory = null;
SecretKey tmp = null;
// crate secureRandom salt and store as member var for later use
mSalt = new byte [SALT_LEN];
SecureRandom rnd = new SecureRandom ();
rnd.nextBytes (mSalt);
Db ("generated salt :" + Hex.encodeHexString (mSalt));
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
/* Derive the key, given password and salt.
*
* in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
* The end user must also install them (not compiled in) so beware.
* see here: http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
*/
KeySpec spec = new PBEKeySpec (mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);
tmp = factory.generateSecret (spec);
SecretKey secret = new SecretKeySpec (tmp.getEncoded(), "AES");
/* Create the Encryption cipher object and store as a member variable
*/
mEcipher = Cipher.getInstance ("AES/CBC/PKCS5Padding");
mEcipher.init (Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = mEcipher.getParameters ();
// get the initialization vectory and store as member var
mInitVec = params.getParameterSpec (IvParameterSpec.class).getIV();
Db ("mInitVec is :" + Hex.encodeHexString (mInitVec));
}
/**
* If a file is being decrypted, we need to know the pasword, the salt and the initialization vector (iv).
* We have the password from initializing the class. pass the iv and salt here which is
* obtained when encrypting the file initially.
*
* @param initvec
* @param salt
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws NoSuchPaddingException
* @throws InvalidKeyException
* @throws InvalidAlgorithmParameterException
* @throws DecoderException
*/
public void setupDecrypt (String initvec, String salt) throws NoSuchAlgorithmException,
InvalidKeySpecException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
DecoderException
{
SecretKeyFactory factory = null;
SecretKey tmp = null;
SecretKey secret = null;
// since we pass it as a string of input, convert to a actual byte buffer here
mSalt = Hex.decodeHex (salt.toCharArray ());
Db ("got salt " + Hex.encodeHexString (mSalt));
// get initialization vector from passed string
mInitVec = Hex.decodeHex (initvec.toCharArray ());
Db ("got initvector :" + Hex.encodeHexString (mInitVec));
/* Derive the key, given password and salt. */
// in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
// The end user must also install them (not compiled in) so beware.
// see here:
// http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(mPassword.toCharArray (), mSalt, ITERATIONS, KEYLEN_BITS);
tmp = factory.generateSecret(spec);
secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Decrypt the message, given derived key and initialization vector. */
mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));
}
/**
* This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
* Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
*
* there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
* into uncertain problems with that.
*
* @param input - the cleartext file to be encrypted
* @param output - the encrypted data file
* @throws IOException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public void WriteEncryptedFile (File input, File output) throws
IOException,
IllegalBlockSizeException,
BadPaddingException
{
FileInputStream fin;
FileOutputStream fout;
long totalread = 0;
int nread = 0;
byte [] inbuf = new byte [MAX_FILE_BUF];
fout = new FileOutputStream (output);
fin = new FileInputStream (input);
while ((nread = fin.read (inbuf)) > 0 )
{
Db ("read " + nread + " bytes");
totalread += nread;
// create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
// and results in full blocks of MAX_FILE_BUF being written.
byte [] trimbuf = new byte [nread];
for (int i = 0; i < nread; i++)
trimbuf[i] = inbuf[i];
// encrypt the buffer using the cipher obtained previosly
byte [] tmp = mEcipher.update (trimbuf);
// I don't think this should happen, but just in case..
if (tmp != null)
fout.write (tmp);
}
// finalize the encryption since we've done it in blocks of MAX_FILE_BUF
byte [] finalbuf = mEcipher.doFinal ();
if (finalbuf != null)
fout.write (finalbuf);
fout.flush();
fin.close();
fout.close();
Db ("wrote " + totalread + " encrypted bytes");
}
/**
* Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
* to disk as (output) File.
*
* I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
* and still have a correctly decrypted file in the end. Seems to work so left it in.
*
* @param input - File object representing encrypted data on disk
* @param output - File object of cleartext data to write out after decrypting
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws IOException
*/
public void ReadEncryptedFile (File input, File output) throws
IllegalBlockSizeException,
BadPaddingException,
IOException
{
FileInputStream fin;
FileOutputStream fout;
CipherInputStream cin;
long totalread = 0;
int nread = 0;
byte [] inbuf = new byte [MAX_FILE_BUF];
fout = new FileOutputStream (output);
fin = new FileInputStream (input);
// creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
cin = new CipherInputStream (fin, mDecipher);
while ((nread = cin.read (inbuf)) > 0 )
{
Db ("read " + nread + " bytes");
totalread += nread;
// create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
byte [] trimbuf = new byte [nread];
for (int i = 0; i < nread; i++)
trimbuf[i] = inbuf[i];
// write out the size-adjusted buffer
fout.write (trimbuf);
}
fout.flush();
cin.close();
fin.close ();
fout.close();
Db ("wrote " + totalread + " encrypted bytes");
}
/**
* adding main() for usage demonstration. With member vars, some of the locals would not be needed
*/
public static void main(String [] args)
{
// create the input.txt file in the current directory before continuing
File input = new File ("input.txt");
File eoutput = new File ("encrypted.aes");
File doutput = new File ("decrypted.txt");
String iv = null;
String salt = null;
Crypto en = new Crypto ("mypassword");
/*
* setup encryption cipher using password. print out iv and salt
*/
try
{
en.setupEncrypt ();
iv = Hex.encodeHexString (en.getInitVec ()).toUpperCase ();
salt = Hex.encodeHexString (en.getSalt ()).toUpperCase ();
}
catch (InvalidKeyException e)
{
e.printStackTrace();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (InvalidKeySpecException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
catch (InvalidParameterSpecException e)
{
e.printStackTrace();
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
/*
* write out encrypted file
*/
try
{
en.WriteEncryptedFile (input, eoutput);
System.out.printf ("File encrypted to " + eoutput.getName () + "\niv:" + iv + "\nsalt:" + salt + "\n\n");
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
/*
* decrypt file
*/
Crypto dc = new Crypto ("mypassword");
try
{
dc.setupDecrypt (iv, salt);
}
catch (InvalidKeyException e)
{
e.printStackTrace();
}
catch (NoSuchAlgorithmException e)
{
e.printStackTrace();
}
catch (InvalidKeySpecException e)
{
e.printStackTrace();
}
catch (NoSuchPaddingException e)
{
e.printStackTrace();
}
catch (InvalidAlgorithmParameterException e)
{
e.printStackTrace();
}
catch (DecoderException e)
{
e.printStackTrace();
}
/*
* write out decrypted file
*/
try
{
dc.ReadEncryptedFile (eoutput, doutput);
System.out.println ("decryption finished to " + doutput.getName ());
}
catch (IllegalBlockSizeException e)
{
e.printStackTrace();
}
catch (BadPaddingException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
답변
바이트 배열에서 자신의 키를 생성하는 것은 쉽습니다.
byte[] raw = ...; // 32 bytes in size for a 256 bit key
Key skey = new javax.crypto.spec.SecretKeySpec(raw, "AES");
그러나 256 비트 키를 만드는 것만으로는 충분하지 않습니다. 키 생성기가 256 비트 키를 생성 할 수없는 경우 Cipher
클래스에서 AES 256 비트도 지원하지 않을 수 있습니다. 무제한 관할권 패치가 설치되어 있으므로 AES-256 암호가 지원되어야하지만 256 비트 키도 지원해야하므로 구성 문제 일 수 있습니다.
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skey);
byte[] encrypted = cipher.doFinal(plainText.getBytes());
AES-256이 지원되지 않는 문제를 해결하려면 AES-256을 무료로 구현하여 사용자 지정 공급자로 사용하십시오. 이것은 당신 자신을 만드는 것을 포함합니다Provider
서브 클래스 사용하는 것이 포함됩니다 Cipher.getInstance(String, Provider)
. 그러나 이것은 관련 프로세스 일 수 있습니다.
답변
내가 과거에 한 일은 SHA256과 같은 것을 통해 키를 해시 한 다음 해시에서 키 바이트로 바이트를 추출합니다.
byte []가 있으면 간단하게 할 수 있습니다 :
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());
답변
@Wufoo의 편집에 추가 한 다음 버전은 파일이 아닌 InputStream을 사용하여 다양한 파일 작업을보다 쉽게합니다. 또한 파일 시작 부분에 IV와 Salt를 저장하므로 암호 만 추적하면됩니다. IV와 소금은 비밀이 될 필요가 없기 때문에 인생이 조금 더 쉬워집니다.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
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 {
public final static int SALT_LEN = 8;
static final String HEXES = "0123456789ABCDEF";
String mPassword = null;
byte[] mInitVec = null;
byte[] mSalt = new byte[SALT_LEN];
Cipher mEcipher = null;
Cipher mDecipher = null;
private final int KEYLEN_BITS = 128; // see notes below where this is used.
private final int ITERATIONS = 65536;
private final int MAX_FILE_BUF = 1024;
/**
* create an object with just the passphrase from the user. Don't do anything else yet
* @param password
*/
public AES(String password) {
mPassword = password;
}
public static String byteToHex(byte[] raw) {
if (raw == null) {
return null;
}
final StringBuilder hex = new StringBuilder(2 * raw.length);
for (final byte b : raw) {
hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
public static byte[] hexToByte(String hexString) {
int len = hexString.length();
byte[] ba = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return ba;
}
/**
* debug/print messages
* @param msg
*/
private void Db(String msg) {
System.out.println("** Crypt ** " + msg);
}
/**
* This is where we write out the actual encrypted data to disk using the Cipher created in setupEncrypt().
* Pass two file objects representing the actual input (cleartext) and output file to be encrypted.
*
* there may be a way to write a cleartext header to the encrypted file containing the salt, but I ran
* into uncertain problems with that.
*
* @param input - the cleartext file to be encrypted
* @param output - the encrypted data file
* @throws IOException
* @throws IllegalBlockSizeException
* @throws BadPaddingException
*/
public void WriteEncryptedFile(InputStream inputStream, OutputStream outputStream)
throws IOException, IllegalBlockSizeException, BadPaddingException {
try {
long totalread = 0;
int nread = 0;
byte[] inbuf = new byte[MAX_FILE_BUF];
SecretKeyFactory factory = null;
SecretKey tmp = null;
// crate secureRandom salt and store as member var for later use
mSalt = new byte[SALT_LEN];
SecureRandom rnd = new SecureRandom();
rnd.nextBytes(mSalt);
Db("generated salt :" + byteToHex(mSalt));
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
/*
* Derive the key, given password and salt.
*
* in order to do 256 bit crypto, you have to muck with the files for Java's "unlimted security"
* The end user must also install them (not compiled in) so beware.
* see here: http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml
*/
KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);
tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/*
* Create the Encryption cipher object and store as a member variable
*/
mEcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
mEcipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = mEcipher.getParameters();
// get the initialization vectory and store as member var
mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
Db("mInitVec is :" + byteToHex(mInitVec));
outputStream.write(mSalt);
outputStream.write(mInitVec);
while ((nread = inputStream.read(inbuf)) > 0) {
Db("read " + nread + " bytes");
totalread += nread;
// create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
// and results in full blocks of MAX_FILE_BUF being written.
byte[] trimbuf = new byte[nread];
for (int i = 0; i < nread; i++) {
trimbuf[i] = inbuf[i];
}
// encrypt the buffer using the cipher obtained previosly
byte[] tmpBuf = mEcipher.update(trimbuf);
// I don't think this should happen, but just in case..
if (tmpBuf != null) {
outputStream.write(tmpBuf);
}
}
// finalize the encryption since we've done it in blocks of MAX_FILE_BUF
byte[] finalbuf = mEcipher.doFinal();
if (finalbuf != null) {
outputStream.write(finalbuf);
}
outputStream.flush();
inputStream.close();
outputStream.close();
outputStream.close();
Db("wrote " + totalread + " encrypted bytes");
} catch (InvalidKeyException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidParameterSpecException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchAlgorithmException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchPaddingException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvalidKeySpecException ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Read from the encrypted file (input) and turn the cipher back into cleartext. Write the cleartext buffer back out
* to disk as (output) File.
*
* I left CipherInputStream in here as a test to see if I could mix it with the update() and final() methods of encrypting
* and still have a correctly decrypted file in the end. Seems to work so left it in.
*
* @param input - File object representing encrypted data on disk
* @param output - File object of cleartext data to write out after decrypting
* @throws IllegalBlockSizeException
* @throws BadPaddingException
* @throws IOException
*/
public void ReadEncryptedFile(InputStream inputStream, OutputStream outputStream)
throws IllegalBlockSizeException, BadPaddingException, IOException {
try {
CipherInputStream cin;
long totalread = 0;
int nread = 0;
byte[] inbuf = new byte[MAX_FILE_BUF];
// Read the Salt
inputStream.read(this.mSalt);
Db("generated salt :" + byteToHex(mSalt));
SecretKeyFactory factory = null;
SecretKey tmp = null;
SecretKey secret = null;
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(mPassword.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);
tmp = factory.generateSecret(spec);
secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Decrypt the message, given derived key and initialization vector. */
mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// Set the appropriate size for mInitVec by Generating a New One
AlgorithmParameters params = mDecipher.getParameters();
mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();
// Read the old IV from the file to mInitVec now that size is set.
inputStream.read(this.mInitVec);
Db("mInitVec is :" + byteToHex(mInitVec));
mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));
// creating a decoding stream from the FileInputStream above using the cipher created from setupDecrypt()
cin = new CipherInputStream(inputStream, mDecipher);
while ((nread = cin.read(inbuf)) > 0) {
Db("read " + nread + " bytes");
totalread += nread;
// create a buffer to write with the exact number of bytes read. Otherwise a short read fills inbuf with 0x0
byte[] trimbuf = new byte[nread];
for (int i = 0; i < nread; i++) {
trimbuf[i] = inbuf[i];
}
// write out the size-adjusted buffer
outputStream.write(trimbuf);
}
outputStream.flush();
cin.close();
inputStream.close();
outputStream.close();
Db("wrote " + totalread + " encrypted bytes");
} catch (Exception ex) {
Logger.getLogger(AES.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* adding main() for usage demonstration. With member vars, some of the locals would not be needed
*/
public static void main(String[] args) {
// create the input.txt file in the current directory before continuing
File input = new File("input.txt");
File eoutput = new File("encrypted.aes");
File doutput = new File("decrypted.txt");
String iv = null;
String salt = null;
AES en = new AES("mypassword");
/*
* write out encrypted file
*/
try {
en.WriteEncryptedFile(new FileInputStream(input), new FileOutputStream(eoutput));
System.out.printf("File encrypted to " + eoutput.getName() + "\niv:" + iv + "\nsalt:" + salt + "\n\n");
} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
e.printStackTrace();
}
/*
* decrypt file
*/
AES dc = new AES("mypassword");
/*
* write out decrypted file
*/
try {
dc.ReadEncryptedFile(new FileInputStream(eoutput), new FileOutputStream(doutput));
System.out.println("decryption finished to " + doutput.getName());
} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
e.printStackTrace();
}
}
}
답변
(유사한 요구 사항을 가진 다른 사람들에게 도움이 될 수 있습니다)
AES-256-CBC
Java에서 암호화 및 암호 해독 을 사용해야하는 비슷한 요구 사항이 있습니다 .
256 바이트 암호화 / 암호 해독을 달성 (또는 지정)하려면 Java Cryptography Extension (JCE)
정책을"Unlimited"
java.security
파일에서 $JAVA_HOME/jre/lib/security
(JDK의 경우) 또는 $JAVA_HOME/lib/security
(JRE의 경우) 에서 설정할 수 있습니다.
crypto.policy=unlimited
또는 코드에서
Security.setProperty("crypto.policy", "unlimited");
Java 9 이상 버전에서는 기본적으로이 기능이 활성화되어 있습니다.