php openssl_pkcs7_verify 验证缺陷

154 阅读1分钟

前言: PHP7有着openssl_pkcs7_verify的ca验签忽略缺陷,知道php8才修复.写这篇文章是为了记录也是为了后面的人方便查阅。

缺陷来源:bugs.php.net/bug.php?id=… 更新版本:github.com/php/php-src…

前因: 由于公司业务需要需要对接阿里云某些特定的接口,需要实现pkcs7验签。原本业务用的php7.3可以是不管怎样也只报验签错误,或者是验签成功无法写入。

pkcs7验签实现: 因为我不需要验证ca所以就没做ca验证。把获取的签名实体,公钥,以及签名,分别写入文件这样才能实现,pkcs7验签。着重讲php的pkcs7签名文件格式。 示例:

MIME-Version: 1.0  
Content-Disposition: attachment; filename="smime.p7m"  
Content-Type: application/x-pkcs7-mime; smime-type=signed-data; name="smime.p7m"  
Content-Transfer-Encoding: base64  

签名

解决方案: 1.捕捉异常错误 openssl_pkcs7_verify(): signature OK, but cannot open for writing 这个就是验签通过的 2.升级php版本之修复版本(不过有业务的公司大概率是没办法升级的) 3.新建一个验签项目,就可以实现验签了。

相关代码

//$data 实体数据
//$signature 签名信息
//$publicKey公钥
private function verifySignature($data, $signature, $publicKey)
{
    $signatureFileName = time() . random_int(0, 999999) . '.p7m';
    $signatureFileCentent = self::der2smime($signature, $signatureFileName);

    Filesystem::disk('verify')->write($signatureFileName, $signatureFileCentent);
    $signaturePath = Filesystem::disk('verify')->path($signatureFileName);

    $dataFileName = time() . random_int(0, 999999) . '.txt';
    Filesystem::disk('verify')->write($dataFileName, $data);
    $dataPath = Filesystem::disk('verify')->path($dataFileName);

    $publicKeyFileName = time() . random_int(0, 999999) . '.txt';
    Filesystem::disk('verify')->write($publicKeyFileName, $publicKey);
    $publicKeyPath = Filesystem::disk('verify')->path($publicKeyFileName);

    $isValid = openssl_pkcs7_verify(
        $signaturePath,           //私钥信息
        PKCS7_NOVERIFY,
        null,
        [],
        $publicKeyPath,              //公钥
        $dataPath                     //文件路径
    );
    Filesystem::disk('verify')->delete($signatureFileName);
    Filesystem::disk('verify')->delete($dataFileName);
    Filesystem::disk('verify')->delete($publicKeyFileName);
    return $isValid ?? false;
}
private static function der2smime($signature,$signatureFileName)
{
    // 去除多余的空白字符
    $signature = preg_replace('/\s+/', '', $signature);

    $smimeContent = "MIME-Version:1.0\n";
    $smimeContent .= "Content-Disposition:attachment;filename=".$signatureFileName."\n";
    $smimeContent .= "Content-Type:application/pkcs7-mime;smime-type=signed-data; ame="$signatureFileName"\n";
    $smimeContent .= "Content-Transfer-Encoding:base64\n\n";
    $smimeContent .= chunk_split($signature);
   return $smimeContent;
}