PHP 使用公共证书验证签名字符串

问题描述 投票:0回答:1

根据下面非常有用的评论,我重写了这个问题。

使用https://www.php.net/manual/en/function.openssl-verify.php中的代码作为示例:

<?php
// $data is assumed to contain the data to be signed

// fetch certificate from file and ready it
$fp = fopen("path/file.pem", "r");
$cert = fread($fp, 8192);
fclose($fp);

// state whether signature is okay or not
// use the certificate, not the public key
$ok = openssl_verify($data, $signature, $cert);
if ($ok == 1) {
    echo "good";
} elseif ($ok == 0) {
    echo "bad";
} else {
    echo "ugly, error checking signature";
}
?>

我们正在开发一个项目,我们接收包含“签名”的数据。这是数据 (UTF8) 的已知部分,已由发送者使用私有 x.509 证书和 base64 编码进行签名。作为数据的一部分,我们收到他们的 Base64 公共证书。

我们可以根据发送的信息构建“检查数据”(

$data
),并且我们收到了
$signature
,因此使用上面的代码示例应该可以工作,但是......

上面的示例从 PEM 文件中获取证书,但我们只是获得了 Base64 编码的公共证书。

以前没有使用过此类东西,如何处理 Base64 证书以便我可以将其用作上面代码中的 $cert ?

编辑:

感谢@Sammitch 对证书的澄清。我现在可以从证书访问公钥。然而,

openssl_verify
仍然无法验证,我想知道这是否是因为原始无符号数据是如何组合在一起的。

openssl_verify
要求
$data
是字符串,但在本例中,数据创建如下:

签名前缀是动词、目的地和日期时间戳的串联,所以类似于:

POST;https://example.com/webhook;2023-10-12T20:53:50+00:00

然后将其转换为字节。

执行此操作的 VB.Net 流程是这样的:

Dim signaturePrefix As String = verb + ";" + Destination + ";" + SignatureDate + ";"
Dim signaturePrefixBytes As Byte() = Encoding.UTF8.GetBytes(signaturePrefix)

Dim originalLength As Integer = signaturePrefixBytes.Length

Array.Resize(signaturePrefixBytes, originalLength + hashBytes.Length)

Array.Copy(hashBytes, 0, signaturePrefixBytes, originalLength, hashBytes.Length)

发送的 JSON 的哈希值也会转换为字节,然后合并两个字节数组。然后使用发送者的 X509 证书对该合并值进行签名。

所以...鉴于

openssl_verify
要求
$data
是一个字符串,但这是一个字节数组,我该如何使用
openssl_verify
来验证签名?

php x509
1个回答
0
投票

我们已经找到了问题的根源,并且这个解决方案有效:

<?php

// Obtain the necessary headers and context variables
$sentSignatureBase64 = "rYYN6S87D3###REDACTED###MfVYeWA==";
$publicCertBase64 = "MIIHA###REDACTED###O0m/kRrA=";
$signedTimestamp = "2023-10-16T12:00:30+01:00";
$contentHashBase64 = "PT8Zbb###REDACTED###PeZEZ3Tc=";

$verb = "POST";
$destination = "https://webhook.site/###REDACTED###";
$body = "[{\"payload\":{###REDACTED###}}]";

try {
    // Create Certificate
    $public_key_pem="-----BEGIN CERTIFICATE-----\n". wordwrap($publicCertBase64, 64, "\n", false) ."\n-----END CERTIFICATE-----";
    $publicCertificate = openssl_pkey_get_public($public_key_pem);

    echo "public_key_pem---------".PHP_EOL;
    echo $public_key_pem.PHP_EOL;
    echo "---------".PHP_EOL;


    echo "Key Details---------".PHP_EOL;
    $keyData = openssl_pkey_get_details($publicCertificate);
    var_dump($keyData);
    echo "---------".PHP_EOL;

    // Get bytes for the sent signature
    $sentSignatureBytes = base64_decode($sentSignatureBase64);

    // Build the signature prefix based on the received data
    $receivedSignaturePreifx = $verb . ";" . $destination . ";" . $signedTimestamp . ";";
echo "\$receivedSignaturePreifx = ".$receivedSignaturePreifx.PHP_EOL;
    // Get the SHA-256 hash of the body
    $body = empty($body) ? "{}" : $body;
    $hashBytes = hash('sha256', $body, true);

    // Combine the two sets of bytes
    $combinedBytes = $receivedSignaturePreifx . $hashBytes;

    // Verify the signature
    $isSignatureValid = openssl_verify($combinedBytes, $sentSignatureBytes, $publicCertificate, "sha256WithRSAEncryption") === 1;

    if (!$isSignatureValid) {
        $problemDetail = array(
            "statusCode" => 401,
            "detail" => "Signature failed validation.",
            "isSignatureValid" => $isSignatureValid
        );
        echo json_encode($problemDetail);
        return;
    }

    $results = array(
        "detail" => "Successful verification.",
        "isSignatureValid" => $isSignatureValid
    );
    echo json_encode($results);

} catch (Exception $ex) {
    $problemDetail = array(
        "statusCode" => 500,
        "detail" => "Error while decrypting signature: " . $ex->getMessage()
    );
    echo json_encode($problemDetail);
    return;
}
?>



© www.soinside.com 2019 - 2024. All rights reserved.