/* $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);
}
}