I'm having trouble trying to convert a Java generated RSA Public KEY to string format so I can use it on the PHP side. I tried converting it to string but the encryption fails on the PHP side. I generate a public Key in Java like this:
KeyPairGenerator k = KeyPairGenerator.getInstance("RSA");
k.initialize(1024);
KeyPair kp = k.genKeyPair();
PublicKey publicKey = kp.getPublic();
PrivateKey privateKey = kp.getPrivate();
and the PublicKey when outputted to String looks something like this:
RSA Public Key
modulus: eded852d98899fd083b6b989cbdbb41c0cf604ccdc3c4b46a6e3bdf92be898db1c53133dba4fcbc3bd7e8934b7b212856146169858ef2177e9c04c995d4fb61f9957eb6ff61a1183de03e5459ecbae7d1196778be844127fd7e80668b57037cab7a3e56c02cb881c3fb2aaddd47e5cae49c14582be01722cfa5352d9bdc97a37
public exponent: 10001
I then encode this PublicKey like this:
byte[] pKbytes = Base64.encode(publicKey.getEncoded(), 0);
String pK = new String(pKbytes);
String pubKey = "-----BEGIN PUBLIC KEY-----\n" + pK + "-----END PUBLIC KEY-----\n";
and that outputted looks something like this:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDt7YUtmImf0
IO2uYnL27QcDPYEzNw8S0am4735
K+iY2xxTEz26T8vDvX6JNLeyEoVhRhaYWO8hd+nATJldT7Yf
mVfrb/YaEYPeA+VFnsuufRGWd4vo
RBJ/1+gGaLVwN8q3o+VsAsuIHD+yqt3UflyuScFFgr4Bciz
6U1LZvcl6NwIDAQAB
-----END PUBLIC KEY-----
which looks similar to the format I have on the PHP side. Encoding it using Base64 gives the final format I intend for the key String.
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS1NSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURD
QmlRS0JnUUR0N1lVdG1JbWYwSU8ydVluTDI3UWNEUFlFek53OFMwYW00NzM1SytpWTJ4eFRFejI2
VDh2RHZYNkpOTGV5RW9WaFJoYVlXTzhoZCtuQVRKbGRUN1lmbVZmcmIvWWFFWVBlQStWRm5zdXVm
UkdXZDR2b1JCSi8xK2dHYUxWd044cTNvK1ZzQXN1SUhEK3lxdDNVZmx5dVNjRkZncjRCY2l6NlUx
TFp2Y2w2TndJREFRQUItLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0=
After all this, the key doesn't seem to work. It encrypts a message with the key I send it but when I send it back to the Java/Android app (which contains the private key) it can't decrypt it. I've been able to do this vice-versa (i.e. take a encoded key string from PHP and convert it to a Public Key to be used by the Java/Android app) Anything I'm doing wrong here? My gut tells me I'm sending the wrong string to the PHP server
Here's what the stack Trace in Java looks like :
03-02 12:02:26.170 W/System.err﹕ java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block
03-02 12:02:26.182 W/System.err﹕ at com.android.org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(CipherSpi.java:464)
03-02 12:02:26.186 W/System.err﹕ at javax.crypto.Cipher.doFinal(Cipher.java:1204)
03-02 12:02:26.186 W/System.err﹕ at com.app.test.NQ.NQCrypto.decrypt(NQCrypto.java:116)
Here is how I attempt to decrypt the data in Java:
public static String decrypt(String data) throws Exception {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// Base 64 encode the encrypted data
byte[] encryptedBytes = Base64.encode(cipher.doFinal(data.getBytes()), 0);
return new String(encryptedBytes);
}
Your decryption code looks wrong:
cipher.doFinal(data.getBytes())
Your data variable is a string, but strings can't hold raw encrypted data without corrupting it. Unless your ciphertext is actually hex-encoded or base64-encoded etc. In which case, getBytes() is not the right way to decode that into a byte array.
So either fix the way you are sending your ciphertext, or correct the way you decode your ciphertext to a byte array.
Related
What does this error mean:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0000b9:ASN.1 encoding routines:OPENSSL_internal:WRONG_TAG
my code is:
public static byte[] encryptByPublicKey(byte[] data, String key)
throws Exception {
key = key.replace("-----BEGIN RSA PUBLIC KEY-----\r\n", "").replace("-----END RSA PUBLIC KEY-----", "");
byte[] bytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(bytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
PublicKey pkPublic = keyFactory.generatePublic(x509KeySpec);
Cipher pkCipher = Cipher.getInstance("RSA");
pkCipher.init(Cipher.ENCRYPT_MODE, pkPublic);
return pkCipher.doFinal(data);
}
I'm sure, i have the correct data and key (that created in php), but i get this error in line:
PublicKey pkPublic = keyFactory.generatePublic(x509KeySpec);
I don't have any problem whit this code in my other android (without php server)apps, and i have correct answer by the same encryption/decryption code.
Is it possible that a function using in two projects, with the same input an output values, in the same software, has different actions? Yes! it's possible, but how about Android Studio, and Phpstorm? Is it about Openssl cer.? or other libraries? or some other reasons? How can I overcome this error?
I appreciate it if you help me. Thanks in advance for your answers.
Please check the key string, maybe contains illegal Base64 char: "\n", "\t", or \s and so on, remove them and try again !
For a PEM private certificate, I assume you need a string like this:
-----BEGIN PRIVATE KEY-----
YzNhYjhmZjEzNzIwZThhZDkwNDdkZDM5NDY2YjNjODk3NGU1OTJjMmZhMzgzZDRhMzk2MDcxNGNhZWYwYzRmMg==
-----END PRIVATE KEY-----
So what I did was:
$privateKey = '-----BEGIN PRIVATE KEY-----';
$privateKey .='\n'.base64_encodehash(('sha256','foobar'));
$privateKey .='\n-----END PRIVATE KEY-----';
But openssl_pkey_get_private($privateKey) returns false and not a valid resource. I assumed PEM is just base64 string. What have I done wrong? (I found this via Google: http://www.cryptosys.net/pki/manpki/pki_pemstring.html)
There are different PEM formats for different types of objects. On the face of it PEM is just base 64 encoded data enclosed within the BEGIN and END markers. PEM files that begin with the "BEGIN PRIVATE KEY" markers contain base64 encoded data that conforms to the PKCS#8 standard. In particular see section 5 of RFC5208 (https://www.rfc-editor.org/rfc/rfc5208). Normally you would not create these files yourself from scratch you would use some sort of library or tool to do it for you (such as OpenSSL).
phpseclib:
php code :
include('Crypt/RSA_XML.php');
$rsa = new Crypt_RSA_XML();
$rsa->loadKeyfromXML($public_key_xml);
$data = "invoice_number=1,100.00&customer_tin=674858994885&serial=ONLYPEMPSERIAL&tin=ONLYPUMPTIN&vat_1_net=2,0000&vat_1_value=3600&vat_2_net=0&vat_2_value=0&vat_3_net=0&vat_3_value=0&vat_4_net=0&vat_4_value=0&vat_5_net=0&vat_5_value=0&vat_6_net=0&vat_6_value=0&vat_7_net=0&vat_7_value=0&vat_8_net=0&vat_8_value=0&payment_mode=3&discount=200&total_gross=9562";
$plaintext = $data;
//define('CRYPT_RSA_PKCS15_COMPAT', true);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$ciphertext = base64_encode(strrev($rsa->encrypt($plaintext)));
echo "data";
echo $ciphertext; // there is encoded value getting
and
then how to decrypt encrypted data using private key:
echo $rsa->decrypt($ciphertext); // this is giving error , decryption error
actually , i want to encrypt in php and decrypt in c# ,
data encrypted successfully and when c# rsa crypto service decrypt this encrypted data then giving error message : invalid base-64 string length
$public_key_xml and $private_key_xml is defined below...
public key :
<RSAKeyValue>
<Modulus>uidt3bPfWozkIkC6nHnRDbXrvjqplfCslV2zP4hKJ6sVjVnPfjMM0ueCuEDFZ9NK+kCWaPNAVhOKKwL8HmoX/7KcFLWkwSoatnrncHTH5STey+bqR1xTFY+Rubj8BZt7D9JJYyLQC46wn4ySVnLWkCZZ9+aaTriEBzGTpUzeRiUTWVprp3oXsA7ZKyn+lhZfMx1ILhcD8dnX7xFHB57jIKvPBxAdT4K7GxdgENeS76I/zmVmlF//JnmtZ/RM1WmRkx8mFmcK/Ky8gLsmIpPPltoyBWIKIf2NQH9kHqHa2gwoPg34LTutV9AACTWuiVOjqU7Gq2BHQcjovXMF8t3Wiw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>
private key :
<RSAKeyValue>
<Modulus>uidt3bPfWozkIkC6nHnRDbXrvjqplfCslV2zP4hKJ6sVjVnPfjMM0ueCuEDFZ9NK+kCWaPNAVhOKKwL8HmoX/7KcFLWkwSoatnrncHTH5STey+bqR1xTFY+Rubj8BZt7D9JJYyLQC46wn4ySVnLWkCZZ9+aaTriEBzGTpUzeRiUTWVprp3oXsA7ZKyn+lhZfMx1ILhcD8dnX7xFHB57jIKvPBxAdT4K7GxdgENeS76I/zmVmlF//JnmtZ/RM1WmRkx8mFmcK/Ky8gLsmIpPPltoyBWIKIf2NQH9kHqHa2gwoPg34LTutV9AACTWuiVOjqU7Gq2BHQcjovXMF8t3Wiw==</Modulus><Exponent>AQAB</Exponent><P>9NiLuI9TjNvpAPQqD9ySdMX37OmEDCF02isoovt8hwPpiXcZYH4FeasNZoydRrBUOHTTRrW3xdUYGsCZI0H9tSg+gIjo/k/JhmECT7RuSgjEL7mLpusAhi1RFv81TNERGvWP8V9HtB4oZONgOpdTuNqJwhyZ3+aA3zyy7k1mKJc=</P><Q>wqJndWnlZ2i8sW8zhX9SPUddyf8E+wHek3SYynUNQ3T7zJbk+woqyjMuSImXXuZO47uBJlwskYwR+mJr/AuCR7Y0+jtByJF8RoqkY7ttdhS8CpJ9J2o5YMGcGw0JoJ16L0W6QvhY5Zxb5IAG5nuiPLDlgZYNo7+oKRcDyF+fbC0=</Q><DP>hwimhjMFsTnXV19Rk03it+Q2x8JBwS7ycyA6WSi5lPzjX5z/sefOvTtJOLV0R/gXestzehveLo1Hrflqe7d5ZN+9GMZpOVhnnGUEEVFBQjNzf56lFnmk4Fs9zaESlDr5ZBTqPgR+VygKvxlniOjMk7ZzI0sC0ikeCgA/7o1hOiE=</DP><DQ>Fpj2fBpcaSIu7kbj63b53GWBzScPs/jml6Ys6yyl6pQVfA507XSjvTAuCnv3GCyTMjud5g9DmW5y0+hDc1L+wEa2ZeslWx0RGbuVFIM5VUFZB700TLQ3jzVLY5Si7oP6IKzu0EG3SIlb3e7DXlUyY+uB6ga69K0W4BZs+QGpJ80=</DQ><InverseQ>nob08brDfoswDV8JAkGJIg5T9ktMBRzn5djbAfSorOCCVwW+iRz/hkzSs4LaeMuoC3V5AnLeTg1T7J3op67KGerRwwjXSgCKO4crs2pODcZuIMkaE8e/5Ti1O40yKl05mQaxLk/SgSAhy97HhHoiteg/ttLcrvsCcSfyyxzHT3M=</InverseQ><D>bwqYEbh7EjOa3gfIiRBtMIWFExtBD6zZ9dtH0i0FNvZpy8B38iqXirMImcohNxal0fN3BTGc/ft33sJQDABzQlaTnhLgLU1lU9aqeb1fhANjVzPuKhUbhm/2mFeNFfcyCDUDC7y3Zz19hB7BKAomjSQjZKnNAAo2z2e2T9Mzf5kV8uuYsnoum6LEvEfluQ3q3+9Ua64P0E4D2j2iaOnvpBzTCpeaBMDfWZEe19MaS40d/OrZOwlyAVPCW9RkT3948fC5KDvE0KetYDsrVApRSKzvBUQCVNmcO2o+rhMO1qKvS+zkw2VW5OxGDk/QuHuXIkCyipUEMa/DmK74hoxCaQ==</D></RSAKeyValue>
You must be using a really old version of phpseclib since phpseclib has had built in support for the XML format for quite a few years. So my first recommendation would be to upgrade to the latest version. After that you should be able to do something like this:
<?php
include('Crypt/RSA.php');
$plaintext = 'zzz';
$rsa = new Crypt_RSA();
$rsa->loadKey('<RSAKeyValue>
<Modulus>uidt3bPfWozkIkC6nHnRDbXrvjqplfCslV2zP4hKJ6sVjVnPfjMM0ueCuEDFZ9NK+kCWaPNAVhOKKwL8HmoX/7KcFLWkwSoatnrncHTH5STey+bqR1xTFY+Rubj8BZt7D9JJYyLQC46wn4ySVnLWkCZZ9+aaTriEBzGTpUzeRiUTWVprp3oXsA7ZKyn+lhZfMx1ILhcD8dnX7xFHB57jIKvPBxAdT4K7GxdgENeS76I/zmVmlF//JnmtZ/RM1WmRkx8mFmcK/Ky8gLsmIpPPltoyBWIKIf2NQH9kHqHa2gwoPg34LTutV9AACTWuiVOjqU7Gq2BHQcjovXMF8t3Wiw==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>');
define('CRYPT_RSA_PKCS15_COMPAT', true);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
echo base64_encode($rsa->encrypt($plaintext));
If that doesn't work it'd help to see the .NET code you're using to decrypt. Also, if you could encrypt a string in .NET and post the public key you used to do the encryption and the ciphertext itself that'd be useful. From that I could play around with the phpseclib options until I found some combination of options that let me decrypt the string, which would, presumably, in turn, give us insight in how to encrypt the string.
I have following steps to perform for decryption
base64 decode the response
Decrypt the first 128 bytes with the RSA1024 public key. Key is in base64 encoded X509 format with PKCS1 padding.
My code looks like this:
$decodedString = $this->base64UrlDecode($string); //does proper url decoding
$publicKey = file_get_contents("public.key",true);
$pub_key = openssl_get_publickey($publicKey);
openssl_public_decrypt($decodedString,$decrypted,$pub_key,OPENSSL_PKCS1_PADDING);
var_dump($decrypted);
I am not able to get anything in $decrypted variable. If I try to base64 decode public key before using it, I am getting error of not a valid public key. What I am missing or doing wrong to achieve mentioned 2 steps?
See this comment for openssl_pkey_get_public:
http://www.php.net/manual/en/function.openssl-pkey-get-public.php#101513
PKCS1 padding poses a problem to that function, it seems.
It was actually a problem with how I was getting response. By doing urldecode before base64 decoding I am able to get proper results.
$decodedString = $this->base64UrlDecode(urldecode($string));
How convert RSA public key, from XML to PEM (PHP)?
I'm assuming that by XML format, you mean XML DSig RSAKeyValue, and that by PEM format you mean what OpenSSL exports in between -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----.
You need first to extract the modulus and public exponent from the XML.
<RSAKeyValue>
<Modulus>xA7SEU+e0yQH5rm9kbCDN9o3aPIo7HbP7tX6WOocLZAtNfyxSZDU16ksL6W
jubafOqNEpcwR3RdFsT7bCqnXPBe5ELh5u4VEy19MzxkXRgrMvavzyBpVRgBUwUlV
5foK5hhmbktQhyNdy/6LpQRhDUDsTvK+g9Ucj47es9AQJ3U=
</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
You can easily convert these into a bit string using base64_decode.
Once this is done, you need to build the ASN.1 public key structure somehow.
What OpenSSL exports between BEGIN/END PUBLIC KEY is an X.509 SubjectPublicKeyInfo structure.
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING }
The subjectPublicKey is made of a sequnce is described in the PKCS#1 spec:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER,
publicExponent INTEGER
}
The algorithm (an AlgorithmIdentifier) is also described in the PKCS#1 spec (see section A.1):
rsaEncryption
OBJECT IDENTIFIER ::= { pkcs-1 1 }
This structure needs to be serialized in DER form, then base64-encoded and then placed between the BEGIN/END delimiters.
I don't know of any PHP library to do the ASN.1/DER encoding unfortunately (the rest is relatively easy, but dealing with ASN.1 tends to be tedious).
The PHP/PEAR Crypt_RSA module can construct RSA public keys from modulus and exponent, but its toString() method uses a custom format (just the base64-encoding of the result of PHP serialize on the array structure, which has nothing to do with the ASN.1/DER encoding).
we know
.pem - (Privacy Enhanced Mail) Base64
encoded DER certificate, enclosed
between "-----BEGIN CERTIFICATE-----"
and "-----END CERTIFICATE-----"
X.509
The SignatureValue element contains
the Base64 encoded signature result -
the signature generated with the
parameters specified in the
SignatureMethod element - of the
SignedInfo element after applying the
algorithm specified by the
CanonicalizationMethod.
XML_Signature
so we end up with
$xml = simplexml_load_file($xmlFile); // or simplexml_load_string
$pem = "-----BEGIN CERTIFICATE-----\n";
$pem .= $xml->SignatureValue;
$pem .= "\n-----END CERTIFICATE-----";
// save to file
if your xml-file isn't a XML_Signature
$xml = simplexml_load_file($xmlFile); // or simplexml_load_string
$pem = "-----BEGIN CERTIFICATE-----\n";
$pem .= $xml->nodeWithWantedValue; // use base64_encode if needed
$pem .= "\n-----END CERTIFICATE-----";
Here's an example of how to read XML RSA keys in PHP:
There is no standard for storing RSA public keys in XML. So the manner of conversion will depend on the XML you have.
Just for completeness, here is a working example of creating the PEM from modulus in python. You could call it in a subprocess from PHP if necessary.
The meat of the solution is:
def big_endian(n):
s = '%x' % n
if len(s) & 1:
s = '0' + s
return s.decode('hex')
from M2Crypto import RSA
e = E_PREFIX + big_endian(public_exponent)
n = N_PREFIX + big_endian(modulus)
new = RSA.new_pub_key((e,n))
new.save_key('foo.pub')
Where E_PREFIX and N_PREFIX are constants that (as far as I can tell) depend on the exponent and key length. Here is a quick table I have constructed:
E_PREFIX = '\x00\x00\x00\x01' # 0x3 (3)
E_PREFIX = '\x00\x00\x00\x03' # 0x10001 (65537)
N_PREFIX = '\x00\x00\x00!\x00' # 256-bit
N_PREFIX = '\x00\x00\x00A\x00' # 512-bit (default)
N_PREFIX = '\x00\x00\x00\x81\x00' # 1024-bit
N_PREFIX = '\x00\x00\x01\x01\x00' # 2048-bit
N_PREFIX = '\x00\x00\x02\x01\x00' # 4096-bit
If someone knows a more general way to compute the prefixes, do tell.
Maybe you should have a look here
Extract the two base64-encoded strings, convert and pass to PEAR::Crypt_RSA, then export as text file, then openssl convert?
Check this too