Java 256비트 AES 패스워드 기반 암호화
256비트 AES 암호화를 실장해야 하는데, 온라인에서 찾은 모든 예에서는 256비트 키를 생성하기 위해 "Key Generator"를 사용하지만, 자체 암호를 사용하고 싶습니다.나만의 키를 작성하려면 어떻게 해야 하나요?256비트로 패딩을 시도했지만 키가 너무 길다는 오류가 나타납니다.관할구역의 무제한 패치가 설치되어 있기 때문에 문제가 되지 않습니다.
즉, Key Generator는 다음과 같습니다.
// 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
)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를 약화시키는 공격이 발견될 경우 안전상의 여유를 크게 주지 않습니다.
적절한 블록 체인 모드에서 사용하면 동일한 파생 키를 사용하여 많은 메시지를 암호화할 수 있습니다.Cipher Block Chaining(CBC; 암호 블록 체인)에서는 메시지별로 랜덤 초기화 벡터(IV)가 생성되어 플레인텍스트가 동일해도 다른 암호텍스트가 생성됩니다.CBC가 사용 가능한 가장 안전한 모드는 아닐 수 있습니다(아래의 AED 참조).보안 속성이 다른 모드는 많이 있습니다만, 모두 같은 랜덤 입력을 사용합니다.어느 경우든 각 암호화 동작의 출력은 암호 텍스트와 초기화 벡터입니다.
/* 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(StandardCharsets.UTF_8));
ciphertext
및iv
시 '''는SecretKey
는 동일한 salt 파라미터와 반복 파라미터의 패스워드를 사용하여 동일한 방법으로 재생성됩니다.이 키와 메시지와 함께 저장된 초기화 벡터를 사용하여 암호를 초기화합니다.
/* 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), StandardCharsets.UTF_8);
System.out.println(plaintext);
Java 7에는 AED 암호 모드에 대한 API 지원이 포함되어 있으며 OpenJDK 및 Oracle 배포판에 포함된 "SunJCE" 공급자는 Java 8부터 이러한 기능을 구현합니다.이러한 모드 중 하나는 CBC 대신 강력히 권장됩니다.데이터의 완전성과 프라이버시를 보호합니다.
A java.security.InvalidKeyException
메시지 "Ilgal key size or default parameters"는 암호화 강도가 제한됨을 의미합니다.무제한 강도 관할 정책 파일이 올바른 위치에 있지 않습니다.JDK 에서는, 다음의 위치에 배치할 필요가 있습니다.${jdk}/jre/lib/security
문제의 설명을 보면 정책 파일이 올바르게 설치되어 있지 않은 것 같습니다.시스템은 여러 Java 런타임으로 쉽게 설정할 수 있습니다. 올바른 위치가 사용되고 있는지 다시 한 번 확인하십시오.
Spring Security Crypto 모듈 사용 검토
Spring Security Crypto 모듈은 대칭 암호화, 키 생성 및 비밀번호 인코딩을 지원합니다.이 코드는 코어 모듈의 일부로 배포되지만 다른 스프링 보안(또는 스프링) 코드에는 의존하지 않습니다.
암호화를 위한 간단한 추상화 기능을 제공하며 여기에 필요한 것과 일치하는 것으로 보입니다.
'표준' 암호화 방식은 PKCS #5의 PBKDF2(Password-Based Key Derivation Function #2)를 사용하는 256비트 AES입니다.이 방법에는 Java 6이 필요합니다.SecretKey 생성에 사용되는 패스워드는 안전한 장소에 보관하고 공유해서는 안 됩니다.솔트는 암호화된 데이터가 손상된 경우 키에 대한 사전 공격을 방지하기 위해 사용됩니다.또한 16바이트 랜덤 초기화 벡터가 적용되어 각 암호화된 메시지가 고유합니다.
질문에서 설명한 바와 같이 여기에는 Java Cryptography Extension(JCE) Unlimited Strength Turritory Policy(이하 참조)도 필요합니다.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"원문 : "*로열 시크릿*"암호화 텍스트: "7c73c5a83fa580b5d6f8208768adc931ef3123291ac8bc335a1277a39d256d9a"복호화된 텍스트: "*로열 시크릿*"성공: 복호화된 텍스트가 일치합니다.
Erickson의 제안을 읽고 다른 몇 개의 게시물과 이 예에서 얻을 수 있는 내용을 수집한 후 권장되는 변경 사항으로 더그의 코드를 업데이트하려고 했습니다.자유롭게 편집하여 개선하십시오.
- 초기화 벡터가 더 이상 고정되어 있지 않습니다.
- 암호화 키는 erickson의 코드를 사용하여 취득됩니다.
- SecureRandom()을 사용하여 setupEncrypt()에서 8바이트 솔트가 생성됩니다.
- 암호 해독 키는 암호화 salt 및 암호에서 생성됩니다.
- 복호화 암호는 복호화 키와 초기화 벡터에서 생성됩니다.
- org.apache.commons 코덱 Hex 루틴 대신 16진수 트위들링 제거
주의사항:이것은 128비트 암호화 키를 사용합니다.자바는 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비트 키를 생성할 수 없는 경우 클래스는 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을 자유롭게 실장하여 커스텀프로바이더로서 사용하는 것입니다.여기에는 자체 서브클래스를 만들어 와 함께 사용하는 것이 포함됩니다.단, 이 과정은 복잡한 과정일 수 있습니다.
과거에는 SHA256과 같은 방법으로 키를 해시한 후 해시에서 키 바이트로 바이트를 추출했습니다.
바이트 []를 취득하면, 다음의 조작을 간단하게 실시할 수 있습니다.
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(clearText.getBytes());
@Wufoo의 편집에 덧붙여, 다음의 버전에서는, 파일이 아닌 InputStreams 를 사용하고, 다양한 파일의 조작을 용이하게 합니다.또한 IV와 Salt를 파일 선두에 저장하기 때문에 패스워드만 추적하면 됩니다.IV와 Salt는 비밀일 필요가 없기 때문에, 이것은 삶을 조금 더 편하게 해준다.
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
암호화 및 복호화를 수행합니다.
의 암호화를 실현 지정) , 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 이후 버전에서는 기본적으로 이 기능이 활성화되어 있습니다.
제가 작성자인 Encryptor4j를 사용하는 것을 고려해 주십시오.
먼저 256비트 AES 키를 사용할 수 있도록 진행하기 전에 Unlimited Strength Curritory Policy 파일이 설치되어 있는지 확인하십시오.
그런 다음 다음을 수행합니다.
String password = "mysupersecretpassword";
Key key = KeyFactory.AES.keyFromPassword(password.toCharArray());
Encryptor encryptor = new Encryptor(key, "AES/CBC/PKCS7Padding", 16);
이제 암호화기를 사용하여 메시지를 암호화할 수 있습니다.필요에 따라 스트리밍 암호화를 수행할 수도 있습니다.사용자의 편의를 위해 자동으로 보안 IV를 생성하고 추가합니다.
압축하고 싶은 파일의 경우는, 「JAVA 를 사용해 AES 를 사용해 큰 파일을 암호화해, 한층 더 심플한 어프로치를 실현합니다」라고 하는 회답을 참조해 주세요.
암호화에는 이 클래스를 사용합니다.그건 효과가 있다.
public class ObjectCrypter {
public static byte[] encrypt(byte[] ivBytes, byte[] keyBytes, byte[] mes)
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
IllegalBlockSizeException,
BadPaddingException, IOException {
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = null;
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
return cipher.doFinal(mes);
}
public static byte[] decrypt(byte[] ivBytes, byte[] keyBytes, byte[] bytes)
throws NoSuchAlgorithmException,
NoSuchPaddingException,
InvalidKeyException,
InvalidAlgorithmParameterException,
IllegalBlockSizeException,
BadPaddingException, IOException, ClassNotFoundException {
AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
SecretKeySpec newKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);
return cipher.doFinal(bytes);
}
}
이것들은 ivBytes와 랜덤 키입니다.
String key = "e8ffc7e56311679f12b6fc91aa77a5eb";
byte[] ivBytes = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
keyBytes = key.getBytes("UTF-8");
언급URL : https://stackoverflow.com/questions/992019/java-256-bit-aes-password-based-encryption
'programing' 카테고리의 다른 글
Java에서 int[]를 Integer[]로 변환하려면 어떻게 해야 합니까? (0) | 2022.08.11 |
---|---|
열거형 변수를 C에서 문자열로 사용하는 쉬운 방법? (0) | 2022.08.11 |
nuxt 앱 내의 Vuex가 "변환 핸들러 외부에 vuex 스토어 상태를 변환하지 않음!"을 슬로우합니다. (0) | 2022.08.11 |
구조 정의는 .h 또는 .c 파일로 해야 합니까? (0) | 2022.08.11 |
vuex 상태 돌연변이가 UI를 차단함 (0) | 2022.08.11 |