来自 PHP 的 SHA256withRSA 签名从 JAVA 验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11116143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 03:52:30  来源:igfitidea点击:

SHA256withRSA sign from PHP verify from JAVA

javaphpcryptography

提问by LostMohican

For my current project I have to send a signature from PHP to Java application. I am using Crypt/RSA right now for signing my data.

对于我当前的项目,我必须将签名从 PHP 发送到 Java 应用程序。我现在正在使用 Crypt/RSA 来签署我的数据。

For test I am signing just "abc" with following code :

为了测试,我只使用以下代码签署“abc”:

$rsa = new Crypt_RSA();
$plaintext = 'abc';

    $rsa->loadKey("MIICXgIBAAKBgQDjh+hNsqJe566JO0Sg7Iq5H1AdkauACdd8QMLp9YNY0HPslVH0
rXaOFo0zgH0Ktu/Ku3lS1lfxbFQAY8b6ywZKvu4eoxlnEwuBwy09CG+3ZiVLBjCj
TZHA/KOkpVLa+tA6KsoP6zv/xI/ACkSCxPGR0q3SiRuhXV/6tacoKxUYnwIDAQAB
AoGBAIC00GOjONYWmFRoglnFdHNjkx4m2KyE5LAUsi1GBBapU+nwTXvq47VcbGNF
u3XkJaC4i9igBv86GApgZp5XWia86On/Lz9NR4fB2EFP6Ydy84GfCDNNvkism4BR
aA+eYdNiQ3Wfyi98ZpUi+rPsoI6Cid4eSkCC4poTUaqzMkiBAkEA9Gn1oIlUEoVI
q/u5Y9vflXRDt95AA9AokJkQj7XTNjsz8ypU8TO6D6ZykpcbK6zjU0UJsQiC3dKj
AgmAR2VzYwJBAO5RETMAyDnR+5g+MtHpwGqGdY4dq0j4y4CsdtOYKWwSTh3VQy+C
eghJoyPRfIpulw2Mk/l+occEI0ohJl0+UJUCQQDSZtjVLwMZwnUx4EvSw/ewL9sP
0Jpo7evNtoaEQDEncUWiYeGnljDowg/FU6FHMtiq2TajmMEXdflvioBMdfAjAkEA
3TB60SbJr/i4Fo6sJm5ZO8W+eAALiTf50VzBERTqZTb8L+5PZFoqn2SROV5mxClu
o5G1idzBlHC/vD7WV7bNnQJAd0FrxaMBurJ4Uv/B8TDP+eeBdB7d9rKw0+TVlcel
cbpIz6BIP6+nmsgy6dbDRnx0eC/MgF2EU0wrCu1DK0PyWA==");
    $rsa->setHash("sha256");
    $signature = $rsa->sign($plaintext);

$signature_encoding = mb_convert_encoding($signature, "UTF-8");
    error_log("signature encoded in UTF-8 :" . $signature_encoding);

    $encoded_sign = base64_encode($signature_encoding);
    error_log("encoded sign for abc: " . $encoded_sign);

I can verify the signature from php code. But when it comes to verifying from JAVA, i was not successfull. Here is the java code that does the verify operation :

我可以验证来自 php 代码的签名。但是当涉及到从 JAVA 验证时,我没有成功。这是执行验证操作的 java 代码:

public boolean verify(String signed, String data, PubKey pubKey) throws Exception{

    PublicKey publicKey = jceProvider.generateRSAPublicKeyFromX509(
            base64.decode(pubKey.getEncodedKey())
    );

    byte[] signature = base64.decode(signed);
    byte[] verifier = data.getBytes(Charset.forName("UTF-8"));

    return jceProvider.verify(signature, verifier, publicKey);

}

public class JCEProvider {

    public boolean verify (byte[] signature, byte[] verifier, PublicKey publicKey) throws Exception{

        Signature rsaSignature = Signature.getInstance("SHA256withRSA");

        rsaSignature.initVerify(publicKey);
        rsaSignature.update(verifier);

        return rsaSignature.verify(signature);

    }

I dont think it is because of keys, I can already verify it from PHP as I told before. There is something that I miss about PHP encoding or byte streams but I am lost for the moment.

我不认为这是因为密钥,正如我之前所说,我已经可以从 PHP 验证它。关于 PHP 编码或字节流,我有些想念,但我暂时迷失了。

Any help would be appreciated.

任何帮助,将不胜感激。

回答by Sascha

I'm using openssl like Whity already mentioned. Here is my striped down example. Be aware of any character encoding, line ending, etc. This results in changed binary representation of your text data.

我正在使用像 Whity 已经提到的那样的 openssl。这是我的精简示例。请注意任何字符编码、行尾等。这会导致文本数据的二进制表示发生变化。

PHP-RSA_SHA256-Sign:

PHP-RSA_SHA256-签名:

<?php

$data = "For my current project I have to send a signature from PHP to Java application. I am using Crypt/RSA right now for signing my data.";

$private_key = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6zxqlVzz0wy2j4kQVUC4Z
RZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQJAL151ZeMKHEU2c1qdRKS9
sTxCcc2pVwoAGVzRccNX16tfmCf8FjxuM3WmLdsPxYoHrwb1LFNxiNk1MXrxjH3R
6QIhAPB7edmcjH4bhMaJBztcbNE1VRCEi/bisAwiPPMq9/2nAiEA3lyc5+f6DEIJ
h1y6BWkdVULDSM+jpi1XiV/DevxuijMCIQCAEPGqHsF+4v7Jj+3HAgh9PU6otj2n
Y79nJtCYmvhoHwIgNDePaS4inApN7omp7WdXyhPZhBmulnGDYvEoGJN66d0CIHra
I2SvDkQ5CmrzkW5qPaE2oO7BSqAhRZxiYpZFb5CI
-----END RSA PRIVATE KEY-----
EOD;

$binary_signature = "";

$algo = "SHA256";
openssl_sign($data, $binary_signature, $private_key, $algo);
print(base64_encode($binary_signature) ."\n");

?>

The output of base64 encoded binary signature is:

base64 编码的二进制签名的输出是:

OnqiWnFQ2nAjOa1S57Du9jDpVr4Wp2nLdMk2FX+/qX1+SAHpVsW1JvQYqQUDlxvbTOE9vg6dlU6i3omR7KipLw==

OnqiWnFQ2nAjOa1S57Du9jDpVr4Wp2nLdMk2FX+/qX1+SAHpVsW1JvQYqQUDlxvbTOE9vg6dlU6i3omR7KipLw==

JAVA-RSA_SHA256-Verify:

JAVA-RSA_SHA256-验证:

import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;

import org.apache.commons.codec.binary.Base64;

public class RsaVerify {

    public static void main(String args[]){
        String publicKey = 
//              "-----BEGIN PUBLIC KEY-----"+
                "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6"+
                "zxqlVzz0wy2j4kQVUC4ZRZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQ==";
//              "-----END PUBLIC KEY-----";

        byte[] data = "For my current project I have to send a signature from PHP to Java application. I am using Crypt/RSA right now for signing my data.".getBytes();
        byte[] signature = Base64.decodeBase64("OnqiWnFQ2nAjOa1S57Du9jDpVr4Wp2nLdMk2FX+/qX1+SAHpVsW1JvQYqQUDlxvbTOE9vg6dlU6i3omR7KipLw==");

        try {
            System.out.println(verify(data, signature, publicKey));
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }

    }

    private static boolean verify(byte[] data, byte[] signature, String publicKey) throws GeneralSecurityException{
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(pubKey);
        sig.update(data);
        return sig.verify(signature);
    }
}

回答by LostMohican

phpseclib uses the more secure PSS padding by default. Java is probably using PKCS#1 padding. So if you were to go the phpseclib route (which I'd recommend doing)... do this:

phpseclib 默认使用更安全的 PSS 填充。Java 可能正在使用 PKCS#1 填充。所以如果你要走 phpseclib 路线(我建议这样做)......这样做:

$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);

回答by Boris Ivanov

I think u need to improve your PHP solution. According to http://php.net/manual/en/function.openssl-get-md-methods.phpyou can use directly [47] => sha256WithRSAEncryption from PHP, probably call openssl from commandline also be possible:

我认为您需要改进您的 PHP 解决方案。根据http://php.net/manual/en/function.openssl-get-md-methods.php你可以直接使用 [47] => sha256WithRSAEncryption from PHP,可能从命令行调用 openssl 也是可能的:

openssl dgst -sha256 -sign my.key -out in.txt.sha256 in.txt