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";
}
?>openssl_verify
Почист и полокален преглед на PHP референцата, со задржана структура од PHP.net и подобра читливост за примери, секции и белешки.
openssl_verify
Референца за `function.openssl-verify.php` со подобрена типографија и навигација.
openssl_verify
(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)
openssl_verify — Потврди потпис
= NULL
string
$data,string
$signature,OpenSSLAsymmetricKey|OpenSSLCertificate|array|string
$public_key,string|int
$algorithm = OPENSSL_ALGO_SHA1,int
$padding = 0): int|false
openssl_verify() потврдува дека
signature е точен за наведениот
data користејќи го јавниот клуч поврзан со
public_key. Ова мора да биде јавниот клуч што одговара на приватниот клуч што се користел за потпишување.
Параметри
data-
Низата податоци што претходно се користела за генерирање на потписот
signature-
Сурова бинарна низа, генерирана од openssl_sign() или слични средства
public_key-
OpenSSLAsymmetricKey - клуч, вратен од openssl_get_publickey()
string - а PEM форматиран клуч (на пр.
-----BEGIN PUBLIC KEY----- MIIBCgK...) algorithm-
int - едно од овие CMS Флагови/Константи.
string - валидна низа вратена од openssl_get_md_methods() пример, "sha1WithRSAEncryption" или "sha512".
padding- RSA PSS подлога за употреба.
Вратени вредности
Враќа 1 ако потписот е точен, 0 ако е неточен, и -1 или false при грешка.
Дневник на промени
| Верзија | = NULL |
|---|---|
| 8.5.0 |
Изборниот параметар padding е додадена.
|
| 8.0.0 |
public_key прифаќа OpenSSLAsymmetricKey
or OpenSSLCertificate инстанца сега; претходно, а resource од тип OpenSSL key or OpenSSL X.509
беше прифатено.
|
Примери
Пример #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);
?>Пример #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_res, OPENSSL_ALGO_SHA256);
if ($ok == 1) {
echo "valid";
} elseif ($ok == 0) {
echo "invalid";
} else {
echo "error: ".openssl_error_string();
}
?>Белешки од корисници 9 белешки
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.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 :-)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
}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.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);//==> falsePHP is the most efficient language for handling web requests. I have tested that PHP's OpenSSL library can decrypt data encrypted by JS using AES, OAEP, and EC-DH, and can also verify data signed by JS using HMAC and RSA-PKCS#1.5. However, it cannot verify data signed by JS using EC-DSA or RSA-PSS. Verifying data signed with EC-DSA and RSA-PSS is necessary. However, PHP's OpenSSL library still does not support verifying EC-DSA and RSA-PSS signatures.
// JS EC-DSA sign
crypto.subtle.generateKey(
{
name: 'ECDSA',
namedCurve: 'P-256' // P-521 corresponds to secp521r1, P-384 corresponds to secp384r1, p-256 corresponds to prime256v1
},
true,
['sign', 'verify']
).then(pair => {
return {
publicKey: crypto.subtle.exportKey('spki', pair.publicKey),
signature: crypto.subtle.sign(
{
name: 'ECDSA',
hash: {
name: 'SHA-1' // or SHA-256, SHA-384, SHA-512
}
},
pair.privateKey,
(new TextEncoder()).encode('test data')
)
};
}).then(data => {
// await crypto.subtle.verify(...) is true;
// convert data.publicKey ArrayBuffer to PEM publicKey and encodeURIComponent it;
// convert data.signature ArrayBuffer to base64 string;
// send data object to PHP
});
// PHP receives the above data and executes openssl_verify
openssl_verify(
base64_decode($_POST['signature']),
'test data',
urldecode($_POST['publicKey']),
OPENSSL_ALGO_SHA1 // or OPENSSL_ALGO_SHA256, OPENSSL_ALGO_SHA384, OPENSSL_ALGO_SHA512
);
// result: -1
// JS RSA-PSS sign
crypto.subtle.generateKey(
{
name: 'RSA-PSS',
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256' // or SHA-1, SHA-384, SHA-512
},
true,
['sign', 'verify']
).then(pair => {
return {
publicKey: crypto.subtle.exportKey('spki', pair.publicKey),
signature: crypto.subtle.sign(
{
name: 'RSA-PSS',
saltLength: 32
},
pair.privateKey,
(new TextEncoder()).encode('test data')
)
};
}).then(data => {
// await crypto.subtle.verify(...) is true;
// convert data.publicKey ArrayBuffer to PEM publicKey and encodeURIComponent it;
// convert data.signature ArrayBuffer to base64 string;
// send data object to PHP
});
// PHP receives the above data and executes openssl_verify
openssl_verify(
base64_decode($_POST['signature']),
'test data',
urldecode($_POST['publicKey']),
OPENSSL_ALGO_SHA256 // or OPENSSL_ALGO_SHA1, OPENSSL_ALGO_SHA384, OPENSSL_ALGO_SHA512
);
// result: -1, but RSA-PKCS#1.5 is OKmikey at badpenguins dot com -- validating an X509 certificate chain in php seems to be possible with openssl_x509_checkpurpose()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.