/**
* TLS-Attacker - A Modular Penetration Testing Framework for TLS
*
* Copyright 2014-2016 Ruhr University Bochum / Hackmanit GmbH
*
* Licensed under Apache License 2.0
* http://www.apache.org/licenses/LICENSE-2.0
*/
package de.rub.nds.tlsattacker.tls.protocol.handshake;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.util.Arrays;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import de.rub.nds.tlsattacker.tls.constants.HandshakeByteLength;
import de.rub.nds.tlsattacker.tls.constants.HandshakeMessageType;
import de.rub.nds.tlsattacker.tls.constants.SignatureAndHashAlgorithm;
import de.rub.nds.tlsattacker.tls.exceptions.ConfigurationException;
import de.rub.nds.tlsattacker.tls.exceptions.InvalidMessageTypeException;
import de.rub.nds.tlsattacker.tls.workflow.TlsContext;
import de.rub.nds.tlsattacker.util.ArrayConverter;
/**
* Handling of the CertificateVerify protocol message:
* http://tools.ietf.org/html/rfc5246#section-7.4.8
*
* The TLS spec as well as wireshark bring some nice confusions: - The TLS spec
* says the message consists of only signature bytes - Wireshark says the
* message consists of the signature length and signature bytes
*
* In fact, the certificate message consists of the following fields: -
* signature algorithm (2 bytes) - signature length (2 bytes) - signature
*
* This structure is of course prepended with the handshake message length, as
* obvious for every handshake message.
*
* @author Juraj Somorovsky <juraj.somorovsky@rub.de>
* @author Philip Riese <philip.riese@rub.de>
* @param <HandshakeMessage>
*/
public class CertificateVerifyHandler<Message extends CertificateVerifyMessage> extends
HandshakeMessageHandler<Message> {
private static final Logger LOGGER = LogManager.getLogger(CertificateVerifyHandler.class);
@SuppressWarnings("unchecked")
public CertificateVerifyHandler(TlsContext tlsContext) {
super(tlsContext);
this.correctProtocolMessageClass = (Class<? extends Message>) CertificateVerifyMessage.class;
}
@Override
public byte[] prepareMessageAction() {
byte[] rawHandshakeBytes = tlsContext.getDigest().getRawBytes();
// LOGGER.debug("All handshake messages: {}",
// ArrayConverter.bytesToHexString(rawHandshakeBytes));
KeyStore ks = tlsContext.getKeyStore();
try {
Key key = ks.getKey(tlsContext.getAlias(), tlsContext.getPassword().toCharArray());
Signature instance = null;
SignatureAndHashAlgorithm selectedSignatureHashAlgo = null;
switch (key.getAlgorithm()) {
case "RSA":
RSAPrivateCrtKey rsaKey = (RSAPrivateCrtKey) key;
selectedSignatureHashAlgo = tlsContext.getSupportedSignatureAndHashAlgorithmsForRSA().get(0);
instance = Signature.getInstance(selectedSignatureHashAlgo.getJavaName());
instance.initSign(rsaKey);
break;
case "EC":
ECPrivateKey ecKey = (ECPrivateKey) key;
selectedSignatureHashAlgo = tlsContext.getSupportedSignatureAndHashAlgorithmsForEC().get(0);
instance = Signature.getInstance(selectedSignatureHashAlgo.getJavaName());
instance.initSign(ecKey);
break;
default:
throw new ConfigurationException("Algorithm " + key.getAlgorithm() + " not supported yet.");
}
LOGGER.debug("Selected SignatureAndHashAlgorithm for CertificateVerify message: {}",
selectedSignatureHashAlgo.getJavaName());
instance.update(rawHandshakeBytes);
byte[] signature = instance.sign();
protocolMessage.setSignature(signature);
protocolMessage.setSignatureLength(protocolMessage.getSignature().getValue().length);
byte[] result = ArrayConverter.concatenate(selectedSignatureHashAlgo.getByteValue(), ArrayConverter
.intToBytes(protocolMessage.getSignatureLength().getValue(), HandshakeByteLength.SIGNATURE_LENGTH),
protocolMessage.getSignature().getValue());
protocolMessage.setLength(result.length);
long header = (protocolMessage.getHandshakeMessageType().getValue() << 24)
+ protocolMessage.getLength().getValue();
protocolMessage.setCompleteResultingMessage(ArrayConverter.concatenate(
ArrayConverter.longToUint32Bytes(header), result));
return protocolMessage.getCompleteResultingMessage().getValue();
} catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | InvalidKeyException
| SignatureException ex) {
throw new ConfigurationException(ex.getLocalizedMessage(), ex);
}
}
@Override
public int parseMessageAction(byte[] message, int pointer) {
if (message[pointer] != HandshakeMessageType.CERTIFICATE_VERIFY.getValue()) {
throw new InvalidMessageTypeException("This is not a Certificate Verify message");
}
protocolMessage.setType(message[pointer]);
int currentPointer = pointer + HandshakeByteLength.MESSAGE_TYPE;
int nextPointer = currentPointer + HandshakeByteLength.MESSAGE_TYPE_LENGTH;
int length = ArrayConverter.bytesToInt(Arrays.copyOfRange(message, currentPointer, nextPointer));
protocolMessage.setLength(length);
currentPointer = nextPointer;
nextPointer = currentPointer + HandshakeByteLength.SIGNATURE_HASH_ALGORITHMS_LENGTH;
SignatureAndHashAlgorithm sigAndHash = SignatureAndHashAlgorithm.getSignatureAndHashAlgorithm(Arrays
.copyOfRange(message, currentPointer, nextPointer));
protocolMessage.setSignatureHashAlgorithm(sigAndHash.getByteValue());
currentPointer = nextPointer;
nextPointer = currentPointer + HandshakeByteLength.SIGNATURE_LENGTH;
int sigLength = ArrayConverter.bytesToInt(Arrays.copyOfRange(message, currentPointer, nextPointer));
protocolMessage.setSignatureLength(sigLength);
currentPointer = nextPointer;
nextPointer = currentPointer + sigLength;
protocolMessage.setSignature(Arrays.copyOfRange(message, currentPointer, nextPointer));
currentPointer = nextPointer;
// TODO maybe verify signature and set a boolean in TLS-Context
protocolMessage.setCompleteResultingMessage(Arrays.copyOfRange(message, pointer, nextPointer));
return currentPointer;
}
}