Just finished a new version of my long-running password keeper application. You can download the cross-platform version, the mac dmg image, and the source code. New changes include the ability to export entries to an encrypted HTML page (see my previous post on javascript cryptography) and bundling the cross-platform version as a single JAR file using one-jar. As usual this version does not rely on javax.crypto APIs to function so it should work wherever a JDK5 compatible VM can run. Feature requests, bug reports and comments are always welcome.
Posts Tagged javascript
JPasskeep Update
May 5
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 ……