前言
同事用3DES加密了一个字符串,让我用C解密试试.
试了一下,解不开.
自己搭个java控制台环境, 开始做实验.
将java用3DES加密, c/c++用3DES解密的实验.
现在加解密流程都跑通了.
对称加密算法中,影响加解密的因素有 :
- 算法实现(e.g. 3DES, AES)
- 密钥(自定义的加解密口令)
- 初始化向量(简称为IV, 这个也是使用者自己定义的)
- 块加密模式(e.g. ECB, CBC, 代表安全性是否好)
- 尾部空闲字节填充方式(e.g. NoPadding, PKCS5Padding, 模式size对齐用的)
- 字符集
解密不正确的原因:
不是知道了算法实现和密钥就可以解开密文.
如果解密方选择的解密参数(IV, 块加密模式, 填充方式)和加密方不同, 同样不能正确的解密.
工程下载点
src_3des_encrypt_by_java_decrypt_by_c_testcase.zip
记录
java 3DES加密代码
编译环境
JDK_1.7.0_64bit.zip
eclipse-java-mars-2-win32-x86_64.zip
java’s 3DES encrypt
class build in on java can encrypt data by 3DES and base64 the cipher text
/**
* @filename
* ls_3des_opt.java
* @brief
* 3DES加解密操作, 将加密参数(除了密钥)重构成常量
* @note
* dev env : eclipse-java-mars-2-win32-x86_64.zip, JDK_1.7.0_64bit.exe
* @tip
* CTRL + SHIFT + B 设置(取消)断点
*/
package java_desede;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
// import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
/**
* @author lostspeed
*
*/
public class ls_3des_opt {
static final String m_str_algorithm_name = "DESede";
static final String m_str_transformation = "DESede/CBC/PKCS5Padding";
static final byte m_iv8_byte_ary[] = {2,0,1,7,0,4,2,1}; // iv is 20170421
static final String m_strcharsetName = "UTF-8";
static final String m_strline80 = "================================================================================";
/**
*
*/
public ls_3des_opt() {
// TODO Auto-generated constructor stub
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String str_key = new String("123456781234567812345678");
String str_plain_text = new String("hello, 3des:)");
byte[] byte_ary_plain_text_in = null;
byte[] byte_ary_cipher_text = null;
byte[] byte_ary_plain_text = null;
String cipher_text_base64 = null;
byte[] byte_ary_cipher_text_unbase64 = null;
String str_plain_text_after_decrypt = null;
try {
System.out.printf("%s\n", m_strline80);
System.out.printf("please input plain text for 3DES : \n\t");
Scanner scanner = new Scanner(System.in);
str_plain_text = scanner.nextLine();
scanner.close();
System.out.printf("\nplain text input is : \n\t%s\n", str_plain_text);
// 加密者: 加密明文流
byte_ary_cipher_text = encrypt(str_plain_text.getBytes(), str_key);
System.out.printf("\ncipher text after 3DES encrypt:\n");
print_byte_ary(byte_ary_cipher_text);
// 加密者 : base64用于传递给解密者
cipher_text_base64 = Base64.encode(byte_ary_cipher_text);
System.out.printf("\ncipher text after base64 : \n\t%s\n", cipher_text_base64);
// 解密者 : unbase64
byte_ary_cipher_text_unbase64 = Base64.decode(cipher_text_base64);
// 解密者 : 解密密文流
byte_ary_plain_text = decrypt(byte_ary_cipher_text_unbase64, str_key);
str_plain_text_after_decrypt = new String(byte_ary_plain_text,"UTF-8");
System.out.printf("\nplain text after unbase64 and decrypt = \n\t%s\n", str_plain_text_after_decrypt);
System.out.printf("%s\n", m_strline80);
/** run result
================================================================================
please input plain text for 3DES :
java console : plain text input for 3DES encrypt and decrypt :)
plain text input is :
java console : plain text input for 3DES encrypt and decrypt :)
cipher text after 3DES encrypt:
{0x38, 0x43, 0x97, 0x5F, 0xCF, 0x54, 0xA0, 0x95,
0x93, 0xE3, 0x7A, 0x9B, 0xB9, 0xB8, 0x1A, 0x16,
0x11, 0xCF, 0xDB, 0x92, 0x2F, 0x4B, 0xC3, 0x07,
0xC9, 0x15, 0x16, 0x30, 0x60, 0x09, 0xA5, 0xEE,
0x15, 0x3B, 0xDC, 0x01, 0x3C, 0xA1, 0xF5, 0x22,
0x9C, 0x9C, 0x4C, 0x9B, 0x5B, 0x71, 0x88, 0x7F,
0xA8, 0xB9, 0x6F, 0xEE, 0xE1, 0x27, 0x5D, 0xA8,
0x76, 0x9E, 0x78, 0xF0, 0xBF, 0xCD, 0xD9, 0x56}
cipher text after base64 :
OEOXX89UoJWT43qbubgaFhHP25IvS8MHyRUWMGAJpe4VO9wBPKH1IpycTJtbcYh/qLlv7uEnXah2
nnjwv83ZVg==
plain text after unbase64 and decrypt =
java console : plain text input for 3DES encrypt and decrypt :)
================================================================================
*/
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void print_byte_ary(byte[] aryIn) {
int i = 0;
int iPosIndex = 0;
System.out.printf("\t{");
for (i = 0; i < aryIn.length; i++) {
if ((0 != i) && (0 == iPosIndex)) {
System.out.printf("\t");
}
System.out.printf("0x%02X", aryIn[i]);
if (i != (aryIn.length - 1)) {
System.out.printf(", ");
}
if (((8 - 1) == iPosIndex) && (i != (aryIn.length - 1))) {
System.out.printf("\n");
iPosIndex = 0;
} else {
iPosIndex++;
}
}
System.out.printf("}\n");
}
public static byte[] encrypt(byte[] plain_text, String cipher_key) throws Exception {
byte[] KeyAry = null;
try {
KeyAry = cipher_key.getBytes(m_strcharsetName);
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
SecretKey key = new SecretKeySpec(KeyAry, 0, KeyAry.length, m_str_algorithm_name);
IvParameterSpec iv = new IvParameterSpec(m_iv8_byte_ary.clone());
Cipher cipher = Cipher.getInstance(m_str_transformation);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] cipherText = cipher.doFinal(plain_text);
return cipherText;
}
public static byte[] decrypt(byte[] message, String cipher_key) throws Exception {
byte[] KeyAry = null;
try {
KeyAry = cipher_key.getBytes(m_strcharsetName);
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
}
SecretKey key = new SecretKeySpec(KeyAry, 0, KeyAry.length, m_str_algorithm_name);
IvParameterSpec iv = new IvParameterSpec(m_iv8_byte_ary.clone());
Cipher decipher = Cipher.getInstance(m_str_transformation);
decipher.init(Cipher.DECRYPT_MODE, key, iv);
return decipher.doFinal(message);
}
}
c/c++'s 3DES decrypt
on c/c++, unbase64 and 3DES only use 3rd code.
3DES used openSSL.
build env : vs2013, pure c code.
// test_3des.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include "LsBase64.h"
#include "openssl\evp.h"
// 从java端来了一个经过3des的base64字符串
// c++端 unbase64 + 3des decrypt, 显示解密完成的明文串.
// openssl 安装后的目录 : C:\some\openssl\dir, 拷贝到本工程目录的3rd_openssl文件夹中
/** openssl 目录结构
文件夹 PATH 列表
卷序列号为 00000041 0E38:D31F
.\3rd_openssl
├─bin
│ libeay32.dll
│ openssl.exe
│ ssleay32.dll
│
├─include
│ └─openssl
│ aes.h
│ applink.c
│ ...
│
├─lib
│ │ libeay32.lib
│ │ ssleay32.lib
│ │
│ └─engines
│ 4758cca.dll
│ aep.dll
│ ...
│
└─ssl
openssl.cnf
*/
// inc目录 : C:\some\openssl\dir\include
// lib目录 : C:\some\openssl\dir\lib
// @ref OpenSSL对数组加密解密的完整实现代码
// http://www.linuxidc.com/Linux/2014-04/99850.htm
#pragma comment(lib, "libeay32.lib")
#pragma comment(lib, "ssleay32.lib")
int des3_DecryptBuffer(unsigned char* key, unsigned char* iv, unsigned char* in_dec, unsigned char* out_dec, int in_len, int* out_len);
void fnTestDecrypt_byOpenSSL();
int main(int argc, char* argv[])
{
fnTestDecrypt_byOpenSSL();
system("pause");
/** run result
decrypt uLenBuf = 64
plain text length : 63
pading length = 1
plain text after decrypt:
java console : plain text input for 3DES encrypt and decrypt :)
请按任意键继续. . .
*/
return 0;
}
void fnTestDecrypt_byOpenSSL()
{
unsigned char key[EVP_MAX_KEY_LENGTH]; //保存密钥的数组
unsigned char iv[EVP_MAX_IV_LENGTH]; //保存初始化向量的数组
// 密文的base64, java端对明文加密后,将密文流base64
const char* pCipherTextBase64 = "OEOXX89UoJWT43qbubgaFhHP25IvS8MHyRUWMGAJpe4VO9wBPKH1IpycTJtbcYh/qLlv7uEnXah2nnjwv83ZVg == ";
unsigned int ulRc = 0;
int iTextLen = strlen(pCipherTextBase64); // 明文size最大不超过base64之后的size
unsigned int uLen = 0;
unsigned char* pUnBase64 = new unsigned char[iTextLen]; // 保存密文unbase64之后的密文(2进制流)
unsigned char* pPlainText = NULL; // 明文size
int i = 0;
memset(pUnBase64, 0, iTextLen);
ulRc = ns_ls::base64_decode((BYTE*)pCipherTextBase64, iTextLen, pUnBase64, uLen);
printf("decrypt uLenBuf = %d\n", uLen);
// 设置IV
// static final byte m_iv8_byte_ary[] = {2,0,1,7,0,4,2,1}; // iv is 20170421
// java加密时, iv is 20170421, 就8个字节
memset(iv, 0, sizeof(iv));
iv[0] = 2;
iv[1] = 0;
iv[2] = 1;
iv[3] = 7;
iv[4] = 0;
iv[5] = 4;
iv[6] = 2;
iv[7] = 1;
// 设置key, 3DES Key is 24bytes
// String str_key = new String("123456781234567812345678");
memcpy(key, "123456781234567812345678", 24);
pPlainText = new unsigned char[uLen + 1];
memset(pPlainText, 0xff, uLen + 1);
/*
* key:加密密钥,3DES密钥size = 24
* iv:加密初始向量
* in_dec:密文数组,输入数组
* out_dec:解密后的数组,输出数组
* in_len:密文长度
* out_len:明文长度
* */
if (0 == des3_DecryptBuffer(key, iv, pUnBase64, pPlainText, uLen, &iTextLen)) {
// (uLen - iTextLen) = pading bytes
printf("plain text length : %d\npading length = %d\n", iTextLen, uLen - iTextLen);
if (iTextLen < uLen) {
pPlainText[iTextLen] = '\0'; // 去除pading
}
// iTextLen包含pading, 现在去掉pading
// java端加密模式采用, "DESede/CBC/PKCS5Padding"
// pading填充的约定
// http://www.cnblogs.com/midea0978/articles/1437257.html
printf("plain text after decrypt:\n%s\n", pPlainText);
}
else {
printf("decrypt failed\n");
}
if (NULL != pPlainText) {
delete[] pPlainText;
pPlainText = NULL;
}
if (NULL != pUnBase64) {
delete[] pUnBase64;
pUnBase64 = NULL;
}
}
/*
* key:加密密钥,3DES密钥size = 24
* iv:加密初始向量
* in_dec:密文数组,输入数组
* out_dec:解密后的数组,输出数组
* in_len:密文长度
* out_len:明文长度
* */
//解密函数
int des3_DecryptBuffer(unsigned char* key, unsigned char* iv, unsigned char* in_dec, unsigned char* out_dec, int in_len, int* out_len)
{
int outl; //第一次使用update解密的数据长度
int outl2; //剩余的字段,经过final解密并去除填充后的长度
int rv;
EVP_CIPHER_CTX ctx;
//初始化ctx
EVP_CIPHER_CTX_init(&ctx);
//设置解密的算法、key和iv
// java端加密模式采用, "DESede/CBC/PKCS5Padding", so 参数2为EVP_des_ede3_cbc()
rv = EVP_DecryptInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, key, iv);
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
//循环读取原文,解密后后保存到明文文件。
rv = EVP_DecryptUpdate(&ctx, out_dec, &outl, in_dec, in_len);//解密
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
//解密结束
rv = EVP_DecryptFinal_ex(&ctx, out_dec + outl, &outl2);
if (rv != 1) {
EVP_CIPHER_CTX_cleanup(&ctx);
return -1;
}
*out_len = (outl + outl2);
EVP_CIPHER_CTX_cleanup(&ctx);//清除EVP加密上下文环境
return 0;
}
// LsBase64.h: interface for the LsBase64 class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_LSBASE64_H__28E02995_79BF_48B8_9809_9A575CB7E700__INCLUDED_)
#define AFX_LSBASE64_H__28E02995_79BF_48B8_9809_9A575CB7E700__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
namespace ns_ls
{
unsigned int base64_encode(const unsigned char* bytes_to_encode, unsigned int in_len, unsigned char* encoded_buffer, unsigned int& out_len);
unsigned int base64_decode(const unsigned char* encoded_string, unsigned int in_len, unsigned char* decoded_buffer, unsigned int& out_len);
}
#endif // !defined(AFX_LSBASE64_H__28E02995_79BF_48B8_9809_9A575CB7E700__INCLUDED_)
// LsBase64.cpp: implementation of the LsBase64 class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string.h>
#include "LsBase64.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
using namespace std;
namespace ns_ls
{
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c)
{
return (isalnum(c) || (c == '+') || (c == '/'));
}
unsigned int base64_encode(const unsigned char* bytes_to_encode, unsigned int in_len, unsigned char* encoded_buffer, unsigned int& out_len)
{
int i = 0;
int j = 0;
unsigned char char_array_3[3] = { 0, 0, 0 };
unsigned char char_array_4[4] = { 0, 0, 0, 0 };
out_len = 0;
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; i < 4 ; i++) {
encoded_buffer[out_len++] = base64_chars[char_array_4[i]];
}
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) {
char_array_3[j] = '\0';
}
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; j < (i + 1); j++) {
encoded_buffer[out_len++] = base64_chars[char_array_4[j]];
}
while (i++ < 3) {
encoded_buffer[out_len++] = '=';
}
}
return out_len;
}
unsigned int base64_decode(const unsigned char* encoded_string, unsigned int in_len, unsigned char* decoded_buffer, unsigned int& out_len)
{
size_t i = 0;
size_t j = 0;
int in_ = 0;
unsigned char char_array_3[3] = { 0, 0, 0 };
unsigned char char_array_4[4] = { 0, 0, 0, 0 };
out_len = 0;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_];
in_++;
if (i == 4) {
for (i = 0; i < 4; i++) {
char_array_4[i] = static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; i < 3; i++) {
decoded_buffer[out_len++] = char_array_3[i];
}
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++) {
char_array_4[j] = 0;
}
for (j = 0; j < 4; j++) {
char_array_4[j] = static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
}
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) {
decoded_buffer[out_len++] = char_array_3[j];
}
}
return out_len;
}
}