AES 有幾個概念,
- Key : 長度分成128bit(16byte)、192bit(24byte)和256bit(byte)
- Mode :
- ECB:為基礎加密模式,plain text 被分割成相同長度的區塊,然後再一一的加密組成cipher text。(本文使用模式)
- CBC:是一種循環模式,前一個分組的密文和當前分組的明文異或操作後再加密,這樣做的目的是增強破解難度。
- CFB/OFB 實際上是一種反饋模式,目的也是增強破解的難度。
- ECB和CBC 的加密結果是不一樣的,兩者的模式不同,而且CBC會在第一個密碼塊運算時加入一個初始化向量。
- PKCS5Padding : 當一次加密的長度不足16byte時,須做填充補足,而PKCS5Padding及填充的方式。
- Java 支援的功能
- AES/CBC/NoPadding (128)
- AES/CBC/PKCS5Padding (128)
- AES/ECB/NoPadding (128)
- AES/ECB/PKCS5Padding (128)
- Source Code : 原始檔
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
class Convert {
/**
* str2byte : String to Byte
* Eg. 00010203 --> 0x00 0x01 0x02 0x03
*/
public static byte[] str2byte(String key)
{
byte[] b = new byte[key.length()/2];
for(int i=0, bStepper=0; i<key.length()+2; i+=2)
if(i !=0)
b[bStepper++]=((byte)Integer.parseInt((key.charAt(i-2)+""+key.charAt(i-1)), 16));
return b;
}
/**
* byte2str : Byte to String
* Eg. 0x00 0x01 0x02 0x03 --> 00010203
*/
public static String byte2str (byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
strbuf.append(" ");
}
return strbuf.toString();
}
}
class AESEncrypt {
private SecretKeySpec skeySpec;
private Cipher cipher;
private Cipher noPadding;
private Cipher padding;
private byte[] key;
public AESEncrypt() throws NoSuchAlgorithmException, NoSuchPaddingException{
KeyGenerator keyG = KeyGenerator.getInstance("AES");
keyG.init(128);
SecretKey secuK = keyG.generateKey();
this.key = secuK.getEncoded();
init();
}
public AESEncrypt(String key) throws NoSuchAlgorithmException, NoSuchPaddingException{
this.key = Convert.str2byte(key);
init();
}
private void init() throws NoSuchAlgorithmException, NoSuchPaddingException{
this.skeySpec = new SecretKeySpec(this.key, "AES");
this.noPadding = Cipher.getInstance("AES/ECB/NoPadding");
this.padding = Cipher.getInstance("AES/ECB/PKCS5Padding");
this.cipher = noPadding;
}
public void setNoPadding() {
this.cipher = noPadding;
}
public void setPKCS5Padding() {
this.cipher = padding;
}
public byte[] encrypt( byte[] byteArray, int offset, int len ) throws Exception{
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(byteArray, offset, len);
}
public byte[] encrypt( byte[] byteArray ) throws Exception{
return encrypt( byteArray, 0, byteArray.length);
}
public byte[] decrypt( byte[] byteArray, int offset, int len ) throws Exception{
this.cipher.init(Cipher.DECRYPT_MODE, skeySpec);
return cipher.doFinal(byteArray, offset, len);
}
public byte[] decrypt( byte[] byteArray ) throws Exception{
return decrypt( byteArray, 0, byteArray.length);
}
public void encryptFile(FileInputStream fim, FileOutputStream fom) throws Exception {
byte[] buffer = new byte[16];
int length = -1;
while((length = fim.read(buffer, 0, buffer.length)) != -1) {
if ( length < buffer.length ) setPKCS5Padding();
else setNoPadding();
byte[] decoder = encrypt(buffer, 0, length);
fom.write(decoder, 0, decoder.length);
}
}
public void decryptFile(FileInputStream fim, FileOutputStream fom) throws Exception {
byte[] buffer = new byte[8192];
int length = -1;
while((length = fim.read(buffer,0,buffer.length)) != -1) {
if ( length < buffer.length ) setPKCS5Padding();
else setNoPadding();
byte[] decoder = decrypt(buffer, 0, length);
fom.write(decoder, 0, decoder.length);
}
}
}
public class SampleAES {
public static void demoStringCrypt() throws Exception {
System.out.println("### Demo String Encrypt and Decrypt");
String plainText = "HelloWorld";
// 128 bit
AESEncrypt aes = new AESEncrypt("000102030405060708090A0B0C0D0E0F");
// encode
if ( (plainText.getBytes().length % 16) == 0) aes.setNoPadding();
else aes.setPKCS5Padding();
byte[] encoder = aes.encrypt(plainText.getBytes());
System.out.println("Encoder: " + Convert.byte2str(encoder));
// decode
aes.setPKCS5Padding();
String decoder = new String(aes.decrypt(encoder));
System.out.println("Decoder: " + decoder);
}
public static void demoFileCrypt() throws Exception {
System.out.println("### Demo File Encrypt and Decrypt");
String plainFile = "plainText.txt";
String encodeFile = "encode.txt";
String decodeFile = "decode.txt";
FileInputStream plainText =
new FileInputStream(new File(plainFile));
FileOutputStream encode =
new FileOutputStream(new File(encodeFile));
AESEncrypt aes = new AESEncrypt();
// encrypt file
System.out.println(plainFile + " encrypt to " + encodeFile);
aes.encryptFile(plainText, encode);
FileInputStream cipherText =
new FileInputStream(new File(encodeFile));
FileOutputStream decode =
new FileOutputStream(new File(decodeFile));
// decrypt file
System.out.println(encodeFile + " decrypt to " + decodeFile);
aes.decryptFile(cipherText, decode);
plainText.close();
encode.close();
cipherText.close();
decode.close();
}
public static void main(String[] args) throws Exception {
demoStringCrypt();
demoFileCrypt();
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
class Convert {
/**
* str2byte : String to Byte
* Eg. 00010203 --> 0x00 0x01 0x02 0x03
*/
public static byte[] str2byte(String key)
{
byte[] b = new byte[key.length()/2];
for(int i=0, bStepper=0; i<key.length()+2; i+=2)
if(i !=0)
b[bStepper++]=((byte)Integer.parseInt((key.charAt(i-2)+""+key.charAt(i-1)), 16));
return b;
}
/**
* byte2str : Byte to String
* Eg. 0x00 0x01 0x02 0x03 --> 00010203
*/
public static String byte2str (byte buf[]) {
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
strbuf.append(" ");
}
return strbuf.toString();
}
}
class AESEncrypt {
private SecretKeySpec skeySpec;
private Cipher cipher;
private Cipher noPadding;
private Cipher padding;
private byte[] key;
public AESEncrypt() throws NoSuchAlgorithmException, NoSuchPaddingException{
KeyGenerator keyG = KeyGenerator.getInstance("AES");
keyG.init(128);
SecretKey secuK = keyG.generateKey();
this.key = secuK.getEncoded();
init();
}
public AESEncrypt(String key) throws NoSuchAlgorithmException, NoSuchPaddingException{
this.key = Convert.str2byte(key);
init();
}
private void init() throws NoSuchAlgorithmException, NoSuchPaddingException{
this.skeySpec = new SecretKeySpec(this.key, "AES");
this.noPadding = Cipher.getInstance("AES/ECB/NoPadding");
this.padding = Cipher.getInstance("AES/ECB/PKCS5Padding");
this.cipher = noPadding;
}
public void setNoPadding() {
this.cipher = noPadding;
}
public void setPKCS5Padding() {
this.cipher = padding;
}
public byte[] encrypt( byte[] byteArray, int offset, int len ) throws Exception{
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(byteArray, offset, len);
}
public byte[] encrypt( byte[] byteArray ) throws Exception{
return encrypt( byteArray, 0, byteArray.length);
}
public byte[] decrypt( byte[] byteArray, int offset, int len ) throws Exception{
this.cipher.init(Cipher.DECRYPT_MODE, skeySpec);
return cipher.doFinal(byteArray, offset, len);
}
public byte[] decrypt( byte[] byteArray ) throws Exception{
return decrypt( byteArray, 0, byteArray.length);
}
public void encryptFile(FileInputStream fim, FileOutputStream fom) throws Exception {
byte[] buffer = new byte[16];
int length = -1;
while((length = fim.read(buffer, 0, buffer.length)) != -1) {
if ( length < buffer.length ) setPKCS5Padding();
else setNoPadding();
byte[] decoder = encrypt(buffer, 0, length);
fom.write(decoder, 0, decoder.length);
}
}
public void decryptFile(FileInputStream fim, FileOutputStream fom) throws Exception {
byte[] buffer = new byte[8192];
int length = -1;
while((length = fim.read(buffer,0,buffer.length)) != -1) {
if ( length < buffer.length ) setPKCS5Padding();
else setNoPadding();
byte[] decoder = decrypt(buffer, 0, length);
fom.write(decoder, 0, decoder.length);
}
}
}
public class SampleAES {
public static void demoStringCrypt() throws Exception {
System.out.println("### Demo String Encrypt and Decrypt");
String plainText = "HelloWorld";
// 128 bit
AESEncrypt aes = new AESEncrypt("000102030405060708090A0B0C0D0E0F");
// encode
if ( (plainText.getBytes().length % 16) == 0) aes.setNoPadding();
else aes.setPKCS5Padding();
byte[] encoder = aes.encrypt(plainText.getBytes());
System.out.println("Encoder: " + Convert.byte2str(encoder));
// decode
aes.setPKCS5Padding();
String decoder = new String(aes.decrypt(encoder));
System.out.println("Decoder: " + decoder);
}
public static void demoFileCrypt() throws Exception {
System.out.println("### Demo File Encrypt and Decrypt");
String plainFile = "plainText.txt";
String encodeFile = "encode.txt";
String decodeFile = "decode.txt";
FileInputStream plainText =
new FileInputStream(new File(plainFile));
FileOutputStream encode =
new FileOutputStream(new File(encodeFile));
AESEncrypt aes = new AESEncrypt();
// encrypt file
System.out.println(plainFile + " encrypt to " + encodeFile);
aes.encryptFile(plainText, encode);
FileInputStream cipherText =
new FileInputStream(new File(encodeFile));
FileOutputStream decode =
new FileOutputStream(new File(decodeFile));
// decrypt file
System.out.println(encodeFile + " decrypt to " + decodeFile);
aes.decryptFile(cipherText, decode);
plainText.close();
encode.close();
cipherText.close();
decode.close();
}
public static void main(String[] args) throws Exception {
demoStringCrypt();
demoFileCrypt();
}
}
- Note: 以下記錄在撰寫中發生的Error Message
- Give final block not properly padded : 再測試加密檔案時,一直發生此錯誤,後來發現是由padding的所造成的,在進行加解密時,都必須確認要做加解密的陣列, 是否需要做padding,需要則呼叫setPKCS5Padding(),不需要則呼叫setNoPadding()。
Reference :
[1] 分组对称加密模式:ECB/CBC/CFB/OFB
[2] Java AES Encrypt & Decrypt Example(加解密)
[3] 使用 Java 進行 AES 加密
[4] Class Cipher
沒有留言:
張貼留言