Java和C/C++進行DES/AES密文傳輸
聲明:對於新手來說很難解決的一個問題,終於在非常煎熬之后找到這篇文章,所以借鑒過來。原文地址http://blog.sina.com.cn/s/blog_48d4cf2d0101eqdf.html。
本來覺得DES、AES這種流行加密算法,使用起來應該很簡單。但研究后發現有兩個變數:
1)分塊的方式。加密是逐塊進行的。分塊方法有:CBC、ECB、CFB……
2)padding的方式。當數據的位數不及塊的大小時,需要填充。填充方式有:NoPadding、PKCS5Padding……
如果加解密端采用不同的分塊方式或padding方式,即使都是采用DES/AES算法,同樣無法解密成功。上次需要C端和Java端進行密文傳輸,就跪在這一點上(那時候沒時間研究)。
參考文章: Java AES算法和openssl配對 ,主要通過其了解openssl里比較高級的EVP系列API(其默認padding和java一樣都是PKCS5Padding),節約了搜索時間。
貼代碼了,以下代碼測試通過了。Java和C++均可以正確解密對方的密文。
約定:分塊和padding采用Java默認的 ECB + PKCS5Padding。
加密程序,輸入是"src.txt"文件(加密無需純文本,只是為了可讀性),輸出保存在"enc.bin"文件。
解碼程序,輸入是"enc.bin"文件,輸出保存在"dec.txt"文件。
------------------------------------------
DES:
Java加密代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class DesEnc {
public static byte[] desEncrypt(byte[] source, byte rawKeyData[])
throws GeneralSecurityException {
// 處理密鑰
SecretKeySpec key = new SecretKeySpec(rawKeyData, "DES");
// 加密
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(source);
}
public static void main(String[] args) throws Exception {
// DES算法要求密鈅64位(8字節)
byte rawKeyData[] = "hellodes".getBytes("UTF-8");
// 讀取文件內容(為了簡單忽略錯誤處理)
File file = new File("src.txt");
byte[] source = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
is.read(source, 0, (int) file.length());
is.close();
// 加密
byte[] enc = desEncrypt(source, rawKeyData);
System.out.println("desEncrypt:" + source.length + "->" + enc.length);
// 輸出到文件
FileOutputStream os = new FileOutputStream(new File("enc.bin"));
os.write(enc, 0, enc.length);
os.close();
}
}
Java解密代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class DesDec {
public static byte[] desDecrypt(byte[] data, byte rawKeyData[])
throws GeneralSecurityException {
// 處理密鑰
SecretKeySpec key = new SecretKeySpec(rawKeyData, "DES");
// 解密
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
// DES算法要求密鈅64位(8字節)
byte rawKeyData[] = "hellodes".getBytes("UTF-8");
// 讀取文件內容(為了簡單忽略錯誤處理)
File file = new File("enc.bin");
byte[] data = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
is.read(data, 0, (int) file.length());
is.close();
// 加密
byte[] dec = desDecrypt(data, rawKeyData);
System.out.println("desDecrypt:" + data.length + "->" + dec.length);
// 輸出到文件
FileOutputStream os = new FileOutputStream(new File("dec.txt"));
os.write(dec, 0, dec.length);
os.close();
}
}
C++加密代碼:
#include 見后文
// 注意:參數和返回值全部是二進制數據
std::string desEncrypt(const std::string& source, const std::string& key)
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int ret = EVP_EncryptInit_ex(&ctx, EVP_des_ecb(), NULL, (const unsigned char*)key.data(), NULL);
assert(ret == 1);
unsigned char* result = new unsigned char[source.length() + 64]; // 弄個足夠大的空間
int len1 = 0;
ret = EVP_EncryptUpdate(&ctx, result, &len1, (const unsigned char*)source.data(), source.length());
assert(ret == 1);
int len2 = 0;
ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2);
assert(ret == 1);
std::cout << len1 << "," << len2 << std::endl;
ret = EVP_CIPHER_CTX_cleanup(&ctx);
assert(ret == 1);
std::string res((char*)result, len1+len2);
delete[] result;
return res;
}
int main()
{
std::string key("hellodes", 8); // 二進制數據,而不是以0結尾的字符串
// 讀取文件內容(簡單起見認為文件內容<100K)
char buf[1024*100];
FILE* fp = fopen("src.txt", "rb");
int bytes = fread(buf, 1, 1024*100, fp);
fclose(fp);
std::string source(buf, bytes); // 二進制數據
// 加密
std::string enc = desEncrypt(source, key);
std::cout << "desEncrypt:" << source.length() << "->" << enc.length() << std::endl;
// 輸出到文件
fp = fopen("enc.bin", "wb");
fwrite(enc.data(), 1, enc.length(), fp);
fclose(fp);
}
C++解密代碼:
#include 見后文
// 注意:參數和返回值全部是二進制數據
std::string desDecrypt(const std::string& ciphertext, const std::string& key)
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int ret = EVP_DecryptInit_ex(&ctx, EVP_des_ecb(), NULL, (const unsigned char*)key.data(), NULL);
assert(ret == 1);
unsigned char* result = new unsigned char[ciphertext.length() + 64]; // 弄個足夠大的空間
int len1 = 0;
ret = EVP_DecryptUpdate(&ctx, result, &len1, (const unsigned char*)ciphertext.data(), ciphertext.length());
assert(ret == 1);
int len2 = 0;
ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2);
assert(ret == 1);
std::cout << len1 << "," << len2 << std::endl;
ret = EVP_CIPHER_CTX_cleanup(&ctx);
assert(ret == 1);
std::string res((char*)result, len1+len2);
delete[] result;
return res;
}
int main()
{
std::string key("hellodes", 8); // 二進制數據,而不是以0結尾的字符串
// 讀取文件內容(簡單起見認為文件內容<100K)
char buf[1024*100];
FILE* fp = fopen("enc.bin", "rb");
int bytes = fread(buf, 1, 1024*100, fp);
fclose(fp);
std::string data(buf, bytes); // 二進制數據
// 加密
std::string dec = desDecrypt(data, key);
std::cout << "desDecrypt:" << data.length() << "->" << dec.length() << std::endl;
// 輸出到文件
fp = fopen("dec.txt", "wb");
fwrite(dec.data(), 1, dec.length(), fp);
fclose(fp);
}
------------------------------------------
AES:
Java加密代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AesEnc {
public static byte[] aesEncrypt(byte[] source, byte rawKeyData[])
throws GeneralSecurityException {
// 處理密鑰
SecretKeySpec key = new SecretKeySpec(rawKeyData, "AES");
// 加密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(source);
}
public static void main(String[] args) throws Exception {
// AES算法要求密鈅128位(16字節)
byte rawKeyData[] = "helloaeshelloaes".getBytes("UTF-8");
// 讀取文件內容(為了簡單忽略錯誤處理)
File file = new File("src.txt");
byte[] source = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
is.read(source, 0, (int) file.length());
is.close();
// 加密
byte[] enc = aesEncrypt(source, rawKeyData);
System.out.println("aesEncrypt:" + source.length + "->" + enc.length);
// 輸出到文件
FileOutputStream os = new FileOutputStream(new File("enc.bin"));
os.write(enc, 0, enc.length);
os.close();
}
}
Java解密代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AesDec {
public static byte[] aesDecrypt(byte[] data, byte rawKeyData[])
throws GeneralSecurityException {
// 處理密鑰
SecretKeySpec key = new SecretKeySpec(rawKeyData, "AES");
// 解密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
// AES算法要求密鈅128位(16字節)
byte rawKeyData[] = "helloaeshelloaes".getBytes("UTF-8");
// 讀取文件內容(為了簡單忽略錯誤處理)
File file = new File("enc.bin");
byte[] data = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
is.read(data, 0, (int) file.length());
is.close();
// 加密
byte[] dec = aesDecrypt(data, rawKeyData);
System.out.println("desDecrypt:" + data.length + "->" + dec.length);
// 輸出到文件
FileOutputStream os = new FileOutputStream(new File("dec.txt"));
os.write(dec, 0, dec.length);
os.close();
}
}
C++加密代碼:
#include 見后文
// 注意:參數和返回值全部是二進制數據
std::string aesEncrypt(const std::string& source, const std::string& key)
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char*)key.data(), NULL);
assert(ret == 1);
unsigned char* result = new unsigned char[source.length() + 64]; // 弄個足夠大的空間
int len1 = 0;
ret = EVP_EncryptUpdate(&ctx, result, &len1, (const unsigned char*)source.data(), source.length());
assert(ret == 1);
int len2 = 0;
ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2);
assert(ret == 1);
std::cout << len1 << "," << len2 << std::endl;
ret = EVP_CIPHER_CTX_cleanup(&ctx);
assert(ret == 1);
std::string res((char*)result, len1+len2);
delete[] result;
return res;
}
int main()
{
std::string key("helloaeshelloaes", 16); // 二進制數據,而不是以0結尾的字符串
// 讀取文件內容(簡單起見認為文件內容<100K)
char buf[1024*100];
FILE* fp = fopen("src.txt", "rb");
int bytes = fread(buf, 1, 1024*100, fp);
fclose(fp);
std::string source(buf, bytes); // 二進制數據
// 加密
std::string enc = aesEncrypt(source, key);
std::cout << "aesEncrypt:" << source.length() << "->" << enc.length() << std::endl;
// 輸出到文件
fp = fopen("enc.bin", "wb");
fwrite(enc.data(), 1, enc.length(), fp);
fclose(fp);
}
C++解密代碼:
#include 見后文
// 注意:參數和返回值全部是二進制數據
std::string aesDecrypt(const std::string& ciphertext, const std::string& key)
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int ret = EVP_DecryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char*)key.data(), NULL);
assert(ret == 1);
unsigned char* result = new unsigned char[ciphertext.length() + 64]; // 弄個足夠大的空間
int len1 = 0;
ret = EVP_DecryptUpdate(&ctx, result, &len1, (const unsigned char*)ciphertext.data(), ciphertext.length());
assert(ret == 1);
int len2 = 0;
ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2);
assert(ret == 1);
std::cout << len1 << "," << len2 << std::endl;
ret = EVP_CIPHER_CTX_cleanup(&ctx);
assert(ret == 1);
std::string res((char*)result, len1+len2);
delete[] result;
return res;
}
int main()
{
std::string key("helloaeshelloaes", 16); // 二進制數據,而不是以0結尾的字符串
// 讀取文件內容(簡單起見認為文件內容<100K)
char buf[1024*100];
FILE* fp = fopen("enc.bin", "rb");
int bytes = fread(buf, 1, 1024*100, fp);
fclose(fp);
std::string data(buf, bytes); // 二進制數據
// 加密
std::string dec = aesDecrypt(data, key);
std::cout << "aesDecrypt:" << data.length() << "->" << dec.length() << std::endl;
// 輸出到文件
fp = fopen("dec.txt", "wb");
fwrite(dec.data(), 1, dec.length(), fp);
fclose(fp);
}
===============================發表后的分割線===============================
1、發表后發現C++代碼中include類庫都由於小括號的緣故,被卡擦掉了。這邊補充一下,需要包含以下頭文件:
[string]
[iostream]
[stdio.h]
[assert.h]
[openssl/objects.h]
[openssl/evp.h]
2、DES、AES加密算法都是針對數據塊,Java加解密函數參數使用byte數組。C++用std::string,那是因為這是C++中使用byte數組的最簡單方式(std::string可以存儲二進制數據,很多人沒想到吧),缺點是拷貝內存的次數可能會略多些。如果想要優化拷貝效率,可以使用自己封裝的Buffer類來代替std::string。
1)分塊的方式。加密是逐塊進行的。分塊方法有:CBC、ECB、CFB……
2)padding的方式。當數據的位數不及塊的大小時,需要填充。填充方式有:NoPadding、PKCS5Padding……
如果加解密端采用不同的分塊方式或padding方式,即使都是采用DES/AES算法,同樣無法解密成功。上次需要C端和Java端進行密文傳輸,就跪在這一點上(那時候沒時間研究)。
參考文章: Java AES算法和openssl配對 ,主要通過其了解openssl里比較高級的EVP系列API(其默認padding和java一樣都是PKCS5Padding),節約了搜索時間。
貼代碼了,以下代碼測試通過了。Java和C++均可以正確解密對方的密文。
約定:分塊和padding采用Java默認的 ECB + PKCS5Padding。
加密程序,輸入是"src.txt"文件(加密無需純文本,只是為了可讀性),輸出保存在"enc.bin"文件。
解碼程序,輸入是"enc.bin"文件,輸出保存在"dec.txt"文件。
------------------------------------------
DES:
Java加密代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class DesEnc {
public static byte[] desEncrypt(byte[] source, byte rawKeyData[])
throws GeneralSecurityException {
// 處理密鑰
SecretKeySpec key = new SecretKeySpec(rawKeyData, "DES");
// 加密
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(source);
}
public static void main(String[] args) throws Exception {
// DES算法要求密鈅64位(8字節)
byte rawKeyData[] = "hellodes".getBytes("UTF-8");
// 讀取文件內容(為了簡單忽略錯誤處理)
File file = new File("src.txt");
byte[] source = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
is.read(source, 0, (int) file.length());
is.close();
// 加密
byte[] enc = desEncrypt(source, rawKeyData);
System.out.println("desEncrypt:" + source.length + "->" + enc.length);
// 輸出到文件
FileOutputStream os = new FileOutputStream(new File("enc.bin"));
os.write(enc, 0, enc.length);
os.close();
}
}
Java解密代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class DesDec {
public static byte[] desDecrypt(byte[] data, byte rawKeyData[])
throws GeneralSecurityException {
// 處理密鑰
SecretKeySpec key = new SecretKeySpec(rawKeyData, "DES");
// 解密
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
// DES算法要求密鈅64位(8字節)
byte rawKeyData[] = "hellodes".getBytes("UTF-8");
// 讀取文件內容(為了簡單忽略錯誤處理)
File file = new File("enc.bin");
byte[] data = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
is.read(data, 0, (int) file.length());
is.close();
// 加密
byte[] dec = desDecrypt(data, rawKeyData);
System.out.println("desDecrypt:" + data.length + "->" + dec.length);
// 輸出到文件
FileOutputStream os = new FileOutputStream(new File("dec.txt"));
os.write(dec, 0, dec.length);
os.close();
}
}
C++加密代碼:
#include 見后文
// 注意:參數和返回值全部是二進制數據
std::string desEncrypt(const std::string& source, const std::string& key)
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int ret = EVP_EncryptInit_ex(&ctx, EVP_des_ecb(), NULL, (const unsigned char*)key.data(), NULL);
assert(ret == 1);
unsigned char* result = new unsigned char[source.length() + 64]; // 弄個足夠大的空間
int len1 = 0;
ret = EVP_EncryptUpdate(&ctx, result, &len1, (const unsigned char*)source.data(), source.length());
assert(ret == 1);
int len2 = 0;
ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2);
assert(ret == 1);
std::cout << len1 << "," << len2 << std::endl;
ret = EVP_CIPHER_CTX_cleanup(&ctx);
assert(ret == 1);
std::string res((char*)result, len1+len2);
delete[] result;
return res;
}
int main()
{
std::string key("hellodes", 8); // 二進制數據,而不是以0結尾的字符串
// 讀取文件內容(簡單起見認為文件內容<100K)
char buf[1024*100];
FILE* fp = fopen("src.txt", "rb");
int bytes = fread(buf, 1, 1024*100, fp);
fclose(fp);
std::string source(buf, bytes); // 二進制數據
// 加密
std::string enc = desEncrypt(source, key);
std::cout << "desEncrypt:" << source.length() << "->" << enc.length() << std::endl;
// 輸出到文件
fp = fopen("enc.bin", "wb");
fwrite(enc.data(), 1, enc.length(), fp);
fclose(fp);
}
C++解密代碼:
#include 見后文
// 注意:參數和返回值全部是二進制數據
std::string desDecrypt(const std::string& ciphertext, const std::string& key)
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int ret = EVP_DecryptInit_ex(&ctx, EVP_des_ecb(), NULL, (const unsigned char*)key.data(), NULL);
assert(ret == 1);
unsigned char* result = new unsigned char[ciphertext.length() + 64]; // 弄個足夠大的空間
int len1 = 0;
ret = EVP_DecryptUpdate(&ctx, result, &len1, (const unsigned char*)ciphertext.data(), ciphertext.length());
assert(ret == 1);
int len2 = 0;
ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2);
assert(ret == 1);
std::cout << len1 << "," << len2 << std::endl;
ret = EVP_CIPHER_CTX_cleanup(&ctx);
assert(ret == 1);
std::string res((char*)result, len1+len2);
delete[] result;
return res;
}
int main()
{
std::string key("hellodes", 8); // 二進制數據,而不是以0結尾的字符串
// 讀取文件內容(簡單起見認為文件內容<100K)
char buf[1024*100];
FILE* fp = fopen("enc.bin", "rb");
int bytes = fread(buf, 1, 1024*100, fp);
fclose(fp);
std::string data(buf, bytes); // 二進制數據
// 加密
std::string dec = desDecrypt(data, key);
std::cout << "desDecrypt:" << data.length() << "->" << dec.length() << std::endl;
// 輸出到文件
fp = fopen("dec.txt", "wb");
fwrite(dec.data(), 1, dec.length(), fp);
fclose(fp);
}
------------------------------------------
AES:
Java加密代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AesEnc {
public static byte[] aesEncrypt(byte[] source, byte rawKeyData[])
throws GeneralSecurityException {
// 處理密鑰
SecretKeySpec key = new SecretKeySpec(rawKeyData, "AES");
// 加密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(source);
}
public static void main(String[] args) throws Exception {
// AES算法要求密鈅128位(16字節)
byte rawKeyData[] = "helloaeshelloaes".getBytes("UTF-8");
// 讀取文件內容(為了簡單忽略錯誤處理)
File file = new File("src.txt");
byte[] source = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
is.read(source, 0, (int) file.length());
is.close();
// 加密
byte[] enc = aesEncrypt(source, rawKeyData);
System.out.println("aesEncrypt:" + source.length + "->" + enc.length);
// 輸出到文件
FileOutputStream os = new FileOutputStream(new File("enc.bin"));
os.write(enc, 0, enc.length);
os.close();
}
}
Java解密代碼:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AesDec {
public static byte[] aesDecrypt(byte[] data, byte rawKeyData[])
throws GeneralSecurityException {
// 處理密鑰
SecretKeySpec key = new SecretKeySpec(rawKeyData, "AES");
// 解密
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
// AES算法要求密鈅128位(16字節)
byte rawKeyData[] = "helloaeshelloaes".getBytes("UTF-8");
// 讀取文件內容(為了簡單忽略錯誤處理)
File file = new File("enc.bin");
byte[] data = new byte[(int) file.length()];
FileInputStream is = new FileInputStream(file);
is.read(data, 0, (int) file.length());
is.close();
// 加密
byte[] dec = aesDecrypt(data, rawKeyData);
System.out.println("desDecrypt:" + data.length + "->" + dec.length);
// 輸出到文件
FileOutputStream os = new FileOutputStream(new File("dec.txt"));
os.write(dec, 0, dec.length);
os.close();
}
}
C++加密代碼:
#include 見后文
// 注意:參數和返回值全部是二進制數據
std::string aesEncrypt(const std::string& source, const std::string& key)
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char*)key.data(), NULL);
assert(ret == 1);
unsigned char* result = new unsigned char[source.length() + 64]; // 弄個足夠大的空間
int len1 = 0;
ret = EVP_EncryptUpdate(&ctx, result, &len1, (const unsigned char*)source.data(), source.length());
assert(ret == 1);
int len2 = 0;
ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2);
assert(ret == 1);
std::cout << len1 << "," << len2 << std::endl;
ret = EVP_CIPHER_CTX_cleanup(&ctx);
assert(ret == 1);
std::string res((char*)result, len1+len2);
delete[] result;
return res;
}
int main()
{
std::string key("helloaeshelloaes", 16); // 二進制數據,而不是以0結尾的字符串
// 讀取文件內容(簡單起見認為文件內容<100K)
char buf[1024*100];
FILE* fp = fopen("src.txt", "rb");
int bytes = fread(buf, 1, 1024*100, fp);
fclose(fp);
std::string source(buf, bytes); // 二進制數據
// 加密
std::string enc = aesEncrypt(source, key);
std::cout << "aesEncrypt:" << source.length() << "->" << enc.length() << std::endl;
// 輸出到文件
fp = fopen("enc.bin", "wb");
fwrite(enc.data(), 1, enc.length(), fp);
fclose(fp);
}
C++解密代碼:
#include 見后文
// 注意:參數和返回值全部是二進制數據
std::string aesDecrypt(const std::string& ciphertext, const std::string& key)
{
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
int ret = EVP_DecryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char*)key.data(), NULL);
assert(ret == 1);
unsigned char* result = new unsigned char[ciphertext.length() + 64]; // 弄個足夠大的空間
int len1 = 0;
ret = EVP_DecryptUpdate(&ctx, result, &len1, (const unsigned char*)ciphertext.data(), ciphertext.length());
assert(ret == 1);
int len2 = 0;
ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2);
assert(ret == 1);
std::cout << len1 << "," << len2 << std::endl;
ret = EVP_CIPHER_CTX_cleanup(&ctx);
assert(ret == 1);
std::string res((char*)result, len1+len2);
delete[] result;
return res;
}
int main()
{
std::string key("helloaeshelloaes", 16); // 二進制數據,而不是以0結尾的字符串
// 讀取文件內容(簡單起見認為文件內容<100K)
char buf[1024*100];
FILE* fp = fopen("enc.bin", "rb");
int bytes = fread(buf, 1, 1024*100, fp);
fclose(fp);
std::string data(buf, bytes); // 二進制數據
// 加密
std::string dec = aesDecrypt(data, key);
std::cout << "aesDecrypt:" << data.length() << "->" << dec.length() << std::endl;
// 輸出到文件
fp = fopen("dec.txt", "wb");
fwrite(dec.data(), 1, dec.length(), fp);
fclose(fp);
}
===============================發表后的分割線===============================
1、發表后發現C++代碼中include類庫都由於小括號的緣故,被卡擦掉了。這邊補充一下,需要包含以下頭文件:
[string]
[iostream]
[stdio.h]
[assert.h]
[openssl/objects.h]
[openssl/evp.h]
2、DES、AES加密算法都是針對數據塊,Java加解密函數參數使用byte數組。C++用std::string,那是因為這是C++中使用byte數組的最簡單方式(std::string可以存儲二進制數據,很多人沒想到吧),缺點是拷貝內存的次數可能會略多些。如果想要優化拷貝效率,可以使用自己封裝的Buffer類來代替std::string。