java aes加解密 使用 php openssl 模仿遇到问题 请大佬赐教

题目描述

现在有个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...

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)

你期待的结果是什么?实际看到的错误信息又是什么?

阅读 5k
1 个回答

刚看了下,你的JAVA和PHP的输出sign字段是没问题,只是加密数据的时候出问题了,出现的问题是两个代码其实不匹配,最主要的是PHP在生成key后不需要做base64,只有在输出加密后的内容时候需要base64。
PHP加密那段用下面的就可以:

function encrypt($string, $key){
    $key = substr(openssl_digest(openssl_digest($key, 'sha1', true), 'sha1', true), 0, 16);
    $data = openssl_encrypt($string, 'AES-128-ECB', $key, OPENSSL_RAW_DATA);
    $data = strtolower(bin2hex($data));
    return base64_encode($data);
}
$data = '{"notaryOfficeId":"cb090588f82711e690a8000c299d85c9","bzjd":"5","notaryPersonName":"李可","itemName":"委托","notaryNumYear":"2019","notaryNumSubfix":"外字第1000号","notaryNum":"(2019)外字第1000号"}';
$encryptKey = 'apiDefault';
$encrypt = encrypt($data, $encryptKey);
echo "encrypt:".$encrypt;

下面是输出,能对应上你JAVA端的结果

[root@localhost ~]# php php.php 
encrypt:ZDk0MDc3MDk3ZWEyNzBmNWY0NTdkMTRhYWQxYzEzMmUwMjU5NjUxZGI1ODExNTMwN2M1YTRhMThlOTVjNTczMjg4NzhiOTVkM2Y1OGI1OGIyY2E1MGZjMmVlNWYwOTE2N2UwNjQxZjBlZDcxOTA2YWQ5NTVmYTgwZDI3MDIwZWQ3YzQ3NTFjN2EwYzVkNjMwMmU1MzViY2MxZTZlODY2Yjg2MWU5YjAxODg4ZDY2MTYwODVhYmQzN2NiOWMzNjY1NDVkODc0ZjQ0MWM5YjI1YzE5OWY1ZmY5YThkMTM5OTQxMzZjZTg5ZGUxYzZlOTFjZjY0MGE5ZWJkZGRmMTYxMTRiNTJkYTU5MDdjY2I2Y2YxNTU0MDEwZTYzNmY1YmI5ZTU5OWNmOGVlM2FlOGZhM2E0MTNiMmUwMTc3OTY4NWI3NDIxMTI0MWZhY2JjNTk2MmNhZjc1YmM0Yjc4MmJmYTkxNzJiMDNlYjdlN2EyNjY1NjY5ZDE5ZjMwZGYzNmFiNjhmODhhM2ZmZGRjOGI4MmQ1ODBhNDNlYWI0ZjA1MTdhOTVmZmM0ODc3ZGNiYmE3MzQ1NjlmNDg5ZTcyYWVmNw==
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏