这几天和业务方有个签名验签的的需求,对方使用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("无效的密钥格式");
    }
}

转载:https://segmentfault.com/q/1010000022260275