I have a task: generate private/public key pairs for banks.
User data like State, City, Company, Name, Email, and some other data should be included. How I can generate those keys with PHP or Shell?
UPDATE 1
I need a private key and cert for a Bank.
PHP offers interface to OpenSSL functions. You need to generate a certificate (the keypair can't include user data), and this is a bit more complicated than generating a keypair. You can generate self-signed certificates (in such certificates Issuer and Subject fields are identical), which is probably what you need.
If you need a CA-signed certificate, than you need to generate a certificate signing request (CSR) and a private key, then send a CSR to the certificate authority that will sign it and send you back the certificate (remember that private key remains on your side, so you need to save it).
Also, google search revealed a good tutorial for you.
Private&public key pairs do not contain identifying information like name and address. Certificates do (and also certificate requests, since those are meant to be turned into certificates).
The openssl command can generate both key pairs and certificate requests, and it can sign certificate requests to produce certificates as well. First, figure out exactly which kinds of objects you need, whether you need to use a central CA to sign certificates, etc... Then you should be able to easily find information on what arguments you need to pass to generate each type of object.
Here is a shell script that I use for generating certificates using openssl.
This is just a test script I wrote, so you might want to set some additional security. Having passwords written somewhere is not a good idea. You might want to run it thoroughly in your test environment or tweak as you like.
Here is what it requires:
A proper CA certificate, its own private key etc, I assume you already have it.
(I generated a self signed one and I would have it in the demoCA folder. Or you can generate using /usr/share/ssl/misc/CA.sh -newca)
Openssl
Text file (cert.input) having Required data {Country, State, City, Company, Org, Common name etc everything in a newline}
Text file (caconfirm.input) for confirmation input (for saying 'yes')
Text file for passwords. (pass.input) I will use MyPassword for all my cert related passwords.
There is only 1 only requirement, the ca private key file should not be password protected. If it is, you can run:
openssl rsa -in demoCA/private/cakey.pem -out
demoCA/private/cakey_nopass.pem
Lets say I stored filename in CERT_FILE_NAME.
In the end you will get a folder with the name you provided (filename) containing:
cert in pem format (filename.pem), cert in crt format (filename.crt), cert in der (binary format, (filename.der)), cert in password protected .p12 format (filename.p12), cert private key password protected (filename_Password.key) and cert private key non password protected. (filename_NoPassword.key)
#!/bin/sh
CERT_FILE_NAME=$1
#Lets generate a typical private key
openssl genrsa -passout pass:MyPassword -des3 -out ${CERT_FILE_NAME}_Password.key 1024
#Now, generate a cert signing request, and recieve the data from cert.input
openssl req -passin pass:MyPassword -new -key ${CERT_FILE_NAME}_Password.key -out ${CERT_FILE_NAME}.csr < cert.input
#Sign the csr with the private key of our CA, and recieve the confirmation from caconfirm.input
openssl ca -in ${CERT_FILE_NAME}.csr -cert demoCA/cacert.pem -keyfile demoCA/private/cakey_nopass.pem -out ${CERT_FILE_NAME}.crt -days 3825 < caconfirm.input
#Export my new cert to a password protected p12 file
openssl pkcs12 -passin pass:MyPassword -passout pass:MyPassword -export -in ${CERT_FILE_NAME}.crt -inkey ${CERT_FILE_NAME}_Password.key -out ${CERT_FILE_NAME}.p12
#(Optional) Export my private key to a plain text private key
openssl rsa -passin file:pass.input -in ${CERT_FILE_NAME}_Password.key -out ${CERT_FILE_NAME}_NOPassword.key
# Output the crt into strict pem format having BEGIN/END lines
grep -A 1000 BEGIN ${CERT_FILE_NAME}.crt > ${CERT_FILE_NAME}.pem
# Convert the pem into der (binary) format
openssl x509 -outform der -in ${CERT_FILE_NAME}.pem -out ${CERT_FILE_NAME}.der
# Create a directory
mkdir ${CERT_FILE_NAME}
# Move all my cert files in the folder
mv ${CERT_FILE_NAME}*.* ${CERT_FILE_NAME}
Now the contents of the text files that we used (every item in newline):
cert.input:
Country
State
CityName
CompanyName
OrgName
CommonName
pass.input:
MyPassword
caconfirm.input:
y
y
Here are PHP codes to generate PRIVATE and PUBLIC KEYS:
===method A) ====
<?php
// generate 2048-bit RSA key
$pk_Generate = openssl_pkey_new(array(
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA
));
// getting private-key
openssl_pkey_export($pk_Generate, $pk_Generate_Private); // we pass 2nd argument as reference
// getting public-key
$pk_Generate_Details = openssl_pkey_get_details($pk_Generate);
$pk_Generate_Public = $pk_Generate_Details['key'];
// free resources
openssl_pkey_free($pk_Generate);
// getting/importing public-key using PEM format
// $pk_Generate_Private now gets into PEM format...
// this is an alternative method compared to above used "public retrieval"
$pk_Import = openssl_pkey_get_private($pk_Generate_Private); // importing
$pk_Import_Details = openssl_pkey_get_details($pk_Import); // same method to get public key, like in previous
$pk_Import_Public = $pk_Import_Details['key'];
openssl_pkey_free($pk_Import); // cleanup
// see output
echo "\r\n\r\n".$pk_Generate_Private."\r\n\r\n".$pk_Generate_Public."\r\n\r\n".$pk_Import_Public ."\r\n\r\n".'Public keys are '.(strcmp($pk_Generate_Public,$pk_Import_Public)?'different':'identical').'.';
?>
====method b) =======
include this [phpsec open-source library][1](with [examples][2]), and then execute:
<?php
include('File/X509.php');
include('Crypt/RSA.php');
// creating private key / x.509 cert for stunnel / website
$priv_Key = new Crypt_RSA();
extract($priv_Key->createKey());
$priv_Key->loadKey($privatekey);
$pub_Key = new Crypt_RSA();
$pub_Key->loadKey($publickey);
$pub_Key->setPublicKey();
$object = new File_X509();
$object->setDNProp('id-at-organizationName', 'phpseclib demo cert');
//$object->removeDNProp('id-at-organizationName');
$object->setPublicKey($pub_Key);
$cert_Issuer = new File_X509();
$cert_Issuer->setPrivateKey($priv_Key);
$cert_Issuer->setDN($object->getDN());
$x_509 = new File_X509();
//$x_509->setStartDate('-1 month'); // default: now
//$x_509->setEndDate('+1 year'); // default: +1 year from now
$result = $x_509->sign($cert_Issuer, $object);
echo "the stunnel.pem contents are as follows:\r\n\r\n".$priv_Key->getPrivateKey()."\r\n\r\n".$x_509->saveX509($result);
?>
What type of private/public are you need? Someone who said you to do this must provide algorythm or type of keys. There is a huge variety of private/public key types, not only RSA.
Related
im working with a web application for some client who needs sign some documents other company give him 2 files
- file1.key
- file2.cer
if im understand thus files are encrypted with a password so the user needs his password
the company give me some example in php and say i need get the private key with thus commands
-convert *.key a *.pem:
openssl pkcs8 -inform DER -in llave.key -out llave.key.pem -passin pass:contrasenia
-Conver *.cer to *.pem:
openssl pkcs8 -inform DER -in llave.key -out llave.key.pem -passin pass:contrasenia
but for the end user i can't request him "run this command" the end user need to indicate his password
the company give me some code in php
$private_file="c:/firma/llave_carlos.key.pem";
$public_file="c:/firma/cert_carlos.cer.pem";
$private_key = openssl_get_privatekey(file_get_contents($private_file));
exito = openssl_sign($cadena_original,$Firma,$private_key, OPENSSL_ALGO_SHA256);
openssl_free_key($private_key);
$public_key = openssl_pkey_get_public(file_get_contents($public_file));
$PubData = openssl_pkey_get_details($public_key);
$result = openssl_verify($cadena_original, $Firma, $public_key, "sha256WithRSAEncryption");
but im working with Nodejs searching in google i found the npm package
node-openssl-cert
but until now i can't found a similar method to php "openssl_get_privatekey"
im thinks the solution is with the crypto module from node
const crypto = require('crypto');
const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {
namedCurve: 'sect239k1'
});
const sign = crypto.createSign('SHA256');
sign.write('some data to sign');
sign.end();
const signature = sign.sign(privateKey, 'hex');
const verify = crypto.createVerify('SHA256');
verify.write('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature));
but my problem its this line
const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {
namedCurve: 'sect239k1'
});
the "privatekey" and "publickey" should come from the files
thanks for the support and sorry for my english idont speak
Let me guess: you're building a CFDI 3.3 and trying to sign the original string to get the digital sign. I ran into the same problem, searched a lot, downloaded lots of Node libraries, and your progress really helped me, so I could integrate a solution using only crypto library, here it is:
Firstful, be sure that the original string is ok (that was my first issue, the original string was wrong), you can use this tool: https://solucionfactible.com/sfic/capitulos/timbrado/cadena_original.jsp#herramientaCO
Once you checked the original string you need to sign the original string with SHA256, here you can find a function I coded for that:
const privateKey = "./EKU9003173C9.key.pem" //Note that I converted the .key to .key.pem
const firmarCadena = cadena => {
let prvKey = fs.readFileSync( privateKey )
var signerObject = crypto.createSign("SHA256")
signerObject.update( cadena )
let signature = signerObject.sign( prvKey, "base64" )
return signature
}
You can also check this tool, so you can compare between the values you are getting and the ones which must be. It generates the original string and digital sign with a .xml, .key and private key password: https://solucionfactible.com/sfic/capitulos/timbrado/sello.jsp#herramientaSello
Finally, you can verify your digital sign here in section "Verificador de sello digital (verificador de firma)": https://solucionfactible.com/sfic/capitulos/timbrado/sello.jsp#herramientaSello
I need to say that I'm using Node 10.x. It already has a Crypto built-in library.
I am encrypting data in the browser with JS, and then attempting to decrypt it on the backend with PHP.
In JS, I am using npm-rsa which was built using browserify.
In PHP, I am using phpseclib
My JS:
key = new rsa({
environment: 'browser',
encryptionScheme: 'pkcs1_oaep',
signingScheme: 'pkcs1-sha256',
b: 2048
});
cleartext = 'this is a test';
console.log(key.encrypt(cleartext, 'base64'));
console.log(this.key.exportKey('pkcs8-private-pem'));
I then take my public key and cyphertext to PHP:
$rsa = new RSA();
$rsa->load($privkey, 'pkcs8');
$cleartext = $rsa->decrypt(base64_decode($cyphertext));
When I run this, $cleartext is null. Using Xdebug, I can see that $rsa->load($pubkey, 'pkcs8'); does appear to be populating the modulus, exponents, primes, coefficients, etc fields of $rsa.
I have even tried $rsa->setHash('sha256'); before calling decrypt(), but it's my understanding that the hash method is only used for encryption and not even used in decryption.
Also, I understand that this is not secure because i'm sharing the private key. I'm just trying to verify compatibility between the two libraries.
I have tested that i can encrypt and decrypt with both libraries independently, but taking a key from the JS lib to the PHP lib I can't get to work.
EDIT:
Exported private key:
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDX7/224Phogd/B
4DGOf81GeNAkE0bAWieN9Tmq6S1Xe0iMY56J9hJ86HveCcJcJCMNAJtGVPjOObNf
HZ58CUrqyMoj6VJ8wXGVPZuwkvBQrVFg4k/h+8+b3p5Z0cb8J9m8WWpnL/hifoyt
9O7aEiSnkvHBWBgEK6qxEYVLJKUPI94HlZe4B+ScCaflUMw1/uMfp1pVGxVUAfER
f8URfCjCUY8cL+yFNQ5/CVNMQ8CTlb6HItpfS/QVBqTUD3wNf97oFZDPzbFfFCD7
M50NbCal6CfRTaMBPoWrhsUGVab/Nj81qcY7dpoYEFyLLnJ80jHR6+o1fKDSinuQ
KIYkQ9PxAgMBAAECggEBANKs7tgx/anYrDo3RaJFxjzvOgM4W1rnmpfBnRXGkdo8
CbgOqWrojXkYSWGEHABRsXDKGrQvyt9JJFu0Rh+14UXXyH/o7/WPtgVpKjDH63aK
4k6n/k/4ocDtHYl8RatWJfTBODKcdrWByjceNLrF4MUtdHiyPhwzjkFbWrTJd9Cf
3wEh0aD83JT/wg1VkRYEOdJTPeq2kCpgoukBFXYAnGj9DYL2+Kityhnx/GB0RG/8
GGpMyG8R72P6zYkXr0YfLhGLlW7+XbLT+WiPThjT9+e00uPAKMj7d0qaerKR6Asf
NGSAg83eWG9ZKT1GXtn8mOP6p+15DcVLBbFXvPAA/cECgYEA7IYRWGNhEJ9l+XJH
DYQVndS9KN8+zMx5OM/wy47v9FiiwGdAnwwHFAX3GyZe6eJ2sAX73tPm56QadRPK
HGZFNnYx3BBdcl7WKT13DrmgpsH9CO6ish+2TwHfYDqOKs5EvT3yHJIG4T/Z50JM
bUKSdrndV6LtOT2gplLxjUwastUCgYEA6bf23y5p8t9Unn55bIYKdFWj/5kLJdI1
d/MBP3eZ405veVzZqvhs04JscElx1M0U1mTx7g96IWnBTuqsgkU9TLlXg8N5nLM0
AWVlFlu657NwJaAc9zfmaamI4UKwNHCXDoxx+u2zJuzNOJwttPV3d7NTBWZOh+P+
DEgAzNYOEq0CgYEA42Oo/U7dnHuCMCTbhnT7yzchfE/UMlRKHoJbP3f13PXx0gPy
LnYDwA5UGLf0++oKrQOzt/AEx6IPBYu2/UKdO9S57pWVIUVno1JCSdfQSUGqoJG7
vH+cZ6ynMf5Ze3G+yCjrwOfq0VbviNNGYqxj3tylnYE3i5ZiAkUvkOYfrNkCgYBa
rZBvyN88Zt+62pEbxOm7dxq46VUE6IjsL/EVAb9IDL99U8Pz7Iy0h06xRh2PFb52
7BVdas3UtuZUSwKBTIHbCbHlomrFnFWaEQ/mW9KpYNorDvMOC7cu2aMM2sXooqJI
976lP6IZgRiiVR36rp4aaA3W83mWiaOgejMtRgORxQKBgQDTMG0Wl3FbPZ57uqmi
rE2Gwr1TiZ7LcYSEt4Z7KQV/u0Q7b9hyNXctaK2DzcEz+wA1eiAzioC9ZULOVVvW
xZzVvj2D741A6EXMITa3E/Wv1XkbotNg8RXG8lvNY/IJ+35TZGBNzJ4gD+n0lOCK
IrdIMYPrt9dcGIqqRE4wwaeLmA==
-----END PRIVATE KEY-----
cyphertext:
mbPLBF3YNmyb5AD1vk6D8K0C9AiRU3C2a2aCKXpzDXb1uUCy7KYUFB3bOoU4ZhU7RXcWr9VHZq3APxtdyqKyEqr48NqVEPeBuYsDjcgDfPBFdPXf36f1FveeCJ7cFtHIvGy9/2EHIyNyXKy/6VaoakGRwBB3V14shXdqCDIW2FfEdUcfka5X8sAroq9pKrTGbN21hwtbiAjP2MmTHDYWu1zhDmrKxdcBbP6wdBgnZodCwGhBw11uXoEAnL1/yYFFqGZeKAhzxfjdPY2irvAuQOPN3U7UDBF0zhyMNF07JbCccCsNIguX4esferShw8w1mVzAxgwHzbjDpudko1/VSg==
I saved these into two files, privatekey.pem and cypher.text. Then I base64 decoded the cyphertext:
cat cypher.text | base64 --decode > -in cypher.bin
And use openssl to decrypt this:
openssl rsautl -decrypt -inkey privatekey.pem -in cypher.bin -oaep
This gives me the cleartext! So it appears that phpseclib has an issue in decoding.
It turns out that the hash has to be set in the decryption method with BOTH:
$rsa->setMGFHash('sha1');
$rsa->setHash('sha1');
Although i had tried it with setHash(), it was not enough without also setMGFHash()
I am studying the digital certificates in PKI ( Public Key Infrastructure ). Almost all manuals / pages regarding this give following similar steps.
Get subject identity + subject public key, (AND/OR Encrypted Message hash with subject's private key) and build a certificate.
Sign certificate with CA's private key
At destination, verify certificate with CA's public key
Now I am able to find way in php (using openssl lib) to for 1st and 2nd step which can generate a certificate and sign it ( optionally generate a signature hash and sign it too ) through openssl APIs.
But Issue with third step, Their are no guide line or function call which show how to verify certificate with CA's public key.
If I am missing something ?
Example code I checking is like below
$data = 'my data';
//create new private and public key
$req_key = openssl_pkey_new(array(
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
));
$dn = array(
"countryName" => "IN",
"stateOrProvinceName" => "Delhi",
"organizationName" => "example.net",
"organizationalUnitName" => "Security",
"commonName" => "example.net"
);
$req_csr = openssl_csr_new ($dn, $req_key);
$req_cert = openssl_csr_sign($req_csr, null, $req_key, 365);
openssl_x509_export ($req_cert, $out_cert);
echo $out_cert;
BACKGROUND : I need to implement PKI based data sharing/validating for some application. It would be involve some data entity (data entity would have its on public and private key) encrypted at source side and then send to destination. Then destination decrypt and get clear data. But that whole must involve PKI that means digital signature + digital certificate implementation along with.
This task isn't pretty straightforward in PHP. In fact, PHP isn't good tool for implementing CA stuff. It supports basic PKI operations but that's all. I see three options:
X.509 is just DER encoded ASN.1 structure. You can inspect it with guiDumpASN-ng for example. There are also some ASN.1 libraries in PHP world. Basicaly, the CA signature is inside certificate. If you'll able to extract it, you can then verify digital signature on your own.
http://php.net/manual/en/function.openssl-verify.php#98282
Try to use function like openssl-x509-checkpurpose that should be able to check certificate chain.
Try to use phpseclib - look at Validate X.509 certificates -> validateSignature().
I hope it will help you a little bit.
Do not mess up with implementing certificates validation by yourself but look for an implementation of the CPV (Certification Path Validation) algorithm, which includes all the validations required to be performed in a certification chain, where certificate signature validation is only one of them.
Now, I've been looking for a PHP API to perform CPV but I found none so I resorted to execute the following external OpenSSL command from PHP:
exec("openssl verify -CAfile trusted_root_cas.pem -untrusted intermediate_cas.pem endentity_cert.pem", $output, $return_var);
if ($return_var == 0) {
echo "Certification path validation completed successfully";
} else {
echo "Certification path validation completed with errors";
}
Where all trusted_root_cas.pem, intermediate_cas.pem and endentity_cert.pem could be temporal files created just for the execution of the previous command.
Now, if you want to know what openssl verify does, execute man verify:
The verify command verifies certificate chains.
I'm trying to send S/Mime signed and encrypted emails.
I have this code:
// Sign the message first
openssl_pkcs7_sign("inc/admin/email/body.txt","inc/admin/email/body/enc.txt",
"signing_cert.pem",array("private_key.pem",
"test"),array());
// Get the public key certificate.
$pubkey = file_get_contents("cert.pem");
//encrypt the message, now put in the headers.
openssl_pkcs7_encrypt("signed.txt", "enc.txt", $pubkey,$headers,0,1);
My question is which cert is which? If I use the certs I have (is this ok) the files I have are:
.key
.csr
.crt
and the public .pem.
Which is which?
Generally speaking PEM can be private key, public key and also certificate.
It gepends how did you create/gain certificate.
But in your case, I expect following:
CSR = Certificate signing request, useless for you now
CRT = Certificate
KEY = Private key
PEM = Public key/Certificate
First of all, certificates are not "SSL certificates". This is a misleading term that leads you to wrong use.
Each X.509 certificate has intended use (as defined in Key Usage and Extended Key Usage fields). Certificates issued for securing SSL servers can not be used for S/MIME and vice versa (unless you build some custom infrastructure which doesn't care about Key Usage).
Now, you sign using your certificate and private key, however you encrypt using recipient's certificate (which includes a public key). If you only have one CRT/KEY file pair, it's probably your certificate and your private key accordingly and you can use them for signing. But unless you encrypt the message for yourself (i.e. you are recipient of the encrypted message) encryption using your certificate makes no sense.
I have a small string of some data (less than 1kb) that I would like to have user agents pass to other sites when they are sent from my site. In order for the other sites to verify that I was the one that created the string I though of two options.
The server pings me back to confirm (like paypal, openid, etc..)
I use public/private keys to prove I sent the message (like PGP, DKIM, etc..)
I don't want to setup HMAC because that would mean I have to use custom keys for each site which would be a pain.
Out of those two choices it seems that #2 would save on bandwidth which makes it seem like a better choice.
So how can you setup public/private key cryptography using PHP and are there any downsides?
Creating a private and public key pair using the PHP Openssl functions:
// Configuration settings for the key
$config = array(
"digest_alg" => "sha512",
"private_key_bits" => 4096,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
// Create the private and public key
$res = openssl_pkey_new($config);
// Extract the private key into $private_key
openssl_pkey_export($res, $private_key);
// Extract the public key into $public_key
$public_key = openssl_pkey_get_details($res);
$public_key = $public_key["key"];
You can then encrypt and decrypt using the private and public keys like this:
// Something to encrypt
$text = 'This is the text to encrypt';
echo "This is the original text: $text\n\n";
// Encrypt using the public key
openssl_public_encrypt($text, $encrypted, $public_key);
$encrypted_hex = bin2hex($encrypted);
echo "This is the encrypted text: $encrypted_hex\n\n";
// Decrypt the data using the private key
openssl_private_decrypt($encrypted, $decrypted, $private_key);
echo "This is the decrypted text: $decrypted\n\n";
I would create S/MIME public/private keypairs using OpenSSL and then use the OpenSSL command to do the encryption & decryption. I believe that this is superior to using PGP because openssl is included with most linux operating systems and PGP isn't. OpenSSL is also standards-based and generally easier to work with, once you have the commands down.
I recommended against a "pure-PHP" solution (by pure-PHP I mean doing the crypto in PHP, rather than using PHP to call an existing library or a separate executable). You don't want to do bulk crypto in PHP. Too slow. And you want to use OpenSSL, because it's high performance and the security is well understood.
Here's the magic.
To make an X.509 key:
$subj="/C=US/ST=California/L=Remote/O=Country Govt./OU=My Dept/CN=Mr. Agent/emailAddress=agent#investiations.com"
openssl req -x509 -newkey rsa:1024 -keyout mycert.key -out mycert.pem -nodes -subj $subj
That puts the private key in mycert.key and the public key in mycert.pem. The private key is not password protected.
Now, to sign a message with S/MIME:
openssl smime -sign -signer mycert.pem -inkey mycert.key <input >output
To encrypt a message with S/MIME:
openssl smime -encrypt -recip yourcert.pem <input >output
To decrypt a message with S/MIME:
openssl smime -decrypt -inkey mycert.key -certfile mycert.pem <input >output
I also have some demos on using OpenSSL from the C language bindings, but not from PHP.
Rule 1: Don't implement it yourself, use a library.
Which library? Here are my recommended PHP public-key cryptography libraries:
Halite, depends on libsodium (but emphasizes simplicity and ease-of-use in addition to security).
libsodium, from PECL
EasyRSA, which implements secure public-key encryption and public-key signatures using RSA in the most secure modes (NOT PKCS1v1.5, ever!)
phpseclib, which EasyRSA piggybacks off of.
In general, you'll want libsodium if security is your goal. Whether or not you use Halite is a matter of taste.
PGP is a good option - it is implemented properly and completely (i.e. you have little room for security mistakes with PGP). I think this SO question will help you with interfacing with GnuPG. The question is whether and how the other sites will verify your signature. You need to either conform to their verification mechanism requirements or provide your own module that those sites will use for verification.
Also it's possible that you can use OAuth or OpenID to identify users on those other sites, but I am not an expert in these technologies.