I generated on server side a pair public/private keys using phpseclib like
include 'Crypt/RSA.php';
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
extract($rsa->createKey());
echo $privatekey;
echo "\n\n\n";
echo $publickey;
Now I want import on client side Public key using Java Bouncy Castle engine.
Here my Public key
-----BEGIN PUBLIC KEY-----
MIGJAoGBAJEGAmaQejDgJaCg/B5+g68arqpMpl6jZ9+p8TBzNRIq+Ygt/n3iqz+pAtltrlRnmqSD
svx0LMluw1wXezQ1pz2tTJTEhg6b69Qui0o//W5UDfle4yOyAHaOs8MD5nubJjXFU8vGiEdektET
jgKqiSr5TBgZoHy+YDWpd4yTemXVAgMBAAE=
-----END PUBLIC KEY-----
But I can do it. I tried to do it several ways but I always get errors.
AsymmetricKeyParameter publicKey =
(AsymmetricKeyParameter) PublicKeyFactory.createKey(b64.decodeBuffer(key));
AsymmetricKeyParameter publicKey =
(AsymmetricKeyParameter) PublicKeyFactory.createKey(key.getBytes())
Also
PEMReader pemReader = new org.bouncycastle.openssl.PEMReader (reader);
PemObject pem = pemReader.readPemObject();
All these ways generate error.
How should I import Public key using Java Bouncy Castle engine?
I found solution
key = key.replaceAll("PUBLIC KEY", "RSA PUBLIC KEY");
final Reader reader = new StringReader(key);
PEMReader pemReader = new PEMReader(reader);
Object obj = pemReader.readObject();
pemReader.close();
BCRSAPublicKey bcPublicKey = (BCRSAPublicKey) obj;
AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter) PublicKeyFactory.createKey(bcPublicKey.getEncoded());
AsymmetricBlockCipher e = new RSAEngine();
e = new org.bouncycastle.crypto.encodings.PKCS1Encoding(e);
e.init(true, publicKey);
byte[] messageBytes = inputData.getBytes();
encryptedData = e.processBlock(messageBytes, 0, messageBytes.length);
Now i can encrypt on Java side and decrypt on Server (PHP) side
Might be worthwhile to try with the latest Git version of phpseclib. Quoting from a semi-recent commit:
https://github.com/phpseclib/phpseclib/commit/2f8d1055ea5a6b06cd7a40eb85661ba688a31320
Quoting from it, the commit "[makes] Crypt_RSA's public keys compatible with OpenSSL".
Related
I'm basically trying to create a signature using "openssl" (PHP) and verify that using "criptografy" (PYTHON). And I'm always getting the error "InvalidSignature", which means I'm doing something wrong. I know that they are different languages and libraries but once I'm using the same algorithm for both I expected to get a valid signature. I appreciate any help.
PHP: creating public key and signature without problems
$config = array(
"digest_alg" => "sha256",
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
$res = openssl_pkey_new($config);
openssl_pkey_export($res, $privKey);
$pubKey = openssl_pkey_get_details($res);
$pubKey = $pubKey["key"];
error_log($pubKey);
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtXyDj9vqTGkR/ITYZ6+e
xXATzalUOdBcqee+qcb5NJ6Z5DgNdYi9lWz/4YfitYKp0EFwPzbem1zBzKbuQSko
y7zRjpmyGbw8Q6wexO4SyA44jxs75JNXMA2x22dkNKajRE5kXngBIF1ixpzCxvvc
kfyewM8C8y2iAy5j02YZYw9ysrQWJegamq6sidnMCJBtokOnPQaNJwbDQTqwrSRS
8IDy7BtBHB7F/bBwLArwxG7aLFjJ9vf2F7HpmZ3VvJa69OhY0pZMSqePQpJBIQ+2
ztIywpKkOukJz22Brqoe0ygMQzVrcYoj2MZ8CSiKUCJL6Wm9ErFXvBh/XqPWjX1t
nWdnF6qSD/2itIw18+PzCWYaoeu6w064dcbRrUQ4UOYxp69IFtrv5OHAsuWPJ27q
2IUCZ9DWWphlwhz+lI4rAb6whd2R8Sb7vEhvSz4Kd5kIjel9Dt8mJ+jGyhTjqIhP
7amgcOQLKZJfmeltYI+F0U8oJcOPhxtlxfFB1MIxPDHvCcdR93LJGgU6NboTwcpx
hnoI86xKblJmnxMxuQbUfPRU8vAuiizKVrpQS8z2k58mlxa9+hykjMcqpAvQ6STM
vLswdj0j9aqyv6I94z2Q2Lgcuoh7xSJcLhKN9QGaarUqjAY/zoZPiDnCxXlnVrav
BMyQZ9PqbsaHsd7pVVpuW8MCAwEAAQ==
-----END PUBLIC KEY-----
$data = 'oi';
$pkeyid = openssl_pkey_get_private($privKey);
openssl_sign($data, $signature, $pkeyid);
error_log(base64_encode($signature));
MoUZzfaHCwME2mmxJnMBQWgo+lTLK2QfpfD+5IEnSwi0wmRtgZgFl7IGsWYlTO+zTQCcB/aKg91CMClmb5P2jEtOkY3ie2pNflEIsc3aGqfWoVpHJUMoft+4hytzgMszqwgbJdo6eac7zIxlrzmeb4jb58APtc4aaLLQru2Gga9oPRyCqbXrD0TXnQ6GzjDNGwi6wP30NU9KaHWaGfeq1WSqsAIaIbi2oG4MeWJYX6SOB/CX1Jg8SJYzWQahpJeybE4Z8fR6ncuvfbjp9aet1aKsPB/DFPQ5VFaAS4oItVb0Ha4wkQ8YgJu5fEUK7X9KzuOwidja5RLZtwDPa0bBSveCw9D8FUsguilaoIu7ueDPTRueK4bQkYstOc2OvVlzInukyOzFXVgZsiV23FTHrQ5FqHd9nCUbfuhgrMqHRYQmRFUgDaeyY+B5ve3bQ4lT3qm282ngKi8AZ+clB0VeaFiQxpF8SCCRMu0bX3dsQMaXRUeSVDipf3ZMABl9xXbzEjqXoJzuvC6tCnEv8K3HzKbjz2S1876eGy7opIixaUMwDu9QOiPoSiFUY/xCsX8D1Im8Mv4eyzWNV3FL1U/GS3LZzkq/NuJdB7wjCib3ieHaYX/bvTpmAp/oO0+vbI2pzq9w71cpqQpsNsOZSGkgY2q1sO6u0I5jCFScdJVbJl0=
PYTHON: InvalidSignature error
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives.serialization import load_pem_public_key
signature= 'MoUZzfaHCwME2mmxJnMBQWgo+lTLK2QfpfD+5IEnSwi0wmRtgZgFl7IGsWYlTO+zTQCcB/aKg91CMClmb5P2jEtOkY3ie2pNflEIsc3aGqfWoVpHJUMoft+4hytzgMszqwgbJdo6eac7zIxlrzmeb4jb58APtc4aaLLQru2Gga9oPRyCqbXrD0TXnQ6GzjDNGwi6wP30NU9KaHWaGfeq1WSqsAIaIbi2oG4MeWJYX6SOB/CX1Jg8SJYzWQahpJeybE4Z8fR6ncuvfbjp9aet1aKsPB/DFPQ5VFaAS4oItVb0Ha4wkQ8YgJu5fEUK7X9KzuOwidja5RLZtwDPa0bBSveCw9D8FUsguilaoIu7ueDPTRueK4bQkYstOc2OvVlzInukyOzFXVgZsiV23FTHrQ5FqHd9nCUbfuhgrMqHRYQmRFUgDaeyY+B5ve3bQ4lT3qm282ngKi8AZ+clB0VeaFiQxpF8SCCRMu0bX3dsQMaXRUeSVDipf3ZMABl9xXbzEjqXoJzuvC6tCnEv8K3HzKbjz2S1876eGy7opIixaUMwDu9QOiPoSiFUY/xCsX8D1Im8Mv4eyzWNV3FL1U/GS3LZzkq/NuJdB7wjCib3ieHaYX/bvTpmAp/oO0+vbI2pzq9w71cpqQpsNsOZSGkgY2q1sO6u0I5jCFScdJVbJl0='
decoded = base64.b64decode(signature)
plaintextMessage="oi"
# /tmp/public contains the public key
alicePubKey = load_pem_public_key(open('/tmp/public', 'rb').read(),default_backend())
ciphertext = alicePubKey.verify(
decoded,
plaintextMessage,
padding.PSS(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256()
)
It's pretty clear that your PHP code:
Signed using RSA with SHA1, not SHA256
Used PKCS#1 version 1.5 padding, not PSS
It looks like you just copy&pasted your code from the cryptography package documentation.
Thus the following python snippet should verify:
ciphertext = alicePubKey.verify(
decoded,
b'oi',
padding.PKCS1v15(),
hashes.SHA1()
)
PSS and SHA-256 are better choices, so you should investigate modifying your PHP code to use those instead of modifying your python code.
As James said, the function openssl_sign was using SHA1 by default, what could be solved with the parameter OPENSSL_ALGO_SHA256.
So I did these changes:
PHP
openssl_sign($data, $signature, $pkeyid, OPENSSL_ALGO_SHA256);
Python
ciphertext = alicePubKey.verify(
decoded,
plaintextMessage,
padding.PKCS1v15(),
hashes.SHA256()
)
To authenticate a google oauth2 token, I'm generating a public key for kid "b863b534069bfc0207197bcf831320d1cdc2cee2" from the modulus (n) and exponent (e) from:
https://www.googleapis.com/oauth2/v3/certs
{
"alg": "RS256",
"n": "8h6tCwOYDPtzyFivNaIguQVc_yBO5eOA2kUu_MAN8s4VWn8tIfCbVvcAz3yNwQuGpkdNg8gTk9QmReXl4SE8m7aCa0iRcBBWLyPUt6TM1RkYE51rOGYhjWxo9V8ogMXSBclE6x0t8qFY00l5O34gjYzXtyvyBX7Sw5mGuNLVAzq2nsCTnIsHrIaBy70IKU3FLsJ_PRYyViXP1nfo9872q3mtn7bJ7_hqss0vDgUiNAqPztVIsrZinFbaTgXjLhBlUjFWgJx_g4p76CJkjQ3-puZRU5A0D04KvqQ_0AWcN1Q8pvwQ9V4uGHm6Bop9nUhIcZJYjjlTM9Pkx_JnVOfekw",
"use": "sig",
"kid": "b863b534069bfc0207197bcf831320d1cdc2cee2",
"e": "AQAB",
"kty": "RSA"
}
I then create the RSA 256 public key in php with:
$rsa = new Crypt_RSA();
$modulus = new Math_BigInteger(base64url_decode($cert["n"]), 256);
$exponent = new Math_BigInteger(base64url_decode($cert["e"]), 256);
$rsa->loadKey(array('n' => $modulus, 'e' => $exponent));
$rsa->setPublicKey();
$public_key = $rsa->getPublicKey();
This generates public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuopEuQyOKMsQq90I/5on
1nNBPc7stMvsN1HC+Pgyu8nJ1qWwaAAqIv4edo2oG/Bo3eg6p+OjG3nbFL62S6hE
aJLUVfxhW5GQuxQlsvaA2MsZuZCRyKTv8bm641wM+biGVZLiDsLRylVdpxf4aGa9
9zZw+QZMVKL4f9B4SunyTugTaCIu8LBOQesCQp/QJaUjqMDhfEvoFQXiCn6zo3rW
EWBiKxiFBizH9jSfWimJecFhn0Vlv/Vs7pRb0X2y66VS3gTvR6/A3ooNz3tYAJPM
GoE8fAiEghYXXHjmWmgdRx9Qt9sa/ACwv7yx0Th27fw+rrsMSrUyaqRpn/fjIMTu
sQIDAQAB
-----END PUBLIC KEY-----
This same method worked with dozens of other RS256 kid's from google, but the public key does not work with this particular kid.
I am verifying the signature with:
openssl_verify($payload_to_verify, $safe_signature, $public_key, OPENSSL_ALGO_SHA256);
Which responds with '0' for failure.
Edit #2: Found google's version of the public key at:
https://www.googleapis.com/oauth2/v1/certs
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8h6tCwOYDPtzyFivNaIg
uQVc/yBO5eOA2kUu/MAN8s4VWn8tIfCbVvcAz3yNwQuGpkdNg8gTk9QmReXl4SE8
m7aCa0iRcBBWLyPUt6TM1RkYE51rOGYhjWxo9V8ogMXSBclE6x0t8qFY00l5O34g
jYzXtyvyBX7Sw5mGuNLVAzq2nsCTnIsHrIaBy70IKU3FLsJ/PRYyViXP1nfo9872
q3mtn7bJ7/hqss0vDgUiNAqPztVIsrZinFbaTgXjLhBlUjFWgJx/g4p76CJkjQ3+
puZRU5A0D04KvqQ/0AWcN1Q8pvwQ9V4uGHm6Bop9nUhIcZJYjjlTM9Pkx/JnVOfe
kwIDAQAB
-----END PUBLIC KEY-----
My generated public key is different. Why my generated public key is wrong?
I confirm that the key is not correctly converted by the RSA Crypt package.
Wrong key
Good key
I tried to convert that key with another application (web-token/jwt-app) and I got the same result as the one provided by Google.
curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar
curl -OL https://github.com/web-token/jwt-app/raw/gh-pages/jose.phar.pubkey
chmod +x jose.phar
./jose.phar key:convert:pkcs1 '{"alg": "RS256","n": "8h6tCwOYDPtzyFivNaIguQVc_yBO5eOA2kUu_MAN8s4VWn8tIfCbVvcAz3yNwQuGpkdNg8gTk9QmReXl4SE8m7aCa0iRcBBWLyPUt6TM1RkYE51rOGYhjWxo9V8ogMXSBclE6x0t8qFY00l5O34gjYzXtyvyBX7Sw5mGuNLVAzq2nsCTnIsHrIaBy70IKU3FLsJ_PRYyViXP1nfo9872q3mtn7bJ7_hqss0vDgUiNAqPztVIsrZinFbaTgXjLhBlUjFWgJx_g4p76CJkjQ3-puZRU5A0D04KvqQ_0AWcN1Q8pvwQ9V4uGHm6Bop9nUhIcZJYjjlTM9Pkx_JnVOfekw","use": "sig","kid": "b863b534069bfc0207197bcf831320d1cdc2cee2","e": "AQAB","kty": "RSA"}'
Best would be to warn phpseclib/phpseclib about that issue.
I trying to encrypt and decrypt a simple string with PHPseclib, but when I try to decrypt it, the result is null or empty. Could any one help me please?
here are my functions
public function myEncrypt($conteudoArquivo, $private_key) {
$rsa = new Crypt_RSA();
$rsa->loadKey($private_key);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$cipherText = base64_encode($rsa->encrypt($conteudoArquivo));
return $cipherText;
}
public function myDecrypt($cipherText, $public_key) {
$rsa = new Crypt_RSA();
$rsa->loadKey($public_key);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$plainText = base64_decode($rsa->decrypt($cipherText));
return $plainText;
}
and the keys values are:
$private_key = "-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQCRwpD+gV0skm+9SHPWFXAHkWV3r37I3N4fom45Z0imih3fDk9E
Q6PuFb56Vfo8IWiNUoP6Gco/eDHcInzO1SSEn07reh86Aosnnj7m/RMg1N5k7A5C
NH26YlATqgJe4DX8SdS/oKLit7xTo3aR+Wg3kZOQQmE5MyRq6TVDywhNyQIDAQAB
AoGAVFk4mN75sUJogSu9RMURGIAOLM2U293ceIgBqxxW0XEZyiu4uTM/WRaiLJ82
eLeIjkeS8hccj9AZYl9exD5Zq7oGTH8HDQAcmZ6p0h3faI1/sR3gnqEB28GZJyd7
MsFRlRiOEdpJAki33m/6UsJ7hRHC6X/w2K4wofzmmHopOIECQQDCAMW7gc4Gmyb1
scP8OwA75QZWgeLo36d7ynUzjqoN682ckSbIxbT6z4eYhEKE/It0gt07WTNmrL0s
vv4DH15xAkEAwFcTC1/FufpDvxt+EKnANmuH5ekri1zcsY3UMJJnjH9e7szfVTH6
JfhTRRSfi/oOe1aRinfcp/zMJhkf36VA2QJAO8K5JlWJ/Yb1rWGhGaWjINAf7637
E/kxQnTPPZ6Iy9kDcWNVKyub4FblUhoL06Nn4fAd7hZAOzSi4ZHD9XpIQQJAc1MC
QTygcp1jB3A1i0oszLR23FyNVldMoE044BK4cZ5hTm+arRt1MFUPoIj4DNbW3g8O
3uZ1cGf8BA/mc5NDKQJAexm+LcJ3DOTaenQwHw77bfXhbvvCtcvAlYOIawkyDlTx
AkBweqy8BEJbsVRSiBv7k6Hh+T+u1ZWaKDm/ZZCMkA==
-----END RSA PRIVATE KEY----- ";
$public_key = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCU+1bLfPmcY7qrF/dTbAtuJlv4R/FVc1WEH9HK
U0jQjX/n/db9vz/x0i3te/bKLNEcwUhBu+PWPnOt/qVURG9BUT6RsCRFUn0CyGiUKoy45o9K/mJA
HmbrNtrUB6ckrYLF75Y50nUNsBVHUDw8yQymmiOBT1gc/KM5s1xTz44LMwIDAQAB
-----END PUBLIC KEY-----";
You need to encrypt with the public key and decrypt with the private key. That is how asymmetric encryption works. In RSA "encrypting" with the private key is actually signing and "decrypting" with the public key is verification of the signature.
The other thing is that you don't reverse the Base64 encoding properly. So you need to first decode and then decrypt:
$plainText = $rsa->decrypt(base64_decode($cipherText));
The keys you presented cannot be used with this code, because they have a different modulus. You can generate the keys this way:
$rsa = new Crypt_RSA();
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$keys = $rsa->createKey(2048);
$private_key = $keys["privatekey"];
$public_key = $keys["publickey"];
I have a server which has its own cert and a client who is trying to have the server generate a new cert for him (and sign it ofc). The client has given the server his public key and the server is supposed to create a cert and sign it. In PHP, how do I have the server create the cert with only the clients public key? openssl_csr_new seems to want the private key.
Thanks!
you can do this with the latest SVN of phpseclib, a pure PHP X.509 parser. eg.
<?php
include('File/X509.php');
include('Crypt/RSA.php');
$privKey = new Crypt_RSA();
$privKey->loadKey('-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----');
$pubKey = new Crypt_RSA();
$pubKey->loadKey('-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----');
$pubKey->setPublicKey();
$subject = new File_X509();
$subject->setPublicKey($pubKey);
$subject->setDNProp('id-at-organizationName', 'whatever');
$issuer = new File_X509();
// load the DN from an existing X.509 cert
$issuer->loadX509('-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----');
$issuer->setPrivateKey($privKey);
$x509 = new File_X509();
$x509->setStartDate('-1 month');
$x509->setEndDate('+1 year');
$x509->setSerialNumber(1);
$result = $x509->sign($issuer, $subject);
echo $x509->saveX509($result);
You'll need your private key and the subjects public key. In this example, I'm getting the issuing DN from a X.509 previously signed by with your private key but you might want to call setDN() instead?
I'm trying to use soap to call a webservice but I keep getting the following error "Warning: SoapClient::SoapClient(): Unable to set private key file".
I'm assuming that the error comes due to the fact the the .cer file I am using only includes public key and no private key. But i'm not sure of another way to use the .cer file. If i don't use the .cer file i can connect just fine and I am able to call and receive results when i use the __getFunctions() method. However, when i try to use other methods i need to be authorized and that leads to the problem. Below is the simple code i am trying to use. Please let me know if more information is required.
ini_set('display_errors',1);
error_reporting(E_ALL);
ini_set('soap.wsdl_cache_enabled', 0);
$username = 'user';
$password = 'pass';
$ns = 'GatewayEDI.WebServices';
$auth = array();
$auth['User'] = new SOAPVar($username, XSD_STRING, null, null, null, $ns);
$auth['Password'] = new SOAPVar($password, XSD_STRING, null, null, null, $ns);
$headerBody = new SOAPVar($auth, SOAP_ENC_OBJECT);
$header = new SOAPHeader($ns, 'AuthSOAPHeader', $headerBody);
$client=new SoapClient('https://url/Service.asmx?WSDL',
array(
'local_cert' => 'file.cer'
));
$client->__setSOAPHeaders(array($header));
$param = array(
'X12Input'=>"testing",
"GediPayerID"=>"52",
"ResponseDataType"=>"Xml"
);
//this leads to private key error
echo $result = $client->DoInquiryByX12Data($param,$header);
I believe your .pem/.cer file should have your private key in it:
-----BEGIN RSA PRIVATE KEY-----
# base64 encoded key
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
# base64 encoded cert
-----END CERTIFICATE-----
If your private key's first line has a directive similar to "Proc-Type: 4,ENCRYPTED" you'll need to include the "passphrase" option when constructing your SoapClient(). You can also strip the passphrase requirement with OpenSSL, my syntax is a bit rusty so you may want to double check if you try it:
openssl rsa -in /path/to/private.key -out /path/to/private.key
"private.key" should be just the private key in this context (you can add it into the .cer/.pem file after the passphrase has been removed.