Android AES 256-bit Encrypt data - android

So I've seen a lot of examples, and done a lot of googling, and looked at examples on Stack Overflow... and I need help. I've got an Android application and I'm storing username and passwords on the device, and I need to encrypt them AES 256. From looking at examples, this is what I have so far:
public class Security {
Cipher ecipher;
Cipher dcipher;
// 8-byte Salt
byte[] salt = {
(byte)0xA9, (byte)0x9B, (byte)0xC8, (byte)0x32,
(byte)0x56, (byte)0x35, (byte)0xE3, (byte)0x03
};
// Iteration count
int iterationCount = 19;
public Security (String passPhrase) {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance(
"PBEWithSHAAndAES").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception e) {
e.printStackTrace();
}
}
public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return Base64.encodeToString(enc, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = Base64.decode(str, Base64.DEFAULT);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
I'm trying to make it password based, so a user will create an account the first time using the username and password needed to communicate back to the server, and create a PIN that will be used as the key for these credentials stored in the database.
What I'm mainly concerned about is does this look secure? I know a fixed salt is bad, how do I fix that?
I know there's been like a billion questions about this, but I want someone to just come out and say "THIS IS SECURE" or "THIS IS NOT SECURE, CHANGE THIS"
Thanks!
EDIT:
So this is the code I have so far, and it seems to be working...
public class Security {
Cipher ecipher;
Cipher dcipher;
byte[] salt = new byte[8];
int iterationCount = 200;
public Security(String passPhrase) {
try {
// generate a random salt
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance(
"PBEWithSHA256And256BitAES-CBC-BC").generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (Exception e) {
e.printStackTrace();
}
}
public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return Base64.encodeToString(enc, Base64.DEFAULT);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = Base64.decode(str, Base64.DEFAULT);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public int getIterationCount() {
return iterationCount;
}
public String getSalt() {
return Base64.encodeToString(salt, Base64.DEFAULT);
}
}
I used this code to test it:
Security s = new Security(pinBox.getText().toString());
String encrypted = s.encrypt(passwordBox.getText().toString());
String decrypted = s.decrypt(encrypted);
builder.setMessage("pin: " + pinBox.getText().toString() + "\n" +
"password: " + passwordBox.getText().toString() + "\n" +
"encrypted: " + encrypted + "\n" +
"decrypted: " + decrypted + "\n" +
"salt: " + s.getSalt());
So I don't need to worry about an initialization vector? Or specifically hardcode a Cipher algorithm?
Thanks again!

EDIT: While the code below is correct, what you have is doing basically the same thing, with the IV derived from the password, so you don't have to store it separately.
Does your code work as expected? For the actual encryption/decryption you would want to use AES, most probably in CBC mode. Then you would need an IV, so it becomes something like this:
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
byte[] enc = ecipher.doFinal(utf8);
Whether it is secure depends on what you are using this for. The purpose of the salt is to make it harder to brute force the passphrase: if it's random the attacker cannot use pre-generated passphrase tables (passphrase->key). If you are not too worried about this sort of attack, you might leave it fixed. If you decide to make it random, just store it with the encrypted data. Same with the IV.

Related

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.

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..

Encrypt data in Android with a RSA public.pem file generated in Ruby

sorry to ask a particular question, but I need to generate in java code a 'signature' like the following code line in ruby:
signature = OpenSSL::PKey::RSA.new(File.read("PUBLIC_PEM_PATH")).public_encrypt('SECRET_KEY')
I have the .pem key file and the SECRET_KEY that's is something like: F6qxlwQTYWRM3gRfgftryKJHKYZiGXdoy5lDm4
How can I do this ?
Thanks!
UPDATE 1
I tried this :
File pubKeyFile = new File(keyFileName);
DataInputStream inputStream;
byte[] signature = null;
try {
inputStream = new DataInputStream(new FileInputStream(pubKeyFile));
byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
inputStream.readFully(pubKeyBytes);
inputStream.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubSpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
signature = cipher.doFinal(secretKey.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return signature;
And got this error:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
UPDATE2
I managed to load a public key from a .pem file. But now, Im getting an error from the cipher.
public static byte[] getSignature(String keyFileName, byte[] secretKey){
byte[] signature = null;
try {
PublicKey pubKey = readKeyFromFile(keyFileName);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
signature = cipher.doFinal(secretKey);
} catch (Exception e) {
e.printStackTrace();
}
return signature;
}
private static PublicKey readKeyFromFile(String keyFileName) throws IOException {
InputStream in = new FileInputStream(keyFileName);
DataInputStream din = new DataInputStream(in);
try {
BigInteger m = BigInteger.valueOf(din.readLong());
BigInteger e = BigInteger.valueOf(din.readLong());
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) fact.generatePublic(keySpec);
return pubKey;
} catch (Exception e) {
throw new RuntimeException("Spurious serialisation error", e);
} finally {
din.close();
}
}
The error log:
javax.crypto.IllegalBlockSizeException: input must be under 8 bytes
Any thoughts??
So finally, after hours of research I came with a solution and finally could get read my public key from .pem file and generate an instance of this key. Hence, I managed to encrypt the data.
But I had to copy and paste the key content without any special character like '\n' and make a publicKeyString with it
----------BEGIN RSA PUBLIC KEY---------
key content
----------END RSA PUBLIC KEY---------
static public PublicKey publicKey(String publicKeyString) {
try {
byte[] decodedPublicKey = Base64.decode(publicKeyString, 0);
ASN1InputStream in = new ASN1InputStream(decodedPublicKey);
ASN1Primitive obj = in.readObject();
RSAPublicKeyStructure keyStruct = RSAPublicKeyStructure.getInstance(obj);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(keyStruct.getModulus(), keyStruct.getPublicExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
I failed even when I tried to use PEMReader from bouncy castle. All the problems have something to do with keys generated in Ruby version prior 1.9.3 as described with details here.
Anyway, thanks a lot for the attention disposed.
Looks like key wrapping (encryption) to me. Use bouncy castle to read the PEM file, then use Cipher.getInstance("RSA/ECB/PKCS1Padding") to encrypt the secret key, and base 64 encode the result... You could also try the mode Cipher.WRAP_MODE to see if that works. Note that encryption will always return a different result, the only way to test for compatibility is to decrypt it with the other software.

Encrypt in Android, Decrypt in PHP using phpseclib

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?

Encrypting a string with PublicKey + Base64 encryption

I have a RSA public key in String format and I want to encrypt a string using the same to Base 64. Here is the code that I am using for this:
function encrypt(String pin){
String key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiOnM5t6w2ZD6dpA4/MzSTAOt0IYpnsmGSAIfIVgGntI+fI4wbvUvMIhaLN3fHrjyuNGFdYw+yuoXYkapajt6VTZJniaatSiq6bwQ7R0UAop6haFSAwjnmReqexJvcKyqUsTfcfFypPpsYRewh/48/jmc/6ND+ugxDd52prkPUrbj+nnO0z3DBoUCpgDMRvW2hWXv6kZ654gp+wIAQnxbdwRMy6FZbrHjkA3tc6U0CHK+KjxAfzWAK+yI+ofskM4qk50J7y9hUZ7lLikqWZWKiqh8xiDk1kgu+FIjVh+fylKpa3gWmPPn0fSpBJjuenc1OQVmZ718a3388DjzFlYOLwIDAQAB";
byte[] sigBytes2 = Base64.decode(key, Base64.DEFAULT);
Log.d("WS", "new key is: " + key);
try {
PublicKey publicKey = KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(sigBytes2));
encryptedBase64PIN = encode(publicKey, pin);
Log.d("WSA", "encoded key is: " + encryptedBase64PIN);
// getSecToken();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encode(Key publicKey, String data)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] byteData = data.getBytes(); // convert string to byte array
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedByteData = cipher.doFinal(byteData);
String s = Base64.encodeToString(encryptedByteData, Base64.NO_WRAP);
return s; // convert encrypted byte array to string and return it
}
When I send the same output to the server, it says "cannot decrypt the PIN". Why is this?
When I send the same output to the server, it says "cannot decrypt the PIN". Why is this?
There could be many reasons for this. If you have the code for the server decryption, please add it to your question.
Reasons may include:
Wrong encryption mode. You've selected RSA/ECB/NoPadding but perhaps the server is expecting something else?
Base64 encoding - is the server definitely expecting this?
The message is "cannot decrypt PIN". If the data you are encrypting is the PIN are you sure you should be encrypting ASCII bytes?

Categories

Resources