3DES encrypt by java, decrypt by c++

前言

同事用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;
    }
}

猜你喜欢

转载自blog.csdn.net/LostSpeed/article/details/70328191
今日推荐