Posts Tagged cryptography

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: , , , ,