How can I store my list of passwords on an iPhone or iPod Touch?
This is the question that I attempted to solve as I geeked out over the weekend. I know I can buy an application like 1Password and sync between a laptop and an iPhone but I already have a pretty good password manager (since I wrote the one I use years ago). After a bit of research I came up with a few possibilities: create an iPhone app to sync with my password manager, create an encrypted bookmarklet (this is the way 1Password used to export passwords to the iPhone), or create a html page which will alter its own structure after I enter a password. I am still learning to write iPhone apps and am impatient, so the native route was put to one side (for now). The bookmarklet sounded cool and a self-decrypting html page seemed possible. Both required some research into cryptography with javascript, so I decided to give the hybrid html/js approach a go.
Step 1: find a javascript cryptography library that is compatible with javax.crypto. After much googling and stuffing around I have found Mark Percival’s Gibberish-AES library on GitHub. It attracted my attention by the promise of compatibility with OpenSSL AES-CBC.
Step 2: find out if I can upload a HTML page to an iPhone and see if Gibberish-AES and JQuery work. I used the Air Sharing app to upload the HTML page to an iPhone. Verdict: Both javascript libraries work without obvious issues.
Step 3: find out how I can encrypt with Java and decrypt with Gibberish-AES. The following is what it took using BouncyCastle’s javax.crypto provider:
import com.tomczarniecki.jpasskeep.RandomUtils;
import org.apache.commons.lang.Validate;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.io.ByteArrayOutputStream;
import java.security.Provider;
public class GibberishAESCrypto {
private static final String CIPHER_ALG = "PBEWITHMD5AND256BITAES-CBC-OPENSSL";
private static final Provider CIPHER_PROVIDER = new BouncyCastleProvider();
private static final String PREFIX = "Salted__";
private static final String UTF_8 = "UTF-8";
public String encrypt(String plainText, char[] password) throws Exception {
byte[] salt = RandomUtils.nextBytes(8);
Cipher cipher = createCipher(Cipher.ENCRYPT_MODE, salt, password);
byte[] cipherText = cipher.doFinal(plainText.getBytes(UTF_8));
ByteArrayOutputStream baos = new ByteArrayOutputStream(cipherText.length + 16);
baos.write(PREFIX.getBytes(UTF_8));
baos.write(salt);
baos.write(cipherText);
return Codec.encodeToBase64(baos.toByteArray());
}
public String decrypt(String cipherText, char[] password) throws Exception {
byte[] input = Codec.decodeFromBase64(cipherText);
String prefixText = new String(input, 0, 8, UTF_8);
Validate.isTrue(prefixText.equals(PREFIX), "Invalid prefix: ", prefixText);
byte[] salt = new byte[8];
System.arraycopy(input, 8, salt, 0, salt.length);
Cipher cipher = createCipher(Cipher.DECRYPT_MODE, salt, password);
byte[] plainText = cipher.doFinal(input, 16, input.length - 16);
return new String(plainText, UTF_8);
}
private Cipher createCipher(int cipherMode, byte[] salt, char[] password)
throws Exception {
PBEKeySpec pbeSpec = new PBEKeySpec(password);
SecretKeyFactory keyFact = SecretKeyFactory.getInstance(CIPHER_ALG, CIPHER_PROVIDER);
PBEParameterSpec defParams = new PBEParameterSpec(salt, 0);
Cipher cipher = Cipher.getInstance(CIPHER_ALG, CIPHER_PROVIDER);
cipher.init(cipherMode, keyFact.generateSecret(pbeSpec), defParams);
return cipher;
}
}
Step 4: Plug this code into my JPasskeep application to generate a HTML page with encrypted content.
Result: Prototype page seems to be working well. I can encrypt usernames and passwords in JPasskeep, render out a HTML page and then open the page in Mozilla, Safari and on the iPhone. Type in a password into the magic field and voila, usernames and passwords appear. Its a bit slow on the iPhone but it works. Ooh yes.
TODO: Still need to do some CSS tweaks to make it pretty and figure out how to invoke the same cryptographic transformation using BouncyCastle’s lightweight crypto APIs.
Will have a new release of JPasskeep on this blog soon, unless something shiny ……