Posts Tagged iPhone

Java and iPhone AES interoperability

I’ve been trying to get my head around cryptography on the iPhone so that I can create a native iPhone app (iPasskeep) that interoperates with my JPasskeep password keeper application. It has taken a while to get my head around CommonCrypto APIs – how to use them, how not to use them and their limitations. It then took a bit of fiddling to find the right incantations in Java to get an interoperable cryptographic transformation.

Caveat: The following Objective-C code is not yet production ready or quality. It passes unit tests, but I haven’t checked it for memory leaks, performance, etc.

Hope this helps.

Listing: Cipher.h

#import <Cocoa/Cocoa.h>

#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>

@interface Cipher : NSObject {
	NSString* cipherKey;
}

@property (retain) NSString* cipherKey;

- (Cipher *) initWithKey:(NSString *) key;

- (NSData *) encrypt:(NSData *) plainText;
- (NSData *) decrypt:(NSData *) cipherText;

- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData;

+ (NSData *) md5:(NSString *) stringToHash;

@end

Listing: Cipher.m

#import "Cipher.h"

@implementation Cipher

@synthesize cipherKey;

- (Cipher *) initWithKey:(NSString *) key {
	self = [super init];
	if (self) {
		[self setCipherKey:key];
	}
	return self;
}

- (NSData *) encrypt:(NSData *) plainText {
	return [self transform:kCCEncrypt data:plainText];
}

- (NSData *) decrypt:(NSData *) cipherText {
	return [self transform:kCCDecrypt data:cipherText];
}

- (NSData *) transform:(CCOperation) encryptOrDecrypt data:(NSData *) inputData {

	// kCCKeySizeAES128 = 16 bytes
	// CC_MD5_DIGEST_LENGTH = 16 bytes
	NSData* secretKey = [Cipher md5:cipherKey];

	CCCryptorRef cryptor = NULL;
	CCCryptorStatus status = kCCSuccess;

	uint8_t iv[kCCBlockSizeAES128];
    memset((void *) iv, 0x0, (size_t) sizeof(iv));

	status = CCCryptorCreate(encryptOrDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
							 [secretKey bytes], kCCKeySizeAES128, iv, &cryptor);

	if (status != kCCSuccess) {
		return nil;
	}

	size_t bufsize = CCCryptorGetOutputLength(cryptor, (size_t)[inputData length], true);

	void * buf = malloc(bufsize * sizeof(uint8_t));
	memset(buf, 0x0, bufsize);

	size_t bufused = 0;
    size_t bytesTotal = 0;

	status = CCCryptorUpdate(cryptor, [inputData bytes], (size_t)[inputData length],
							 buf, bufsize, &bufused);

	if (status != kCCSuccess) {
		free(buf);
		CCCryptorRelease(cryptor);
		return nil;
	}

	bytesTotal += bufused;

	status = CCCryptorFinal(cryptor, buf + bufused, bufsize - bufused, &bufused);

	if (status != kCCSuccess) {
		free(buf);
		CCCryptorRelease(cryptor);
		return nil;
	}

	bytesTotal += bufused;

	CCCryptorRelease(cryptor);

	return [NSData dataWithBytesNoCopy:buf length:bytesTotal];
}

+ (NSData *) md5:(NSString *) stringToHash {

	const char *src = [stringToHash UTF8String];

	unsigned char result[CC_MD5_DIGEST_LENGTH];

	CC_MD5(src, strlen(src), result);

	return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
}

@end

Listing: Cipher.java

package com.tomczarniecki.iphone;

import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class Cipher {

    private final String password;

    public Cipher(String password) {
        this.password = password;
    }

    public byte[] encrypt(byte[] plainText) throws Exception {
        return transform(true, plainText);
    }

    public byte[] decrypt(byte[] cipherText) throws Exception {
        return transform(false, cipherText);
    }

    private byte[] transform(boolean encrypt, byte[] inputBytes) throws Exception {
        byte[] key = DigestUtils.md5(password.getBytes("UTF-8"));

        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
        cipher.init(encrypt, new KeyParameter(key));

        ByteArrayInputStream input = new ByteArrayInputStream(inputBytes);
        ByteArrayOutputStream output = new ByteArrayOutputStream();

        int inputLen;
        int outputLen;

        byte[] inputBuffer = new byte[1024];
        byte[] outputBuffer = new byte[cipher.getOutputSize(inputBuffer.length)];

        while ((inputLen = input.read(inputBuffer)) > -1) {
            outputLen = cipher.processBytes(inputBuffer, 0, inputLen, outputBuffer, 0);
            if (outputLen > 0) {
                output.write(outputBuffer, 0, outputLen);
            }
        }

        outputLen = cipher.doFinal(outputBuffer, 0);
        if (outputLen > 0) {
            output.write(outputBuffer, 0, outputLen);
        }

        return output.toByteArray();
    }
}

Tags: , , , , , ,

Javascript Cryptography on the iPhone

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

Tags: , , , ,

Bluetooth fun and games

My application for an iPhone was rejected last week. Apparently I am not a good credit risk for O2 even though I can buy the iPhone and a 12 month contract outright. I wonder if Apple know how many customers are turned away by O2′s business practices?

So I have bought myself a Sony Ericcson W980. Very nice little toy, looks very cool, 8GB memory and a 3.2 megapixel camera. Oh yes, Vodafone don’t seem to think that I am unworthy of a phone plan.

One of the big annoyances since I got the phone was my inability to send all my contacts from the phone to my mac via bluetooth. It used to work on Tiger with my older phone, but something was wrong on Leopard. I just kept on getting transfer rejected messages. With a bit of searching on the internet, it appears that you have to go to System Preferences > Sharing and then enable Bluetooth Sharing for Leopard to accept contacts via bluetooth. Simple, once you find out how to do it ;-)

Tags: , , , , , ,