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:
danapple 2011-02-28 21:27:58 -06:00
parent 7b82061535
commit 8a3e1336e0
4 changed files with 73 additions and 100 deletions

View 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;
}
}

View file

@ -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));
// }
//
}

View file

@ -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);

View file

@ -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];