/* $Id$ */ package org.noreply.fancydress.crypto; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERInputStream; import org.bouncycastle.asn1.x509.RSAPublicKeyStructure; import org.bouncycastle.crypto.AsymmetricBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.RSAEngine; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.encodings.OAEPEncoding; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.noreply.fancydress.misc.Util; /** * This class is a wrapper for the bouncycastle RSA functionality. */ public class RSAPublicKey { /** * The overhead (in octets) added by OAEP padding. */ public static final int PK_OVERHEAD_LEN = 42; /** * The length of encrypted data. (we use 2048 bit keys) */ public static final int PK_ENC_LEN = 256; /** * The maximum amount of octets that can be encoded in an RSA message. */ public static final int PK_MAX_DATA_LEN = PK_ENC_LEN - PK_OVERHEAD_LEN; /** * the RSA cipher from bouncycastle. */ private AsymmetricBlockCipher rsa_cipher; /** * a key structure */ private RSAPublicKeyStructure key; /** * key parameters */ private RSAKeyParameters keyParams; /** * initialize rsa cipher. */ private void init() { String p = new String("He who would make his own liberty secure, must guard even his enemy from oppression."); rsa_cipher = new OAEPEncoding(new RSAEngine(), new SHA1Digest(), Util.toOctets(p)); } /** * Construct an instance of an RSAPublicKey from the ASN1 encoded key. * * @param asnEncoded ASN1 encoded RSA key. */ public RSAPublicKey(byte[] asnEncoded) { init(); ByteArrayInputStream bIn = new ByteArrayInputStream(asnEncoded); DERInputStream dIn = new DERInputStream(bIn); try { key = new RSAPublicKeyStructure((ASN1Sequence)dIn.readObject()); } catch (IOException e) { throw new Error(e); } keyParams = new RSAKeyParameters(false, key.getModulus(), key.getPublicExponent()); }; /** * Construct an instance of an RSAPublicKey from the modulus and public exponend. * * @param modulus Modulus of the key * @param exponent public exponend of the key */ public RSAPublicKey(BigInteger modulus, BigInteger exponent) { init(); keyParams = new RSAKeyParameters(false, modulus, exponent); key = new RSAPublicKeyStructure(modulus, exponent); }; /** * Encrypt a message. * * Encrypts message m to this key. * * @param m the message to encrypt. * @return the encrypted message (PK_ENC_LEN octets long) */ public byte[] encrypt(byte[] m) { rsa_cipher.init(true, keyParams); byte[] result; try { result = rsa_cipher.processBlock(m, 0, m.length); } catch (InvalidCipherTextException e) { throw new Error(e); } return(result); } /** * Verify a signature. * * Verify that the information signed in signature sig * matches the value in m. * * @param sig the signature * @param m message to compare the signed information to * @return true if the signature is valid * @throws InvalidCipherTextException */ public boolean verify(byte[] sig, byte[] m) throws InvalidCipherTextException { rsa_cipher.init(false, keyParams); byte[] signedDigest = rsa_cipher.processBlock(sig, 0, sig.length); byte[] digest = CryptoPrimitives.hash(m); boolean verifies = Util.equal(digest, signedDigest); return(verifies); } /** * Return the ASN 1 encoding of this key. * * @return this key, asn1 encoded */ public byte[] encode() { DEROctetString asn = new DEROctetString(key); byte[] result = asn.getOctets(); return(result); } /** * Get this key's fingerprint. * * The fingerprint (or keyid) is the hash of the asn1 representation of this key. * * @return the fingerprint. */ public byte[] getFingerprint() { DEROctetString asn = new DEROctetString(key); byte[] encoded = asn.getOctets(); byte[] result = CryptoPrimitives.hash(encoded); return(result); } }