I was rather surprised at how incredibly slow the encryption process is on Android using the Blowfish encryption from Bouncy Castle. A 3 mb file took over 3 minutes. Is there some other algorithm that is extremely fast? I can live with less reliable security. Here's the code. Everything is done in memory. No files.
private BufferedBlockCipher cipher;
private KeyParameter key;
public Encryption(byte[] key)
{
try
{
BlowfishEngine blowfishEngine = new BlowfishEngine();
CBCBlockCipher cbcBlockCipher = new CBCBlockCipher(blowfishEngine);
cipher = new org.spongycastle.crypto.modes.PaddedBlockCipher(cbcBlockCipher);
this.key = new KeyParameter(key);
}
catch (Exception ex)
{
}
}
public Encryption(String key)
{
this(key.getBytes());
}
public synchronized byte[] Encrypt(byte[] data) throws CryptoException
{
try
{
if (data == null || data.length == 0)
{
return new byte[0];
}
cipher.init(true, key);
return CallCipher(data);
}
catch (Exception ex)
{
return null;
}
}
private byte[] CallCipher(byte[] data) throws CryptoException
{
try
{
int size = cipher.getOutputSize(data.length);
byte[] result = new byte[size];
int olen = cipher.processBytes(data, 0, data.length, result, 0);
olen += cipher.doFinal(result, olen);
if (olen < size)
{
byte[] tmp = new byte[olen];
System.arraycopy(result, 0, tmp, 0, olen);
result = tmp;
}
return result;
}
catch (Exception ex)
{
return null;
}
}
Blowfish is actually a fast cipher. Even with slowness of Java it should give some Mb/sec.
Most likely the problem is in how you use it (for instance, writing to file in 8-byte blocks). Or, how BouncyCastle team is coded it.
Try AES-128, it should be faster.
And, the fastest solution (and much less secure) is RC4.
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 choose a text file from internal storage of Android e.g Sample.txt that convert the file to byte array using convertFileToByteArray( Uri documentUri)
Context applicationContext = MainActivity.getContextOfApplication();
private byte[] convertFileToByteArray(Context context, Uri documentUri) {
ByteArrayOutputStream buffer = null;
try {
InputStream inputStream = applicationContext.getContentResolver().openInputStream(documentUri);
int nRead;
byte[] data = new byte[16384];
buffer = new ByteArrayOutputStream();
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
} catch (Exception e) {
e.getLocalizedMessage();
}
return buffer.toByteArray();
}
In this line
InputStream inputStream = applicationContext.getContentResolver().openInputStream(documentUri);
no catch on exception error however, it catch Exception and return "No such directory" when I choose a file that cannot be identified their extension type, for example, Sample.lev1, Sample.abc, Sample.rty
This android app is for encryption and decryption, so my extension for encrypted file would be weird, such as, .lev1, .abc, .rty.
Encryption are fine because I choose a file that can be readable (.txt, .pdf) but decryption no because when trying to convert file(.lev1, .abc, .rty) to byte array, it throws "FileNotFoundException",
case R.id.btnGO://WILL CRYPT file
Uri doctUri = chosenFile;
keyType = MainActivity.keyType;
if (keyType == null || doctUri == null || radioGroup.getCheckedRadioButtonId() == -1) {
setMessage("Selection not Complete !");
} else {
mimeType = getMimeType(getContext(), doctUri);//check file whether it suitable or not.
// and to be used for save file
if (functionCode == ENCRYPT_FILE) {
try {
if (mimeType == null || isBCDMimeType(getFileName(doctUri))) {
if (isBCDMimeType(getFileName(doctUri))) {
//
} else {
}
} else {
byte[] b = convertFileToByteArray(doctUri);
launchRingDialogForEF(null, b, mimeType, keyType);
}
} catch (Exception e) {
e.getLocalizedMessage();
}
} else if (functionCode == DECRYPT_FILE) {
if (isVendorMimeType(getFileName(doctUri))) {
try {
byte[] b = convertFileToByteArray(doctUri);
size = b.length;
encryptDecryptHex ead = new encryptDecryptHex();
ead.decrypt(usbDeviceConnection, epOUT, epIN, b);
returnEData = ead.deData;
output = ead.decryptedData;
showMessageAfterTryToCrypt(returnEData, output, "Decrypt File", ead.m, ead.k);//m= mimeType k=keyType
goOk(usbDeviceConnection, epOUT, epIN);
} catch (Exception e) {
e.getLocalizedMessage();
}
} else {
}
}
}
You have get the permission at runtime before accessing any device storage if the os version is 6.0 and more. Check the following official documentation for more information
https://developer.android.com/training/permissions/requesting.html
the uri for Encrypted.ab(for example) was appended by "%00"
content://com.android.externalstorage.documents/document/primary%3AEncrypted.a%00b%00%00%00%00%00
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.
I have TCP client which received audio (encoded) data through socket. I have to keep received data in some buffer and provide to decoder when requested by decoder chunk by chunk. Data is in byte[] and array size can vary. What should I use as buffer to keep the received byte[] chunks. I know about BlockingQueue but it doesn't provided adding and retrieving N size of byte[].
Does Java provide any data structure for this purpose, can we use ByteBuffer?
Does your client know how the byte array size varies?
A well known approach (it needs you manage this also at the server side) is to encapsulate the data inside a packet with the following fields: | TYPE | SIZE | DATA |
so, supposing the client has an open socket connection and instantiate a DataInputStream (dataInputStream = new DataInputStream(socket.getInputStream())), the client can read the data in a thread which run method is something like this:
#Override
public void run() {
while(running) {
int pckType = readPacketType();
int pckSize = readPacketSize();
byte[] data = readPacketContent(pckSize);
// do something with data
// you can use Semaphore instances in order to maintains a set of permits if needed
}
}
where
private int readPacketType() {
int result = -1;
byte[] array = new byte[1];
try {
dataInputStream.read(array);
result = (int)array[0];
} catch (IOException ex) {
return -1;
}
return result;
}
private int readPacketSize() {
int result = -1;
byte[] array = new byte[4];
try {
dataInputStream.read(array);
result = ByteBuffer.wrap(array).getInt();
} catch (IOException ex) {
return -1;
}
return result;
}
private byte[] readPacketContent(int contentSize) {
byte[] result = new byte[contentSize];
try {
dataInputStream.read(result);
} catch (IOException ex) {}
return result;
}
noting that I assigned, for example, 1 byte for the TYPE field and 4 byte for the SIZE field
EDIT:
You can use an implementation of the BlockingQueue interface like LinkedBlockingQueue. You can put byte[] of different size and retrieve them coherently (keeping the order and the size). Follows an easy check ;)
LinkedBlockingQueue<byte[]> lbq = new LinkedBlockingQueue<byte[]>();
String str1 = "blablabla";
String str2 = "blabla";
try {
lbq.put(str1.getBytes());
lbq.put(str2.getBytes());
lbq.put(str1.getBytes());
} catch (InterruptedException e) {
e.printStackTrace();
}
byte[] b = null;
int size = lbq.size();
for(int i=0; i<size; i++) {
try {
b = lbq.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(b.length);
}
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.