这几天和业务方有个签名验签的的需求,对方使用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("无效的密钥格式");
}
}
OpenJDK 12 、PHP 7.3 测试交叉验证都通过。