Encrypt in Android, Decrypt in PHP using phpseclib - android

I encrypt data in Android application and encode it to send over to php webservice.
Then I decode it and decrypt it using $rsa->decrypt(base64_decode($data));
$rsa->decrypt return false. Why is that so?
This is the code in android
//get private key
String privateKeyString = "MIICWwIBAAKBgQDVIJ8H3Oszc5fWdgpwymWksF1WxkXJHIfdS6Ta1bHeqwEzPIkN f3iVk14LfaoSZpRb9Yvi/jvkXxIzJbHq6aKfnQOC6tKIiixvVvpCfxr1eV4urDdz H9RNy9bqGdXzTQdgQi+KRx0Dcy9RNsl7ZGLAGrUFRnPI4GTdH+7wm4QogQIDAQAB AoGAcUcKX7KC7HDm5h0NRY+94H/AzItLsi3Q5MT81Tc5d+EqHSJysdLrs4yFMtRS 3b7Z4dqrxDVefe4uDTNe0j3lqboKz8oKwAU+paKx3wubHb7aeQnfzwM9mPQJHgEO zBjlvbL4oEa/gklu3VohZAc1daqpPajdWuOQQp4S+jUllrECQQDrITlSjvkxt8ud /vYIcEXHew3iW4nzaAH3z4PRAGZofRpk/OusGZ6cdZoYMTZcdxYTCCbZ5eeyGukW 5QCadie1AkEA6Atx8Z0F7WhLI2lGvCGy+vIOL0vBDZSma0cvLYLAXMx8duoWQ9J2 LwT7SsnRXMeq/8wlNHL7mFEf+YFZBKKlHQJAO78kfrr/zUdjwREBWaGVyZuWKpeS FTyvi1W6rAgK/bAUXeb6x69241DqyAzxQEuuW0WuAZ5u4o39/qhQH++4JQJAAepe RW1TaDNNM3yh/dmVXabz4QYSEOeiPA55YDnNFrcFbAHgryyklxzGakaiOM7ZJYVs 5TLxyr8YsXmU34nsLQJALzC8CaFXJcnU0+6+KoKX7iq1aP3X4LgP4Gianix6pfRo aV8UHnfFLRSgPdn1ZYmKtJfnsJXJYoE+o9xEErb5EQ==";
// converts the String to a PublicKey instance
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(privateKeyString.toString(), Base64.DEFAULT)));
// encrypts the message
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrpytedText = cipher.doFinal(Base64.encode(phoneUid.getBytes("CP1252"), Base64.DEFAULT));
data = new String(encrpytedText, "CP1252");
This is the code in php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
//Set the encryption mode
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
//check for required fields
if (isset($_POST['data']) {
$data= $_POST['data'];
$key = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVIJ8H3Oszc5fWdgpwymWksF1W xkXJHIfdS6Ta1bHeqwEzPIkNf3iVk14LfaoSZpRb9Yvi/jvkXxIzJbHq6aKfnQOC 6tKIiixvVvpCfxr1eV4urDdzH9RNy9bqGdXzTQdgQi+KRx0Dcy9RNsl7ZGLAGrUF RnPI4GTdH+7wm4QogQIDAQAB
-----END PUBLIC KEY-----';
$rsa->loadKey($key );
$decrypted = $rsa->decrypt(base64_decode($data));

I solved my problem. Here is a reference for those who are doing similar thing to me. Hope that this will help other people.
In android (RSA encrypt with private key)
String encoded = null;
byte[] encrypted = null;
String plaintext = "...";
try {
String privKeyPEM = "...";
byte[] decoded = Base64.decode(privKeyPEM, Base64.DEFAULT);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
encrypted = cipher.doFinal(plaintext.getBytes());
encoded = Base64.encodeToString(encrypted, Base64.DEFAULT);
}
catch (Exception e) {
e.printStackTrace();
}
In PHP (decrypt with public key) (using phpseclib)
$publickey = '...';
$rsa = new Crypt_RSA();
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
//decode and decrypt
$decoded = base64_decode($encrypted);
$rsa->loadKey($publickey);
$decrypted = $rsa->decrypt($decoded);

Are you passing the right parameters to cipher.doFinal?
Quoting the javadoc entry:
doFinal(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset)
Also, it looks like you're base64-encoding the plaintext before encryption when you should be encrypting the ciphertext after encryption?

Related

pad block corrupted?

I have been using the following two methods to encrypt and decrypt sensitive info.
public static String encryptSensitiveInfo(String strToEncrypt,
String saltToEncrypt) throws Exception {
String encryptedString = "";
byte[] encryptedValue;
Key key = new SecretKeySpec(saltToEncrypt.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
encryptedValue = cipher.doFinal(strToEncrypt.getBytes());
encryptedString = new String(Base64.encodeBase64(encryptedValue));
encryptedValue = null;
return encryptedString;
}
public static String decryptSensitiveInfo(String strToDecrypt,
String saltToDecrypt) throws Exception {
String decryptedString = "";
byte[] decryptedValue;
Key key = new SecretKeySpec(saltToDecrypt.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
decryptedValue = cipher.doFinal(Base64.decodeBase64(strToDecrypt
.getBytes()));
decryptedString = new String(decryptedValue);
decryptedValue = null;
return decryptedString;
}
At the time of decryption I get "pad block corrupted" execption. Any help to resolve this issue would be very much appreciated. Thanks in advance.
You're correctly performing base 64 on the ciphertext because the ciphertext consists of random looking bytes. However, you forget to do the same with your key (which, inexplicably, is called saltToDecrypt in your code). If the keys do not match or if the ciphertext has become corrupted then you will almost certainly run into a BadPaddingException.
If the amount of ciphertext has changed an IllegalBlockSizeException is more likely and if the key isn't of the right size for AES, an InvalidKeyException.

encrypt rsa android from public key .pem format

i already wandered in many stackoverflows post but still cant figured out whats wrong with my code.
public class KripAsim {
String hasil;
public String encrypt(String text) {
try {
String PUBLIC_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvCNqSslgCndo8vfNrkXFDLXmst024Oi8D7LEiJFjYBva4kBKKISe8rKp58kCLLHjv90RN+Dy2KWcf0eFkKaqc3zILBI99JhV1z8TFOzmt5dfgW6fD1ucBfsK6pWxK84DddyOqKldwHlReqjuDHT2jLue51vpXaCa12WV5bMnGsfy3vZKnp699YCguqRpTR1MijZ9pz8WqldrR0a/DCaq5YxZ7lvjwuWIodQy3S3XRHAaeaUrFHFFLumzXAGuP447oRYR0p+1qsy8+wOtrsGm8m8bMg+C1XGMblkODtOFHz3wtrRZ5OwzgEm7J7odmSX8mSYBZYLcnUVqIFRsQkZLiwIDAQAB";
byte [] decoded = Base64.decode(PUBLIC_KEY,Base64.NO_WRAP);
KeyFactory keyFac = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
PublicKey pubkey = keyFac.generatePublic(keySpec);
Cipher rsa;
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, pubkey);
hasil = Base64.encodeToString(rsa.doFinal(text.getBytes("UTF-8")),Base64.NO_WRAP);
return hasil;
} catch (Exception e) {
e.printStackTrace();
}
return hasil;
}
}
it returns null. please help. thanks in advance
The line PublicKey pubkey = keyFac.generatePublic(keySpec); throws an InvalidKeySpecException because you have incorrectly supplied a PKCS8EncodedKeySpec when you should have supplied an X509EncodedKeySpec.
You should also completely specify the transformation string in the Cipher.getInstance() method. The following fragment taken from your code illustrates this:
byte[] decoded = Base64.decode(PUBLIC_KEY, Base64.DEFAULT);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);
PublicKey pubkey = KeyFactory.getInstance("RSA").generatePublic(keySpec);
Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding");
rsa.init(Cipher.ENCRYPT_MODE, pubkey);
String hasil = Base64.encodeToString(rsa.doFinal(text.getBytes("UTF-8")),
Base64.NO_WRAP);
return hasil;
Finally, you should avoid encrypting data directly with RSA. The correct method is to use a hybrid encryption scheme in which the data is encrypted with a block cipher, say AES in GCM mode, using a randomly generated AES key, and then this key is encrypted using RSA. There are many examples of this around for reference.

RSA Encrypt string with given public key XML - modules and exponent - ANDROID

I'm trying to implement the login logic in my android app.
This is the scenario:
on click the app send a GET Request to Azure Server and receive an XML string with the RSA Public Key (module + exp)
the password is hashed and encripted
RSA(hash(pwd)) is sent to my server.
I'm using this logic in my angular app and it working well.
The problem
When I'm trying to encrypt my hashed password the method retur some illeggible character.. A conversion problem?
The Android Code
if (jsonToken.has("data"))
{
// Converte la Stringa in XML
String publicToken = jsonToken.getString("data");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document publicTokenXML = builder.parse(new InputSource(new StringReader(publicToken)));
Log.i("out", token.toString());
Log.i("outXML", publicTokenXML.getFirstChild().getFirstChild().getNextSibling().getTextContent());
// prendo le due parti che mi servono dall'XML
byte[] expBytes = Base64.decode(publicTokenXML.getFirstChild().getFirstChild().getNextSibling().getTextContent(), Base64.NO_PADDING);
byte[] modBytes = Base64.decode(publicTokenXML.getFirstChild().getFirstChild().getTextContent(), Base64.NO_PADDING);
byte[] dBytes = Base64.decode(d, Base64.DEFAULT);
BigInteger modules = new BigInteger(1, modBytes);
BigInteger exponent = new BigInteger(1, expBytes);
BigInteger d = new BigInteger(1, dBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("RSA");
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modules, exponent);
PublicKey pubKey = keyFactory.generatePublic(pubSpec);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = cipher.doFinal(bin2hex(getHash(pwd)).getBytes("UTF-8"));
// new String(encrypted)) is correct?!?
Log.i("encrypted: ", new String(encrypted));
RSAPrivateKeySpec privSpec = new RSAPrivateKeySpec(modules, d);
PrivateKey privKey = keyFactory.generatePrivate(privSpec);
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("decrypted: " + new String(decrypted));
Log.i("hash", bin2hex(getHash(pwd)));
}
'decrypted' and 'hash' logs return to me the same hash string. I logged the part of the public and private key and they are correct.
BUT
How I can read it in the right form?
Thanks everyone.
** EDIT/Solution **
For who, how me, followed some tutorial on the web for implement RSA encryption in .NET, this is the solution that worked for me for the scenario I described at the top of the SO Query:
The Android Code
if (jsonToken.has("data"))
{
// Converte la Stringa in XML
String publicToken = jsonToken.getString("data");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document publicTokenXML = builder.parse(new InputSource(new StringReader(publicToken)));
Log.i("out", token.toString());
Log.i("outXML", publicTokenXML.getFirstChild().getFirstChild().getNextSibling().getTextContent());
// <RSAKeyValue><Modulus>*Modulus*</Modulus><Exponent>*Exponent*</Exponent></RSAKeyValue>
byte[] expBytes = Base64.decode(publicTokenXML.getFirstChild().getFirstChild().getNextSibling().getTextContent(), Base64.DEFAULT);
byte[] modBytes = Base64.decode(publicTokenXML.getFirstChild().getFirstChild().getTextContent(), Base64.DEFAULT);
byte[] dBytes = Base64.decode(d, Base64.DEFAULT);
BigInteger modules = new BigInteger(1, modBytes);
BigInteger exponent = new BigInteger(1, expBytes);
BigInteger d = new BigInteger(1, dBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// *****
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// *****
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modules, exponent);
PublicKey pubKey = keyFactory.generatePublic(pubSpec);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = cipher.doFinal(bin2hex(getHash(pwd)).getBytes("UTF-8"));
// *****
Log.i("encrypted: ", Base64.encodeToString(encrypted, Base64.DEFAULT).replace("\n", ""));
// *****
// Decrypt
RSAPrivateKeySpec privSpec = new RSAPrivateKeySpec(modules, d);
PrivateKey privKey = keyFactory.generatePrivate(privSpec);
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("decrypted: " + new String(decrypted));
Log.i("hash", bin2hex(getHash(pwd)));
}
** The .NET Core code **
private string RsaMyAndroidDecrypt(string passwordCrypt)
{
// Don't use OAEP padding (PKCS#1 v2).
var doOaepPadding = false;
// ------------------------------------------------
// Decrypt
// ------------------------------------------------
// Convert base64 string back to bytes.
var encryptedBytes = Convert.FromBase64String(passwordCrypt);
// Create a new instance of RSACryptoServiceProvider.
var rsa = new RSACryptoServiceProvider();
// Import the RSA Key information.
rsa.ImportParameters(keyForUserPassword.ExportParameters(true));
// Decrypt byte array.
var decryptedBytes = rsa.Decrypt(encryptedBytes, doOaepPadding);
// Get decrypted data.
return Encoding.UTF8.GetString(decryptedBytes);
}
For .NET core you shouldn't use RSACryptoServiceProvider, but rather:
private byte[] Decrypt(string input)
{
// Don't use OAEP padding (PKCS#1 v2).
RSAEncryptionPadding padding = RSAEncryptionPadding.Pkcs1;
// ------------------------------------------------
// Decrypt
// ------------------------------------------------
// Convert base64 string back to bytes.
byte[] encryptedBytes = Convert.FromBase64String(passwordCrypt);
// Create a new instance of RSA.
using (RSA rsa = RSA.Create())
{
// Import the RSA Key information.
rsa.ImportParameters(keyForUserPassword.ExportParameters(true));
// Decrypt byte array.
byte[] decryptedBytes = rsa.Decrypt(encryptedBytes, padding);
return decryptedBytes;
}
}
I'm ignoring the part here where you're turning it into a UTF-8 string. And the part where you're recovering a thing that you call a password.
RSACryptoServiceProvider only works on Windows. RSA.Create() will return an appropriate implementation of RSA for whatever platform the code is executing on.

Trying to get asymmetric RSA encryption to work

The following method does not work. decodedMessage ends up with garbage in it instead of the expected results.
I'm following an example here that supposedly works.
public static void POCSimple()
{
String secretMessage = "short message";
PublicKey publicKey = null;
PrivateKey privateKey = null;
String encodedMessage = "";
byte[] encodedBytes = null;
String decodedMessage ="";
byte[] decodedBytes = null;
try
{
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
Cipher c1 = Cipher.getInstance("RSA");
c1.init(Cipher.ENCRYPT_MODE, publicKey);
encodedBytes = c1.doFinal(secretMessage.getBytes());
encodedMessage = Base64.encodeToString(encodedBytes, Base64.DEFAULT);
Cipher c2 = Cipher.getInstance("RSA");
c2.init(Cipher.DECRYPT_MODE, privateKey);
decodedBytes = c2.doFinal(encodedBytes);
decodedMessage = Base64.encodeToString(decodedBytes, Base64.DEFAULT);
String mystring = "look at results";
}
catch (Exception e)
{
String status = e.toString();
}
}
Any help would be most appreciated.
Thanks,
Dean
Of course you are getting garbage, this is your sequence:
Plaintext message -> Encrypt -> Encode -> Encoded message
Encoded message -> Decrypt -> Decode -> GARBAGE
You need to undo the Base64 encoding before you can decrypt the message, you are doing the reverse process in the incorrect order!
Edit
Actually its worse, this is your sequence:
Plaintext message -> Encrypt -> Encode -> Encoded message
Encrypted message -> Decrypt -> Encode -> GARBAGE
Try this:
Cipher c1 = Cipher.getInstance("RSA");
c1.init(Cipher.ENCRYPT_MODE, publicKey);
encodedBytes = c1.doFinal(secretMessage.getBytes());
encodedMessage = Base64.encodeToString(encodedBytes, Base64.DEFAULT);
Cipher c2 = Cipher.getInstance("RSA");
c2.init(Cipher.DECRYPT_MODE, privateKey)
decodedBytes = Base64.decode(encodedMessage.toByteArray(), Base64.DEFAULT);
decryptedMessage = c2.doFinal(decodedBytes);
It turns out that in my original code, decodedBytes contained the properly decrypted bytes. The following command was turning decodedBytes into junk characters ...
decodedMessage = Base64.encodeToString(decodedBytes, Base64.DEFAULT);
I replaced that code with ...
String str = new String(decodedBytes, "UTF-8");
And this solved the problem probably because decodedBytes had never been Base64 encoded in the first place.
I also found that using straight RSA I can only encrypt a maximum of 245 bytes if I use a 2048 bit key. less if I use a 1024 bit key.
If larger strings need to be encrypted using asymmetric Public/Private keys then I need to first encrypt a string using symmetric AES and then encrypt the AES key with the public RSA key and send both the encrypted AES key and the encrypted message over the wire where the receiver can decrypt the AES key using their private RSA key. The AES key can be randomly generated in the sending code.

Generating PublicKey from Base64 encoded String

I need to use PublicKey to encrypt session key but I have public key as Base64 encoded string so I tried to convert it as :
KeyFactory kf = KeyFactory.getInstance("RSA"); // or "EC" or whatever
byte[] keyBytes = _publicKey.getBytes();
byte[] publicKeyBytes = Base64.decode(keyBytes, Base64.DEFAULT);
X509EncodedKeySpec publicKeySpecs = new X509EncodedKeySpec(publicKeyBytes);
Log.v("", "X509 KS" + publicKeySpecs);
publicKey = kf.generatePublic(publicKeySpecs);
But I am getting public key as null value and getting exception as :
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
If anyone has idea how to do it or have working code then please help me.
Thanks in Advance
Hi guys I generated Public Key from Base64 encoded String .
after wondering for few days this code worked for me.
String _publicKey= base64encoded_publickey;
// InputStream fileInputStream = null;
try {
// Receiving side
byte[] decodedBytes = Base64.decode(_publicKey, Base64.DEFAULT);
fileInputStream = new ByteArrayInputStream(decodedBytes);
// Print the decoded string
String decodedString = new String(decodedBytes);
CertificateFactory certFactory = CertificateFactory
.getInstance(CERTIFICATE_TYPE);
// certFactory.
X509Certificate cert = (X509Certificate) certFactory
.generateCertificate(fileInputStream);
publicKey = cert.getPublicKey();
Log.d("TAG", "Public key of the certificate:" + publicKey);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Could not intialize encryption module",
e);
} finally {
if (keyStream != null) {
try {
keyStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Hope it will help you...Thanks...
well, last time it worked for me (I used bouncyCastle library..), you might need to modify it if it doesn't suit your need..
KeyPairGenerator keyGen = null;
KeyPair kp = null;
keyGen = KeyPairGenerator.getInstance("ECDSA", "BC");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("brainpoolp160r1");
keyGen.initialize(ecSpec, new SecureRandom());
kp = keyGen.generateKeyPair();
EDIT:
(oops my bad, that's for initial generation..)
at that time I stored them as one..
and I need to convert the Base64 string back to array of byte first, and split them into part 1 and part 2..
private void splitKeyPair (byte[] thekeypair, byte[] part1, byte[] part2)
{
System.arraycopy(thekeypair, 0, part1, 0, 68);
System.arraycopy(thekeypair, 68, part2, 0, 115);
}
byte[] part1 = new byte[68];
byte[] part2 = new byte[115];
splitKeyPair(inputBuffer, part1, part2);
fact = KeyFactory.getInstance("ECDSA", "BC");
kp = new KeyPair(fact.generatePublic(new X509EncodedKeySpec(part1)),
fact.generatePrivate(new PKCS8EncodedKeySpec(part2)));
hope it helps..
*note: the size might vary, depending on the spec..

Categories

Resources