在 C 和 PHP 中进行数字签名的正确算法是什么?

What is the right algorithm for digitally signing in C and veryfing in PHP?

我正在尝试在 C 中对 files/messages 进行数字签名并在 PHP 中验证签名。 我可以在 C 中正确验证它,但是当我尝试导入签名并在 PHP 中验证它时失败了。

我正在使用此方法在 C 语言中签署消息:

bool RSASign( RSA* rsa,
              const unsigned char* Msg,
              size_t MsgLen,
              unsigned char** EncMsg,
              size_t* MsgLenEnc) {
    EVP_MD_CTX* m_RSASignCtx = EVP_MD_CTX_create();
    EVP_PKEY_assign_RSA(privkey, rsa);
    if (EVP_DigestSignInit(m_RSASignCtx,NULL, EVP_sha256(), NULL,privkey)<=0) {
        return false;
    }
    if (EVP_DigestSignUpdate(m_RSASignCtx, Msg, MsgLen) <= 0) {
        return false;
    }
    if (EVP_DigestSignFinal(m_RSASignCtx, NULL, MsgLenEnc) <=0) {
        return false;
    }
    *EncMsg = (unsigned char*)malloc(*MsgLenEnc);
    if (EVP_DigestSignFinal(m_RSASignCtx, *EncMsg, MsgLenEnc) <= 0) {
        return false;
    }
    EVP_MD_CTX_free(m_RSASignCtx);
    return true;
}

如我所说,如果我尝试仅在 C 中验证此签名,它工作正常。 私钥是前面代码中用这行代码获取的:

pin = (char *) getpass("Enter PIN: ");
rc = PKCS11_login(slot, 0, pin);
authkey = PKCS11_find_key(&certs[0]);

如果有必要,我可以 post 获取插槽的代码,但我认为目前没有必要。

我正在尝试使用以下代码验证 PHP 中的签名:

function isOriginal() {
    $signature = base64_decode("HZwn2Zz4ir5Isvvo+fEj8IFydQs/tuW3I6HPzadHMIq+i5qCBURXqK/B5X1gYyoCPyHzgyY/zJI1skoFdKwPwx6ySEwLBZ5QTFsxp56jGi/UZgXu8X+jfvHfss89VhqcIxiZdklhZtlf5sMdJ045KbfvHDeAyfXj2C/3Nvk8IQMR+q3eUGAkiEZJIWpivp5WufrjFlsmKRdDm8j2szJPtIVipyj1AwIYkiAJOggC4JWd3LWn8C4bt84ys2CezFbc7BDckLe2IGBkaDMsCTr8PE0SP81npqHd9CyfvU4zd5LyXkBNZqz3QnAN19iI0b5EdUeJj3UQ83gmuugGy1088g==");
    $publicRSAKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJYozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+vw1HocOAZtWK0z3r26uA8kQYOKX9Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQApfc9jB9nTzphOgM4JiEYvlV8FLhg9yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68i6T4nNq7NWC+UNVjQHxNQMQMzU6lWCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoVPpY72+eVthKzpMeyHkBn7ciumk5qgLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUywQIDAQAB-----END PUBLIC KEY-----";       

    $plaintext = "My secret message.";  

    $id = openssl_pkey_get_public(MY_PUB_CERT_IN_A_CONST_STR);

    echo (openssl_verify($plaintext, $signature, $id, OPENSSL_ALGO_SHA256) ? 'verified' : 'unverified'); 
} 

这失败了。我也试过使用 PHPSECLIB:

function isOriginal() {
    $signature = base64_decode("HZwn2Zz4ir5Isvvo+fEj8IFydQs/tuW3I6HPzadHMIq+i5qCBURXqK/B5X1gYyoCPyHzgyY/zJI1skoFdKwPwx6ySEwLBZ5QTFsxp56jGi/UZgXu8X+jfvHfss89VhqcIxiZdklhZtlf5sMdJ045KbfvHDeAyfXj2C/3Nvk8IQMR+q3eUGAkiEZJIWpivp5WufrjFlsmKRdDm8j2szJPtIVipyj1AwIYkiAJOggC4JWd3LWn8C4bt84ys2CezFbc7BDckLe2IGBkaDMsCTr8PE0SP81npqHd9CyfvU4zd5LyXkBNZqz3QnAN19iI0b5EdUeJj3UQ83gmuugGy1088g==");
    $publicRSAKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy8Dbv8prpJ/0kKhlGeJYozo2t60EG8L0561g13R29LvMR5hyvGZlGJpmn65+A4xHXInJYiPuKzrKUnApeLZ+vw1HocOAZtWK0z3r26uA8kQYOKX9Qt/DbCdvsF9wF8gRK0ptx9M6R13NvBxvVQApfc9jB9nTzphOgM4JiEYvlV8FLhg9yZovMYd6Wwf3aoXK891VQxTr/kQYoq1Yp+68i6T4nNq7NWC+UNVjQHxNQMQMzU6lWCX8zyg3yH88OAQkUXIXKfQ+NkvYQ1cxaMoVPpY72+eVthKzpMeyHkBn7ciumk5qgLTEJAfWZpe4f4eFZj/Rc8Y8Jj2IS5kVPjUywQIDAQAB-----END PUBLIC KEY-----";        

    $rsa = new RSA();
    $rsa->loadKey($publicRSAKey);
    $rsa->setHash('sha256');

    $plaintext = "My secret message.";  

    echo ($rsa->verify($plaintext, $signature) ? 'verified' : 'unverified');
} 

结果相同。有谁知道它为什么失败? AFAIU 我在 C 和 PHP 中使用相同的算法。 任何帮助表示赞赏。

您可能还想知道为什么我要尝试这样做以及为什么我不在 PHP 中使用 PHP 函数和 C 中的 C 函数分别进行验证。这是因为我想签名带有 A3 令牌的消息和文件只能从 C 中的 .so 文件访问。AFAIU PHP 没有任何使用 A3 令牌对文件进行签名的功能。

如果有任何选项可以使用来自 PHP 的 A3 令牌对文件进行签名,我真的很想知道。

对于 phpseclib,请在 $rsa->verify():

之前执行此操作
$rsa->setSignatureMode(RSA::SIGNATURE_PKCS1);

要使用 X.509 证书执行此操作...

$x509 = new \phpseclib\File\X509;
$x509->loadX509('...');
$rsa = $x509->getPublicKey();