题目描述
现在有个java的加解密程序,需要改成php的方法来实现
现在有对方的java加密程序 我用openssl得到的结果不一样
题目来源及自己的思路
JAVA 示例中:
public static byte[] generateAesKey(int keysize, String key) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
//防止linux下 随机生成key
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
keyGenerator.init(keysize, secureRandom);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
} catch (GeneralSecurityException e) {
throw Exceptions.unchecked(e);
}
}
我用PHP获取key如下
$key = base64_encode(substr(openssl_digest(openssl_digest($encryptKey, 'sha1', true), 'sha1', true), 0, 16));
问题:我拿到的key是 "COgmiQhm0EKpD1eDW+kTMA=="同我在 java示例中 String keyStr = Base64.encodeBase64String(key);得到的一样 但是使用 "COgmiQhm0EKpD1eDW+kTMA="加密的结果 与JAVA示例中 使用byte[]类型的data和key 不同 感觉问题就在这里 java示例用的byte[]类型的data和key加密
PHP openssl 是string。
相关代码
// 请把代码文本粘贴到下方(请勿用图片代替代码)
Java 示例 :
public static void aes() {
String data = "{\"notaryOfficeId\":\"cb090588f82711e690a8000c299d85c9\",\"bzjd\":\"5\",\"notaryPersonName\":\"李可\",\"itemName\":\"委托\",\"notaryNumYear\":\"2019\",\"notaryNumSubfix\":\"外字第1000号\",\"notaryNum\":\"(2019)外字第1000号\"}";
String clientId = "d726cfb97e364174a6b8a13bf67a5eea";
String secretKey = "G9uLRtKofu22";
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("client_id", clientId);
//使用对接工具类里的Cryptos 配合字符串"apiDefault" 生成AES密钥
//我遇到的问题 JAVA获取到的 key 是 byte[]类型 PHP openssl_encrypt 加密 只能使用string
byte[] key = Cryptos.generateAesKey("apiDefault");
//我尝试打印 byte[] key转换成string 得到”COgmiQhm0EKpD1eDW+kTMA==“ 我在PHP中也能拿到 但是这里加密使用的 byte[] PHP不能使用 byte[]
String keyStr = Base64.encodeBase64String(key);
//使用对接工具类里的Cryptos里的aesEncrypt 将byte[]类型的 data 和 key 加密
//我遇到问题 java示例里的加密 使用的是 byte[]类型 PHP不能用byte[]类型加密 我在PHP中 将byte[]转换成string加密 得到的结果就不一样了
byte[] encryptDataByte = Cryptos.aesEncrypt(data.getBytes(), key);
String encodeData = Base64.encodeBase64String(Hexs.parseByte2HexStr(encryptDataByte).getBytes());
map.put("data", encodeData);
//使用SignUtil获取签名
SignUtil.sign(map, secretKey);
System.out.println("========================》"+map.toString());
}
打印MAP得到的结果(期望结果):
{
client_id=d726cfb97e364174a6b8a13bf67a5eea,
data=RDk0MDc3MDk3RUEyNzBGNUY0NTdEMTRBQUQxQzEzMkUwMjU5NjUxREI1ODExNTMwN0M1QTRBMThFOTVDNTczMjg4NzhCOTVEM0Y1OEI1OEIyQ0E1MEZDMkVFNUYwOTE2N0UwNjQxRjBFRDcxOTA2QUQ5NTVGQTgwRDI3MDIwRUQ3QzQ3NTFDN0EwQzVENjMwMkU1MzVCQ0MxRTZFODY2Qjg2MUU5QjAxODg4RDY2MTYwODVBQkQzN0NCOUMzNjY1NDVEODc0RjQ0MUM5QjI1QzE5OUY1RkY5QThEMTM5OTQxMzZDRTg5REUxQzZFOTFDRjY0MEE5RUJERERGMTYxMTRCNTJEQTU5MDdDQ0I2Q0YxNTU0MDEwRTYzNkY1QkI5RTU5OUNGOEVFM0FFOEZBM0E0MTNCMkUwMTc3OTY4NUI3NDIxMTI0MUZBQ0JDNTk2MkNBRjc1QkM0Qjc4MkJGQTkxNzJCMDNFQjdFN0EyNjY1NjY5RDE5RjMwREYzNkFCNjhGODhBM0ZGRERDOEI4MkQ1ODBBNDNFQUI0RjA1MTdBOTVGRkM0ODc3RENCQkE3MzQ1NjlGNDg5RTcyQUVGNw====,
sign=o3MtfqSHKAkyG+BM3iwCPku0Mx0hQAN5GRssgpvrplg=
}
相关调用:
/*******************************************************************************
* Copyright (c) 2005, 2014 springside.github.io
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
*******************************************************************************/
package com.notary.util;
import com.google.common.base.Charsets;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Arrays;
/**
* 支持HMAC-SHA1消息签名 及 DES/AES对称加密的工具类.
* <p/>
* 支持Hex与Base64两种编码方式.
*
* @author calvin
*/
public class Cryptos {
private static final String AES = "AES";
private static final String AES_CBC = "AES/CBC/PKCS5Padding";
private static final String HMACSHA1 = "HmacSHA1";
private static final int DEFAULT_HMACSHA1_KEYSIZE = 160; // RFC2401
private static final int DEFAULT_AES_KEYSIZE = 128;
private static final int DEFAULT_IVSIZE = 16;
private static SecureRandom random = new SecureRandom();
// -- HMAC-SHA1 funciton --//
/**
* 使用HMAC-SHA1进行消息签名, 返回字节数组,长度为20字节.
*
* @param input 原始输入字符数组
* @param key HMAC-SHA1密钥
*/
public static byte[] hmacSha1(byte[] input, byte[] key) {
try {
SecretKey secretKey = new SecretKeySpec(key, HMACSHA1);
Mac mac = Mac.getInstance(HMACSHA1);
mac.init(secretKey);
return mac.doFinal(input);
} catch (GeneralSecurityException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 校验HMAC-SHA1签名是否正确.
*
* @param expected 已存在的签名
* @param input 原始输入字符串
* @param key 密钥
*/
public static boolean isMacValid(byte[] expected, byte[] input, byte[] key) {
byte[] actual = hmacSha1(input, key);
return Arrays.equals(expected, actual);
}
/**
* 生成HMAC-SHA1密钥,返回字节数组,长度为160位(20字节). HMAC-SHA1算法对密钥无特殊要求,
* RFC2401建议最少长度为160位(20字节).
*/
public static byte[] generateHmacSha1Key() {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(HMACSHA1);
keyGenerator.init(DEFAULT_HMACSHA1_KEYSIZE);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
} catch (GeneralSecurityException e) {
throw Exceptions.unchecked(e);
}
}
// -- AES funciton --//
/**
* 使用AES加密原始字符串.
*
* @param input 原始输入字符数组
* @param key 符合AES要求的密钥
*/
public static byte[] aesEncrypt(byte[] input, byte[] key) {
return aes(input, key, Cipher.ENCRYPT_MODE);
}
/**
* 使用AES加密原始字符串.
*
* @param input 原始输入字符数组
* @param key 符合AES要求的密钥
* @param iv 初始向量
*/
public static byte[] aesEncrypt(byte[] input, byte[] key, byte[] iv) {
return aes(input, key, iv, Cipher.ENCRYPT_MODE);
}
/**
* 使用AES解密字符串, 返回原始字符串.
*
* @param input Hex编码的加密字符串
* @param key 符合AES要求的密钥
*/
public static String aesDecrypt(byte[] input, byte[] key) {
byte[] decryptResult = aes(input, key, Cipher.DECRYPT_MODE);
return new String(decryptResult, Charsets.UTF_8);
}
/**
* 使用AES解密字符串, 返回原始字符串.
*
* @param input Hex编码的加密字符串
* @param key 符合AES要求的密钥
* @param iv 初始向量
*/
public static String aesDecrypt(byte[] input, byte[] key, byte[] iv) {
byte[] decryptResult = aes(input, key, iv, Cipher.DECRYPT_MODE);
return new String(decryptResult);
}
/**
* 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果.
*
* @param input 原始字节数组
* @param key 符合AES要求的密钥
* @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
*/
private static byte[] aes(byte[] input, byte[] key, int mode) {
try {
SecretKey secretKey = new SecretKeySpec(key, AES);
Cipher cipher = Cipher.getInstance(AES);
cipher.init(mode, secretKey);
return cipher.doFinal(input);
} catch (GeneralSecurityException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 使用AES加密或解密无编码的原始字节数组, 返回无编码的字节数组结果.
*
* @param input 原始字节数组
* @param key 符合AES要求的密钥
* @param iv 初始向量
* @param mode Cipher.ENCRYPT_MODE 或 Cipher.DECRYPT_MODE
*/
private static byte[] aes(byte[] input, byte[] key, byte[] iv, int mode) {
try {
SecretKey secretKey = new SecretKeySpec(key, AES);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(AES_CBC);
cipher.init(mode, secretKey, ivSpec);
return cipher.doFinal(input);
} catch (GeneralSecurityException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 生成AES密钥,返回字节数组, 默认长度为128位(16字节).
*/
public static byte[] generateAesKey() {
return generateAesKey(DEFAULT_AES_KEYSIZE);
}
public static byte[] generateAesKey(String key) {
return generateAesKey(DEFAULT_AES_KEYSIZE, key);
}
/**
* 生成AES密钥,可选长度为128,192,256位.
*/
public static byte[] generateAesKey(int keysize) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
keyGenerator.init(keysize);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
} catch (GeneralSecurityException e) {
throw Exceptions.unchecked(e);
}
}
public static byte[] generateAesKey(int keysize, String key) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
//防止linux下 随机生成key
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
keyGenerator.init(keysize, secureRandom);
SecretKey secretKey = keyGenerator.generateKey();
return secretKey.getEncoded();
} catch (GeneralSecurityException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 生成随机向量,默认大小为cipher.getBlockSize(), 16字节.
*/
public static byte[] generateIV() {
byte[] bytes = new byte[DEFAULT_IVSIZE];
random.nextBytes(bytes);
return bytes;
}
}
SignUtil
package com.notary.comity.util;
import com.google.common.base.Strings;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
/**
* @author daihy
* Created on 2016/6/21.
*/
public class SignUtil {
static Logger logger = LoggerFactory.getLogger(SignUtil.class);
private static String getSign(Map<String, Object> map, String secretKey) {
try {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256");
sha256_HMAC.init(secretKeySpec);
StringBuffer prepareSign = new StringBuffer();
for (Map.Entry<String, Object> entry : map.entrySet()) {
prepareSign.append(entry.getKey()).append("=");
prepareSign.append(entry.getValue());
}
return Base64.encodeBase64String(sha256_HMAC.doFinal(prepareSign.toString().getBytes()));
} catch (NoSuchAlgorithmException e) {
logger.error(ExceptionUtils.getStackTrace(e));
} catch (InvalidKeyException e) {
logger.error(ExceptionUtils.getStackTrace(e));
}
return "";
}
public static void sign(Map<String, Object> map, String secretKey) {
map.put("sign", getSign(map, secretKey));
}
public static boolean signValidate(Map<String, Object> map, String secretKey, String sign) {
if(Strings.isNullOrEmpty(secretKey) || Strings.isNullOrEmpty(sign)){
return false;
}
if (getSign(map, secretKey).equals(sign)) {
return true;
}
return false;
}
}
你期待的结果是什么?实际看到的错误信息又是什么?
问题描述
问题出现的环境背景及自己尝试过哪些方法
相关代码
// 请把代码文本粘贴到下方(请勿用图片代替代码)
我的php代码:
调用:
$AES = new AES();
$clientId = 'd726cfb97e364174a6b8a13bf67a5eea';
$secretKey = 'G9uLRtKofu22';
$encryptKey = 'apiDefault';
$map = [];
$map['client_id'] = $clientId;
$data = '{"notaryOfficeId":"cb090588f82711e690a8000c299d85c9","bzjd":"5","notaryPersonName":"李可","itemName":"委托","notaryNumYear":"2019","notaryNumSubfix":"外字第1000号","notaryNum":"(2019)外字第1000号"}';
//ps 会不会JAVA和php在声明这个data时 就已经不同了
//加密
$encryptionData = $AES->encrypt($data, $encryptKey);
$map['data'] = $encryptionData;
//测试解密
$decryptData = $AES->decrypt($encryptionData, $encryptKey);
//获取签名 因为加密结果不同 所以这里结果也同java示例不同
$sign = $AES->sign($map, $secretKey);
$map['sign'] = $sign;
print_r($map);
exit;
-----------------------AES---------------------------------------
class AES
{
public function encrypt($data, $encryptKey)
{
/*
* 我拿到的key是 "COgmiQhm0EKpD1eDW+kTMA=="
* 同我在 java示例中 String keyStr = Base64.encodeBase64String(key);得到的一样
* 但是使用 "COgmiQhm0EKpD1eDW+kTMA="加密的结果 与JAVA示例中 使用byte[]类型的data和key 不同
* 问题就在这里 如何才能结果相同
*/
$key = base64_encode(substr(openssl_digest(openssl_digest($encryptKey, 'sha1', true), 'sha1', true), 0, 16));
$encRaw = openssl_encrypt($data, 'AES-128-ECB', $key, OPENSSL_RAW_DATA);
$encHex = $this->strToHex($encRaw);
return base64_encode($encHex);
}
public function decrypt($enc, $encryptKey)
{
$key = base64_encode(substr(openssl_digest(openssl_digest($encryptKey, 'sha1', true), 'sha1', true), 0, 16));
$hex = base64_decode($enc);
$enByte = $this->hexToStr($hex);
$dec = openssl_decrypt($enByte, 'AES-128-ECB', $key, OPENSSL_RAW_DATA, '');
return $dec;
}
public function strToHex($str)
{
$hex = '';
for ($i = 0; $i < strlen($str); $i++) {
$he = dechex(ord($str[$i]));
if (strlen($he) == 1) {
$he = '0' . $he;
}
$hex .= $he;
}
$hex = strtoupper($hex);
return $hex;
}
public static function getBytes($string)
{
$bytes = [];
for ($i = 0; $i < strlen($string); $i++) {
$bytes[] = ord($string[$i]);
}
return $bytes;
}
public function hexToStr($hex)
{
$str = '';
for ($i = 0; $i < strlen($hex) - 1; $i += 2) {
$str .= chr(hexdec($hex[$i] . $hex[$i + 1]));
}
return $str;
}
public function sign($arr, $key)
{
$s = '';
foreach ($arr as $k => $v) {
$s .= "{$k}={$v}";
}
$s = hash_hmac('sha256', $s, $key, true);
return base64_encode($s);
}
}
你期待的结果是什么?实际看到的错误信息又是什么?
期望结果能同JAVA的一样
{
client_id=d726cfb97e364174a6b8a13bf67a5eea,
data=RDk0MDc3MDk3RUEyNzBGNUY0NTdEMTRBQUQxQzEzMkUwMjU5NjUxREI1ODExNTMwN0M1QTRBMThFOTVDNTczMjg4NzhCOTVEM0Y1OEI1OEIyQ0E1MEZDMkVFNUYwOTE2N0UwNjQxRjBFRDcxOTA2QUQ5NTVGQTgwRDI3MDIwRUQ3QzQ3NTFDN0EwQzVENjMwMkU1MzVCQ0MxRTZFODY2Qjg2MUU5QjAxODg4RDY2MTYwODVBQkQzN0NCOUMzNjY1NDVEODc0RjQ0MUM5QjI1QzE5OUY1RkY5QThEMTM5OTQxMzZDRTg5REUxQzZFOTFDRjY0MEE5RUJERERGMTYxMTRCNTJEQTU5MDdDQ0I2Q0YxNTU0MDEwRTYzNkY1QkI5RTU5OUNGOEVFM0FFOEZBM0E0MTNCMkUwMTc3OTY4NUI3NDIxMTI0MUZBQ0JDNTk2MkNBRjc1QkM0Qjc4MkJGQTkxNzJCMDNFQjdFN0EyNjY1NjY5RDE5RjMwREYzNkFCNjhGODhBM0ZGRERDOEI4MkQ1ODBBNDNFQUI0RjA1MTdBOTVGRkM0ODc3RENCQkE3MzQ1NjlGNDg5RTcyQUVGNw====,
sign=o3MtfqSHKAkyG+BM3iwCPku0Mx0hQAN5GRssgpvrplg=
}
实际结果:对不上~
题目描述
题目来源及自己的思路
https://stackoverflow.com/que...
相关代码
// 请把代码文本粘贴到下方(请勿用图片代替代码)
刚看了下,你的JAVA和PHP的输出sign字段是没问题,只是加密数据的时候出问题了,出现的问题是两个代码其实不匹配,最主要的是PHP在生成key后不需要做base64,只有在输出加密后的内容时候需要base64。
PHP加密那段用下面的就可以:
下面是输出,能对应上你JAVA端的结果