I use PHP to sign a string with openssl_sign. So far all ok.
The problem is that I want to verify the signature from Windows. For that I pass the certificate, the message, and it's signature to the windows app.
How do I use CryptVerifyDetachedMessageSignature to force using the certificate that the PHP code used?
I tried it, but it returns "asn1 bad tag value met" on the signature created by PHP ...
Thanks...
It's hard to say since you haven't posted your code or a sample signature / plaintext / key. But, in lieu of that, here's how I'd do it (with phpseclib):
<?php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
//$rsa->setPassword('password');
$rsa->loadKey('...'); // private key
$plaintext = '...';
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($plaintext);
$rsa->loadKey('...'); // public key
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
?>
If the signature mode is PSS just do $rsa->setSignatureMode() (without any parameters) instead.
If the signature and plaintext are both in the same blob you'll need to separate it per whatever file format you're using.
No luck.
I finally resorted to openssl_pkcs7_sign which outputs a S/MIME compatible message, which I can handle in Windows.
Related
Is there a coldfusion alternaitive to this php function: openssl_verify:
openssl_verify() verifies that the signature is correct for the
specified data using the public key associated with pub_key_id. This
must be the public key corresponding to the private key used for
signing.
I've looked all over but there doesn't seem to be any. Thanks in advance for any info?
There are no built in functions, AFAIK. However, java supports signature verification, which you could adapt with a bit of java code.
Convert the data you want to verify into binary. The exact steps depends on what you are verifying, but say it is a physical file:
dataBytes = fileReadBinary( "c:\path\someFile.zip" );
Decode the signature value into binary. Again, the "how" depends on the signature format. If it is a base64 encoded string:
signatureBytes = binaryDecode( base64SignatureString, "base64" );
Load the certificate from your keystore (or from a file) and extract the public key:
// Example: "C:\ColdFusion\jre\lib\security\cacerts"
fis = createObject("java", "java.io.FileInputStream").init( pathToKeyStore );
keyStore = createObject("java", "java.security.KeyStore").getInstance("JKS");
// Default keystore password is "changeit" (do not keep the default. change it)
keyStore.load(fis, keyStorePassword.toCharArray());
publicKey = keyStore.getCertificate( "yourCertAlias" ).getPublicKey();
Create a Signature object to perform the verification. Initialize it with the appropriate algorithm (ie SHA1withRSA, etcetera), public key and the data to verify:
sign = createObject("java", "java.security.Signature").getInstance("SHA1withRSA");
sign.initVerify( publicKey );
sign.update( dataBytes );
Finally, feed in the signature and verify its status:
isVerified = sign.verify(signatureBytes);
writeDump( isVerified );
For more details, see Lesson: Generating and Verifying Signatures and Weaknesses and Alternatives.
You could attempt using cfexecute along with the OpenSSL CLI. https://www.openssl.org/docs/manmaster/apps/verify.html
Update
Apparently, even though I thought I was generating keys that did not have a password, gnupg still expected a password for them (which the gnupg extension no longer supports). I regenerated a new keypair using Kleopatra on Windows and bypassed all the "no passphrase" warnings and I was able to successfully sign/encrypt with those keys.
So, the bottom line is be very sure that your key does not have a passphrase.
I am attempting to sign a message using PHP's gnupg extension. I have the environment setup correctly, and I can successfully import the key, and adding it using gnupg_addsignkey is successful (returns true).
When I attempt to sign the message using gnupg_sign($res, "my message"), I get the following error and gnupg_sign returns false:
gnupg_sign(): data signing failed
I can't seem to find any way to get more verbose information to figure out why it's failing.
I've tried the procedural methods, as well as the OO methods, and get the same result. The permission are all correct on the server.
Here's the OO code I've used:
# /tmp/.gnupg is there (but empty if that helps figure out the problem)
putenv("GNUPGHOME=/tmp/.gnupg");
$gpg = new gnupg();
$gpg->seterrormode(GNUPG_ERROR_WARNING);
$ascii = file_get_contents('/etc/my.key'); // Yes, this reads successfully
$start = strpos($ascii, '-----BEGIN PGP PRIVATE KEY BLOCK-----');
$end = strpos($ascii, '-----END PGP PRIVATE KEY BLOCK-----')+34;
$key = substr($ascii, $start, ($end-$start));
$info = $gpg->import($key); // Fingerprint is there and everything seems OK
$gpg->addsignkey($info['fingerprint']);
$signed = $gpg->sign("test!"); // fails with any string I try
$signed is false, and I get the PHP warning gnupg::sign(): data signing failed
Is your private key password protected?
According to pecl/gnupg documentation you cannot pass a plaintext password for gnupg ≥ version 2.
So all you can do is use a private key that has no password set, I guess.
IMO pecl/gnupg errors are quite misleading.
I'm having trouble to decrypt a cipher text in client side/javascript which is encrypted in server side/PHP.
For encryption in PHP, I'm using the phpseclib and here is my sample code block:
define('PUK', 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwMKuLMVuo7vHDwFCbpgxx+RNf xSVC2GRWq9rW0fl4snnhRdrpprPRUO1RMV0wA/ao+fvzi1Sl0lcws3srpe28iLAj Wh5vFM/pFstwzjoHBv/6n4rQouIVy2NT18aFrxlQUCs4DHy5eOo21MjQXrgHwCeP HEc7OK+VpaQ/yKKX8wIDAQAB');
include ('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$plaintext = 'My Test Msg';
$rsa -> loadKey(PUK);
$ciphertext = $rsa -> encrypt($plaintext);
//echo $ciphertext;//This also not working!
//echo strrev(base64_encode($ciphertext)); //this is also not working! ref: http://www.frostjedi.com/phpbb3/viewtopic.php?f=46&t=141187
echo base64_encode($ciphertext);
For decryption in client side/Javascript, I'm using "jsencrypt" and here is the code block:
var decrypt = new JSEncrypt();
decrypt.setPrivateKey('MIICXQIBAAKBgQDwMKuLMVuo7vHDwFCbpgxx+RNfxSVC2GRWq9rW0fl4snnhRdrp prPRUO1RMV0wA/ao+fvzi1Sl0lcws3srpe28iLAjWh5vFM/pFstwzjoHBv/6n4rQ ouIVy2NT18aFrxlQUCs4DHy5eOo21MjQXrgHwCePHEc7OK+VpaQ/yKKX8wIDAQAB AoGBAL2EuaZvwLIwL6VUVoYp5AH+FVJo3Ti8Q5e7rEX6kgyxTsf4dX4NIi9T2p1J BQ2A4xx7e1i0pIreyBtOUy6ik0y7e3MlmZidG91pz2KllQqwAMKrOZgPTBWBF7fr xIZERfOlZcIRrqp8ECbeHDyO6fUbfQm+o7vkxMypwjixBslJAkEA+mF8Sxvw+7D6 ntev+XsYj9Xp4wumqR2hK4WcXAAWbFmcd29tgTMKfcgw0Ru6FCGQdUvqu61PniS4 ie+u6zPORwJBAPWUos5KvEixkgSUY0PZOQavRwoXS1GEEvkjlFOyqWqUiKViT9iy UsXKxk3NAVMqIdF5RdAQ/ob9NxtxiuSxYvUCQQDUfFsBWwsebsmieCVNslvb5YyC NOcRaqXWy6MwqJpfBYW2Doh+NxTWPki/japTX1C7WtwwvhpteXhrB1AJJ4QNAkB1 RrsM6vHJgUsq9rYE07qA77lsHz2vuvPYmF4gLkTrie1LlYxt/pK6tCBJTSphzdAC mfh16XezfT8Q0wMyPWf1AkAxS//2T3J1g+dbG9dEKREcpwANxlFIEnOm9XsFd2vO I6Jr0ksaS4o0IeUBDWmMFOgCWVPdJkGrlqlVPQ6P9owA');
var uncrypted = decrypt.decrypt(ciphertext); //The ciphertext is obtained from the server by an AJAX call.
BUT the "uncrypted" is found always to be null!
Please note that if I use the mentioned Private/Public key pair either in phpseclib or jsencrypt then it is working fine. The problem is creating only on encryption in PHP and decryption in Javascript.
It will be really appreciable if anyone can help me on this regard.
What happens if you encrypt in Javascript and try to decrypt in PHP? Does that work?
If not then if you could post the ciphertext produced by Java that'd be helpful. That'd give us a chance to breakdown the encoding of the plaintext and see which padding method - if any - is being used.
That said, in lieu of having that, my guess, off hand, would be that you need to do $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1).
I am trying to sign a string with:
$rsa = new Crypt_RSA();
//$rsa->setPassword('*****');
$rsa->loadKey(file_get_contents('i.pem')); // private key
$plaintext = 'f2e140eb-2b09-44ab-8504-87b25d81914c';
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$ciphertext = $rsa->sign($plaintext);
$reto = base64_encode($ciphertext);
when I verify it locally, with:
$pubb_key = openssl_pkey_get_public(file_get_contents('instancia_imta_ope.crt'));
$keyData = openssl_pkey_get_details($pubb_key);
$pkeyy = $keyData['key'];
$rsa->loadKey($pkeyy); // PUBLIC key
echo $rsa->verify($plaintext, $ciphertext) ? 'verified' : 'unverified';
it shows VERIFIED,
when I use a tester page with my broker, THIS SAME CODE, doesnt work. It doesnt recover the original string.
Trying to use something different, I tried the following weird code:
$rsa = new Crypt_RSA();
$rsa->loadKey(file_get_contents('i.pem')); // PRIVATE key, IT SHOULD BE PUBLIC
$plaintext = 'f2e140eb-2b09-44ab-8504-87b25d81914c';
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$ciphertext = $rsa->encrypt($plaintext);
$reto = base64_encode($ciphertext);
its weird or not logical, because I am using a private key to encrypt, it is suposed to be the public key, son the target uses its private key, to deencrypt the message. Strangely, this weid code makes the tester page send an OK, it recovers the string. I dont know why.
All this is part of a bigger message, which it is finally processed with xml signature, when I process all (adding the xml signature), the other tester page of the broker sends invalid signatures, I can bet that it is because of the weird code. Never mind, question:
WHY THE CORRECT CODE (rsa->sign....) DOESNT WORK? WHAT DO YOU THING OF ALL THIS?
thanks
mario
So the code works on one machine but not the other? Seems like in that case your .crt files might be different. That said, you don't need to use openssl_* to extract the public key - you can do so with phpseclib just fine. eg.
<?php
include('File/X509.php');
$x509 = new File_X509();
$cert = $x509->loadX509('...'); // see google.crt
echo $x509->getPublicKey()->getPublicKey();
?>
I'm not really sure what you're asking about with the encryption question.. although normally you'd encrypt with the public key you can do so with the private key as well. They both do the same operation - a modular exponentiation. I can't comment on the tester page since the source to it hasn't been posted.
When dealing with encryption and certification. You SIGN using a private key, which guarantees that it came from you (or AN Other holder of the key which should be kept safely in a secure keystore).
You ENCRYPT with a public key, so that only the intended recipient can decrypt (again or AN Other holder of the private key of the asymmetric pair).
This is how SSL works roughly under the hood (skipping the encrypted AES symmetric key part).
Example of whole doc (fields truncated for security), SEE UPPERSOCRE COMMENTS PLEASE:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<SolicitudRegistro xmlns="http://www.cidge.gob.mx/SCG/Interoperabilidad"
IdMensaje="f2e140eb-2b09-44ab-8504-87b25d81914c">
<FechaEnvio>2013-04-19T02:09:08</FechaEnvio>
<Registrante EndPoint="https://200.34.175.46:443/InteropOPE
/MensajeInteroperabilidadService" Nombre="Institnologia del Agua"
NombreCorto="IMTA" URI="op.mx">
<DatosDeContacto AreaOficina="Inmatica"
CorreoElectronico="jbloc.imta.mx"
Nombre="JoChacon" Puesto="Subdireclecomunicaciones">
<Telefonos>
<Telefono Extension=" " NumeroTelefonico="7773293644"/>
</Telefonos>
</DatosDeContacto>
<CertificadoInstancia>MIIFETCCA/mgAwIBAgIUMDAwMDAwM
</Registrante>
<Reto> //THIS IS THE STRING SIGNED WITH PRIVATE KEY, is part of info
<CadenaCifrada>Ln0BAsnwrNg6IzjW7hk2c/Nxx/x //I COPY THIS IN FIRST TESTER AND FAILS
</Reto> //UNLESS I USE ENCRYPT CODE WITH PRIV KEY (RARE)
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-
c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09
/xmldsig#enveloped-signatu
<ds:KeyInfo><ds:X509Data>
<ds:X509Certificate>MIIFETCCA/mgAwIBAgIUMDAwMDAwMDAwMDAwMDAwMDI1MzMwDQY
How do I take the Base64EncodedString provided in the developer console and make it into a .pem that can be used for openssl_verify()?
Add the appropriate header and footer and break lines as described here: http://www.openssl.org/docs/apps/rsa.html
Test using OpenSSL commands.
phpseclib, a pure PHP RSA implementation, will take public keys in just about any format and verify signatures with them. eg.
<?php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey('publickey.txt');
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
echo $rsa->verify(
file_get_contents('plaintext.txt'),
file_get_contents('signature.txt')
) ? 'verified' : 'unverified';
Here's another stackoverflow question where the person was able to use phpseclib even without the appropriate headers:
https://stackoverflow.com/a/13015915/569976