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 ……