mirror of
https://github.com/moparisthebest/k-9
synced 2024-12-25 00:58:50 -05:00
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
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 void exportPreferences(Context context, String uuid, String fileName, String encryptionKey) throws StorageImportExportException {
|
||||
try {
|
||||
Base64 base64 = new Base64();
|
||||
K9Krypto krypto = new K9Krypto(encryptionKey, K9Krypto.MODE.ENCRYPT);
|
||||
File outFile = new File(fileName);
|
||||
PrintWriter pf = new PrintWriter(outFile);
|
||||
long keysEvaluated = 0;
|
||||
@ -68,8 +68,8 @@ public class StorageExporter {
|
||||
}
|
||||
}
|
||||
}
|
||||
String keyEnc = SimpleCrypto.encrypt(encryptionKey, key, base64);
|
||||
String valueEnc = SimpleCrypto.encrypt(encryptionKey, value, base64);
|
||||
String keyEnc = krypto.encrypt(key);
|
||||
String valueEnc = krypto.encrypt(value);
|
||||
String output = keyEnc + ":" + valueEnc;
|
||||
//Log.i(K9.LOG_TAG, "For key " + key + ", output is " + output);
|
||||
pf.println(output);
|
||||
|
@ -20,7 +20,6 @@ import com.fsck.k9.Preferences;
|
||||
public class StorageImporterVersion1 implements IStorageImporter {
|
||||
public int importPreferences(Preferences preferences, SharedPreferences.Editor editor, String data, String encryptionKey) throws StorageImportExportException {
|
||||
try {
|
||||
Base64 base64 = new Base64();
|
||||
List<Integer> accountNumbers = Account.getExistingAccountNumbers(preferences);
|
||||
Log.i(K9.LOG_TAG, "Existing accountNumbers = " + accountNumbers);
|
||||
Map<String, String> uuidMapping = new HashMap<String, String>();
|
||||
@ -31,6 +30,7 @@ public class StorageImporterVersion1 implements IStorageImporter {
|
||||
String line = null;
|
||||
int settingsImported = 0;
|
||||
int numAccounts = 0;
|
||||
K9Krypto krypto = new K9Krypto(encryptionKey, K9Krypto.MODE.DECRYPT);
|
||||
do {
|
||||
line = br.readLine();
|
||||
if (line != null) {
|
||||
@ -39,8 +39,8 @@ public class StorageImporterVersion1 implements IStorageImporter {
|
||||
if (comps.length > 1) {
|
||||
String keyEnc = comps[0];
|
||||
String valueEnc = comps[1];
|
||||
String key = SimpleCrypto.decrypt(encryptionKey, keyEnc, base64);
|
||||
String value = SimpleCrypto.decrypt(encryptionKey, valueEnc, base64);
|
||||
String key = krypto.decrypt(keyEnc);
|
||||
String value = krypto.decrypt(valueEnc);
|
||||
String[] keyParts = key.split("\\.");
|
||||
if (keyParts.length > 1) {
|
||||
String oldUuid = keyParts[0];
|
||||
|
Loading…
Reference in New Issue
Block a user