Java与PHP签名验签问题(SHA256 with RSA算法)

这几天和业务方有个签名验签的的需求,对方使用Java对业务数据进行签名,我方使用PHP验签,使用SHA256withRSA算法签名验签,PHP和Java单独签名和验签都没问题,但是由Java签名的数据请求到PHP端时一直验签不通过。
分别对比了Java和PHP生成的待验签字符串和生成的签名,都是一致的,同时也确认过私钥和公钥是一对,但是一到PHP的验签方法openssl_verify()是就是通不过,是在不知道原因出在哪。请求大神们帮忙看看是什么原因,万分感激。

PHP代码

class LubanPay{
    /**
     * 字符串签名
     * @param string $resource 经过urlencode处理过的字符串
     * @return string
     */
    public function SignStrMessage(string $resource):string {
        $privateKey = openssl_get_privatekey($this->GetPrivateKey());
        $res        = openssl_sign($resource, $signature, $privateKey,OPENSSL_ALGO_SHA256);
        openssl_free_key($privateKey);
        if ($res) {
            return base64_encode($signature);
        }else {
            throw new \Exception("String Sign Failed",10004);
        }
    }
    /**
     * 字符串验签
     * @param string $resource
     * @param string $signature
     * @return bool
     */
    public function VerifyStrMessage(string $resource,string $signature,string $mch_public_key):bool {
        $signature  = base64_decode($signature);
        $publicKey  = openssl_get_publickey($this->GetPublicKey($mch_public_key));
        $res        = openssl_verify($resource, $signature, $publicKey,OPENSSL_ALGO_SHA256);
        openssl_free_key($publicKey);
        return $res===1?true:false;
    }

    /**
     * 获取平台私钥
     * @return string
     */
    private function GetPrivateKey():string {
        $privateKey     = Config::$luban_private_key;
        $privateKey     = chunk_split($privateKey, 64, "\n");
        $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n$privateKey-----END RSA PRIVATE KEY-----\n";
        return $privateKey;
    }

    /**
     * 获取商户公钥
     * @param string $type
     * @return string
     */
    private function GetPublicKey($mer_public_key):string{
        $publicKey               = chunk_split($mer_public_key, 64, "\n");
        $publicKey = "-----BEGIN PUBLIC KEY-----\n$publicKey-----END PUBLIC KEY-----\n";
        return $publicKey;
    }

}

Java代码

public static String private_key = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC4RPxDH2GdsIcyDq5ApUfLU0+Gst6aJZwjoqvVxxGSRTlKx10VZHHZ7goqrNSNWVK+WPbz6vCItSgHb1t3eRMkwPEaGcstvcf7krEGOH6CWYXe74OTbFet9Oq1Jdzz7UHNNeRyHO/JnNbVh8kPSqIL2368g+lII0u7zP4OWGw+qht75VY0vKI3dQdK2jFidkRHtEmV0+ui+9RVEtIlURYxkaAHWoUNWov8505cAc+pmKrUYOJf7Q50t+AlClIgngzF2yqT5HAKKYbQ9R6CgKvB9JcAXL0nXi2bExjOQsQa3SalLXxjeNKpjJKtytUAEVaehtA5PCi0VV1jZSGKpTxlAgMBAAECggEAGIJsf00UQdIyGVFkkgqp4vyAzmzKOPyZqQ/BBV1GFAuLFEwyMF882XzU81orp2VjIRhaOJVeSwC1g0+nfdun1TKonw0hPkNI70hSrX4kLZhUuxNmj9xQST4TXebcXcGICBCMAzWgG1P2K061Sohlx2f5kn+FLugq8Z7Rh/zw4OCrFeDZ0YJZKpNk1JwBp3arbSphlbw3ZzTF6g7kukfilXuWNnoqDf341H5rVUY7k4cbej8x6pc6Zrnla33VM5m37+5bXnGnE1Sx7PjVgn+2ZADIhTn8e94bLZacJofrF1kCcScO1TRH4eaqlyhiXsjMy0Cm4OxCY+Uydj3NtOFvHQKBgQD5Nsr83bfAuHCA3dEaclQoBbAD1Qiz3rQPvHh2Zt7WIzKJ4NWVnypp22eCYTgft/X7cXXF7D2yokfOCc9DMfLVOOZzcRSwifsakPWXWKAtcNZZW+1ffgObq4I4tNO9Llb6hIN3pcxf7yBtrMib9C/cqwUAQM3k5bsFTZgjCZI5qwKBgQC9SXsJel+FDFlT4jfda/9fEx8uSOP4jflSn83CLI7UQ9/utR5ldJbllwWwrS5k6nqgi5K9AzOhxxfQ3m6FGJRDzqwyfK05uvuM1CqRMpe5FyQKTpPodWJ3+8oIcluRNaWwuAp+FNWPiGSNnhksS7i54jJAOh9nIdB1aSseq9vyLwKBgFJENinW/wuNVwYTMy2pxAIaLop1TpQh1grDyng7aSADKnG9WIQ1sIiVNswhT6eY0IiaYaheXdeUHmPzdQnXeTPNvrUpBQ1p3wxcAdZeGTIm53tED03QiVxf93LErojqvSehisx6XMbmZywNN4PTzeDoS5RT0CPZei07+hbG2BBVAoGAW6HszAPPper6e18xyCD1+SKan59trO+d2N+/jdZgNmW9TCOl2Vt9iRt5B7Ruly/juUCYAqRAJHrrDpP/ULM7Yy/zsGUmvqHEEMLM8IlbZaDMM6kidRAOYSMlBL3Hkh40Xb5aZfrT/635b40vhoAJpwLXbLw2Y4i9D3mgBDMSQMUCgYBLkj6pnTvJtgR7xCAKaVoERYXmsDjP/gIhSrmnqfO8EZDheVoh+XbN5o3D27aBmWiWTwgJoMuF5GxEL2tSbQBvkT6smTrVBDF8Q8+tgBvP2CV0gwto+Spv5b9h7dNWk5UI0ZOWnlF1aI37UJBKmyp2kYCoDwnA1vtFpXncmaNHxw==";  
  
public static void main(String[] args) throws UnsupportedEncodingException {  
    test();
}  
  
  
public static void test() throws UnsupportedEncodingException {  
//构造待签名字符串  
String string = "content=hello"+"&mch_id=1&method=pay&nonce_str=1&time_stamp=1";  
System.out.println("拼接字符串:"+string);  
//加密字符串  
String str = sign(string.getBytes("utf-8"));  
System.out.println("字符串加密:"+str);  
String authorization = "mch_id=1&method=pay&nonce_str=1&sign="+URLEncoder.encode(str,"UTF-8")+"&time_stamp=1";  
System.out.println("请求头:"+authorization);  
System.out.println("请求结果:"+DemoCall.doPost("pay/api/v1","hello",authorization));  
}  
  
  
/**  
* 将字节数组内信息使用签名进行加密  
* @param message  
* @return  
*/  
public static String sign(byte[] message){  
Signature sign;  
try {  
sign = Signature.getInstance("SHA256withRSA");  
sign.initSign(getPrivateKey());  
sign.update(message);  
return Base64.getEncoder().encodeToString(sign.sign());  
} catch (NoSuchAlgorithmException e) {  
e.printStackTrace();  
} catch (SignatureException e) {  
e.printStackTrace();  
} catch (InvalidKeyException e) {  
e.printStackTrace();  
}  
return null;  
}  
  
/**  
* 获取私钥。  
* @param filename 私钥文件路径 (required)  
* @return 私钥对象  
*/  
public static PrivateKey getPrivateKey(){  
String content = private_key;  
try {  
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")  
.replace("-----END PRIVATE KEY-----", "")  
.replaceAll("\\s+", "");  
KeyFactory kf = KeyFactory.getInstance("RSA");  
return kf.generatePrivate(  
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));  
} catch (NoSuchAlgorithmException e) {  
throw new RuntimeException("当前Java环境不支持RSA", e);  
} catch (InvalidKeySpecException e) {  
throw new RuntimeException("无效的密钥格式");  
}  
}
阅读 15.6k
2 个回答

OpenJDK 12 、PHP 7.3 测试交叉验证都通过。

<?php
// 原文
$data = '二〇二〇年四月四日 01:43:37';
// 私钥
$private = <<<PRI
-----BEGIN RSA PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCow6pxwoAIFXWs
ZY3QMoUmB4uAYk0cAROITeCLROpzwRDtcXRDJ3TP/LrwgviTlmPD/VAtxa01eW7+
xmCUCY7IuaG9UmRAF7q8RXQ+T74ce8zSF6KIWRbWv+DOq860zCmaa9zr5DBHgf5X
mwC6/SqBhQlE7WxrecNOJC7Qx5o1V/LhZ5hAP5nxg6zpoSvTMZitiQSRVuenqotP
t0iD8b4TRcX7siNGIE2mo7xoCnjJ7Vv3iuLi6kczfpH5WoB/k54hZncZPc/wN4m4
SWY4f1uoDr2XtkCDNAODuhu7LMz1Yvk7yRsyX7NwopmLHzhtjdSOSPnJszTQUkNa
+8DCSxJhAgMBAAECggEAbxJgUoDdfPSKvw0Tmcw2P8JFeRi5gU3gChyfRWn1GHwf
5PZ/u1bzlZPTgUnhylj3jl1g8M8iYYhrmfj8RVJJDCjIn27jlh9IAnN/vycCi4Kb
Wka97RkdY/djQQJoiCu60exduU80y0tuF0Bn6amH2Tiy0g+lBNNdzEcaHMTR5HrN
PS7wRCnU0i3aIZbGSfFT6EHQES5FE2q3hTrySrv4cqR2Jnk+UJPYMyl7/cgc/X7K
1cHCVpHmahhgxzXK2KBoOs2fmLcpHvlxu8roJm2OWxUDAoZFKtP5W6j0lbIl3afb
OJFjpH1SLOP55lAMGAU65Ili68yVWWQuJi+h23dDcQKBgQDR4ZKK8AvsWHSHIcuh
6/bBkKSXCLatZhqXE5sjaQy1syzXtLQ1JDYilDgorpvmqOfwClScF/eQLTYO+qM5
zQEfZpMjciLiywAvRCRFKlUSOE4jivfsMZLsZ617QSUMrxXRKlUXNMOEDP/wE5p+
LiaxORGWMuKJYavfabnmeEnH3QKBgQDN2SblEQok5+Q6kx5twrIJUhKeexLwtDHV
ybObO95iYMTCjbfxxA48w7jmJl1cPADqNINeDPOHNIl16RQ4UHH6MO32FVoHlei9
AtVqKxCpBiJvl7T7vg0n8Zr3uCANVmexdsSnq6DbqTrXCPufo9vmfkScGjK3lmgA
xqyJUkouVQKBgGteobBQQ1lCm0JySJFqfI7jpz/Y5lNo05uMHSaNXEIsCnnDaRly
j/s6pkwxn3Ht4NHNByHfpPduGaSqFgzA0p00xXsxraUmQs7rZj63/FNY2KiYNGLx
rX8hPv+6APEvNNMPe/5mMMuCNwCjlrqMc6DgWB3lpDyx6dJebQr5aI1FAoGAJLrw
s8L8mmU+Vi1WKqOo/PzGEb1IPecJVWpuP+7I2akGsuhywBMJr1IFNhv2YLTcPO4t
2qRY9/Ep7f4u+3VvQQNmEpjwvZXEN6W/yvfwOxi7IEpjot/gnRYBXt5d6cNXMVVN
9dUsGMXzl9ckfvHQFSrGt0v9bMDLwgexVbd3QRkCgYEAzTVg+3hY9lt1cdrwklOs
Yfw3K33lq+Ip8RZ7WjeuO6W2Fuvh/MHEBxnwcPEZSKIhOpOV2VdYQo80uqinE4il
4IQqhf2qFOshGHrOspDbJUgtuVH5JkmQDeh5yT5+Upd3FXj8sOVjzbH2sz//o9sU
jOYUhzLO1jeLIc6l0wnzRfE=
-----END RSA PRIVATE KEY-----
PRI;
// 公钥
$public = <<<PUB
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqMOqccKACBV1rGWN0DKF
JgeLgGJNHAETiE3gi0Tqc8EQ7XF0Qyd0z/y68IL4k5Zjw/1QLcWtNXlu/sZglAmO
yLmhvVJkQBe6vEV0Pk++HHvM0heiiFkW1r/gzqvOtMwpmmvc6+QwR4H+V5sAuv0q
gYUJRO1sa3nDTiQu0MeaNVfy4WeYQD+Z8YOs6aEr0zGYrYkEkVbnp6qLT7dIg/G+
E0XF+7IjRiBNpqO8aAp4ye1b94ri4upHM36R+VqAf5OeIWZ3GT3P8DeJuElmOH9b
qA69l7ZAgzQDg7obuyzM9WL5O8kbMl+zcKKZix84bY3Ujkj5ybM00FJDWvvAwksS
YQIDAQAB
-----END PUBLIC KEY-----
PUB;

// JAVA 生成的签名
// 验证
//$encrypt = 'flCNTyn6pydpBy7zPuJ6zaIpVMz47RdhmqsFsdRgHZF79H78R9Hhkw5sf3T86f6ESDSCD+3Fj/JSHZE9+hXyUt4i6NH41MKBVLr8pgZBpkwExPcBXa13vSa2dFniDbu2ZHiKRJGZWtGTmQPOHAxOqfnNxEv0StP8Hit8Plva/ug7tdGCi4j6sjhTWpB2vUFI2tzSnbeOQWJ+dfa6zrwbIC8EHBYltUiRmMry3H7YYNI+rt16HvS7TrbVWPi90ClMdj64CEahlS9TLpzErMUuJcyubs12QLUhHKL9IMvqNMa8SF8eztp3ZmWTtC1YxFfSHhQBUiLV/bA+iJ7CYROwIw==';
//$verify = openssl_verify($data, base64_decode($encrypt), $public, OPENSSL_ALGO_SHA256);
//var_dump($verify);
//die;

//
//// 生成签名去JAVA验证
//$isOK = openssl_sign($data, $sign, $private, OPENSSL_ALGO_SHA256);
//if ($isOK) {
//    echo base64_encode($sign);
//}
package com.company;

import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class Main {

    public static void main(String[] args) throws Exception {
        String origin = "二〇二〇年四月四日 01:43:37";
        String encrypt = "flCNTyn6pydpBy7zPuJ6zaIpVMz47RdhmqsFsdRgHZF79H78R9Hhkw5sf3T86f6ESDSCD+3Fj/JSHZE9+hXyUt4i6NH41MKBVLr8pgZBpkwExPcBXa13vSa2dFniDbu2ZHiKRJGZWtGTmQPOHAxOqfnNxEv0StP8Hit8Plva/ug7tdGCi4j6sjhTWpB2vUFI2tzSnbeOQWJ+dfa6zrwbIC8EHBYltUiRmMry3H7YYNI+rt16HvS7TrbVWPi90ClMdj64CEahlS9TLpzErMUuJcyubs12QLUhHKL9IMvqNMa8SF8eztp3ZmWTtC1YxFfSHhQBUiLV/bA+iJ7CYROwIw==";
        if (verify(getPublic(), origin, encrypt)) {
            System.out.println("验证通过!");
        }
        //
//        System.out.println(signSHA256RSA(origin, getPrivate()));
    }

    private static String signSHA256RSA(String input, String strPk) throws Exception {
        byte[] b1 = Base64.getDecoder().decode(strPk);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b1);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        Signature privateSignature = Signature.getInstance("SHA256withRSA");
        privateSignature.initSign(kf.generatePrivate(spec));
        privateSignature.update(input.getBytes(StandardCharsets.UTF_8));
        byte[] s = privateSignature.sign();
        return Base64.getEncoder().encodeToString(s);
    }

    private static boolean verify(String publicKeyStr, String originStr, String encrypted) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException {
        byte[] decode = Base64.getDecoder().decode(encrypted);
        byte[] data = originStr.getBytes();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        Signature sha256withRSA = Signature.getInstance("SHA256withRSA");
        sha256withRSA.initVerify(publicKey);
        sha256withRSA.update(data);
        return sha256withRSA.verify(decode);
    }

    public static String getPrivate() {
        return "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCow6pxwoAIFXWsZY3QMoUmB4uAYk0cAROITeCLROpzwRDtcXRDJ3TP/LrwgviTlmPD/VAtxa01eW7+xmCUCY7IuaG9UmRAF7q8RXQ+T74ce8zSF6KIWRbWv+DOq860zCmaa9zr5DBHgf5XmwC6/SqBhQlE7WxrecNOJC7Qx5o1V/LhZ5hAP5nxg6zpoSvTMZitiQSRVuenqotPt0iD8b4TRcX7siNGIE2mo7xoCnjJ7Vv3iuLi6kczfpH5WoB/k54hZncZPc/wN4m4SWY4f1uoDr2XtkCDNAODuhu7LMz1Yvk7yRsyX7NwopmLHzhtjdSOSPnJszTQUkNa+8DCSxJhAgMBAAECggEAbxJgUoDdfPSKvw0Tmcw2P8JFeRi5gU3gChyfRWn1GHwf5PZ/u1bzlZPTgUnhylj3jl1g8M8iYYhrmfj8RVJJDCjIn27jlh9IAnN/vycCi4KbWka97RkdY/djQQJoiCu60exduU80y0tuF0Bn6amH2Tiy0g+lBNNdzEcaHMTR5HrNPS7wRCnU0i3aIZbGSfFT6EHQES5FE2q3hTrySrv4cqR2Jnk+UJPYMyl7/cgc/X7K1cHCVpHmahhgxzXK2KBoOs2fmLcpHvlxu8roJm2OWxUDAoZFKtP5W6j0lbIl3afbOJFjpH1SLOP55lAMGAU65Ili68yVWWQuJi+h23dDcQKBgQDR4ZKK8AvsWHSHIcuh6/bBkKSXCLatZhqXE5sjaQy1syzXtLQ1JDYilDgorpvmqOfwClScF/eQLTYO+qM5zQEfZpMjciLiywAvRCRFKlUSOE4jivfsMZLsZ617QSUMrxXRKlUXNMOEDP/wE5p+LiaxORGWMuKJYavfabnmeEnH3QKBgQDN2SblEQok5+Q6kx5twrIJUhKeexLwtDHVybObO95iYMTCjbfxxA48w7jmJl1cPADqNINeDPOHNIl16RQ4UHH6MO32FVoHlei9AtVqKxCpBiJvl7T7vg0n8Zr3uCANVmexdsSnq6DbqTrXCPufo9vmfkScGjK3lmgAxqyJUkouVQKBgGteobBQQ1lCm0JySJFqfI7jpz/Y5lNo05uMHSaNXEIsCnnDaRlyj/s6pkwxn3Ht4NHNByHfpPduGaSqFgzA0p00xXsxraUmQs7rZj63/FNY2KiYNGLxrX8hPv+6APEvNNMPe/5mMMuCNwCjlrqMc6DgWB3lpDyx6dJebQr5aI1FAoGAJLrws8L8mmU+Vi1WKqOo/PzGEb1IPecJVWpuP+7I2akGsuhywBMJr1IFNhv2YLTcPO4t2qRY9/Ep7f4u+3VvQQNmEpjwvZXEN6W/yvfwOxi7IEpjot/gnRYBXt5d6cNXMVVN9dUsGMXzl9ckfvHQFSrGt0v9bMDLwgexVbd3QRkCgYEAzTVg+3hY9lt1cdrwklOsYfw3K33lq+Ip8RZ7WjeuO6W2Fuvh/MHEBxnwcPEZSKIhOpOV2VdYQo80uqinE4il4IQqhf2qFOshGHrOspDbJUgtuVH5JkmQDeh5yT5+Upd3FXj8sOVjzbH2sz//o9sUjOYUhzLO1jeLIc6l0wnzRfE=";
    }

    public static String getPublic() {
        return "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqMOqccKACBV1rGWN0DKFJgeLgGJNHAETiE3gi0Tqc8EQ7XF0Qyd0z/y68IL4k5Zjw/1QLcWtNXlu/sZglAmOyLmhvVJkQBe6vEV0Pk++HHvM0heiiFkW1r/gzqvOtMwpmmvc6+QwR4H+V5sAuv0qgYUJRO1sa3nDTiQu0MeaNVfy4WeYQD+Z8YOs6aEr0zGYrYkEkVbnp6qLT7dIg/G+E0XF+7IjRiBNpqO8aAp4ye1b94ri4upHM36R+VqAf5OeIWZ3GT3P8DeJuElmOH9bqA69l7ZAgzQDg7obuyzM9WL5O8kbMl+zcKKZix84bY3Ujkj5ybM00FJDWvvAwksSYQIDAQAB";
    }
}

这种情况首先看字符集是否正确 ,特别是内容中带有中文字符的时候

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