Path: blob/master/src/java.base/share/classes/sun/security/ssl/CertificateStatus.java
41159 views
/*1* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.ssl;2627import java.io.IOException;28import java.nio.ByteBuffer;29import java.text.MessageFormat;30import java.util.List;31import java.util.ArrayList;32import java.util.Locale;33import javax.net.ssl.SSLHandshakeException;34import java.security.cert.X509Certificate;35import sun.security.provider.certpath.OCSPResponse;36import sun.security.ssl.SSLHandshake.HandshakeMessage;37import static sun.security.ssl.CertStatusExtension.*;38import static sun.security.ssl.CertificateMessage.*;3940/**41* Consumers and producers for the CertificateStatus handshake message.42* This message takes one of two related but slightly different forms,43* depending on the type of stapling selected by the server. The message44* data will be of the form(s):45*46* [status_request, RFC 6066]47*48* struct {49* CertificateStatusType status_type;50* select (status_type) {51* case ocsp: OCSPResponse;52* } response;53* } CertificateStatus;54*55* opaque OCSPResponse<1..2^24-1>;56*57* [status_request_v2, RFC 6961]58*59* struct {60* CertificateStatusType status_type;61* select (status_type) {62* case ocsp: OCSPResponse;63* case ocsp_multi: OCSPResponseList;64* } response;65* } CertificateStatus;66*67* opaque OCSPResponse<0..2^24-1>;68*69* struct {70* OCSPResponse ocsp_response_list<1..2^24-1>;71* } OCSPResponseList;72*/73final class CertificateStatus {74static final SSLConsumer handshakeConsumer =75new CertificateStatusConsumer();76static final HandshakeProducer handshakeProducer =77new CertificateStatusProducer();78static final HandshakeAbsence handshakeAbsence =79new CertificateStatusAbsence();8081/**82* The CertificateStatus handshake message.83*/84static final class CertificateStatusMessage extends HandshakeMessage {8586final CertStatusRequestType statusType;87final int encodedResponsesLen;88final int messageLength;89final List<byte[]> encodedResponses = new ArrayList<>();9091CertificateStatusMessage(HandshakeContext handshakeContext) {92super(handshakeContext);9394ServerHandshakeContext shc =95(ServerHandshakeContext)handshakeContext;9697// Get the Certificates from the SSLContextImpl amd the Stapling98// parameters99StatusResponseManager.StaplingParameters stapleParams =100shc.stapleParams;101if (stapleParams == null) {102throw new IllegalArgumentException(103"Unexpected null stapling parameters");104}105106X509Certificate[] certChain =107(X509Certificate[])shc.handshakeSession.getLocalCertificates();108if (certChain == null) {109throw new IllegalArgumentException(110"Unexpected null certificate chain");111}112113// Walk the certificate list and add the correct encoded responses114// to the encoded responses list115statusType = stapleParams.statReqType;116int encodedLen = 0;117if (statusType == CertStatusRequestType.OCSP) {118// Just worry about the first cert in the chain119byte[] resp = stapleParams.responseMap.get(certChain[0]);120if (resp == null) {121// A not-found return status means we should include122// a zero-length response in CertificateStatus.123// This is highly unlikely to happen in practice.124resp = new byte[0];125}126encodedResponses.add(resp);127encodedLen += resp.length + 3;128} else if (statusType == CertStatusRequestType.OCSP_MULTI) {129for (X509Certificate cert : certChain) {130byte[] resp = stapleParams.responseMap.get(cert);131if (resp == null) {132resp = new byte[0];133}134encodedResponses.add(resp);135encodedLen += resp.length + 3;136}137} else {138throw new IllegalArgumentException(139"Unsupported StatusResponseType: " + statusType);140}141142encodedResponsesLen = encodedLen;143messageLength = messageLength(statusType, encodedResponsesLen);144}145146CertificateStatusMessage(HandshakeContext handshakeContext,147ByteBuffer m) throws IOException {148super(handshakeContext);149150statusType = CertStatusRequestType.valueOf((byte)Record.getInt8(m));151if (statusType == CertStatusRequestType.OCSP) {152byte[] respDER = Record.getBytes24(m);153// Convert the incoming bytes to a OCSPResponse strucutre154if (respDER.length > 0) {155encodedResponses.add(respDER);156encodedResponsesLen = 3 + respDER.length;157} else {158throw handshakeContext.conContext.fatal(159Alert.HANDSHAKE_FAILURE,160"Zero-length OCSP Response");161}162} else if (statusType == CertStatusRequestType.OCSP_MULTI) {163int respListLen = Record.getInt24(m);164encodedResponsesLen = respListLen;165166// Add each OCSP reponse into the array list in the order167// we receive them off the wire. A zero-length array is168// allowed for ocsp_multi, and means that a response for169// a given certificate is not available.170while (respListLen > 0) {171byte[] respDER = Record.getBytes24(m);172encodedResponses.add(respDER);173respListLen -= (respDER.length + 3);174}175176if (respListLen != 0) {177throw handshakeContext.conContext.fatal(178Alert.INTERNAL_ERROR,179"Bad OCSP response list length");180}181} else {182throw handshakeContext.conContext.fatal(183Alert.HANDSHAKE_FAILURE,184"Unsupported StatusResponseType: " + statusType);185}186messageLength = messageLength(statusType, encodedResponsesLen);187}188189private static int messageLength(190CertStatusRequestType statusType, int encodedResponsesLen) {191if (statusType == CertStatusRequestType.OCSP) {192return 1 + encodedResponsesLen;193} else if (statusType == CertStatusRequestType.OCSP_MULTI) {194return 4 + encodedResponsesLen;195}196197return -1;198}199200@Override201public SSLHandshake handshakeType() {202return SSLHandshake.CERTIFICATE_STATUS;203}204205@Override206public int messageLength() {207return messageLength;208}209210@Override211public void send(HandshakeOutStream s) throws IOException {212s.putInt8(statusType.id);213if (statusType == CertStatusRequestType.OCSP) {214s.putBytes24(encodedResponses.get(0));215} else if (statusType == CertStatusRequestType.OCSP_MULTI) {216s.putInt24(encodedResponsesLen);217for (byte[] respBytes : encodedResponses) {218s.putBytes24(respBytes);219}220} else {221// It is highly unlikely that we will fall into this section222// of the code.223throw new SSLHandshakeException("Unsupported status_type: " +224statusType.id);225}226}227228@Override229public String toString() {230StringBuilder sb = new StringBuilder();231232// Stringify the encoded OCSP response list233for (byte[] respDER : encodedResponses) {234if (respDER.length > 0) {235try {236OCSPResponse oResp = new OCSPResponse(respDER);237sb.append(oResp.toString()).append("\n");238} catch (IOException ioe) {239sb.append("OCSP Response Exception: ").append(ioe)240.append("\n");241}242} else {243sb.append("<Zero-length entry>\n");244}245}246247MessageFormat messageFormat = new MessageFormat(248"\"CertificateStatus\": '{'\n" +249" \"type\" : \"{0}\",\n" +250" \"responses \" : [\n" + "{1}\n" + " ]\n" +251"'}'",252Locale.ENGLISH);253Object[] messageFields = {254statusType.name,255Utilities.indent(Utilities.indent(sb.toString()))256};257258return messageFormat.format(messageFields);259}260}261262/**263* The CertificateStatus handshake message consumer.264*/265private static final class CertificateStatusConsumer266implements SSLConsumer {267// Prevent instantiation of this class.268private CertificateStatusConsumer() {269// blank270}271272@Override273public void consume(ConnectionContext context,274ByteBuffer message) throws IOException {275ClientHandshakeContext chc = (ClientHandshakeContext)context;276CertificateStatusMessage cst =277new CertificateStatusMessage(chc, message);278279// Log the message280if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {281SSLLogger.fine(282"Consuming server CertificateStatus handshake message",283cst);284}285286// Pin the received responses to the SSLSessionImpl. It will287// be retrieved by the X509TrustManagerImpl during the certificate288// checking phase.289chc.handshakeSession.setStatusResponses(cst.encodedResponses);290291// Now perform the check292T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);293294// Update the handshake consumers to remove this message, indicating295// that it has been processed.296chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_STATUS.id);297}298}299300/**301* The CertificateStatus handshake message consumer.302*/303private static final class CertificateStatusProducer304implements HandshakeProducer {305// Prevent instantiation of this class.306private CertificateStatusProducer() {307// blank308}309310@Override311public byte[] produce(ConnectionContext context,312HandshakeMessage message) throws IOException {313// Only the server-side should be a producer of this message314ServerHandshakeContext shc = (ServerHandshakeContext)context;315316// If stapling is not active, immediately return without producing317// a message or any further processing.318if (!shc.staplingActive) {319return null;320}321322// Create the CertificateStatus message from info in the323CertificateStatusMessage csm = new CertificateStatusMessage(shc);324if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {325SSLLogger.fine(326"Produced server CertificateStatus handshake message", csm);327}328329// Output the handshake message.330csm.write(shc.handshakeOutput);331shc.handshakeOutput.flush();332333// The handshake message has been delivered.334return null;335}336}337338private static final class CertificateStatusAbsence339implements HandshakeAbsence {340// Prevent instantiation of this class341private CertificateStatusAbsence() {342// blank343}344345@Override346public void absent(ConnectionContext context,347HandshakeMessage message) throws IOException {348ClientHandshakeContext chc = (ClientHandshakeContext)context;349350// Processing should only continue if stapling is active351if (chc.staplingActive) {352// Because OCSP stapling is active, it means two things353// if we're here: 1) The server hello asserted the354// status_request[_v2] extension. 2) The CertificateStatus355// message was not sent. This means that cert path checking356// was deferred, but must happen immediately.357if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {358SSLLogger.fine("Server did not send CertificateStatus, " +359"checking cert chain without status info.");360}361T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);362}363}364}365}366367368369