I'm having serious problems with what I was hoping would be a simple call to PHP's
openssl_public_encrypt();
Sadly, I get an invalid public key warning.
My goal is to simply RSA encrypt a users password using public key provided by their API which currently looks like this:
+Tir6+unMOaQ5tHqjjjwnlAMhccnCSMFEi3a0mhIxbW+O/GukjomGyzckQT2h0Ys70JezHbNq5YS3sYkNF29kCkz4HuNfy9eEjE/clA9/zyfT8ZcbnusLcLz2xNgbTp62fQdzBnReI5+dpj/N24krYvHaYIr8ACxDqBv2TR3E9M=AQAB
Apparently a working implementation using the same service I'm trying to use found a solution with these steps:
Extract the modulus and exponent from the public key
Re-constructs the key in the MS PUBLICKEYBLOB format
Call OpenSSL to convert the key to PEM format
Load the PEM public key from the converted file
Encrypts the password (after converting to UTF-16LE)
However not only that I dont know if its possible to do in PHP , I think there Must be an easier way!
One post I saw hinted that the exponent may come after the last = sign (so AQAB) but I don't know if this is reliable.
Something like this do what you want?:
<?php
$key = '+Tir6+unMOaQ5tHqjjjwnlAMhccnCSMFEi3a0mhIxbW+O/GukjomGyzckQT2h0Ys70JezHbNq5YS3sYkNF29kCkz4HuNfy9eEjE/clA9/zyfT8ZcbnusLcLz2xNgbTp62fQdzBnReI5+dpj/N24krYvHaYIr8ACxDqBv2TR3E9M=AQAB';
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey(array(
'e' => new Math_BigInteger(65537),
'n' => new Math_BigInteger(substr($key, 0, -4), -256)
));
$ciphertext = $rsa->encrypt('password');
echo bin2hex($ciphertext);
?>
This example uses phpseclib, a pure PHP RSA implementation. Although phpseclib does not support the format of the key you posted it does support raw public keys and as such converting to PKCS1 style keys is unnecessar. And the ciphertext's produced by phpseclib are perfectly interoperable with OpenSSL.
OK, so what you need to do:
remove the AQAB part and use the value 65537 for the public exponent
base 64 decode the modulus
create a DER encoding of the public key in PKCS#1 format (see below)
base64 encode the DER encoding using 64 character line length and DOS line endings
add PEM header & footer
use the PEM encoded string to create a public key and finally
encrypt the password
The following ASN.1 spec defines the public key in PKCS#1 format:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
Now the identifier octet or tag of SEQUENCE is 30 in hex, INTEGER is 02 and the length should be specified like this. So you get something like:
30818902818100F938ABEBEBA730E690E6D1EA8E38F09E500C85C727092305122DDAD26848C5B5BE3BF1AE923A261B2CDC9104F687462CEF425ECC76CDAB9612DEC624345DBD902933E07B8D7F2F5E12313F72503DFF3C9F4FC65C6E7BAC2DC2F3DB13606D3A7AD9F41DCC19D1788E7E7698FF376E24AD8BC769822BF000B10EA06FD9347713D30203010001
in hexadecimals. So after base 64 encoding and adding the header and footer line you should get:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQH5OKvr66cw5pDm0eqOOPCeUAyFxycJIwUSLdrSaEjFtb478a6SOiYbLNyRBPaHRizvQl7Mds2rlhLexiQ0Xb2QKTPge41/L14SMT9yUD3/PJ9Pxlxue6wt
wvPbE2BtOnrZ9B3MGdF4jn52mP83biSti8dpgivwALEOoG/ZNHcT0wIDAQAB
-----END RSA PUBLIC KEY-----
Happy coding.
Related
I generated my own RSA keys at 2048bits using a C++ library and I have all the keys in an array.
I mean N, E, D, p, q, dP, dQ, qInv.
I want to use them in php to decrypt an incoming message to my page.
The most suggested solution around is to use OpenSSL which needs a .pem file and cannot load my Keys in the plain format (including the CRT).
Here is an example of my public Modulus N
[HEX RSA - N-LEN 256]::9FD8DAD3365447AFAC07E8A1D2D729DE099C6CCFD3861F6B78EBF588C2CD6E2D2FCE8A368DF9959E94E9BF463AC12E084B69CF7591FB48D4F46A25C3370779BC57FA8D2F41E5507B6DDC767E5174426B13AB52EE06B1FAA033DE0829EEDA38C89E51823A12214E76F723D09A26A733226A4F650B362130730288A9A49B7E65195E37A78F806D3ED322DFBED83354B609CE08999397E5747389D7631CA79536915C289E27FAF8821A454CF5A238C21CA0F783E4177C387CABEE00B7335F2C1EE5B69100C14320932C3F953C11A4762C964212F09666C8DDE622CC4E3FDB79154025E30B386591DA3499AFC010680FF9648D66596F15ACABBEECE08E38D90E04E4
[HEX RSA - E-LEN 4]::00010001
The other elements of the key I keep them obviously secret.
If I try to convert them using the online conversion tools from XML to .PEM I format them in base64:
<RSAKeyValue>
<Modulus>n9ja0zZUR6+sB+ih0tcp3gmcbM/Thh9reOv1iMLNbi0vzoo2jfmVnpTpv0Y6wS4IS2nPdZH7SNT0aiXDNwd5vFf6jS9B5VB7bdx2flF0QmsTq1LuBrH6oDPeCCnu2jjInlGCOhIhTnb3I9CaJqczImpPZQs2ITBzAoippJt+ZRleN6ePgG0+0yLfvtgzVLYJzgiZk5fldHOJ12Mcp5U2kVwonif6+IIaRUz1ojjCHKD3g+QXfDh8q+4AtzNfLB7ltpEAwUMgkyw/lTwRpHYslkIS8JZmyN3mIsxOP9t5FUAl4ws4ZZHaNJmvwBBoD/lkjWZZbxWsq77s4I442Q4E5A==</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
I used this site to test it: https://superdry.apphb.com/tools/online-rsa-key-converter
which bases the conversion on BouncyCastle library.
The online tools they return me invalid parameter while if I use an OpenSSL generated file it works ok:
Plain N Key generated by OpenSSL:
00b122bfa082ee36df33f4cada576f1f5e9a653db95c9deecfa48863507eebce356e937f783a9c58e5d1ed60d1fa19743c4b5d6830f684f7a100e1cb7037f86aab100a840d6b6391008c9e3002db5f1370170719fdfae4c46ff6af1a398a5f7f0c9c006dec7a298c697040c91375d2a400ed71d89bd83b2190487ee6e939b521bf1e97493565e46bc42160e985f0c1d374ad87d76a8d205b3e638f1b544fc946fa34564c7835e33b7187bd4bab214054a3f053e0a4c24fefe13ae889773dcfb08b29cffff25acf486e88f7da5949bf413c89160a323a4874e9df1eb961084e08ccc190102ac42b7be0f1d454f8d3cd31d64570d71a40f3cbd58725f03770da8399
base64 Encrypted
ALEiv6CC7jbfM/TK2ldvH16aZT25XJ3uz6SIY1B+6841bpN/eDqcWOXR7WDR+hl0PEtdaDD2hPehAOHLcDf4aqsQCoQNa2ORAIyeMALbXxNwFwcZ/frkxG/2rxo5il9/DJwAbex6KYxpcEDJE3XSpADtcdib2DshkEh+5uk5tSG/HpdJNWXka8QhYOmF8MHTdK2H12qNIFs+Y48bVE/JRvo0Vkx4NeM7cYe9S6shQFSj8FPgpMJP7+E66Il3Pc+wiynP//Jaz0huiPfaWUm/QTyJFgoyOkh06d8euWEITgjMwZAQKsQre+Dx1FT4080x1kVw1xpA88vVhyXwN3Dag5k=
XML Formatted for conversion:
<RSAKeyValue>
<Modulus>ALEiv6CC7jbfM/TK2ldvH16aZT25XJ3uz6SIY1B+6841bpN/eDqcWOXR7WDR+hl0PEtdaDD2hPehAOHLcDf4aqsQCoQNa2ORAIyeMALbXxNwFwcZ/frkxG/2rxo5il9/DJwAbex6KYxpcEDJE3XSpADtcdib2DshkEh+5uk5tSG/HpdJNWXka8QhYOmF8MHTdK2H12qNIFs+Y48bVE/JRvo0Vkx4NeM7cYe9S6shQFSj8FPgpMJP7+E66Il3Pc+wiynP//Jaz0huiPfaWUm/QTyJFgoyOkh06d8euWEITgjMwZAQKsQre+Dx1FT4080x1kVw1xpA88vVhyXwN3Dag5k=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Result
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsSK/oILuNt8z9MraV28f
XpplPblcne7PpIhjUH7rzjVuk394OpxY5dHtYNH6GXQ8S11oMPaE96EA4ctwN/hq
qxAKhA1rY5EAjJ4wAttfE3AXBxn9+uTEb/avGjmKX38MnABt7HopjGlwQMkTddKk
AO1x2JvYOyGQSH7m6Tm1Ib8el0k1ZeRrxCFg6YXwwdN0rYfXao0gWz5jjxtUT8lG
+jRWTHg14ztxh71LqyFAVKPwU+Ckwk/v4TroiXc9z7CLKc//8lrPSG6I99pZSb9B
PIkWCjI6SHTp3x65YQhOCMzBkBAqxCt74PHUVPjTzTHWRXDXGkDzy9WHJfA3cNqD
mQIDAQAB
-----END PUBLIC KEY-----
And this is OK.
Does anybody understand why this is not working in my case?
Another point:
I exported OpenSSL N modulus Key and I noticed that it is made of 257 bytes instead of 256 and compared with two keypairs they both starts with 00 which is obviously too wrong.
The bottom line is how can I easily take my HEX plain keys values and encapsulte them into a PEM file or the way around how can I get the correct plain HEX keys values generated by OpenSSL and use them in my C Library (using the .PEM in php)?
Added:
Given that this is the RSA Key structure:
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- d mod (p-1)
exponent2 INTEGER, -- d mod (q-1)
coefficient INTEGER, -- (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
If I make an inspection with ASN.1 Editor it clearly shows me:
That the Key length of N is 257 Bytes instead of 256 and prime1, prime2 and exponent1 are made of 129 bytes instead of 128.
Super confusing for me....
How to sign the Tx data including ETH Keccak256 hashed addresses signature with secp256k1 (ECDSA) keys and encode to DER format in hex and verify in php?
I want to use only php libraries come with, openssl and others, to make secp256k1 private and public keys, sign, and convert to DER format in hex for making a sample of my cryptcoin.
Ethereum addresses are made by secp256k1 EDSA keys, hash the 256 bits/64 character pubic key made from the private key with Keccak256 (sha-3-256) hash algorithm to calculate hash, then take last 40 characters from the hash string in hex, add checksum, and add x0 prefix to the hashed string.
The example is shown in github.com/simplito/elliptic-php repo as toDER() function in elliptic-php/lib/EC/Signature.php. The library is using BN-php, but I want to use only php library, openssl and others.
// https://github.com/simplito/elliptic-php ECDSA sample
<?php
use Elliptic\EC;
// Create and initialize EC context
// (better do it once and reuse it)
$ec = new EC('secp256k1');
// Generate keys
$key = $ec->genKeyPair();
// Sign message (can be hex sequence or array)
$msg = 'ab4c3451';
$signature = $key->sign($msg);
// Export DER encoded signature to hex string
$derSign = $signature->toDER('hex');
openssl_pkey_new() and openssl_sign() functions can make new secp256k1 private and public keys and sign, but can not convert the public key to DER format in hex to make address by hashing keccak256 algorithm .
Signing Tx hash requires from/to ETH addresses in JSON string Tx data {address:{to:address1,from:address2},amount:0,timestamp:timestamp tx, hash:sha256hash, previousHash:hash, difficulty:2...} and openssl_pkey_new() openssl_pkey_export() made private and public keys are needed to make ETH address.
Since, openssl_pkey_new() openssl_pkey_export() for OPENSSL_KEYTYPE_EC return DER bin keys, I need to convert them to hex.
I have tried openssl_pkey_new() and openssl_sign() functions but I can not convert to DER format in hex with simple function.
openssl_pkey_new() function to make new secp256k1 key pairs, just
shown in Generating the 256bit ECDSA private key question.
$config = [
"config" => getenv('OPENSSL_CONF'),
'private_key_type' => OPENSSL_KEYTYPE_EC,
'curve_name' => 'secp256k1'
];
$res = openssl_pkey_new($config);
if (!$res) {
echo 'ERROR: Fail to generate private key. -> ' . openssl_error_string();
exit;
}
// Generate Private Key
openssl_pkey_export($res, $priv_key, NULL, $config);
// Get The Public Key
$key_detail = openssl_pkey_get_details($res);
$pub_key = $key_detail["key"];
echo "priv_key:<br>".$priv_key;
echo "<br><br>pub_key:<br>".$pub_key;
Link
And sign with the key.
openssl_sign($hashdata, $signature, $private_key);
Is there any way, with php libraries, to convert the keys to DER in hex?
Okay, now I know what you want I've looked into this, and unfortunately I think you're out of luck. First to clarify, for Ethereum you want the signature in DER and the key format can vary depending on your software, but for an Ethereum account address you do NOT want the key in DER. Specifically an address is computed by:
represent the publickey point as raw X,Y coordinates, with no metadata like DER or even a single byte like X9.62 and no encoding like base64 or hex
take a Keccak256 hash and take the last 20 bytes, which can be represented (e.g. displayed) in hex with 0x prefixed for a total of 42 characters
For step 1, the keys used by OpenSSL, and thus by php's builtin module, are PEM format, as you see in your example, which consists of an ASN.1 (DER or sometimes BER) body encoded in base64 with linebreaks and header/trailer lines. Specifically the publickey is in the format defined by RFC7468 section 13 whose content is the SubjectPublicKeyInfo structure defined by X.509 and PKIX i.e. RFC5280 4.1.2.7 whose contents for a particular algorithm is defined in other standards; the case here, X9-style EC, is defined in RFC3279 2.3.5 and simplified by RFC5480 2.2 which reference X9.62 and SEC1. (Whew!) While these standards allow several options, OpenSSL in PHP always uses X9.62-uncompressed point format and DER encoding, so for this curve the data you need for Ethereum is simply the last 64 bytes of the DER decoded from the PEM:
$der=base64_decode(str_replace(array("-----BEGIN PUBLIC KEY-----\n","-----END PUBLIC KEY-----\n"),"",$pub_key));
$raw=substr($der,-64);
But step 2 is a problem. Although SHA3 and SHAKE as standardized in FIPS202 are instances of Keccak, the hash Ethereum uses is a different and nonstandard instance of Keccak, which OpenSSL does not implement, therefore neither does php by itself (without adding a library).
I have a RSA Public Key in this format:
OpenSSLRSAPublicKey{modulus=9ee9f82dd8429d9fa7f091c1d375b9c289bcf2c39ec57e175a2998b4bdd083465ef0fe6c7955c821b7e883929d017a9164a60290f1622f664a72096f5d2ffda7c7825c3d657c2d13d177445fa6cdd5d68b96346006a96040f5b09baae56d0c3efeaa77d57602f69018f5cefd60cb5c71b6b6f8a4b0472e8740367266917d8c13,publicExponent=10001}
So, I have the modulus and the exponent.
How can I convert this in a format that is accepted by openssl_encrypt() in PHP?
I have searched on Google and didn't find anything good. Isn't there an easy way to format it?
Assuming you mean openssl_public_encrypt() instead of openssl_encrypt(), then try this (using phpseclib 1.0):
<?php
include('Crypt/RSA.php')
include('Math/BigInteger.php');
$rsa = new Crypt_RSA();
$rsa->loadKey(array(
'n' => new Math_BigInteger('9ee9f82dd8429d9fa7f091c1d375b9c289bcf2c39ec57e175a2998b4bdd083465ef0fe6c7955c821b7e883929d017a9164a60290f1622f664a72096f5d2ffda7c7825c3d657c2d13d177445fa6cdd5d68b96346006a96040f5b09baae56d0c3efeaa77d57602f69018f5cefd60cb5c71b6b6f8a4b0472e8740367266917d8c13', 16),
'e' => new Math_BigInteger('10001', 16)
));
echo $rsa;
Altho that said, if you're gonna use phpseclib to convert the key, why not use it to encrypt whatever it is that you're trying to encrypt as well?:
//$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->encrypt('plaintext');
No, there isn't any way to do this. Public/private keys can only be used in asymmetric cryptography. openssl_encrypt() requires symmetric cryptography.
openssl_public_encrypt() which you need for asymmetric cryptography wants a pubic key resource. This can be obtained using openssl_pkey_get_public().
Now, openssl_pkey_get_public() needs a PEM file. The docs describe it as:
an X.509 certificate resource
a string having the format file://path/to/file.pem. The named file must contain a PEM encoded certificate/public key (it may contain both).
A PEM formatted public key.
So you need to create a PEM file from the public key configuration you have in your question. The modulus and exponent you have are enough to do this.
There are several examples on the internet on how to archive this. Have a look at:
Generate .pem RSA public key file using base 10 modulus and exponent?
rawrsa (https://github.com/JonathonReinhart/rawrsa)
how to convert raw modulus & exponent to RSA public key (.pem format)
Basically you need to create a "ASN1-formatted text file" and employ some openssl commands.
I need to use pkcs8 private key in my project as example below, I can't found any library or method relate to pkcs8 signature only pkcs1 are available for the Crypto.Signature.
My python code example:
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
privatekey='''-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOuxJs5RD6s2ECEV
C38Jfj6xBBrcX6W1Qy+GLiXhh+wKj650BPSerQNQI+XPx/+uUemk/dJM3RMm75Di
mXJlp7/5LY11VyKSqS+x6ELIvWz8lo2OhHW6/wU4jiQ1MlburYbn/pRRClGGA8i4
FjHMvMu9/QCNPlNTlf7qJDO6lDVlAgMBAAECgYB0iP76/DGXIgAPm2w3v+Xf8X5q
GZRhRqKVmO6wZDbkisRIKa1ZlitNfA6DzpzA2tw9fgrSNJcKpTHGnYPpgEHUQrcw
t471yuAvzAOBffaqVjIt7vKyL3QnkNfxWenliasacX3olwBpKPrkvG75H2KBe9S0
WKpju1Cm6MebAJ3z8QJBAP8gSx3l9nNTqJcBBmP+TGDcTL9Sif2nBZrU/pWnzzop
HolaFIQTYXPIj8rMoVT/c0gLo1gcdHoSUJTbB0WO7V8CQQDsf9FAWCI3fkWgS/fW
21ApO6B07/388CpNHOTjvfsdlent4yXLL+Guh5apMH4Oq0tkGkEij5YPG1EZSYXi
48+7AkEAiX3m3ZMMMXTZe5/CyOrIUL8I4WbjFP8JJzs4hICuTmLQoScZvWAQeeyR
ibKkE4GjqCUVf6u+Hfd20/ICRjtTswJANCjEh8JoWYDZ7k6S7KoV9eIWs3OyurRl
P/idarUdyxqjKzorvbJjvdBdpBbz1lxlFkDMGMk+OTq3GjKi+rVvvQJAGhBieR2Q
69JNeoOg7J8Xk7evNBJHwtJfRDQXKJpN5YZsKclf6oRaYFEQOM6ThuNoYHISfGKw
UbDoUsPZoQFPtQ==
-----END PRIVATE KEY-----'''
signstr = testdata123
key = RSA.importKey(privatekey)
h = SHA.new(signstr)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
sign = base64.b64encode(signature)
The signature generated by code above do not match the public key in the api system I am using.
For PHP side I am able to sign correctly. The working code for PHP as below:
$private_key= openssl_get_privatekey($private_key);
openssl_sign($signStr,$sign_info,$private_key,OPENSSL_ALGO_MD5);
$sign = base64_encode($sign_info);
Any advice on how to sign with RSA pkcs8 format private key using python is much appreciated.
Hash algorithm
Of course, it must be different, because you're using different hash algorithms.
Your Python code uses SHA-1 and your PHP code uses MD5. The default hash algorithm in PHP is OPENSSL_ALGO_SHA1 as the documentation describes:
if (!openssl_sign($signStr, $sign_info, $private_key)) {
echo "Failed to sign";
}
Key encoding
There are many ways of encoding a private key. Two of the most common ways are PKCS#1 and PKCS#8 encodings. See more information on that here.
The key encoding is not a concern here, because both pycrypto and openssl use the correct one here.
Padding
There are two common padding schemes that are used for signing: PKCS#1 v1.5 padding and RSA-PSS (technically RSASSA-PKCS1-V1_5 and RSASSA-PSS). Both of them are defined in PKCS#1, but the latter was introduced in PKCS#1 v2.x.
Pycrypto supports both padding schemes, but openssl only supports PKCS#1 v1.5 in PHP.
Compatibility
If you want to check for compatibility of the same algorithm between different languages, you need to sign in one using the private key and verify the signature in the other using the public key. After you've done that, you can try the other way around if you also need the other direction.
This is only relevant for RSA-PSS, because that is randomized.
If you're using PKCS#1 v1.5 padding which is not randomized, you should be able to create a signature in both Python and PHP and compare them directly to check for compatibility.
I was working on a Project which purely involves PROVEN Mathematical equations, One of step requires value of private key, not for decryption or encryption.
In RSA we do have (e,n) as public key, (d,n) as private key. I am using phpseclib RSA implementation, there's option for us to get public key in RAW format, but there's none for private key. I came to know from documentation that phpseclib's RSA pads & then encrypts (Which I do not want, as application is purely a mathematical).
Solution for me would be to get either of following two
(a) Get Private Key in Raw Format.
(b) Use some other implementation of RSA which gives me RAW values of both Private Key & Public Key or if there's none implement my own RSA algorithm.
I did go through documentation of phpseclib, no useful solution was found. My question may not be properly formatted, but any help in this matter is welcome. Thanks in advance...
After you load the key you could probably do something like this:
$rsa = new Crypt_RSA();
$rsa->loadKey('...');
echo $rsa->modulus;
echo $rsa->exponent; // private exponent
echo $rsa->publicExponent;
echo $rsa->exponents[1]; // dp
echo $rsa->exponents[2]; // dq
echo $rsa->coefficients[2]; // inverseq
echo $rsa->primes[1]; // p
echo $rsa->primes[2]; // q