i need some help for solve my problem.
Problem :
I want to encrypt a number (A) with public RSA Key from Android platform and then decrypt it on PHP Server with the private key.
On each platform, i can encrypt and decrypt data (it works well), but when the PHP script try to decrypt data encrypted from ANDROID, it doesn't work !!
Problem is not from HTTP Transmission, because I try to decrypt directly a generating Encryption from ANDROID (coded in Base64) and it not work at all ...
Findhere after my PHP Code for decrypt data :
class MyEncryption
{
public $privkey = '';
public $pubkey = '';
public function __construct(){
}
public function initialize() {
$fp=fopen("./encryption/asasap_public.pub","r");
$temp=fread($fp,8192);
fclose($fp);
$this->pubkey = openssl_pkey_get_public($temp);
$fp=fopen("./encryption/asasap.pem","r");
$temp=fread($fp,8192);
fclose($fp);
$this->privkey = openssl_get_privatekey($temp,'');
}
public function encrypt($data)
{
if (openssl_public_encrypt($data, $encrypted, $this->pubkey))
$data = base64_encode($encrypted);
else
throw new Exception('Unable to encrypt data. Perhaps it is bigger than the key size?');
return $data;
}
public function decrypt($data)
{
if (openssl_private_decrypt(base64_decode($data), $decrypted, $this->privkey))
$data = $decrypted;
else
$data = '';
return $data;
}
public function hex2bin($hexdata) {
$bindata = '';
for ($i = 0; $i < strlen($hexdata); $i += 2) {
$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
}
return $bindata;
}
}
And i use this class like here :
$enc = new MyEncryption();
$enc->initialize();
$data_1 = 'K27booXr0zZK4BQlI45MIPJJjPPkpCCPELGvoK/wKYUwShIWE6szlZtrmV83C5eBIrT/3lxWTH3+IOA+5mefurVUvXmQIV7fXEHNHLphyM6L9gQsMAGZMCroPjWKvJM59OMS/d5dwwhiRgzVarxXSKpxBYhEYWJTu7nRJ+bZKjumeoqnCSpmntIiV+tRYgkYflOU6j2QlesjO5tzj/TL6n7vHSO/O1qafJkzHcv8Kn2hTy+IH7QXm7z5vtjXOucHkvBm1xWORXdifh+ChyVvP16dSEmCaCAH6KqtA4viX/HwRFEi4mIWaYSIQk74NdcnQOpFcTgEu2nDwtHaBMqahw==';
$data_2 = $enc->decrypt($data_1);
Here data_1 is initialized from the encrypt data (A=5) from android with the RSA Public Key (note : decrypt works well on Android), but after decryption in PHP, i get empty String ...
------------------------------------------ UPDATE -------
Please find here after the code for ANDROID part :
public byte[] encryptRSA(final InputStream publicKeyFile, String in) throws IOException, NoSuchAlgorithmException,
InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException {
byte[] encodedKey = new byte[5000];
publicKeyFile.read(encodedKey);
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pkPublic = kf.generatePublic(publicKeySpec);
// Encrypt
Cipher pkCipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
pkCipher.init(Cipher.ENCRYPT_MODE, pkPublic);
return pkCipher.doFinal(in.getBytes());
}
After encrypt data, i convert the byte[] into Base64 (Base64.encodeToString(input, Base64.DEFAULT)).
For the certificate, i use RSA 2048 Bits convert into DER Format for Android.
------------------------------------------ SOLUTION -------
Error are in following Lines :
byte[] encodedKey = new byte[5000];
publicKeyFile.read(encodedKey);
We must read exactely the Public Key :
byte[] encodedKey = new byte[/*lenght of file*/];
publicKeyFile.read(encodedKey);
There are a lot of places this can go wrong:
You are passing 5000 bytes to X509EncodedKeySpec, most of which are 0. Are you sure you are getting the proper public key?
How long is the in String?
String.getBytes() uses the platform default encoding and may have unintended results. Use getBytes("ASCII") or getBytes("UTF-8").
Generally, you should just use SSL, and don't try to implement asymmetric encryption yourself.
Related
How to do I decrypt data using PHP that I have encrypted with using JSEcrypt
Following is how I encrypted my code
async function Encrypt(form = {}) {
const JSEncrypt = (await import("jsencrypt")).default;
let encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
let data = btoa(JSON.stringify(form));
let encrypted = encrypt.encrypt(data);
return encrypted;
}
When I decrypt it using JS, it works fine
async function Decrypt(encrypted) {
const JSEncrypt = (await import("jsencrypt")).default;
let encrypt = new JSEncrypt();
encrypt.setPrivateKey(privateKey);
let decrypted = encrypt.decrypt(encrypted);
return decrypted;
}
How do I do the same decrypt function in PHP ?
This is what I have tried and it does not work
function decrypt($data) {
$privateKey = file_get_contents("../keys/private_key.pem");
$key = openssl_get_privatekey($privateKey);
$data = base64_decode($data);
$result = openssl_private_decrypt($data, $decrypted, $key , OPENSSL_PKCS1_PADDING); //Keeps returning false
if ($result) {
# code...
$decrypted_data = base64_decode($decrypted);
return json_decode($decrypted_data);
}
return "No data";
}
PHP decrypt function returns "No data" because $result keeps returning a false value.
Thank you.
Sample Data that I am using
Blockquote
**Public Key**
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+QJEeRV7zs0Eh3W1/A1L
Bi9Rh8CXO8mdC1GICj3CUfUJ5xHoNGo03XsMZl7rU2szcmYh/T4Iidnr5hkZB/FG
RqmBy9xUA6IKTJANIkhuAGuzhkCXwnKiCjHBHr3HlqBq201BzPqw4+6+TMTmOe1p
DJ6xmy4YALqf5ovZ9HxJ9DcBzzuDgcNBTMcHAskGZexK3C66OJTFeDXqUS8VuYfa
robAuLeH/8LPnFfAKAYvoZQvUM0zvfn/VIJkvPbCeTr5RYvcpCj2t53tSBpaGKRa
EmiIfT3gKUraJDGz4BEtw7skc4Li3vZAt96UA6XoJdglphxwNztA1ZUaovVg2foN
EQIDAQAB
-----END PUBLIC KEY-----
**Private Key**
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD5AkR5FXvOzQSH
dbX8DUsGL1GHwJc7yZ0LUYgKPcJR9QnnEeg0ajTdewxmXutTazNyZiH9PgiJ2evm
GRkH8UZGqYHL3FQDogpMkA0iSG4Aa7OGQJfCcqIKMcEevceWoGrbTUHM+rDj7r5M
xOY57WkMnrGbLhgAup/mi9n0fEn0NwHPO4OBw0FMxwcCyQZl7ErcLro4lMV4NepR
LxW5h9quhsC4t4f/ws+cV8AoBi+hlC9QzTO9+f9UgmS89sJ5OvlFi9ykKPa3ne1I
GloYpFoSaIh9PeApStokMbPgES3DuyRzguLe9kC33pQDpegl2CWmHHA3O0DVlRqi
9WDZ+g0RAgMBAAECggEBAO7twg39E0MnbYUc6XXku2w/0xdTMMnpdor5vHM3N1G0
sb/KauiAUCGEhC1mPp9YaZEHdu2rrD25oKS0yFPaqvf6hdS6oNKrlP5J6pBOt14n
+aaELBokLF9jxk5dAzoAyweKZeztTvYmiurWs6I5r2awjvK7k8R/ThbFcmkpKTzt
9oOvq2QoA+fCeqby63SAvl+KmQs3SCEYYQHAkNhwa6BvYTld6FbzL23BxZs2Zr5F
156X91D4lh1eAtgKW/Qq3O0y+Jgeff1VFYasVEsg26XjzY4cVzYtLjXT0SogKxEy
zQS0xPczkweN+LWNo0TzpKzfCdgqPIc5hxxUK+Aj8AECgYEA/KrkKPO0d+AJQoo3
/9ehf7DLKzvpa1qjdw8GI0w/XVzc1nt2YTziZgH1Rj+f17DqbEZFn8c2dl7Cqk7x
XVW16js92g265avovy745xTZ/GhG/4IvS12cZLggedV33RHXrZ035vy/Ahs/QAOJ
WIkV0u3fcopkIewPnJ/XlxWzCsECgYEA/EsF441w/r1FoYUQ8UqVKsFPsJLKLiGL
FNGyUvhoeI+DpBXAz4ZNYAILHYqOKoV60uZhg73FN0m5ihtgCBT7pnsSTkugDLsv
9zjkogdeRypN4Of/PIKUyylDJCGvIX+YmQ06STuH9H2lbvwg9E0/vElUVL+qC0Bj
UanVmb57JlECgYAVs6PnNI9yoGaLNY37xoyTiB7bNskktGNH7Z1sWLc0kK5TKcWQ
MoeLlW14vCxBfWuVIzAFujmeTyNPN3qZtqvqU25LlVRRN8PE8Abm6i6S9G3sskTX
p1GwgPQhIsmpzkeFHPTZPSo2xxpWqDcayKN8O3wuvV4+X6UzobMFwhJfwQKBgQCA
56AppOH/9hr2rPN8JZxTjRO3ZzjQGylhyMaxJ+TQ/JrJIqgyIC+2hzTAskNzLGIW
EcnvMu3qEswu6U/GsVnmtOkkgfVTq+yVB0eiFduwxsely0OY2itGJl38vw0pM3V3
dc+7DSSo4e296Cq6SCHxhRIGfytVtl9IoVrWxAFOEQKBgFmITpM9StkIHJqd/Lkb
gscOOHRpWd20lPos/n42LoIspz7zgbiEi2saiO+yZZ+xK56PYaGkAIyNvzqOVoKi
HBy3FrbxOc+EGIMemvVPv0rK0Jpz8vdgTmXf+4NbWu7jRCvQCrNbCflWyka/w438
mqlInSoviiAMRu2BcS2cojys
-----END PRIVATE KEY-----
Data Set 1
**Clear Text**
o4XuRo4Im3wkJImjLq96XvODUzR5Y+1GCqViKQqStyoidS8+9aQbLrWFpjhyHuE019YzJLHhuWxWlo7yfR3aebm5kObvq/QBe821dZgnOLtvdeUM4NP8iapjV945DsjRkp9g8vEwH3gGN0p/RrzWvbsF57OZ3eLRFjkFv2TS9PcUVlVHYbxpTjfhskpOQrDJeJJZjnCaRyPOa2VG4k3Y3p7WJzU+oT9DJK2aUscuS8zyDvLHSWvS09KOe/5xu+554Q54QHaLmAUxqu//4EjK+8yDko8Ji3mnmpFD0eyXfO2uMlw8dAUJ4w1++hNr7pr60K4rV47aCP1mh6za9lzmUg==
**Signature String**
7MC7xeKz+wbBTm1uxcKKV6AS3hM5TUS9T2ZWOxnxZNptBvClsoVktdfb8LVCTaDOKU9dopawZhcLl1fPZwHSoeQMhkkI2IRq9eHry4XWmXyDwSTLn+90AIdyxh+RuFrasGvcOmjRc75Eij393eDfl/MHyVeipIZXMUrWmRTgIjekXEffsoVrpuEGygGnltHMINXA+H1EbsFfJL4ok8zm4JhX8NdZSAcM3zeHrsf1DXbDRPOm4rwdZiuR5y4JGiVKXnEqfu3MA+tllQObE0UTngaUVVea5BFAjMYgE58cvYslzUbQAzKJERfABMg+LeBBuWTRPMrA4y+xKf8r2UHUBg==
Signature passes smoothly
Data Set 2
**Clear Text**
34JPmAmu4avmsBDL2A9t3NvC5ljfQgcLNNcEclXbTs+Efi6vj3QZb2eMjIA2uOKgBP3ChVJIAI4Zj9rjq31CmrRVtCMPPX5okgOVOH0wK/v+tScwW87SMWOdGhiBn+HXSYQQO3gpLYczVuD+RHzs5/7FEYSnyfg4aT+UTKDrkIxJFOdbdYUKB9zDeGFDiCqSgphU8qZdVoxAk+yCg5gec+/JnK4hrd209fj1tbE4vzFax9fW+jGscHsIn2Fr6gwpVs2zUAHYn8lbiTfI8ao3TJ2BE7aEkcwsq+SZAsKmWDFnwf7aUtZtr8pCkSvT6A/dMeH1Ib9tWl1A6KpczfHH6g==
**Signature**
x6mXZvdZggPBKxMtmnHIdw2j+7WJgfqfsc8udymJE+Z6tEonQvgr5RKr2OUdLUuYul13G/GCgkm+BpbensfsM7G7IwJenjCJs4FXgtYjLCG3QTLNz2OalgLy1dw1SyRW4XETmsbTZysjxvpaBh0/ggC/Dh2oYFsZYVRGDnFgQTWzmMfMIuq2cDT95MmUyN1wLjRxuiQD4vAowkmf5igJpaw1Bd9+ifT3K7LS9dtmq9bl3n1RKV9I7nWbTgT6qgujOsOwTpYjfuGT2xBuqBvDd+al4FPiMaVSt8oDVr5SLuClXYc9Ky+1OcXEgsjdyHqP7kio58PvitUa++q6m+i5bA==
Signature passes smoothly as well
These are my sample data . Hopefully, it is sufficient enough. Thank you
FULL PHP CODE
Decrypt.php
<?php
function decrypt($data) {
$privateKey = file_get_contents("../keys/private_key.pem");
$key = openssl_get_privatekey($privateKey);
$data = base64_decode($data);
$result = openssl_private_decrypt($data, $decrypted, $key,OPENSSL_PKCS1_OAEP_PADDING);
if ($result) {
# code...
$decrypted_data = base64_decode($decrypted);
return json_decode($decrypted_data);
}
return "No data";
}
?>
VerifySignature.php
<?php
include('./Decrypt.php');
$cert = file_get_contents("../keys/public_key.pem");
/**Run node index.js in terminal to get data & signature */
$data = "o4XuRo4Im3wkJImjLq96XvODUzR5Y+1GCqViKQqStyoidS8+9aQbLrWFpjhyHuE019YzJLHhuWxWlo7yfR3aebm5kObvq/QBe821dZgnOLtvdeUM4NP8iapjV945DsjRkp9g8vEwH3gGN0p/RrzWvbsF57OZ3eLRFjkFv2TS9PcUVlVHYbxpTjfhskpOQrDJeJJZjnCaRyPOa2VG4k3Y3p7WJzU+oT9DJK2aUscuS8zyDvLHSWvS09KOe/5xu+554Q54QHaLmAUxqu//4EjK+8yDko8Ji3mnmpFD0eyXfO2uMlw8dAUJ4w1++hNr7pr60K4rV47aCP1mh6za9lzmUg==";
$signature = "7MC7xeKz+wbBTm1uxcKKV6AS3hM5TUS9T2ZWOxnxZNptBvClsoVktdfb8LVCTaDOKU9dopawZhcLl1fPZwHSoeQMhkkI2IRq9eHry4XWmXyDwSTLn+90AIdyxh+RuFrasGvcOmjRc75Eij393eDfl/MHyVeipIZXMUrWmRTgIjekXEffsoVrpuEGygGnltHMINXA+H1EbsFfJL4ok8zm4JhX8NdZSAcM3zeHrsf1DXbDRPOm4rwdZiuR5y4JGiVKXnEqfu3MA+tllQObE0UTngaUVVea5BFAjMYgE58cvYslzUbQAzKJERfABMg+LeBBuWTRPMrA4y+xKf8r2UHUBg==";
$pubkeyid = openssl_pkey_get_public($cert);
//verify signature
$result = openssl_verify($data, base64_decode($signature), $pubkeyid,OPENSSL_ALGO_SHA256);
// $result = openssl_verify($data, $signature, $pubkeyid,OPENSSL_ALGO_SHA256);
if ($result == 1) {
# code...
var_dump("Valid Signature");
$decrypted = decrypt($data);
} else {
var_dump("Invalid Signature");
}
?>
Others have suggested to just lean on HTTPS, a few things to bare in mind:
HTTPS encrypts the communication between the client and the server using SSL/TLS protocols. This protects all data, including query strings in URLs, from being tampered with or intercepted while in transit.
Query strings in URLs are sent as regular text as part of the request, but when HTTPS is used, the entire request, including the query strings, are encrypted, making them secure during the transmission.
Now to your problem...
Assuming your public key is definitely married to your private key.
OpenSSL functions are typically used to decrypt payloads from OpenSSL functions, whilst both OpenSSL and JSEncrypt use the RSA algorithm, it's fair to say that their implementation may be different.
You could try using phpseclib
composer require phpseclib/phpseclib
<?php
use phpseclib3\Crypt\PublicKeyLoader;
function decrypt($encrypted)
{
$privateKey = file_get_contents('/path/to/your/private.key');
/** #var \phpseclib3\Crypt\RSA\PrivateKey $loader */
$loader = PublicKeyLoader::loadPrivateKey($privateKey);
$decrypted = $loader->decrypt(base64_decode($encrypted));
return json_decode(base64_decode($decrypted), true);
}
Disclaimer: Code above is untested
I'm using RSA and AES encryption/Decryption
I create RSA public key and private key in Android
I send public key to PHP server
Utilizing AES encryption, PHP encrypts some large data with a password and utilizing RSA encyption and the received public key, it encodes the password and then returns back the encrypted large data with the password and the encoded encrypted password where the encode function is as following:
base64_encode(the encrypted password)
In Android, I first decode the encoded encrypted password where the decode instruction is as following:Base64.decode(the encoded encrypted password, Base64.DEFAULT)
PHP code is as following:
openssl_public_encrypt($password, $encrypted, $public_key);
$mystr= base64_encode($encrypted);
echo "mystrStart=$mystr mystrEnd\n"; //mystr will be retrieved in Android
Android code is as following:
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encryptedBytes,decryptedBytes;
encryptedBytes = Base64.decode(mystr, Base64.DEFAULT);
decryptedBytes = cipher.doFinal(encryptedBytes);
String DecryptedPassword = new String(decryptedBytes);
The problem is I see the password at the end of DecryptedPassword but some other junk data before the password is yet seen:
The password in PHP is "Hellooooo"
mystr (the encoded encrypted password) received in Android is as following:
sNfmwifoIEqXQzzge0zOFmBTFPlDqQZkAWqOPtGe3jNuI/zdV4SsCTxzcNZvww5RaaMOZ4ubQWrGXLuyuimKeQ==
The final result (DecryptedPassword) is as following:
����/
+�*#7JY��,��7��$C�-|8�[��p��Ĭ���,9v��}��Hellooooo
edit:
The code is as following:
KeyPairGenerator kpg;
KeyPair kp;
PublicKey publicKey;
final PrivateKey privateKey;
try {
kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
byte[] pKbytes = Base64.encode(publicKey.getEncoded(), 0);
String pK = new String(pKbytes);
String pubKey = "-----BEGIN PUBLIC KEY-----\n" + pK + "-----END PUBLIC KEY-----\n";
Log.e(TAG, "pubKey:"+pubKey);
And then this pubKey is saved to a file named U.txt and then sent to the server
SendJSONToServerTest someTask = new SendJSONToServerTest(getApplicationContext(), "FileName"+".txt", PHP_address, getApplicationContext().getFilesDir().getPath() + "/" + "U.txt", new SendJSONToServerTest.OnEventListener<String>() {
#Override
public void onSuccess(String result) {
Log.e(TAG, "log1 = "+result);
String mstr = getBetweenStrings(result,"mystrStart=","mystrEnd");
Log.e(TAG, "log2 = "+mstr);
try {
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] encryptedBytes, decryptedBytes;
encryptedBytes = Base64.decode(mstr, Base64.DEFAULT);
decryptedBytes = cipher.doFinal(encryptedBytes);
String DecryptedPassword = new String(decryptedBytes);
Log.e(TAG, "log3= "+DecryptedPassword);
} catch(GeneralSecurityException e) {
System.out.println(e);
}
Log.e(TAG, "Endddddddddddddd");
}
#Override
public void onFailure(String f) {
Log.e(TAG, "errR.......");
}
});
someTask.execute();
PHP code related to file content:
$fileContentStr = file_get_contents($_FILES["uploaded_file"]["tmp_name"]);
echo "\nfileContentStr = {$fileContentStr}\n";
$public_key = $fileContentStr;
echo "\npublic_key = {$public_key}\n";
I have the same problem and went searching and found the answer here.
Basically, the message is padded and the byte just before your original method is the 0x00 byte (NUL character).
The following code was extracted from the link which removes the padding and returns the original message.
def pkcs1_unpad(text):
if len(text) > 0 and text[0] == '\x02':
# Find end of padding marked by nul
pos = text.find('\x00')
if pos > 0:
return text[pos+1:]
return None
I am decoding the encrypted message in python but it is easy to port it to PHP.
I hope this help you, too.
I am using Swift Sodium on client side as my server is using libsodium to encrypt the data before sharing it with me through API.
Now I have a existing private key and a public key in a String format with me. I want to now decrypt the encrypted data at my end on iOS using Swift.
How do I generate a Sodium Key Pair using the public and private key that I have?
Also Ideally I should use only the private key to decrypt the data. So how do I do that using only private key as String.
My Code for decryption is shown below -
func decryptData(dataString: String) -> String? {
let sodium = Sodium()
let privateKey = sodium?.utils.hex2bin("MY_SECRET_KEY")
let publicKey = sodium?.utils.hex2bin("MY_PUBLIC_KEY")
let message = dataString.data(using: .utf8)!
if let decrypted = sodium?.box.open(anonymousCipherText: message, recipientPublicKey: publicKey!, recipientSecretKey: privateKey!){
// authenticator is valid, decrypted contains the original message
return String(data: decrypted, encoding: String.Encoding.utf8) as String!
}
return nil
}
In the above Code my decrypted String is always empty.
The server is encrypting the data using the below function -
protected function crypt($response)
{
$message = new HiddenString($response);
$repository = \App::make(EncryptionKeysRepository::class);
$enc = $repository->findOneBy(['device' => 'android']);
$dir = $enc->getDevice();
$publicKey = $enc->getPublicKey();
$storage = storage_path();
if(!file_exists($storage."/{$dir}/")) {
mkdir($storage."/{$dir}/");
}
// save key pair to key store
$pubFilename = \tempnam($storage."/{$dir}/", 'pub_key');
file_put_contents($pubFilename, $publicKey);
$public = KeyFactory::loadEncryptionPublicKey($pubFilename);
unlink($pubFilename);
rmdir($storage."/{$dir}/");
$message = Crypto::seal($message, $public);
return $message;
}
Decrypting Logic at server
protected function deCrypt($response)
{
$repository = \App::make(EncryptionKeysRepository::class);
$enc = $repository->findOneBy(['device' => 'android']);
$dir = $enc->getDevice();
$publicKey = $enc->getSecretKey();
$storage = storage_path();
if(!file_exists($storage."/{$dir}/")) {
mkdir($storage."/{$dir}/");
}
// save key pair to key store
$secFilename = \tempnam($storage."/{$dir}/", 'sec_key');
file_put_contents($secFilename, $publicKey);
$secret = KeyFactory::loadEncryptionSecretKey($secFilename);
unlink($secFilename);
rmdir($storage."/{$dir}/");
$res = Crypto::unseal($response, $secret);
$message = $res->getString();
return response()->json(compact('message'));
}
So, server-side, you're apparently using PHP, with a library called Halite.
I'm not very familiar with Halite, but looking at its code, Crypto::seal() uses sealed boxes, so Box::open(anonymousCipherText: Data, recipientPublicKey: PublicKey, recipientSecretKey: SecretKey) -> Data? is the correct method to decrypt this in Swift.
However, Halite also seems to encode the result as a BASE64 string (urlsafe variant).
So, you need to decode this first. Or, and this is going to be way more efficient, do not encode the ciphertext. Unless you have to read them aloud, the keys probably don't need to be encoded either.
Im trying to decrypt a text sent from the server to an android application.
On PHP, I have the following:
$rsa = new Crypt_RSA();
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$key = "-----BEGIN PUBLIC KEY-----\n" . ($PublicKey)
. '-----END PUBLIC KEY-----';
$rsa->loadKey($key);
$base64 = 'data:image/' . $type . ';base64,' . base64_encode($data);
$imageEncrypt = base64_encode($rsa->encrypt($base64));
The encoding and the encryption work well.
When I send the encrypted text to android, i cannot decrypt. I used the code:
public static String decryptString(String alias,String cipherText) {
try {
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);
// RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();
Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding");
output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey());
CipherInputStream cipherInputStream = new CipherInputStream(
new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), output);
ArrayList<Byte> values = new ArrayList<>();
int nextByte;
while ((nextByte = cipherInputStream.read()) != -1) {
values.add((byte)nextByte);
}
byte[] bytes = new byte[values.size()];
for(int i = 0; i < bytes.length; i++) {
bytes[i] = values.get(i).byteValue();
}
String finalText = new String(bytes, 0, bytes.length, "UTF-8");
return finalText;
//decryptedText.setText(finalText);
} catch (Exception e) {
Toast.makeText(context, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show();
Log.e("DecryptStringTAG", Log.getStackTraceString(e));
}
return "EMPTY";
}
The error is:
java.io.IOException: Error while finalizing cipher
Caused by: javax.crypto.IllegalBlockSizeException
The odd thing is that when i try to send from PHP a message like "Hello", android decrypts it successfully. But when i send the encrypted image, I get the stated error.
I've been struggling to find the error.
Any help?
Thanks
RSA asymmetric key encryption which is what public key encryption uses, that is RSA is essentially public key encryption. If you must use public/private key pair encryption the answer is hybrid encryption, similar to what SSL does.
Create a random symmetric key use it to encrypt the data with AES. Then encrypt the symmetric key with the RSA public key.
On decryption first decrypt the symmetric key with the RSA private key and use that to decrypt the data with the symmetric AES.
If you are looking for secure encryption you really need to get someone who is a domain expert to at least design and vett the implementation. Security is very hard to get right, if it isn't right is provides no security.
I have a web-service in php that generates a keypair to encrypt a message, and one application in java that retrives the privatekey and decrypt the message.
For php I'm using http://phpseclib.sourceforge.net/ and have this two files:
keypair.php
<?php
set_time_limit(0);
if( file_exists('private.key') )
{
echo file_get_contents('private.key');
}
else
{
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->createKey();
$res = $rsa->createKey();
$privateKey = $res['privatekey'];
$publicKey = $res['publickey'];
file_put_contents('public.key', $publicKey);
file_put_contents('private.key', $privateKey);
}
?>
encrypt.php
<?php
include('Crypt/RSA.php');
//header("Content-type: text/plain");
set_time_limit(0);
$rsa = new Crypt_RSA();
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP);
$rsa->loadKey(file_get_contents('public.key')); // public key
$plaintext = 'Hello World!';
$ciphertext = $rsa->encrypt($plaintext);
echo base64_encode($ciphertext);
?>
and in java I have this code:
package com.example.app;
import java.io.DataInputStream;
import java.net.URL;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
public class MainClass {
/**
* #param args
*/
public static void main(String[] args)
{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
try {
BASE64Decoder decoder = new BASE64Decoder();
String b64PrivateKey = getContents("http://localhost/api/keypair.php").trim();
String b64EncryptedStr = getContents("http://localhost/api/encrypt.php").trim();
System.out.println("PrivateKey (b64): " + b64PrivateKey);
System.out.println(" Encrypted (b64): " + b64EncryptedStr);
SecretKeySpec privateKey = new SecretKeySpec( decoder.decodeBuffer(b64PrivateKey) , "AES");
Cipher cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plainText = decoder.decodeBuffer(b64EncryptedStr);
System.out.println(" Message: " + plainText);
}
catch( Exception e )
{
System.out.println(" Error: " + e.getMessage());
}
}
public static String getContents(String url)
{
try {
String result = "";
String line;
URL u = new URL(url);
DataInputStream theHTML = new DataInputStream(u.openStream());
while ((line = theHTML.readLine()) != null)
result = result + "\n" + line;
return result;
}
catch(Exception e){}
return "";
}
}
My questions are:
Why I'm having a exception saying "not an RSA key!"?
How can I improve this code? I have used base64 to avoid encoding and comunication errors between Java and PHP.
This concept is correct? I mean, I'm using it correctly?
With the help of above answer, I got that SecretKeySpec is used wrong, and I found that PEM file from OpenSSL isn't a 'standart format', so I need to use the PEMReader to convert it to PrivateKey class.
Here is my working class:
package com.example.app;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.StringReader;
import java.net.URL;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.Security;
import javax.crypto.Cipher;
import org.bouncycastle.openssl.PEMReader;
import sun.misc.BASE64Decoder;
public class MainClass {
/**
* #param args
*/
public static void main(String[] args)
{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
try {
BASE64Decoder decoder = new BASE64Decoder();
String b64PrivateKey = getContents("http://localhost/api/keypair.php").trim();
String b64EncryptedStr = getContents("http://localhost/api/encrypt.php").trim();
System.out.println("PrivateKey (b64): " + b64PrivateKey);
System.out.println(" Encrypted (b64): " + b64EncryptedStr);
byte[] decodedKey = decoder.decodeBuffer(b64PrivateKey);
byte[] decodedStr = decoder.decodeBuffer(b64EncryptedStr);
PrivateKey privateKey = strToPrivateKey(new String(decodedKey));
Cipher cipher = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plainText = cipher.doFinal(decodedStr);
System.out.println(" Message: " + new String(plainText));
}
catch( Exception e )
{
System.out.println(" Error: " + e.getMessage());
}
}
public static String getContents(String url)
{
try {
String result = "";
String line;
URL u = new URL(url);
DataInputStream theHTML = new DataInputStream(u.openStream());
while ((line = theHTML.readLine()) != null)
result = result + "\n" + line;
return result;
}
catch(Exception e){}
return "";
}
public static PrivateKey strToPrivateKey(String s)
{
try {
BufferedReader br = new BufferedReader( new StringReader(s) );
PEMReader pr = new PEMReader(br);
KeyPair kp = (KeyPair)pr.readObject();
pr.close();
return kp.getPrivate();
}
catch( Exception e )
{
}
return null;
}
}
Here is my keypair.php
<?php
set_time_limit(0);
if( file_exists('private.key') )
{
echo base64_encode(file_get_contents('private.key'));
}
else
{
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->setHash('sha1');
$rsa->setMGFHash('sha1');
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP);
$rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1);
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1);
$res = $rsa->createKey(1024);
$privateKey = $res['privatekey'];
$publicKey = $res['publickey'];
file_put_contents('public.key', $publicKey);
file_put_contents('private.key', $privateKey);
echo base64_encode($privateKey);
}
?>
and my encrypt.php
<?php
include('Crypt/RSA.php');
set_time_limit(0);
$rsa = new Crypt_RSA();
$rsa->setHash('sha1');
$rsa->setMGFHash('sha1');
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP);
$rsa->loadKey(file_get_contents('public.key')); // public key
$plaintext = 'Hello World!';
$ciphertext = $rsa->encrypt($plaintext);
$md5 = md5($ciphertext);
file_put_contents('md5.txt', $md5);
file_put_contents('encrypted.txt', base64_encode($ciphertext));
echo base64_encode($ciphertext);
?>
I hope it helps anyone and thanks.
A few thoughts.
Shouldn't you be echo'ing out $privatekey in the else as well?
Are you using the latest Git version of phpseclib? I ask because a while ago there was this commit:
https://github.com/phpseclib/phpseclib/commit/e4ccaef7bf74833891386232946d2168a9e2fce2#phpseclib/Crypt/RSA.php
The commit was inspired by https://stackoverflow.com/a/13908986/569976
Might be worthwhile if you change your tags up a bit to include bouncycastle and phpseclib. I'd add those tags but some tags will have to be removed since you're already at the limit of 5. I'll let you decide which ones to remove (if you even want to do that).
SecretKeySpec privateKey = new SecretKeySpec( decoder.decodeBuffer(b64PrivateKey) , "AES");
b64PrivateKey is supposed to contain the private key right? 'cause looking it up in the docs it looks like SecretKeySpec is only intended for symmetric algorithms (like AES) - not asymmetric ones like RSA.
I did some digging into the classes you're using and it seems like what you've posted has most of the default parameters matching your explicit parameters. However, this doesn't ensure that your configuration has those all set to match the current documentation if you're using older implementations.
Also, a tip from a senior Facebook security engineer who discussed a similar issue in a lecture recently; different libraries implementing the same security protocols will oftentimes be incompatible and even the same libraries in different environments or languages will oftentimes fail to work together. With that in mind, a few things to try given that working examples similar to your setup exist online:
Make sure you're using the latest versions of libraries. Also note that some javax functions and classes are deprecated and suggest using java.security now (didn't check if this applies to your case).
Force the key size to be consistent (1024 or 2048). The java implementation can do either and I didn't find consistent documentation for both libraries saying what your configuration default would be used (could be causing problem #2, though you might get a different exception for invalid key size).
Ensure that your privatekey matches expectation (length/reads the same between java and php).
Force default values to be explicitly defined (set CryptRSA hash to sha1, key lengths, anything else you can explicitly set).
Try encrypting the same message with both java and php to see if you can get the same outputs. Do this after ensuring your keys read the same and don't throw exceptions while being used in both applications. Encrypting a single character can tell you if the same padding scheme is actually being used (it appears from the source code that both use MGF1, but it never hurts to check outputs).
Finally try taking an example for php to java encryption which is said to already work and do one change at a time until you get back to your current encryption scheme. I saw a few examples googling quickly which used different parameters and settings with CryptRSA and java security that stated they were working together. Take a working example and try swapping the hash function and then the encryption, etc.