背景
前段时间在跟后台联调敏感数据的加密和解密时遇到了一些问题,设备使用的是android平台提供的des加密方式,后台使用的javax提供的des加密方式和PHP提供加密方式,在对接过程中就出现了同一段字符,在前后端解密得到的字符串不一样,最后经过一段时间的调试,得到了结果一致的实现方式,下面记录下来。
实现平台
java平台
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.util.Base64;
public class DESUtil {
private static final String KEY = "test";
private static final String DES = "DES";
private static final String DES_P = "DES/ECB/PKCS5Padding";
private static final String CHARSET = "UTF-8";
/**
* 进行DES加密
*
* @param string String 需要加密的字符串
* @return String 加密字符串
*/
public static String encryptByDes(String string) {
return encryptByDes(string, KEY);
}
/**
* DES 解密
*
* @param string String 加密串
* @return String 明文
*/
public static String decryptByDes(String string) {
return decryptByDes(string, KEY);
}
/**
* 进行DES加密
*
* @param string
* @param key
* @return
*/
private static String encryptByDes(String string, String key) {
try {
if (string != null && string.length() > 0) {
byte[] tmpOriginalStr = string.getBytes(CHARSET);
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
// 创建一个DESKeySpec对象
DESKeySpec dks = new DESKeySpec(key.getBytes());
// 将DESKeySpec对象转换成SecretKey对象
SecretKey secretKey = keyFactory.generateSecret(dks);
// 用密匙初始化Cipher对象
Cipher cipher = Cipher.getInstance(DES_P);
// Cipher对象实际完成加密操作
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// 真正开始加密操作
byte[] encryptStr = cipher.doFinal(tmpOriginalStr);
if (encryptStr != null) {
return Base64.getEncoder().encodeToString(encryptStr);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* DES 解密
*
* @param string
* @param key
* @return
*/
private static String decryptByDes(String string, String key) {
try {
if (string != null && string.length() > 0) {
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
// 创建一个DESKeySpec对象
DESKeySpec dks = new DESKeySpec(key.getBytes(CHARSET));
// 将DESKeySpec对象转换成SecretKey对象
SecretKey secretKey = keyFactory.generateSecret(dks);
// 用密匙初始化Cipher对象
Cipher cipher = Cipher.getInstance(DES_P);
// Cipher对象实际完成解密操作
cipher.init(Cipher.DECRYPT_MODE, secretKey);
// 真正开始解密操作
byte[] decryptBytes = cipher.doFinal(Base64.getDecoder().decode(string));
if (decryptBytes != null) {
return new String(decryptBytes, CHARSET);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
android平台
/**
* DES 工具类
*/
public class DESUtil {
@IntDef({Cipher.ENCRYPT_MODE, Cipher.DECRYPT_MODE})
@interface DESType {}
/**
* Des加密/解密
*
* @param content 字符串内容
* @param password 密钥
* @param type 加密:{@link Cipher#ENCRYPT_MODE},解密:{@link Cipher#DECRYPT_MODE}
* @return 加密/解密结果
*/
public static String des(String content, String password, @DESType int type) {
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
@SuppressLint("GetInstance") Cipher cipher = Cipher.getInstance("DES");
cipher.init(type, keyFactory.generateSecret(desKey), random);
if (type == Cipher.ENCRYPT_MODE) {
byte[] byteContent = content.getBytes("utf-8");
return parseByte2HexStr(cipher.doFinal(byteContent));
} else {
byte[] byteContent = parseHexStr2Byte(content);
return new String(cipher.doFinal(byteContent));
}
} catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException |
UnsupportedEncodingException | InvalidKeyException | NoSuchPaddingException |
InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
/**
* des加密
*
* @param plainText 待加密字符串
* @return 加密后的字符串
*/
public static String encryptByDes(String plainText){
return EncryptionClient.encryptByDes(plainText);
}
/**
* des解密
*
* @param cipherText 待解密字符串
* @return 解密后的字符串
*/
public static String decryptByDes(String cipherText) throws Exception{
return EncryptionClient.decryptByDes(cipherText);
}
}
ios平台
#pragma mark- 加密算法
+(NSString *) encryptUseDES:(NSString *)plainText key:(NSString *)key{
NSString *ciphertext = nil;
NSData *textData = [plainText dataUsingEncoding:NSUTF8StringEncoding];
NSUInteger dataLength = [textData length];
unsigned char buffer[1024];
memset(buffer, 0, sizeof(char));
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
[key UTF8String],
kCCKeySizeDES,
nil,
[textData bytes],
dataLength,
buffer,
1024,
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
ciphertext = [GTMBase64 stringByEncodingData:data];
}
return ciphertext;
}
#pragma mark- 解密算法
+(NSString *)decryptUseDES:(NSString *)cipherText key:(NSString *)key{
NSString *plaintext = nil;
NSData *cipherdata = [GTMBase64 decodeString:cipherText];
unsigned char buffer[1024];
memset(buffer, 0, sizeof(char));
size_t numBytesDecrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmDES,
kCCOptionPKCS7Padding | kCCOptionECBMode,
[key UTF8String],
kCCKeySizeDES,
nil,
[cipherdata bytes],
[cipherdata length],
buffer,
1024,
&numBytesDecrypted);
if(cryptStatus == kCCSuccess) {
NSData *plaindata = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesDecrypted];
plaintext = [[NSString alloc]initWithData:plaindata encoding:NSUTF8StringEncoding];
}
return plaintext;
}
Web前端
import CryptoJS from "crypto-js"
KEY = 'test'
function encodeData(data) {
if (data) {
let key = CryptoJS.enc.Utf8.parse(KEY);
return CryptoJS.DES.encrypt(data, key, {
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.ECB
}).toString()
} else {
return ""
}
}
function decodeData(data) {
if (data) {
try {
let key = CryptoJS.enc.Utf8.parse(KEY);
return CryptoJS.DES.decrypt(data, key, {
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.ECB
}).toString(CryptoJS.enc.Utf8)
} catch (e) {
return "decode failure"
} finally {
}
} else {
return ""
}
}
C#平台
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace SenseKeeperV3_guest_sample
{
/// <summary>
/// 类名称 :CryptTools
/// 类说明 :加解密算法
/// 完成日期 :
/// </summary>
public static class CryptTools
{
private static string inlineKey = "test";
#region DES加密字符串
///// <summary>
/////
///// 注意:密钥必须为8位
///// </summary>
/// <summary>
/// 加密字符串
/// </summary>
/// <param name="p_strInput">明码</param>
/// <returns>加密后的密码</returns>
public static string DesEncryptFixKey(string p_strInput,string key)
{
byte[] byKey = null;
byte[] IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
try
{
byKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Mode = CipherMode.ECB;
byte[] inputByteArray = Encoding.UTF8.GetBytes(p_strInput);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(byKey, IV), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
return Convert.ToBase64String(ms.ToArray());
}
catch (System.Exception ex)
{
throw (ex);
}
}
#endregion
#region DES解密字符串
//const string m_strEncryptKey = "kinghuns"; zh注释
/// <summary>
/// 解密字符串
/// </summary>
/// <param name="this.inputString">加了密的字符串</param>
/// <param name="decryptKey">密钥</param>
public static string DesDecryptFixKey(string p_strInput,string key)
{
byte[] byKey = null;
byte[] IV = { 0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF };
byte[] inputByteArray = new Byte[p_strInput.Length];
try
{
byKey = System.Text.Encoding.UTF8.GetBytes(key.Substring(0, 8));
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
des.Mode = CipherMode.ECB;
inputByteArray = Convert.FromBase64String(p_strInput);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(byKey, IV), CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
System.Text.Encoding encoding = new System.Text.UTF8Encoding();
return encoding.GetString(ms.ToArray());
}
catch (System.Exception ex)
{
throw (ex);
}
}
#endregion
#region DES加密字符串
///// <summary>
/////
///// 注意:密钥必须为8位
///// </summary>
/// <summary>
/// 加密字符串,使用内部默认秘钥
/// </summary>
/// <param name="p_strInput">明码</param>
/// <returns>加密后的密码</returns>
public static string DesEncryptFixKey(string p_strInput)
{
return DesEncryptFixKey(p_strInput,inlineKey);
}
#endregion
#region DES解密字符串
//const string m_strEncryptKey = "kinghuns"; zh注释
/// <summary>
/// 解密字符串,使用内部默认秘钥
/// </summary>
/// <param name="this.inputString">加了密的字符串</param>
/// <param name="decryptKey">密钥</param>
public static string DesDecryptFixKey(string p_strInput)
{
return DesDecryptFixKey(p_strInput,inlineKey);
}
#endregion
public static String md5(String s)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(s);
bytes = md5.ComputeHash(bytes);
md5.Clear();
string ret = "";
for (int i = 0; i < bytes.Length; i++)
{
ret += Convert.ToString(bytes[i], 16).PadLeft(2, '0');
}
return ret.PadLeft(32, '0');
}
}
}
jni层实现
最后
对于跨平台联合开发,记得除了在自己这个平台验证外,一定要在其他平台也做相应的验证,不要盲目自信。