How to verify digital signature in php

I'm signing a message on javascript and then trying to verify on php, however it returns always bad.

Im using this http://cdn9.atwikiimg.com/kurushima/pub/jsrsa/sample-rsasign.html as example for my javascript.

my javascript is

function doSign() {
 var rsa = new RSAKey();
  rsa.readPrivateKeyFromPEMString(document.form1.prvkey1.value);
  var hashAlg = document.form1.hashalg.value;
  var str1 = document.form1.msgsigned.value;
  var str2 = document.form1.msgsigned1.value;
  var res = str1.concat(str2);
  var hSig = rsa.signString(res, hashAlg);
  document.form1.siggenerated.value = linebrk(hSig, 64);
}

function doVerify() {
  var sMsg = document.form1.msgverified.value;
  var hSig = document.form1.sigverified.value;

  var x509 = new X509();
  x509.readCertPEM(document.form1.cert.value);
  var result = x509.subjectPublicKeyRSA.verifyString(sMsg, hSig);

  // display verification result
  if (result) {
    _displayStatus("valid");
  } else {
    _displayStatus("invalid");
  }
}

function copyMsgAndSig() {
  _displayStatus("reset");
  document.form1.msgverified.value = document.form1.msgsigned.value;
  document.form1.msgverified1.value = document.form1.msgsigned1.value;   
  document.form1.sigverified.value = document.form1.siggenerated.value; 
}

function _displayStatus(sStatus) {
  var div1 = document.getElementById("verifyresult");
  if (sStatus == "valid") {
    div1.style.backgroundColor = "skyblue";
    div1.innerHTML = "This signature is *VALID*.";
  } else if (sStatus == "invalid") {
    div1.style.backgroundColor = "deeppink";
    div1.innerHTML = "This signature is *NOT VALID*.";
  } else {
    div1.style.backgroundColor = "yellow";
    div1.innerHTML = "Please fill values below and push [Verify this sigunature] button.";
  }
}

And this is my php

<?php
        $msgAssinada=$_POST['msgsigned'];
        $msgAssinada1=$_POST['msgsigned1'];
        $assinatura=$_POST['siggenerated'];
        echo "<p>".$_POST['msgsigned']."</p>";
        echo "<p>".$_POST['msgsigned1']."</p>";
        echo "<p>".$_POST['siggenerated']."</p>";

        $fp = fopen("publicCert.pem", "r");
        $cert = fread($fp, 8192);
        fclose($fp);
        $pubkeyid = openssl_pkey_get_public($cert);
        $result = $msgAssinada . $msgAssinada1;
        echo $result;

        $ok = openssl_verify($result, $assinatura, $pubkeyid);

        if ($ok == 1) {
            echo "good";
        } elseif ($ok == 0) {
            echo "<p>bad</p>";
        } else {
            echo "ugly, error checking signature";
        }

        openssl_free_key($pubkeyid);
    ?>

and im using this keys:

private key:

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEArS+LAkcvzrv5yICTGrJFkK+Z6uIJ1VizM8Cs8sj1uBYg6R+Z
(...)
9VA6LyRzv11n2wpJ0tOusRv7+XhF+BE28gkEfZtBg9mZakqVH7zO9A==
-----END RSA PRIVATE KEY-----

Public Key

-----BEGIN CERTIFICATE-----
MIIFfzCCA2egAwIBAgIDDguBMA0GCSqGSIb3DQEBBQUAMHkxEDAOBgNVBAoTB1Jv
(...)
Etj7VCXe3h2xHgQUfkUvnRw8vw==
-----END CERTIFICATE-----

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

openssl_verifyVerify signature

Return Values

Returns 1 if the signature is correct, 0 if it is incorrect, and -1 or false on error.

Examples

Example #1 openssl_verify() example

<?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);
?>

Example #2 openssl_verify() example

<?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"sha256WithRSAEncryption");//verify signature
$ok openssl_verify($data$signature$public_key_resOPENSSL_ALGO_SHA256);
if (
$ok == 1) {
    echo 
"valid";
} elseif (
$ok == 0) {
    echo 
"invalid";
} else {
    echo 
"error: ".openssl_error_string();
}
?>

Stiv

16 years ago

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";
}
?>

steve dot venable at lmco dot com

20 years ago

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.

mikey at badpenguins dot com

12 years ago

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.

phpdev at fpierrat dot fr

11 months ago

As stated from the doc: "Returns 1 if the signature is correct, 0 if it is incorrect, and -1 or false on error. "

In the second example as a well as in Stiv's note, following condition will match for both 0 or false, which have different meaning:
elseif ($ok == 0) {
    echo "bad";
}

On should do an identical test here (===) instead of an equal test (==):
elseif ($ok === 0) {
    echo "bad";
}
---
var_dump(0==false); //==> true
var_dump(0===false);//==> false

peter dot labos at gmail dot com

4 years ago

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
}

meint dot post at bigfoot dot com

21 years ago

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 :-)

jeremie dot gomez at gmail dot com

11 years ago

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.

How do I verify a digital signature?

In the signature Properties window click Show Signers Certificate button. Following screen will be displayed once you click on it..
Once you click on OK button, following window will be displayed. ... .
Click Ok button in the following window. ... .
Once the certificate validation is done, you will get the following approval..

How do I know if my digital signature is enabled?

Open the file that contains the certificate you want to view. Click File > Info > View Signatures. In the list, on a signature name, click the down-arrow, and then click Signature Details. In the Signature Details dialog box, click View.

How do I verify my signature in PDF?

Open the pdf file and Right click on the Question Mark showing the Validity Unknown. Click on Show Signature Properties. Click on Show Signer's Certificate & Select Trust. Click Add to Trusted Identities & Click OK.