Although I believe SimplyCrypto was made available without
restriction, with no license, K9Krypto is a completely new, completely taint-free implementation of encryption for K-9 Mail settings files. Also, K9Krypto reuses the cryptography infrastructure between strings, so should be more efficient.
This commit is contained in:
parent
7b82061535
commit
8a3e1336e0
4 changed files with 73 additions and 100 deletions
67
src/com/fsck/k9/preferences/K9Krypto.java
Normal file
67
src/com/fsck/k9/preferences/K9Krypto.java
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
package com.fsck.k9.preferences;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
|
||||||
|
public class K9Krypto
|
||||||
|
{
|
||||||
|
final Base64 mBase64;
|
||||||
|
final Cipher mCipher;
|
||||||
|
|
||||||
|
private final static String AES = "AES";
|
||||||
|
private final static String SECURE_RANDOM_TYPE = "SHA1PRNG";
|
||||||
|
|
||||||
|
public enum MODE
|
||||||
|
{
|
||||||
|
ENCRYPT(Cipher.ENCRYPT_MODE), DECRYPT(Cipher.DECRYPT_MODE);
|
||||||
|
|
||||||
|
int mode;
|
||||||
|
private MODE(int nMode)
|
||||||
|
{
|
||||||
|
mode = nMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public K9Krypto(String key, MODE mode) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException {
|
||||||
|
mBase64 = new Base64();
|
||||||
|
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES);
|
||||||
|
SecureRandom secureRandom = SecureRandom.getInstance(SECURE_RANDOM_TYPE);
|
||||||
|
secureRandom.setSeed(key.getBytes());
|
||||||
|
keyGenerator.init(128, secureRandom);
|
||||||
|
SecretKey secretKey = keyGenerator.generateKey();
|
||||||
|
byte[] processedKey = secretKey.getEncoded();
|
||||||
|
mCipher = setupCipher(mode.mode, processedKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String encrypt(String plainText) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
|
||||||
|
byte[] encryptedText = mCipher.doFinal(plainText.getBytes());
|
||||||
|
byte[] encryptedEncodedText = mBase64.encode(encryptedText);
|
||||||
|
return new String(encryptedEncodedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String decrypt(String encryptedEncodedText) throws IllegalBlockSizeException, BadPaddingException {
|
||||||
|
byte[] encryptedText = mBase64.decode(encryptedEncodedText.getBytes());
|
||||||
|
byte[] plainText = mCipher.doFinal(encryptedText);
|
||||||
|
return new String(plainText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cipher setupCipher(int mode, byte[] processedKey) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException
|
||||||
|
{
|
||||||
|
SecretKeySpec secretKeySpec = new SecretKeySpec(processedKey, AES);
|
||||||
|
Cipher cipher = Cipher.getInstance(AES);
|
||||||
|
cipher.init(mode, secretKeySpec);
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,94 +0,0 @@
|
||||||
package com.fsck.k9.preferences;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.KeyGenerator;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copied from:
|
|
||||||
* http://www.androidsnippets.org/snippets/39/index.html
|
|
||||||
* a page which had no licensing or copyright notice
|
|
||||||
* and appeared to be intended for public use
|
|
||||||
* package net.sf.andhsli.hotspotlogin;
|
|
||||||
* Usage:
|
|
||||||
* <pre>
|
|
||||||
* String crypto = SimpleCrypto.encrypt(masterpassword, cleartext)
|
|
||||||
* ...
|
|
||||||
* String cleartext = SimpleCrypto.decrypt(masterpassword, crypto)
|
|
||||||
* </pre>
|
|
||||||
* @author ferenc.hechler
|
|
||||||
*/
|
|
||||||
public class SimpleCrypto {
|
|
||||||
|
|
||||||
public static String encrypt(String seed, String cleartext, Base64 base64) throws Exception {
|
|
||||||
byte[] rawKey = getRawKey(seed.getBytes());
|
|
||||||
byte[] result = encrypt(rawKey, cleartext.getBytes());
|
|
||||||
return new String(base64.encode(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String decrypt(String seed, String encrypted, Base64 base64) throws Exception {
|
|
||||||
byte[] rawKey = getRawKey(seed.getBytes());
|
|
||||||
byte[] enc = base64.decode(encrypted.getBytes());
|
|
||||||
byte[] result = decrypt(rawKey, enc);
|
|
||||||
return new String(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getRawKey(byte[] seed) throws Exception {
|
|
||||||
KeyGenerator kgen = KeyGenerator.getInstance("AES");
|
|
||||||
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
|
|
||||||
sr.setSeed(seed);
|
|
||||||
kgen.init(128, sr); // 192 and 256 bits may not be available
|
|
||||||
SecretKey skey = kgen.generateKey();
|
|
||||||
byte[] raw = skey.getEncoded();
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
|
|
||||||
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
|
|
||||||
Cipher cipher = Cipher.getInstance("AES");
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
|
|
||||||
byte[] encrypted = cipher.doFinal(clear);
|
|
||||||
return encrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
|
|
||||||
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
|
|
||||||
Cipher cipher = Cipher.getInstance("AES");
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
|
|
||||||
byte[] decrypted = cipher.doFinal(encrypted);
|
|
||||||
return decrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// public static byte[] toByte(String hexString) {
|
|
||||||
// int len = hexString.length()/2;
|
|
||||||
// byte[] result = new byte[len];
|
|
||||||
// for (int i = 0; i < len; i++)
|
|
||||||
// result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
|
|
||||||
// return result;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public static String toHex(byte[] buf) {
|
|
||||||
// if (buf == null)
|
|
||||||
// return "";
|
|
||||||
// StringBuffer result = new StringBuffer(2*buf.length);
|
|
||||||
// for (int i = 0; i < buf.length; i++) {
|
|
||||||
// appendHex(result, buf[i]);
|
|
||||||
// }
|
|
||||||
// return result.toString();
|
|
||||||
// }
|
|
||||||
// private final static String HEX = "0123456789ABCDEF";
|
|
||||||
// private static void appendHex(StringBuffer sb, byte b) {
|
|
||||||
// sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class StorageExporter {
|
||||||
//public static String VALIDITY = "K-9MailExport"; // Does outputting a fixed string in a known location make the encrypted data easier to break?
|
//public static String VALIDITY = "K-9MailExport"; // Does outputting a fixed string in a known location make the encrypted data easier to break?
|
||||||
public static void exportPreferences(Context context, String uuid, String fileName, String encryptionKey) throws StorageImportExportException {
|
public static void exportPreferences(Context context, String uuid, String fileName, String encryptionKey) throws StorageImportExportException {
|
||||||
try {
|
try {
|
||||||
Base64 base64 = new Base64();
|
K9Krypto krypto = new K9Krypto(encryptionKey, K9Krypto.MODE.ENCRYPT);
|
||||||
File outFile = new File(fileName);
|
File outFile = new File(fileName);
|
||||||
PrintWriter pf = new PrintWriter(outFile);
|
PrintWriter pf = new PrintWriter(outFile);
|
||||||
long keysEvaluated = 0;
|
long keysEvaluated = 0;
|
||||||
|
@ -68,8 +68,8 @@ public class StorageExporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String keyEnc = SimpleCrypto.encrypt(encryptionKey, key, base64);
|
String keyEnc = krypto.encrypt(key);
|
||||||
String valueEnc = SimpleCrypto.encrypt(encryptionKey, value, base64);
|
String valueEnc = krypto.encrypt(value);
|
||||||
String output = keyEnc + ":" + valueEnc;
|
String output = keyEnc + ":" + valueEnc;
|
||||||
//Log.i(K9.LOG_TAG, "For key " + key + ", output is " + output);
|
//Log.i(K9.LOG_TAG, "For key " + key + ", output is " + output);
|
||||||
pf.println(output);
|
pf.println(output);
|
||||||
|
|
|
@ -20,7 +20,6 @@ import com.fsck.k9.Preferences;
|
||||||
public class StorageImporterVersion1 implements IStorageImporter {
|
public class StorageImporterVersion1 implements IStorageImporter {
|
||||||
public int importPreferences(Preferences preferences, SharedPreferences.Editor editor, String data, String encryptionKey) throws StorageImportExportException {
|
public int importPreferences(Preferences preferences, SharedPreferences.Editor editor, String data, String encryptionKey) throws StorageImportExportException {
|
||||||
try {
|
try {
|
||||||
Base64 base64 = new Base64();
|
|
||||||
List<Integer> accountNumbers = Account.getExistingAccountNumbers(preferences);
|
List<Integer> accountNumbers = Account.getExistingAccountNumbers(preferences);
|
||||||
Log.i(K9.LOG_TAG, "Existing accountNumbers = " + accountNumbers);
|
Log.i(K9.LOG_TAG, "Existing accountNumbers = " + accountNumbers);
|
||||||
Map<String, String> uuidMapping = new HashMap<String, String>();
|
Map<String, String> uuidMapping = new HashMap<String, String>();
|
||||||
|
@ -31,6 +30,7 @@ public class StorageImporterVersion1 implements IStorageImporter {
|
||||||
String line = null;
|
String line = null;
|
||||||
int settingsImported = 0;
|
int settingsImported = 0;
|
||||||
int numAccounts = 0;
|
int numAccounts = 0;
|
||||||
|
K9Krypto krypto = new K9Krypto(encryptionKey, K9Krypto.MODE.DECRYPT);
|
||||||
do {
|
do {
|
||||||
line = br.readLine();
|
line = br.readLine();
|
||||||
if (line != null) {
|
if (line != null) {
|
||||||
|
@ -39,8 +39,8 @@ public class StorageImporterVersion1 implements IStorageImporter {
|
||||||
if (comps.length > 1) {
|
if (comps.length > 1) {
|
||||||
String keyEnc = comps[0];
|
String keyEnc = comps[0];
|
||||||
String valueEnc = comps[1];
|
String valueEnc = comps[1];
|
||||||
String key = SimpleCrypto.decrypt(encryptionKey, keyEnc, base64);
|
String key = krypto.decrypt(keyEnc);
|
||||||
String value = SimpleCrypto.decrypt(encryptionKey, valueEnc, base64);
|
String value = krypto.decrypt(valueEnc);
|
||||||
String[] keyParts = key.split("\\.");
|
String[] keyParts = key.split("\\.");
|
||||||
if (keyParts.length > 1) {
|
if (keyParts.length > 1) {
|
||||||
String oldUuid = keyParts[0];
|
String oldUuid = keyParts[0];
|
||||||
|
|
Loading…
Reference in a new issue