文档章节

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

李有常
 李有常
发布于 2017/03/27 15:40
字数 1943
阅读 22
收藏 1

本来觉得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。

本文转载自:

上一篇: openssl 1.0.2K编译
下一篇: pjsip android
李有常
粉丝 6
博文 129
码字总数 32131
作品 0
威海
后端工程师
私信 提问
Node系统模块crypto实现md5 Cipher等多种加密方式

crypto加密模块是C/C++实现这些算法后,暴露为javascript接口的模块,包含对 OpenSSL 的哈希、HMAC、加密、解密、签名、以及验证功能的一整套封装。Cipher Cipher类用于加密数据,属于对称密...

曲先森
2018/07/31
0
0
JNI调用c++实现AES加密解密

最近项目中用到数据加密解密的功能,由于Android、iOS以及服务器端都需要用到这个功能。而不同平台上加密出来的密文是不一样的,这样导致互相之间密文无法使用。于是决定使用C/C++完成加密解...

whoisliang
2018/01/25
292
0
RSA算法 c++/java相互进行加密解密出现“解密失败”

场景:Java生成的RSA公钥传给C++,C++用该RSA公钥加密32位随机数,返回密文给Java,Java用RSA私钥解密,出现“解密失败”。 公钥模: 8B1960C2B5309F109AD28E80150A0F6F80FFE266891BB1B9B86A...

知行旅人
2017/05/09
1K
3
GO语言版鹅厂广告交易实时平台价格解析

腾讯广告实时交易平台在向竞价胜出一方返回成交价的时候,先对价格进行TEA加密,再对密文进行BASE64编码,接收方先对BASE64解码,再对密文解密,双方事先约定密钥。鹅厂官网提供了C#、C++、J...

厉力文武
2018/08/01
883
0
java与C++通过des、blowfish互相加解密

在简单的服务器端与客户端通信的应用中,这种做法比较常见 DES、blowfish扫盲: 1.des的常见模式分为四种 ECB / CBC / CFB / OFB 这里使用默认的ECB ECB的缺陷:能从密文看出明文的规律 加密...

sumekey
2011/11/05
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Java Web 中对 ServletRequest 的一些非常规操作解决方案

1. 前言 ServletRequest 是我们搞 Java Web 经常接触的 Servlet Api 。有些时候我们要经常对其进行一些操作。这里列举一些经常的难点操作。 2. 提取 body 中的数据 前后端交互我们会在 body...

码农小胖哥
26分钟前
2
0
《Dual Encoding U-Net for Retinal Vessel Segmentation》阅读笔记-MICCAI2019

作者:Bo Wang1,2, Shuang Qiu2, and Huiguang He1,2,3 目的:Retinal Vessel Segmentation is an essential step for the early diagnosis of eye-related diseases, such as diabetes and ......

JungleKing
28分钟前
2
0
一次看懂 Https 证书认证

TLS > 传输层安全性协定 TLS(Transport Layer Security),及其前身安全套接层 SSL(Secure Sockets Layer)是一种安全协议,目的是为网际网路通信,提供安全及数据完整性保障。 如图,TLS...

极客收藏夹
42分钟前
4
0
https证书买哪家好?有哪些供应商

在选购https证书前除了要了解类型外,还需要了解https证书供应商,毕竟不同的供应商,提供的产品质量与服务也是有差异的。今天小编就为大家讲讲https证书供应商方面的内容,希望各位会喜欢。...

安信证书
44分钟前
5
0
Zuul 配置

概述:zuul底层是基于servlet,是由一系列的filter链构成。 1、路由配置 a、单例serverId映射 zuul: routes: client-a: path: /client/** serviceId: client-a 意思是...

java框架开发者
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部