• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • openssl_verify()

    (PHP 4 >= 4.0.4, PHP 5, PHP 7)

    验证签名

    说明

    openssl_verify(string $data,string $signature, mixed $pub_key_id[,mixed $signature_alg= OPENSSL_ALGO_SHA1]): int

    openssl_verify()使用与$pub_key_id关联的公钥验证指定数据$data的签名$signature是否正确。这必须是与用于签名的私钥相对应的公钥。

    参数

    $data

    以前用来生成签名的数据字符串。

    $signature

    原始二进制字符串,通过openssl_sign()或类似的函数生成。

    $pub_key_id

    resource- 一个密钥,通过openssl_get_publickey()函数返回。

    string- 一个 PEM 格式的密钥,比如,"-----BEGIN PUBLIC KEY-----MIIBCgK..."

    $signature_alg

    int- 以下签名算法之一Signature Algorithms.

    string- 由openssl_get_md_methods()函数返回的可用字符串,比如,"sha1WithRSAEncryption"或者"sha512".

    返回值

    如果签名正确返回 1,签名错误返回 0,内部发生错误则返回-1.

    更新日志

    版本说明
    5.2.0添加了$signature_alg参数。

    范例

    openssl_verify()范例:

    <?php
    // $data and $signature are assumed to contain the data and the signature
    // fetch public key from certificate and ready it
    $pubkeyid = openssl_pkey_get_public("file://src/openssl-0.9.6/demos/sign/cert.pem");
    // state whether signature is okay or not
    $ok = openssl_verify($data, $signature, $pubkeyid);
    if ($ok == 1) {
        echo "good";
    } elseif ($ok == 0) {
        echo "bad";
    } else {
        echo "ugly, error checking signature";
    }
    // free the key from memory
    openssl_free_key($pubkeyid);
    ?>
    

    openssl_verify()范例:

    <?php
    //data you want to sign
    $data = 'my data';
    //create new private and public key
    $private_key_res = openssl_pkey_new(array(
        "private_key_bits" => 2048,
        "private_key_type" => OPENSSL_KEYTYPE_RSA,
    ));
    $details = openssl_pkey_get_details($private_key_res);
    $public_key_res = openssl_pkey_get_public($details['key']);
    //create signature
    openssl_sign($data, $signature, $private_key_res, "sha1WithRSAEncryption");
    //verify signature
    $ok = openssl_verify($data, $signature, $public_key_res, OPENSSL_ALGO_SHA1);
    if ($ok == 1) {
        echo "valid";
    } elseif ($ok == 0) {
        echo "invalid";
    } else {
        echo "error: ".openssl_error_string();
    }
    ?>
    

    参见

    A note about the openssl_verify() (and some of the other functions). The public key comes from a certificate in any of the support formats (as the example shows, use openssl_get_publickey() to get the resource id). But after some trial and error I found the signature string MUST BE BINARY. While no error occurs, passing a base64-formatted signature string (PEM format?), you simply get a mismatch. When I did the base64 decode myself, the verify returned a match (return value 1). You can simply drop the begin/end lines and take the output of the 'base64_decode()' function.
    I've finally found a way to verify signature. Sample in the documentation doesn't work. Code bellow DOES work :)
    <?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";
    }
    ?>
    
    I spent days scouring the php openssl documentation trying to figure out how to do what sounds like a simple task - given two PEM encoded certificates, is one the signer of the other? Nowhere in the openssl_verify() documentation or comments is it explained where to obtain the signature of an existing certificate. The openssl_x509_parse() function looked promising, but it is an unstable API that may change.
    I had to write my own code to determine if one cert signed another, it is located here: http://badpenguins.com/source/misc/isCertSigner.php?viewSource
    In a nutshell here is what I learned...
    The signature data in a signed X.509 certificate contains DER formatted data about the signature that is encrypted with the signers public key. The data contains a hash of the original subject certificate and information about what encryption algorithm was used to create the signature.
    So you need to get this signature data and a copy of the original certificate with the issuer and signature sequences removed. Hash a copy of the original certificate (sans issuer/signature sequences) with the same algorithm the issuer used and if the hashes match, you have the issuer cert that signed the certificate.
    openssl_verify() is populating openssl_error_string() even on false. 
    When openssl_verify() returns 0, openssl_error_string() is populated with 1.
    I spent lot of time to understand, while my next call to openssl was failing with checks for error.
    <?php
    $c = file_get_contents($filename);
    $publicKey = openssl_pkey_get_public($c);
    $result = openssl_verify('freedom', 'someirrelevantnosign', $publicKey);
    $error = "";
    while ($msg = openssl_error_string() !== false) {
      $error .= $msg;
    }
    if (!empty($error)) {
      echo $error; // 1
    }
    Anbybody trying to get a Win32 CryptoAPI based digital signature component to work with the openssl_verify() function should be aware that the CryptoAPI PKCS1 (RSA) method uses bytes in reverse order while the openssl_verify() method expects a correctly formatted PKCS1 digital signature (as should be). I learned this the hard way and it took me some time to dig this out. A simple solution in VBScript to reverse the byte order:
    N = Len(Blob.Hex)
    ' reverse bytes in the signature using Hex format 
    For i = 1 To N - 1 Step 2 
      s = Mid(Blob, i, 2) & s 
    Next 
    s contains the digital signature in reverse order. Blob is an arbitrary binary container.
    Send the signature off in Hex format and use a hex2bin method in PHP to convert to the correct format for openssl_verify(), i.e.
    function hex2bin($data) {
      $len = strlen($data);
      return pack("H" . $len, $data);
    } 
    That's it, hope it helps out. BTW I used ASPEncrypt to toy around with on Win32 platform. Works only with Internet Explorer but you could also use a Java applet and have none of the abovementioned problems :-)
    You can actually use the public key as third parameter and not the certificate.
    If you can't make it work, make sure that :
    1) Your public key is well formatted. It seems that it must have the ----BEGIN PUBLIC KEY---- and ----END PUBLIC KEY----
    2) Your signature is in binary format. You can use the php base64_decode for this.
    mikey at badpenguins dot com -- validating an X509 certificate chain in php seems to be possible with openssl_x509_checkpurpose()