package sun.security.ssl;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.PublicKey;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertPathValidatorException.BasicReason;
import java.security.cert.CertPathValidatorException.Reason;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
import static sun.security.ssl.ClientAuthType.CLIENT_AUTH_REQUIRED;
import sun.security.ssl.ClientHello.ClientHelloMessage;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
final class CertificateMessage {
static final SSLConsumer t12HandshakeConsumer =
new T12CertificateConsumer();
static final HandshakeProducer t12HandshakeProducer =
new T12CertificateProducer();
static final SSLConsumer t13HandshakeConsumer =
new T13CertificateConsumer();
static final HandshakeProducer t13HandshakeProducer =
new T13CertificateProducer();
static final class T12CertificateMessage extends HandshakeMessage {
final List<byte[]> encodedCertChain;
T12CertificateMessage(HandshakeContext handshakeContext,
X509Certificate[] certChain) throws SSLException {
super(handshakeContext);
List<byte[]> encodedCerts = new ArrayList<>(certChain.length);
for (X509Certificate cert : certChain) {
try {
encodedCerts.add(cert.getEncoded());
} catch (CertificateEncodingException cee) {
throw handshakeContext.conContext.fatal(
Alert.INTERNAL_ERROR,
"Could not encode certificate (" +
cert.getSubjectX500Principal() + ")", cee);
}
}
this.encodedCertChain = encodedCerts;
}
T12CertificateMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
int listLen = Record.getInt24(m);
if (listLen > m.remaining()) {
throw handshakeContext.conContext.fatal(
Alert.ILLEGAL_PARAMETER,
"Error parsing certificate message:no sufficient data");
}
if (listLen > 0) {
List<byte[]> encodedCerts = new LinkedList<>();
while (listLen > 0) {
byte[] encodedCert = Record.getBytes24(m);
listLen -= (3 + encodedCert.length);
encodedCerts.add(encodedCert);
}
this.encodedCertChain = encodedCerts;
} else {
this.encodedCertChain = Collections.emptyList();
}
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CERTIFICATE;
}
@Override
public int messageLength() {
int msgLen = 3;
for (byte[] encodedCert : encodedCertChain) {
msgLen += (encodedCert.length + 3);
}
return msgLen;
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
int listLen = 0;
for (byte[] encodedCert : encodedCertChain) {
listLen += (encodedCert.length + 3);
}
hos.putInt24(listLen);
for (byte[] encodedCert : encodedCertChain) {
hos.putBytes24(encodedCert);
}
}
@Override
public String toString() {
if (encodedCertChain.isEmpty()) {
return "\"Certificates\": <empty list>";
}
Object[] x509Certs = new Object[encodedCertChain.size()];
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int i = 0;
for (byte[] encodedCert : encodedCertChain) {
Object obj;
try {
obj = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(encodedCert));
} catch (CertificateException ce) {
obj = encodedCert;
}
x509Certs[i++] = obj;
}
} catch (CertificateException ce) {
int i = 0;
for (byte[] encodedCert : encodedCertChain) {
x509Certs[i++] = encodedCert;
}
}
MessageFormat messageFormat = new MessageFormat(
"\"Certificates\": [\n" +
"{0}\n" +
"]",
Locale.ENGLISH);
Object[] messageFields = {
SSLLogger.toString(x509Certs)
};
return messageFormat.format(messageFields);
}
}
private static final
class T12CertificateProducer implements HandshakeProducer {
private T12CertificateProducer() {
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
HandshakeContext hc = (HandshakeContext)context;
if (hc.sslConfig.isClientMode) {
return onProduceCertificate(
(ClientHandshakeContext)context, message);
} else {
return onProduceCertificate(
(ServerHandshakeContext)context, message);
}
}
private byte[] onProduceCertificate(ServerHandshakeContext shc,
SSLHandshake.HandshakeMessage message) throws IOException {
X509Possession x509Possession = null;
for (SSLPossession possession : shc.handshakePossessions) {
if (possession instanceof X509Possession) {
x509Possession = (X509Possession)possession;
break;
}
}
if (x509Possession == null) {
throw shc.conContext.fatal(Alert.INTERNAL_ERROR,
"No expected X.509 certificate for server authentication");
}
shc.handshakeSession.setLocalPrivateKey(
x509Possession.popPrivateKey);
shc.handshakeSession.setLocalCertificates(x509Possession.popCerts);
T12CertificateMessage cm =
new T12CertificateMessage(shc, x509Possession.popCerts);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced server Certificate handshake message", cm);
}
cm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
return null;
}
private byte[] onProduceCertificate(ClientHandshakeContext chc,
SSLHandshake.HandshakeMessage message) throws IOException {
X509Possession x509Possession = null;
for (SSLPossession possession : chc.handshakePossessions) {
if (possession instanceof X509Possession) {
x509Possession = (X509Possession)possession;
break;
}
}
if (x509Possession == null) {
if (chc.negotiatedProtocol.useTLS10PlusSpec()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"No X.509 certificate for client authentication, " +
"use empty Certificate message instead");
}
x509Possession =
new X509Possession(null, new X509Certificate[0]);
} else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"No X.509 certificate for client authentication, " +
"send a no_certificate alert");
}
chc.conContext.warning(Alert.NO_CERTIFICATE);
return null;
}
}
chc.handshakeSession.setLocalPrivateKey(
x509Possession.popPrivateKey);
if (x509Possession.popCerts != null &&
x509Possession.popCerts.length != 0) {
chc.handshakeSession.setLocalCertificates(
x509Possession.popCerts);
} else {
chc.handshakeSession.setLocalCertificates(null);
}
T12CertificateMessage cm =
new T12CertificateMessage(chc, x509Possession.popCerts);
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Produced client Certificate handshake message", cm);
}
cm.write(chc.handshakeOutput);
chc.handshakeOutput.flush();
return null;
}
}
static final
class T12CertificateConsumer implements SSLConsumer {
private T12CertificateConsumer() {
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
HandshakeContext hc = (HandshakeContext)context;
hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id);
T12CertificateMessage cm = new T12CertificateMessage(hc, message);
if (hc.sslConfig.isClientMode) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming server Certificate handshake message", cm);
}
onCertificate((ClientHandshakeContext)context, cm);
} else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming client Certificate handshake message", cm);
}
onCertificate((ServerHandshakeContext)context, cm);
}
}
private void onCertificate(ServerHandshakeContext shc,
T12CertificateMessage certificateMessage )throws IOException {
List<byte[]> encodedCerts = certificateMessage.encodedCertChain;
if (encodedCerts == null || encodedCerts.isEmpty()) {
shc.handshakeConsumers.remove(
SSLHandshake.CERTIFICATE_VERIFY.id);
if (shc.sslConfig.clientAuthType !=
ClientAuthType.CLIENT_AUTH_REQUESTED) {
throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,
"Empty server certificate chain");
} else {
return;
}
}
X509Certificate[] x509Certs =
new X509Certificate[encodedCerts.size()];
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int i = 0;
for (byte[] encodedCert : encodedCerts) {
x509Certs[i++] = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(encodedCert));
}
} catch (CertificateException ce) {
throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,
"Failed to parse server certificates", ce);
}
checkClientCerts(shc, x509Certs);
shc.handshakeCredentials.add(
new X509Credentials(x509Certs[0].getPublicKey(), x509Certs));
shc.handshakeSession.setPeerCertificates(x509Certs);
}
private void onCertificate(ClientHandshakeContext chc,
T12CertificateMessage certificateMessage) throws IOException {
List<byte[]> encodedCerts = certificateMessage.encodedCertChain;
if (encodedCerts == null || encodedCerts.isEmpty()) {
throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,
"Empty server certificate chain");
}
X509Certificate[] x509Certs =
new X509Certificate[encodedCerts.size()];
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int i = 0;
for (byte[] encodedCert : encodedCerts) {
x509Certs[i++] = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(encodedCert));
}
} catch (CertificateException ce) {
throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,
"Failed to parse server certificates", ce);
}
if (chc.reservedServerCerts != null &&
!chc.handshakeSession.useExtendedMasterSecret) {
String identityAlg = chc.sslConfig.identificationProtocol;
if ((identityAlg == null || identityAlg.isEmpty()) &&
!isIdentityEquivalent(x509Certs[0],
chc.reservedServerCerts[0])) {
throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,
"server certificate change is restricted " +
"during renegotiation");
}
}
if (chc.staplingActive) {
chc.deferredCerts = x509Certs;
} else {
checkServerCerts(chc, x509Certs);
}
chc.handshakeCredentials.add(
new X509Credentials(x509Certs[0].getPublicKey(), x509Certs));
chc.handshakeSession.setPeerCertificates(x509Certs);
}
private static boolean isIdentityEquivalent(X509Certificate thisCert,
X509Certificate prevCert) {
if (thisCert.equals(prevCert)) {
return true;
}
Collection<List<?>> thisSubjectAltNames = null;
try {
thisSubjectAltNames = thisCert.getSubjectAlternativeNames();
} catch (CertificateParsingException cpe) {
if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
SSLLogger.fine(
"Attempt to obtain subjectAltNames extension failed!");
}
}
Collection<List<?>> prevSubjectAltNames = null;
try {
prevSubjectAltNames = prevCert.getSubjectAlternativeNames();
} catch (CertificateParsingException cpe) {
if (SSLLogger.isOn && SSLLogger.isOn("handshake")) {
SSLLogger.fine(
"Attempt to obtain subjectAltNames extension failed!");
}
}
if (thisSubjectAltNames != null && prevSubjectAltNames != null) {
Collection<String> thisSubAltIPAddrs =
getSubjectAltNames(thisSubjectAltNames, 7);
Collection<String> prevSubAltIPAddrs =
getSubjectAltNames(prevSubjectAltNames, 7);
if (thisSubAltIPAddrs != null && prevSubAltIPAddrs != null &&
isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs)) {
return true;
}
Collection<String> thisSubAltDnsNames =
getSubjectAltNames(thisSubjectAltNames, 2);
Collection<String> prevSubAltDnsNames =
getSubjectAltNames(prevSubjectAltNames, 2);
if (thisSubAltDnsNames != null && prevSubAltDnsNames != null &&
isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames)) {
return true;
}
}
X500Principal thisSubject = thisCert.getSubjectX500Principal();
X500Principal prevSubject = prevCert.getSubjectX500Principal();
X500Principal thisIssuer = thisCert.getIssuerX500Principal();
X500Principal prevIssuer = prevCert.getIssuerX500Principal();
return (!thisSubject.getName().isEmpty() &&
!prevSubject.getName().isEmpty() &&
thisSubject.equals(prevSubject) &&
thisIssuer.equals(prevIssuer));
}
private static Collection<String> getSubjectAltNames(
Collection<List<?>> subjectAltNames, int type) {
HashSet<String> subAltDnsNames = null;
for (List<?> subjectAltName : subjectAltNames) {
int subjectAltNameType = (Integer)subjectAltName.get(0);
if (subjectAltNameType == type) {
String subAltDnsName = (String)subjectAltName.get(1);
if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) {
if (subAltDnsNames == null) {
subAltDnsNames =
new HashSet<>(subjectAltNames.size());
}
subAltDnsNames.add(subAltDnsName);
}
}
}
return subAltDnsNames;
}
private static boolean isEquivalent(Collection<String> thisSubAltNames,
Collection<String> prevSubAltNames) {
for (String thisSubAltName : thisSubAltNames) {
for (String prevSubAltName : prevSubAltNames) {
if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) {
return true;
}
}
}
return false;
}
static void checkServerCerts(ClientHandshakeContext chc,
X509Certificate[] certs) throws IOException {
X509TrustManager tm = chc.sslContext.getX509TrustManager();
String keyExchangeString;
if (chc.negotiatedCipherSuite.keyExchange ==
CipherSuite.KeyExchange.K_RSA_EXPORT ||
chc.negotiatedCipherSuite.keyExchange ==
CipherSuite.KeyExchange.K_DHE_RSA_EXPORT) {
keyExchangeString = CipherSuite.KeyExchange.K_RSA.name;
} else {
keyExchangeString = chc.negotiatedCipherSuite.keyExchange.name;
}
try {
if (tm instanceof X509ExtendedTrustManager) {
if (chc.conContext.transport instanceof SSLEngine) {
SSLEngine engine = (SSLEngine)chc.conContext.transport;
((X509ExtendedTrustManager)tm).checkServerTrusted(
certs.clone(),
keyExchangeString,
engine);
} else {
SSLSocket socket = (SSLSocket)chc.conContext.transport;
((X509ExtendedTrustManager)tm).checkServerTrusted(
certs.clone(),
keyExchangeString,
socket);
}
} else {
throw new CertificateException(
"Improper X509TrustManager implementation");
}
chc.handshakeSession.setPeerCertificates(certs);
} catch (CertificateException ce) {
throw chc.conContext.fatal(getCertificateAlert(chc, ce), ce);
}
}
private static void checkClientCerts(ServerHandshakeContext shc,
X509Certificate[] certs) throws IOException {
X509TrustManager tm = shc.sslContext.getX509TrustManager();
PublicKey key = certs[0].getPublicKey();
String keyAlgorithm = key.getAlgorithm();
String authType;
switch (keyAlgorithm) {
case "RSA":
case "DSA":
case "EC":
case "RSASSA-PSS":
authType = keyAlgorithm;
break;
default:
authType = "UNKNOWN";
}
try {
if (tm instanceof X509ExtendedTrustManager) {
if (shc.conContext.transport instanceof SSLEngine) {
SSLEngine engine = (SSLEngine)shc.conContext.transport;
((X509ExtendedTrustManager)tm).checkClientTrusted(
certs.clone(),
authType,
engine);
} else {
SSLSocket socket = (SSLSocket)shc.conContext.transport;
((X509ExtendedTrustManager)tm).checkClientTrusted(
certs.clone(),
authType,
socket);
}
} else {
throw new CertificateException(
"Improper X509TrustManager implementation");
}
} catch (CertificateException ce) {
throw shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce);
}
}
private static Alert getCertificateAlert(
ClientHandshakeContext chc, CertificateException cexc) {
Alert alert = Alert.CERTIFICATE_UNKNOWN;
Throwable baseCause = cexc.getCause();
if (baseCause instanceof CertPathValidatorException) {
CertPathValidatorException cpve =
(CertPathValidatorException)baseCause;
Reason reason = cpve.getReason();
if (reason == BasicReason.REVOKED) {
alert = chc.staplingActive ?
Alert.BAD_CERT_STATUS_RESPONSE :
Alert.CERTIFICATE_REVOKED;
} else if (
reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
alert = chc.staplingActive ?
Alert.BAD_CERT_STATUS_RESPONSE :
Alert.CERTIFICATE_UNKNOWN;
} else if (reason == BasicReason.ALGORITHM_CONSTRAINED) {
alert = Alert.UNSUPPORTED_CERTIFICATE;
} else if (reason == BasicReason.EXPIRED) {
alert = Alert.CERTIFICATE_EXPIRED;
} else if (reason == BasicReason.INVALID_SIGNATURE ||
reason == BasicReason.NOT_YET_VALID) {
alert = Alert.BAD_CERTIFICATE;
}
}
return alert;
}
}
static final class CertificateEntry {
final byte[] encoded;
private final SSLExtensions extensions;
CertificateEntry(byte[] encoded, SSLExtensions extensions) {
this.encoded = encoded;
this.extensions = extensions;
}
private int getEncodedSize() {
int extLen = extensions.length();
if (extLen == 0) {
extLen = 2;
}
return 3 + encoded.length + extLen;
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\n'{'\n" +
"{0}\n" +
" \"extensions\": '{'\n" +
"{1}\n" +
" '}'\n" +
"'}',", Locale.ENGLISH);
Object x509Certs;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
x509Certs =
cf.generateCertificate(new ByteArrayInputStream(encoded));
} catch (CertificateException ce) {
x509Certs = encoded;
}
Object[] messageFields = {
SSLLogger.toString(x509Certs),
Utilities.indent(extensions.toString(), " ")
};
return messageFormat.format(messageFields);
}
}
static final class T13CertificateMessage extends HandshakeMessage {
private final byte[] requestContext;
private final List<CertificateEntry> certEntries;
T13CertificateMessage(HandshakeContext context,
byte[] requestContext, X509Certificate[] certificates)
throws SSLException, CertificateException {
super(context);
this.requestContext = requestContext.clone();
this.certEntries = new LinkedList<>();
for (X509Certificate cert : certificates) {
byte[] encoded = cert.getEncoded();
SSLExtensions extensions = new SSLExtensions(this);
certEntries.add(new CertificateEntry(encoded, extensions));
}
}
T13CertificateMessage(HandshakeContext handshakeContext,
byte[] requestContext, List<CertificateEntry> certificates) {
super(handshakeContext);
this.requestContext = requestContext.clone();
this.certEntries = certificates;
}
T13CertificateMessage(HandshakeContext handshakeContext,
ByteBuffer m) throws IOException {
super(handshakeContext);
if (m.remaining() < 4) {
throw new SSLProtocolException(
"Invalid Certificate message: " +
"insufficient data (length=" + m.remaining() + ")");
}
this.requestContext = Record.getBytes8(m);
if (m.remaining() < 3) {
throw new SSLProtocolException(
"Invalid Certificate message: " +
"insufficient certificate entries data (length=" +
m.remaining() + ")");
}
int listLen = Record.getInt24(m);
if (listLen != m.remaining()) {
throw new SSLProtocolException(
"Invalid Certificate message: " +
"incorrect list length (length=" + listLen + ")");
}
SSLExtension[] enabledExtensions =
handshakeContext.sslConfig.getEnabledExtensions(
SSLHandshake.CERTIFICATE);
List<CertificateEntry> certList = new LinkedList<>();
while (m.hasRemaining()) {
byte[] encodedCert = Record.getBytes24(m);
if (encodedCert.length == 0) {
throw new SSLProtocolException(
"Invalid Certificate message: empty cert_data");
}
SSLExtensions extensions =
new SSLExtensions(this, m, enabledExtensions);
certList.add(new CertificateEntry(encodedCert, extensions));
}
this.certEntries = Collections.unmodifiableList(certList);
}
@Override
public SSLHandshake handshakeType() {
return SSLHandshake.CERTIFICATE;
}
@Override
public int messageLength() {
int msgLen = 4 + requestContext.length;
for (CertificateEntry entry : certEntries) {
msgLen += entry.getEncodedSize();
}
return msgLen;
}
@Override
public void send(HandshakeOutStream hos) throws IOException {
int entryListLen = 0;
for (CertificateEntry entry : certEntries) {
entryListLen += entry.getEncodedSize();
}
hos.putBytes8(requestContext);
hos.putInt24(entryListLen);
for (CertificateEntry entry : certEntries) {
hos.putBytes24(entry.encoded);
if (entry.extensions.length() == 0) {
hos.putInt16(0);
} else {
entry.extensions.send(hos);
}
}
}
@Override
public String toString() {
MessageFormat messageFormat = new MessageFormat(
"\"Certificate\": '{'\n" +
" \"certificate_request_context\": \"{0}\",\n" +
" \"certificate_list\": [{1}\n]\n" +
"'}'",
Locale.ENGLISH);
StringBuilder builder = new StringBuilder(512);
for (CertificateEntry entry : certEntries) {
builder.append(entry.toString());
}
Object[] messageFields = {
Utilities.toHexString(requestContext),
Utilities.indent(builder.toString())
};
return messageFormat.format(messageFields);
}
}
private static final
class T13CertificateProducer implements HandshakeProducer {
private T13CertificateProducer() {
}
@Override
public byte[] produce(ConnectionContext context,
HandshakeMessage message) throws IOException {
HandshakeContext hc = (HandshakeContext)context;
if (hc.sslConfig.isClientMode) {
return onProduceCertificate(
(ClientHandshakeContext)context, message);
} else {
return onProduceCertificate(
(ServerHandshakeContext)context, message);
}
}
private byte[] onProduceCertificate(ServerHandshakeContext shc,
HandshakeMessage message) throws IOException {
ClientHelloMessage clientHello = (ClientHelloMessage)message;
SSLPossession pos = choosePossession(shc, clientHello);
if (pos == null) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No available authentication scheme");
}
if (!(pos instanceof X509Possession)) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No X.509 certificate for server authentication");
}
X509Possession x509Possession = (X509Possession)pos;
X509Certificate[] localCerts = x509Possession.popCerts;
if (localCerts == null || localCerts.length == 0) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"No X.509 certificate for server authentication");
}
shc.handshakePossessions.add(x509Possession);
shc.handshakeSession.setLocalPrivateKey(
x509Possession.popPrivateKey);
shc.handshakeSession.setLocalCertificates(localCerts);
T13CertificateMessage cm;
try {
cm = new T13CertificateMessage(shc, (new byte[0]), localCerts);
} catch (SSLException | CertificateException ce) {
throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Failed to produce server Certificate message", ce);
}
shc.stapleParams = StatusResponseManager.processStapling(shc);
shc.staplingActive = (shc.stapleParams != null);
SSLExtension[] enabledCTExts = shc.sslConfig.getEnabledExtensions(
SSLHandshake.CERTIFICATE,
Arrays.asList(ProtocolVersion.PROTOCOLS_OF_13));
for (CertificateEntry certEnt : cm.certEntries) {
shc.currentCertEntry = certEnt;
certEnt.extensions.produce(shc, enabledCTExts);
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Produced server Certificate message", cm);
}
cm.write(shc.handshakeOutput);
shc.handshakeOutput.flush();
return null;
}
private static SSLPossession choosePossession(
HandshakeContext hc,
ClientHelloMessage clientHello) throws IOException {
if (hc.peerRequestedCertSignSchemes == null ||
hc.peerRequestedCertSignSchemes.isEmpty()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"No signature_algorithms(_cert) in ClientHello");
}
return null;
}
Collection<String> checkedKeyTypes = new HashSet<>();
for (SignatureScheme ss : hc.peerRequestedCertSignSchemes) {
if (checkedKeyTypes.contains(ss.keyAlgorithm)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Unsupported authentication scheme: " + ss.name);
}
continue;
}
if (SignatureScheme.getPreferableAlgorithm(
hc.peerRequestedSignatureSchemes,
ss, hc.negotiatedProtocol) == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Unable to produce CertificateVerify for " +
"signature scheme: " + ss.name);
}
checkedKeyTypes.add(ss.keyAlgorithm);
continue;
}
SSLAuthentication ka = X509Authentication.valueOf(ss);
if (ka == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Unsupported authentication scheme: " + ss.name);
}
checkedKeyTypes.add(ss.keyAlgorithm);
continue;
}
SSLPossession pos = ka.createPossession(hc);
if (pos == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Unavailable authentication scheme: " + ss.name);
}
continue;
}
return pos;
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning("No available authentication scheme");
}
return null;
}
private byte[] onProduceCertificate(ClientHandshakeContext chc,
HandshakeMessage message) throws IOException {
ClientHelloMessage clientHello = (ClientHelloMessage)message;
SSLPossession pos = choosePossession(chc, clientHello);
X509Certificate[] localCerts;
if (pos == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("No available client authentication scheme");
}
localCerts = new X509Certificate[0];
} else {
chc.handshakePossessions.add(pos);
if (!(pos instanceof X509Possession)) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"No X.509 certificate for client authentication");
}
localCerts = new X509Certificate[0];
} else {
X509Possession x509Possession = (X509Possession)pos;
localCerts = x509Possession.popCerts;
chc.handshakeSession.setLocalPrivateKey(
x509Possession.popPrivateKey);
}
}
if (localCerts != null && localCerts.length != 0) {
chc.handshakeSession.setLocalCertificates(localCerts);
} else {
chc.handshakeSession.setLocalCertificates(null);
}
T13CertificateMessage cm;
try {
cm = new T13CertificateMessage(
chc, chc.certRequestContext, localCerts);
} catch (SSLException | CertificateException ce) {
throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
"Failed to produce client Certificate message", ce);
}
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine("Produced client Certificate message", cm);
}
cm.write(chc.handshakeOutput);
chc.handshakeOutput.flush();
return null;
}
}
private static final class T13CertificateConsumer implements SSLConsumer {
private T13CertificateConsumer() {
}
@Override
public void consume(ConnectionContext context,
ByteBuffer message) throws IOException {
HandshakeContext hc = (HandshakeContext)context;
hc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE.id);
T13CertificateMessage cm = new T13CertificateMessage(hc, message);
if (hc.sslConfig.isClientMode) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming server Certificate handshake message", cm);
}
onConsumeCertificate((ClientHandshakeContext)context, cm);
} else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.fine(
"Consuming client Certificate handshake message", cm);
}
onConsumeCertificate((ServerHandshakeContext)context, cm);
}
}
private void onConsumeCertificate(ServerHandshakeContext shc,
T13CertificateMessage certificateMessage )throws IOException {
if (certificateMessage.certEntries == null ||
certificateMessage.certEntries.isEmpty()) {
shc.handshakeConsumers.remove(
SSLHandshake.CERTIFICATE_VERIFY.id);
if (shc.sslConfig.clientAuthType == CLIENT_AUTH_REQUIRED) {
throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,
"Empty client certificate chain");
} else {
return;
}
}
X509Certificate[] cliCerts =
checkClientCerts(shc, certificateMessage.certEntries);
shc.handshakeCredentials.add(
new X509Credentials(cliCerts[0].getPublicKey(), cliCerts));
shc.handshakeSession.setPeerCertificates(cliCerts);
}
private void onConsumeCertificate(ClientHandshakeContext chc,
T13CertificateMessage certificateMessage )throws IOException {
if (certificateMessage.certEntries == null ||
certificateMessage.certEntries.isEmpty()) {
throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,
"Empty server certificate chain");
}
SSLExtension[] enabledExtensions =
chc.sslConfig.getEnabledExtensions(SSLHandshake.CERTIFICATE);
for (CertificateEntry certEnt : certificateMessage.certEntries) {
certEnt.extensions.consumeOnLoad(chc, enabledExtensions);
}
X509Certificate[] srvCerts =
checkServerCerts(chc, certificateMessage.certEntries);
chc.handshakeCredentials.add(
new X509Credentials(srvCerts[0].getPublicKey(), srvCerts));
chc.handshakeSession.setPeerCertificates(srvCerts);
}
private static X509Certificate[] checkClientCerts(
ServerHandshakeContext shc,
List<CertificateEntry> certEntries) throws IOException {
X509Certificate[] certs =
new X509Certificate[certEntries.size()];
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int i = 0;
for (CertificateEntry entry : certEntries) {
certs[i++] = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(entry.encoded));
}
} catch (CertificateException ce) {
throw shc.conContext.fatal(Alert.BAD_CERTIFICATE,
"Failed to parse server certificates", ce);
}
String keyAlgorithm = certs[0].getPublicKey().getAlgorithm();
String authType;
switch (keyAlgorithm) {
case "RSA":
case "DSA":
case "EC":
case "RSASSA-PSS":
authType = keyAlgorithm;
break;
default:
authType = "UNKNOWN";
}
try {
X509TrustManager tm = shc.sslContext.getX509TrustManager();
if (tm instanceof X509ExtendedTrustManager) {
if (shc.conContext.transport instanceof SSLEngine) {
SSLEngine engine = (SSLEngine)shc.conContext.transport;
((X509ExtendedTrustManager)tm).checkClientTrusted(
certs.clone(),
authType,
engine);
} else {
SSLSocket socket = (SSLSocket)shc.conContext.transport;
((X509ExtendedTrustManager)tm).checkClientTrusted(
certs.clone(),
authType,
socket);
}
} else {
throw new CertificateException(
"Improper X509TrustManager implementation");
}
shc.handshakeSession.setPeerCertificates(certs);
} catch (CertificateException ce) {
throw shc.conContext.fatal(Alert.CERTIFICATE_UNKNOWN, ce);
}
return certs;
}
private static X509Certificate[] checkServerCerts(
ClientHandshakeContext chc,
List<CertificateEntry> certEntries) throws IOException {
X509Certificate[] certs =
new X509Certificate[certEntries.size()];
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
int i = 0;
for (CertificateEntry entry : certEntries) {
certs[i++] = (X509Certificate)cf.generateCertificate(
new ByteArrayInputStream(entry.encoded));
}
} catch (CertificateException ce) {
throw chc.conContext.fatal(Alert.BAD_CERTIFICATE,
"Failed to parse server certificates", ce);
}
String authType = "UNKNOWN";
try {
X509TrustManager tm = chc.sslContext.getX509TrustManager();
if (tm instanceof X509ExtendedTrustManager) {
if (chc.conContext.transport instanceof SSLEngine) {
SSLEngine engine = (SSLEngine)chc.conContext.transport;
((X509ExtendedTrustManager)tm).checkServerTrusted(
certs.clone(),
authType,
engine);
} else {
SSLSocket socket = (SSLSocket)chc.conContext.transport;
((X509ExtendedTrustManager)tm).checkServerTrusted(
certs.clone(),
authType,
socket);
}
} else {
throw new CertificateException(
"Improper X509TrustManager implementation");
}
chc.handshakeSession.setPeerCertificates(certs);
} catch (CertificateException ce) {
throw chc.conContext.fatal(getCertificateAlert(chc, ce), ce);
}
return certs;
}
private static Alert getCertificateAlert(
ClientHandshakeContext chc, CertificateException cexc) {
Alert alert = Alert.CERTIFICATE_UNKNOWN;
Throwable baseCause = cexc.getCause();
if (baseCause instanceof CertPathValidatorException) {
CertPathValidatorException cpve =
(CertPathValidatorException)baseCause;
Reason reason = cpve.getReason();
if (reason == BasicReason.REVOKED) {
alert = chc.staplingActive ?
Alert.BAD_CERT_STATUS_RESPONSE :
Alert.CERTIFICATE_REVOKED;
} else if (
reason == BasicReason.UNDETERMINED_REVOCATION_STATUS) {
alert = chc.staplingActive ?
Alert.BAD_CERT_STATUS_RESPONSE :
Alert.CERTIFICATE_UNKNOWN;
}
}
return alert;
}
}
}