I'm developing a native android app and hybrid IOS app. I'm encrypting the password before sending the request to BL. Below is my native code.
public String Encrypt (String plain) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
try {
AssetManager assets = context.getAssets();
byte[] key = readFully(
assets.open("encryption.der", AssetManager.ACCESS_BUFFER));
KeySpec publicKeySpec = new X509EncodedKeySpec(key);
KeyFactory kf = KeyFactory.getInstance("RSA");
Key pk = kf.generatePublic(publicKeySpec);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pk);
ByteArrayOutputStream out = new ByteArrayOutputStream();
CipherOutputStream cout = new CipherOutputStream(out, cipher);
try {
cout.write(plain.getBytes(UTF_8));
cout.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
cout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
encrypted = new String(encode(out.toByteArray(), DEFAULT), "UTF-8");
return encrypted;
} catch (IOException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return null;
}
static byte[] readFully(InputStream inputStream) throws IOException {
InputStream in = new BufferedInputStream(inputStream);
byte[] tmp = new byte[1024];
int readLen, size = 0;
ByteArrayOutputStream out = new ByteArrayOutputStream();
while ((readLen = in.read(tmp)) != -1) {
if (((long) size + readLen) > Integer.MAX_VALUE) {
// woah! did we just ship an app of 2GB?
throw new IllegalStateException("Invalid file. File size exceeds expected "
+ "maximum of 2GB");
}
size += readLen;
out.write(tmp, 0, readLen);
}
return out.toByteArray();
}
I have my key in encryption.der file. Everything works fine in android. Now coming to IOS which I'm using Ionic to develop. I'm not able to achieve the encryption part. I have used the "cryptico" : link : https://github.com/wwwtyro/cryptico/blob/master/README.md .
And finally converting to Base64 like these.
var EncryptionPassword = cryptico.encrypt($scope.userInfo.Password, publicKey);
$scope.encPass = base64.encode(EncryptionPassword.cipher);
But I'm getting ArrayIndexOutOfBound Exception from BL. Can you suggest exact same solution has android for angularjs too. So RSA encrytion works on both IOS and Android.
Create a Service and place your public Key inside that.
.service('Settings', function(){
this.publicKey = 'MIIBIjANBgdcssvsvsfvsfvsfvrefvfvfviuwoihijwfoiw278499080989i09M+KC8MYYOu/NRLmFg85LRrfRszyI/vZ/k8982789uiwbgchdbhU+3joQZoJ3Sxq/GbIIFf/3y4f9DuKI53y1qR2qD4xIskfa9rPVqvBtAu2KSNRd8V4J8RKI2gT2YEA+A3Z0mQq4GBRS8iYmGLqRQyPfNUSankylBrTpOIVFBZORdZehjJMmwl98UynyfnyMIHUIFuhefuibiufbeufbsoijn93fD7nxt+siZryfazn3EAgBaTKTV/U5xIepzDN6ZYJ4qnC93u6erdb1X4m1zU6RGapwzCOPOORTyzw/uWJ8twcODNt0cqVp+sYQIDAQAB';
})
Now in your JS encrypt using public key and JSEncrypt.
var encrypt = new JSEncrypt(); encrypt.setPublicKey(Settings.publicKey);
EncryptionPin = encrypt.encrypt($scope.customerInfo.Pin);
EncryptionPin is the final key.
Related
I want to calculate the MD5 hash of the APK file from inside the app.
PackageInfo info = App.getInstance().getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA);
File file = new File(info.applicationInfo.sourceDir);
String hash = MD5.calculateMD5(file);
The MD5 hash is calculated like this:
private String calculateMD5() {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
return null;
}
InputStream is;
try {
is = new FileInputStream(file);
} catch (FileNotFoundException e) {
return null;
}
byte[] buffer = new byte[8192];
int read;
try {
while ((read = is.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
byte[] md5sum = digest.digest();
BigInteger bigInt = new BigInteger(1, md5sum);
String output = bigInt.toString(16);
output = String.format("%32s", output).replace(' ', '0');
return output;
} catch (IOException e) {
throw new RuntimeException("Unable to process file for MD5", e);
} finally {
try {
is.close();
} catch (IOException e) {
}
}
}
However I keep getting the same hash when running in the emulator, even when I change the source code.
What is wrong here?
Double check that you are truly running against a modified build. I suggest completely uninstalling and then reinstalling after the code change. I learned this the hard way after debugging an web app for two hours to find that I hadn't deployed the change I made.
I have to implement encryption in android app. The web developer is using CryptoJs library. means Encryption alog is AES256 encryption.
Both iOS and android platforms give different strings and iOS one is accepted at web.It should be same for sample strings.
I am using below code snippets (there are 2 different diffrent functions):
private void newEnc() {
String secret = "LSC#SD2017#ps";
String cipherText = "{\"device_type\":\"iOS\",\"email\" : \"jhon#gmail.com\",\"device_id\" : \"14105DA4-CEE5-431E-96A2-2331CDA7F062\",\"password\" : \"123456\",\"device_token\" : \"B44777563552882EC3139A0317E401B55D6FC699D0AC3D279F392927CAF9B566\"}";
KeyGenerator kgen = null;
try {
kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(secret.getBytes("UTF8"));
kgen.init(256, sr);
SecretKey skey = kgen.generateKey();
Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec skeySpec = new SecretKeySpec(skey.getEncoded(), "AES");
c.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] decrypted = c.doFinal(cipherText.getBytes());
System.out.println(Base64.encodeToString(decrypted, Base64.NO_WRAP));
// decrypted = Base64.encodeBase64(decrypted);
// byte[] iv = Base64.encodeBase64(c.getIV());
// Log.e("encryptString", new String(decrypted));
// Log.d("encryptString iv", new String(iv));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
I also used :
private void enctest(String cipherText) {
String secret = "LSC#SD2017#ps";
// String cipherText = "{\"device_type\":\"iOS\",\"email\" : \"jhon#gmail.com\",\"device_id\" : \"14105DA4-CEE5-431E-96A2-2331CDA7F062\",\"password\" : \"123456\",\"device_token\" : \"B44777563552882EC3139A0317E401B55D6FC699D0AC3D279F392927CAF9B566\"}";
MessageDigest md5 = null;
try {
// String cipherText = "U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";
byte[] cipherData = Base64.decode(cipherText.getBytes(), Base64.NO_WRAP);
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16);
md5 = MessageDigest.getInstance("MD5");
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes("UTF-8"), md5);
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);
byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length);
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
aesCBC.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] decryptedData = aesCBC.doFinal(cipherText.getBytes("UTF-8"));
// String plainText = "Hello, World! This is a Java/Javascript AES test.";
// SecretKey key = new SecretKeySpec(
// Base64.decodeBase64("u/Gu5posvwDsXUnV5Zaq4g=="), "AES");
// AlgorithmParameterSpec iv = new IvParameterSpec(
// Base64.decodeBase64("5D9r9ZVzEYYgha93/aUK2w=="));
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// cipher.init(Cipher.ENCRYPT_MODE, key, iv);
// System.out.println(Base64.encodeBase64String(cipher.doFinal(
// plainText.getBytes("UTF-8"))));
// String decryptedText = new String(decryptedData, "UTF-8");
System.out.println(Base64.encodeToString(decryptedData, Base64.NO_WRAP));
// enctest(decryptedText);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
But none gives the same results.
in iOS they are using https://github.com/etienne-martin/CryptoJS.swift
What should I do that both of our encrypted strings match.
The actual cipherText (not to be confused the character string with the same variable name) is formatted and starts with "Salted__" and presumably encryption parameters. The two different functions create different outputs with different formats. They can not produce the same output.
Note 1, confusing cipherText:
// String cipherText = "{\"device_type\":\"iOS\",\"email\" : \"jhon#gmail.com\",\"device_id\" : \"14105DA4-CEE5-431E-96A2-2331CDA7F062\",\"password\" : \"123456\",\"device_token\" : \"B44777563552882EC3139A0317E401B55D6FC699D0AC3D279F392927CAF9B566\"}";
// String cipherText = "U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk=";
Note 2:
Base64 is so un-useful for humans, it is designed for computers, hex is for humans and computers with a direct bits to bytes correspondence.
I think I am missing something, i believe the image(converted to bytes) is being encrypted but not decrypted when it arrives at the client side. The image seems to pass the RSA signature verification but somehow it can not be viewed.
Client Side Code:
public void aliceEncrypt(byte[] plaintext, byte[] sharedSecret) {
Cipher cipher;
byte[] encrypted = null;
try {
cipher = Cipher.getInstance("RC4");
Key sk = new SecretKeySpec(sharedSecret, "RC4");
cipher.init(Cipher.ENCRYPT_MODE, sk);
encrypted = cipher.doFinal(plaintext);
CipherOutputStream cos = new CipherOutputStream(socket.getOutputStream(), cipher);
ObjectOutputStream oos = new ObjectOutputStream(cos);
oos.writeObject(encrypted);
oos.flush();
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException | InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
Server Side Code:
public byte[] bobDecrypt( byte[] sharedSecret) {
Cipher cipher = null;
byte[] bytes = null;
byte[] decrypted = null;
try {
cipher = Cipher.getInstance("RC4");
Key sk = new SecretKeySpec(sharedSecret, "RC4");
cipher.init(Cipher.DECRYPT_MODE, sk);
CipherInputStream cis = new CipherInputStream(socket.getInputStream(), cipher);
ObjectInputStream ois = new ObjectInputStream(cis);
bytes = (byte[])ois.readObject();
decrypted = cipher.doFinal(bytes);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException | InvalidKeyException | ClassNotFoundException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return decrypted;
}
CipherInputStream and CipherOutputStream are meant to do all the heavy lifting, so you just supply them with an initialized Cipher instance and then use the write and read stream methods. For the most part you can layer these as with out input and output streams, but there is one subtlety: when a block cipher is used, there is no good way to signal the CipherOutputStream that it needs to call Cipher.doFinal(). The only supported way is to call the close() method. These close() calls propagate
to other wrapped streams, and in this case where a socket Outputstream is wrapped it ends up closing the socket as a side effect. This may be perfectly acceptable behavior, but you need to be aware of it. In this case, because you are using a byte-oriented stream cipher (RC4), there is no padding, so Cipher.doFinal()is basically a no-op anyway (well, it does reset the cipher state), so calling flush() is as good as calling close(). The code below is basically your code modified to correctly show how to layer and use the various streams.
public void aliceEncrypt(byte[] plaintext, byte[] sharedSecret, Socket socket) {
try {
Cipher cipher = Cipher.getInstance("RC4/ECB/NoPadding");
Key sk = new SecretKeySpec(sharedSecret, "RC4");
cipher.init(Cipher.ENCRYPT_MODE, sk);
CipherOutputStream cos = new CipherOutputStream(socket.getOutputStream(), cipher);
ObjectOutputStream oos = new ObjectOutputStream(cos);
oos.writeObject(plaintext);
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public byte[] bobDecrypt( byte[] sharedSecret, Socket socket) {
try {
Cipher cipher = Cipher.getInstance("RC4/ECB/NoPadding");
Key sk = new SecretKeySpec(sharedSecret, "RC4");
cipher.init(Cipher.DECRYPT_MODE, sk);
CipherInputStream cis = new CipherInputStream(socket.getInputStream(), cipher);
ObjectInputStream ois = new ObjectInputStream(cis);
byte[] bytes = (byte[]) ois.readObject();
return bytes;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I'm looking to encrypt my access token that is generated when the user inputs a username and password. The question I'm asking here is what are the different ways of doing this and what would the community think is the best method to do so. I've looked at Cryptography and Keystore but not sure if these are the right way to go? I'm a noob to encryption so any documentation would also be very helpful.
Thank you very much :D
Please try with android SecretKeySpec class
for encryption
public void setPassCode(String value) throws Exception {
try {
SecretKeySpec sks = null;
sks = getEncryptKey();
byte[] userLatENC=null;
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE,sks );
userLatENC = c.doFinal(value.getBytes());
passCode = Base64.encodeToString(userLatENC, Base64.DEFAULT);
} catch (Exception e) {
throw e;
}
}
for decryption
public String getPassCode() throws Exception {
SecretKeySpec sks = null;
try {
String encVal = "pass_code";
if (encVal.isEmpty()) {
return encVal;
}
sks = getDecryptKey();
byte[] latDEC=null;
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, sks);
latDEC = c.doFinal(Base64.decode(encVal, Base64.DEFAULT));
return new String(latDEC);
} catch (Exception e) {
throw e;
}
}
Get Encrypted Key
private SecretKeySpec getEncryptKey() throws Exception{
SecretKeySpec sks = null;
SecretKey key =null;
byte[] keyToSave;
try {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed("any data used as random seed".getBytes());
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(128, sr);
key= kg.generateKey();
keyToSave =key.getEncoded();
sks = new SecretKeySpec(keyToSave, "AES");
ks.load(null,null);
ks.setKeyEntry("aliasKey",key,null, null);
FileOutputStream ksout = context.openFileOutput("keystore_android", Context.MODE_PRIVATE);
ks.store(ksout, null);
ksout.close();
return sks;
} catch (Exception e) {
throw e;
}
}
For Decrypted Key
private SecretKeySpec getDecryptKey() throws Exception{
SecretKeySpec sks = null;
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fis = null;
fis = context.openFileInput("keystore_android");
keyStore.load(fis,null);
sks=new SecretKeySpec((keyStore.getKey("aliasKey", null)).getEncoded(), "AES");
return sks;
} catch (Exception e) {
throw e;
}
}
First of all, I've reviewed all the entries on the forum, and I still can not find a solution to my problem.
I have to measure the time it takes to encode and decode a text with DES, and make a comparison with other algorithms.
When I run the code, I have this error: BadPaddingException: pad block corrupted. When I debug, the code fails in this line:
byte [] plaintext = cipher.doFinal (cipherBytes);
I use class Base64 to encode/decode String <--> byte[]
Any idea?
thanks
private static final String CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding";
private static int KEY_LENGTH = 64;
public static SecretKey deriveKeyDES() {
try {
long start = System.currentTimeMillis();
KeyGenerator kgen = KeyGenerator.getInstance("DES");
kgen.init(KEY_LENGTH);
SecretKey result = kgen.generateKey();
long elapsed = System.currentTimeMillis() - start;
return result;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
public static String encrypt(String plaintext, SecretKey key) {
try {
long start = System.currentTimeMillis();
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding")
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(plaintext.getBytes("UTF-8"));
long elapsed = System.currentTimeMillis() - start;
return toBase64(cipherText);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static String toBase64(byte[] bytes) {
return Base64.encodeToString(bytes, Base64.NO_WRAP).trim();
}
public static String decrypt(String ciphertext, SecretKey key) {
try {
byte[] cipherBytes = fromBase64(ciphertext);
long start = System.currentTimeMillis();
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
cipher.update(cipherBytes);
// This is where I get exception
byte[] plaintext = cipher.doFinal(cipherBytes);
String plainrStr = new String(plaintext, "UTF-8").trim();
long elapsed = System.currentTimeMillis() - start;
return plainrStr;
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
public static byte[] fromBase64(String base64) {
return Base64.decode(base64, Base64.NO_WRAP);
}
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
cipher.update(cipherBytes);
// byte[] plaintext = cipher.doFinal(cipherBytes);
// ^-- You shouldn't pass cipherBytes twice.
// v-- instead use the parameter-less method:
byte[] plaintext = cipher.doFinal();
Padding exception occur when the last cipher text block does not compute to valid plain text. This would happen if last ciphertext block is corrupted or the key is incorrect. For CBC mode it would also happen if the second to last ciphertext was altered (but you are using ECB mode encryption).
In your case, the deriveKeyDES() is always generating a random key. Although we didn't get the actual calls to the security methods, I would presume you use a different key for encryption and decryption. In that case there is a very high chance that the resulting plain text does not contain valid padding bytes.
Rasmus answer certainly points to an error in your code, and it would screw up your timings and return a the plain text two times, but it would not remove the BadPaddingException.
I had the same problem in one source code, and IllegalBlockSizeException in another one.
Solved this two problems by return encoding data like:
public String encrypt(String input) {
try {
byte[] inputBytes = input.getBytes("UTF-8");
byte[] enc = encryptCipher.doFinal(inputBytes);
// and problem was in return encoding. That's how i fixed it
return Base64.encodeToString(enc,Base64.DEFAULT);
.....
}
}
Give u a code for decrypt:
public String decrypt(String input) {
try {
byte[] dec = Base64.decode(input.getBytes(), Base64.DEFAULT);
//here had exception
byte[] utf8 = decryptCipher.doFinal(dec);
return new String(utf8,"UTF8");
} catch (IOException | BadPaddingException | IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
I should submit, that had BadPaddingException and IllegalBlockSizeException
only in decrypt method byte[] utf8 = decryptCipher.doFinal(dec); (u had exeption in the same place: byte[] plaintext = cipher.doFinal(cipherBytes);), but real wrong is in encrypt method(return value)
That's why i recommend u to use that code in encrypt method:
return Base64.encodeToString(enc,Base64.DEFAULT);
P.S Tried to a give full answer on your question.