package org.limewire.promotion.containers;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.Arrays;
import org.limewire.io.BadGGEPBlockException;
import org.limewire.io.BadGGEPPropertyException;
import org.limewire.io.GGEP;
import org.limewire.io.IOUtils;
import org.limewire.security.certificate.CertificateVerifier;
import org.limewire.security.certificate.CipherProvider;
import org.limewire.security.certificate.KeyStoreProvider;
import org.limewire.security.certificate.CipherProvider.SignatureType;
import org.limewire.util.StringUtils;
/**
* Container to hold some other container along with a signature to validate the
* contents of the wrapped container. Always signs with
* {@link SignatureType#SHA1_WITH_RSA}. Stores the encoded message, signature,
* and other metadata in GGEP encoded form.
* <p>
* GGEP Keys:
* <ul>
* <li>W=wrapped message (byte array)
* <li>S=signature (byte array)
* <li>A=key alias (String as UTF-8 byte array)
* </ul>
*/
public class SignedMessageContainer implements MessageContainer {
private static final String KEY_WRAPPED_BYTES = "W";
private static final String KEY_SIGNATURE = "S";
private static final String KEY_ALIAS = "A";
private GGEP payload = new GGEP();
public byte[] getType() {
return StringUtils.toUTF8Bytes("SIGN");
}
public byte[] encode() {
payload.put(TYPE_KEY, getType());
return payload.toByteArray();
}
public void decode(GGEP rawGGEP) throws BadGGEPBlockException {
if (!Arrays.equals(getType(), rawGGEP.get(TYPE_KEY)))
throw new BadGGEPBlockException("Incorrect type.");
if (!rawGGEP.hasKey(KEY_ALIAS))
throw new BadGGEPBlockException("Missing alias");
if (!rawGGEP.hasKey(KEY_SIGNATURE))
throw new BadGGEPBlockException("Missing signature");
if (!rawGGEP.hasKey(KEY_WRAPPED_BYTES))
throw new BadGGEPBlockException("Missing wrapped message");
this.payload = rawGGEP;
}
/**
* Calls the {@link #encode()} method on the passed in message and stores
* the signed result into the payload.
*
* @throws IOException if there is a problem signing.
*/
public void setAndSignWrappedMessage(MessageContainer wrappedMessage,
CipherProvider cipherProvider, PrivateKey privateKey, String keyAlias)
throws IOException {
byte[] messagePayload = wrappedMessage.encode();
byte[] signature = cipherProvider.sign(messagePayload, privateKey,
SignatureType.SHA1_WITH_RSA);
payload = new GGEP();
payload.put(KEY_WRAPPED_BYTES, messagePayload);
payload.put(KEY_SIGNATURE, signature);
payload.put(KEY_ALIAS, StringUtils.toUTF8Bytes(keyAlias));
}
/**
* Returns a {@link MessageContainer} parsed from the <code>payload</code>.
*
* @param cipherProvider verifies the signature
* @param keyStore holds the certificate
* @param certificateVerifier verifies the certificate
* @return a {@link MessageContainer} parsed from the <code>payload</code>.
*/
public MessageContainer getAndVerifyWrappedMessage(CipherProvider cipherProvider,
KeyStoreProvider keyStore, CertificateVerifier certificateVerifier) throws IOException {
try {
String keyAlias = StringUtils.toUTF8String(payload.getBytes(KEY_ALIAS));
Certificate cert = keyStore.getKeyStore().getCertificate(keyAlias);
if (!certificateVerifier.isValid(cert))
throw new IOException("Invalid certificate retrieved.");
byte[] wrappedBytes = payload.getBytes(KEY_WRAPPED_BYTES);
if (!cipherProvider.verify(wrappedBytes, payload.getBytes(KEY_SIGNATURE), cert
.getPublicKey(), SignatureType.SHA1_WITH_RSA))
throw new IOException("Wrapped message did not match the signature.");
return new MessageContainerParser().parse(wrappedBytes);
} catch (BadGGEPBlockException ex) {
throw IOUtils.getIOException("BadGGEPBlockException parsing contents:", ex);
} catch (KeyStoreException ex) {
throw IOUtils.getIOException("KeyStoreException parsing contents:", ex);
} catch (BadGGEPPropertyException ex) {
throw IOUtils.getIOException("BadGGEPPropertyException parsing contents:", ex);
}
}
}