/*
* Copyright (c) 2015 Huawei, Inc and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
package org.opendaylight.usc.crypto.dtls;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Hashtable;
import java.util.Vector;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.crypto.tls.AlertDescription;
import org.bouncycastle.crypto.tls.AlertLevel;
import org.bouncycastle.crypto.tls.CertificateRequest;
import org.bouncycastle.crypto.tls.CipherSuite;
import org.bouncycastle.crypto.tls.ClientCertificateType;
import org.bouncycastle.crypto.tls.DefaultTlsClient;
import org.bouncycastle.crypto.tls.MaxFragmentLength;
import org.bouncycastle.crypto.tls.ProtocolVersion;
import org.bouncycastle.crypto.tls.SignatureAlgorithm;
import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
import org.bouncycastle.crypto.tls.TlsAuthentication;
import org.bouncycastle.crypto.tls.TlsCredentials;
import org.bouncycastle.crypto.tls.TlsExtensionsUtils;
import org.bouncycastle.crypto.tls.TlsSession;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DtlsClient extends DefaultTlsClient {
private static final Logger log = LoggerFactory.getLogger(DtlsClient.class);
protected TlsSession session;
private final File root;
private final File cert;
private final File key;
public DtlsClient(TlsSession session, File root, File cert, File key) {
this.session = session;
this.root = root;
this.cert = cert;
this.key = key;
}
public TlsSession getSessionToResume() {
return this.session;
}
public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause) {
PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
out.println("DTLS client raised alert: " + AlertLevel.getText(alertLevel) + ", "
+ AlertDescription.getText(alertDescription));
if (message != null) {
out.println(message);
}
if (cause != null) {
cause.printStackTrace(out);
}
}
public void notifyAlertReceived(short alertLevel, short alertDescription) {
PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
out.println("DTLS client received alert: " + AlertLevel.getText(alertLevel) + ", "
+ AlertDescription.getText(alertDescription));
out.close();
}
public ProtocolVersion getClientVersion() {
return ProtocolVersion.DTLSv12;
}
public ProtocolVersion getMinimumVersion() {
return ProtocolVersion.DTLSv10;
}
public int[] getCipherSuites() {
return Arrays.concatenate(super.getCipherSuites(), new int[] {
CipherSuite.DRAFT_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, });
}
public Hashtable<?, ?> getClientExtensions() throws IOException {
Hashtable<?, ?> clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
TlsExtensionsUtils.addEncryptThenMACExtension(clientExtensions);
// TODO[draft-ietf-tls-session-hash-01] Enable once code-point assigned (only for compatible server though)
// TlsExtensionsUtils.addExtendedMasterSecretExtension(clientExtensions);
TlsExtensionsUtils.addMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
TlsExtensionsUtils.addTruncatedHMacExtension(clientExtensions);
return clientExtensions;
}
public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException {
super.notifyServerVersion(serverVersion);
log.trace("Negotiated " + serverVersion);
}
public TlsAuthentication getAuthentication() throws IOException {
return new TlsAuthentication() {
public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate)
throws IOException {
Certificate[] chain = serverCertificate.getCertificateList();
log.trace("Received server certificate chain of length " + chain.length);
for (int i = 0; i != chain.length; i++) {
Certificate entry = chain[i];
// TODO Create fingerprint based on certificate signature algorithm digest
log.trace(" fingerprint:SHA-256 " + DtlsUtils.fingerprint(entry) + " (" + entry.getSubject()
+ ")");
}
}
public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException {
short[] certificateTypes = certificateRequest.getCertificateTypes();
if (certificateTypes == null || !Arrays.contains(certificateTypes, ClientCertificateType.rsa_sign)) {
return null;
}
SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
Vector<?> sigAlgs = certificateRequest.getSupportedSignatureAlgorithms();
if (sigAlgs != null) {
for (int i = 0; i < sigAlgs.size(); ++i) {
SignatureAndHashAlgorithm sigAlg = (SignatureAndHashAlgorithm) sigAlgs.elementAt(i);
if (sigAlg.getSignature() == SignatureAlgorithm.rsa) {
signatureAndHashAlgorithm = sigAlg;
break;
}
}
if (signatureAndHashAlgorithm == null) {
return null;
}
}
return DtlsUtils.loadSignerCredentials(context,
new String[] { cert.getAbsolutePath(), root.getAbsolutePath() }, key.getAbsolutePath(),
signatureAndHashAlgorithm);
}
};
}
public void notifyHandshakeComplete() throws IOException {
super.notifyHandshakeComplete();
TlsSession newSession = context.getResumableSession();
if (newSession != null) {
byte[] newSessionID = newSession.getSessionID();
String hex = Hex.toHexString(newSessionID);
if (this.session != null && Arrays.areEqual(this.session.getSessionID(), newSessionID)) {
log.trace("Resumed session: " + hex);
} else {
log.trace("Established session: " + hex);
}
this.session = newSession;
}
}
}