Java和C/C++进行DES/AES密文传输

30 篇文章 0 订阅
18 篇文章 0 订阅

本来觉得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。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
DES算法(Data Encryption Standard)是一种对称加密算法,是美国国家标准局于1977年确定的数据加密标准。DES算法使用一个64位的密钥对数据进行加密和解密操作。其加密过程可以简单描述为,将64位的明文数据分成两个32位的部分L0和R0,经过16轮的加密操作后,得到加密后的64位密文。 下面是用C语言实现DES算法的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/des.h> int main() { // 设置密钥 const char* key = "12345678"; DES_cblock desKey; DES_key_schedule keySchedule; memcpy(desKey, key, sizeof(desKey)); DES_set_odd_parity(&desKey); DES_set_key_checked(&desKey, &keySchedule); // 加密数据 const char* plaintext = "Hello World!"; DES_cblock input; memcpy(input, plaintext, sizeof(input)); DES_ecb_encrypt(&input, &input, &keySchedule, DES_ENCRYPT); // 输出加密结果 printf("DES encrypted: "); for(int i = 0; i < sizeof(input); ++i) { printf("%02x", input[i]); } printf("\n"); return 0; } ``` AES算法(Advanced Encryption Standard)是一种对称加密算法,是目前应用最广泛的加密算法之一。AES算法使用128位、192位或256位的密钥对数据进行加密和解密操作。其加密过程可以简单描述为,将明文数据分成一组个128位的数据块,经过多轮的加密操作后,得到加密后的数据块。 下面是用C语言实现AES算法的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/aes.h> int main() { // 设置密钥 const char* key = "12345678901234567890123456789012"; AES_KEY aesKey; AES_set_encrypt_key(key, 256, &aesKey); // 加密数据 const char* plaintext = "Hello World!"; unsigned char input[AES_BLOCK_SIZE]; memcpy(input, plaintext, sizeof(input)); AES_encrypt(input, input, &aesKey); // 输出加密结果 printf("AES encrypted: "); for(int i = 0; i < sizeof(input); ++i) { printf("%02x", input[i]); } printf("\n"); return 0; } ``` 以上代码分别演示了使用C语言利用OpenSSL库实现DES算法和AES算法的加密部分,通过设置密钥、输入明文数据并调用相应的加密函数,最终输出加密后的结果。需要注意的是,这只是加密部分的示例代码,解密部分的实现是类似的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值