| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.ssl;  | 
 | 
 | 
 | 
import java.io.IOException;  | 
 | 
import java.nio.ByteBuffer;  | 
 | 
import java.text.MessageFormat;  | 
 | 
import java.util.List;  | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.Locale;  | 
 | 
import javax.net.ssl.SSLHandshakeException;  | 
 | 
import java.security.cert.X509Certificate;  | 
 | 
import sun.security.provider.certpath.OCSPResponse;  | 
 | 
import sun.security.ssl.SSLHandshake.HandshakeMessage;  | 
 | 
import static sun.security.ssl.CertStatusExtension.*;  | 
 | 
import static sun.security.ssl.CertificateMessage.*;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
final class CertificateStatus { | 
 | 
    static final SSLConsumer handshakeConsumer =  | 
 | 
            new CertificateStatusConsumer();  | 
 | 
    static final HandshakeProducer handshakeProducer =  | 
 | 
            new CertificateStatusProducer();  | 
 | 
    static final HandshakeAbsence handshakeAbsence =  | 
 | 
            new CertificateStatusAbsence();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    static final class CertificateStatusMessage extends HandshakeMessage { | 
 | 
 | 
 | 
        final CertStatusRequestType statusType;  | 
 | 
        int encodedResponsesLen = 0;  | 
 | 
        int messageLength = -1;  | 
 | 
        final List<byte[]> encodedResponses = new ArrayList<>();  | 
 | 
 | 
 | 
        CertificateStatusMessage(HandshakeContext handshakeContext) { | 
 | 
            super(handshakeContext);  | 
 | 
 | 
 | 
            ServerHandshakeContext shc =  | 
 | 
                    (ServerHandshakeContext)handshakeContext;  | 
 | 
 | 
 | 
            // Get the Certificates from the SSLContextImpl amd the Stapling  | 
 | 
              | 
 | 
            StatusResponseManager.StaplingParameters stapleParams =  | 
 | 
                    shc.stapleParams;  | 
 | 
            if (stapleParams == null) { | 
 | 
                throw new IllegalArgumentException(  | 
 | 
                        "Unexpected null stapling parameters");  | 
 | 
            }  | 
 | 
 | 
 | 
            X509Certificate[] certChain =  | 
 | 
                (X509Certificate[])shc.handshakeSession.getLocalCertificates();  | 
 | 
            if (certChain == null) { | 
 | 
                throw new IllegalArgumentException(  | 
 | 
                        "Unexpected null certificate chain");  | 
 | 
            }  | 
 | 
 | 
 | 
            // Walk the certificate list and add the correct encoded responses  | 
 | 
              | 
 | 
            statusType = stapleParams.statReqType;  | 
 | 
            if (statusType == CertStatusRequestType.OCSP) { | 
 | 
                  | 
 | 
                byte[] resp = stapleParams.responseMap.get(certChain[0]);  | 
 | 
                if (resp == null) { | 
 | 
                    // A not-found return status means we should include  | 
 | 
                    // a zero-length response in CertificateStatus.  | 
 | 
                      | 
 | 
                    resp = new byte[0];  | 
 | 
                }  | 
 | 
                encodedResponses.add(resp);  | 
 | 
                encodedResponsesLen += resp.length + 3;  | 
 | 
            } else if (statusType == CertStatusRequestType.OCSP_MULTI) { | 
 | 
                for (X509Certificate cert : certChain) { | 
 | 
                    byte[] resp = stapleParams.responseMap.get(cert);  | 
 | 
                    if (resp == null) { | 
 | 
                        resp = new byte[0];  | 
 | 
                    }  | 
 | 
                    encodedResponses.add(resp);  | 
 | 
                    encodedResponsesLen += resp.length + 3;  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                throw new IllegalArgumentException(  | 
 | 
                        "Unsupported StatusResponseType: " + statusType);  | 
 | 
            }  | 
 | 
 | 
 | 
            messageLength = messageLength();  | 
 | 
        }  | 
 | 
 | 
 | 
        CertificateStatusMessage(HandshakeContext handshakeContext,  | 
 | 
                ByteBuffer m) throws IOException { | 
 | 
            super(handshakeContext);  | 
 | 
 | 
 | 
            statusType = CertStatusRequestType.valueOf((byte)Record.getInt8(m));  | 
 | 
            if (statusType == CertStatusRequestType.OCSP) { | 
 | 
                byte[] respDER = Record.getBytes24(m);  | 
 | 
                  | 
 | 
                if (respDER.length > 0) { | 
 | 
                    encodedResponses.add(respDER);  | 
 | 
                    encodedResponsesLen = 3 + respDER.length;  | 
 | 
                } else { | 
 | 
                    throw handshakeContext.conContext.fatal(  | 
 | 
                            Alert.HANDSHAKE_FAILURE,  | 
 | 
                            "Zero-length OCSP Response");  | 
 | 
                }  | 
 | 
            } else if (statusType == CertStatusRequestType.OCSP_MULTI) { | 
 | 
                int respListLen = Record.getInt24(m);  | 
 | 
                encodedResponsesLen = respListLen;  | 
 | 
 | 
 | 
                // Add each OCSP reponse into the array list in the order  | 
 | 
                // we receive them off the wire.  A zero-length array is  | 
 | 
                // allowed for ocsp_multi, and means that a response for  | 
 | 
                  | 
 | 
                while (respListLen > 0) { | 
 | 
                    byte[] respDER = Record.getBytes24(m);  | 
 | 
                    encodedResponses.add(respDER);  | 
 | 
                    respListLen -= (respDER.length + 3);  | 
 | 
                }  | 
 | 
 | 
 | 
                if (respListLen != 0) { | 
 | 
                    throw handshakeContext.conContext.fatal(  | 
 | 
                            Alert.INTERNAL_ERROR,  | 
 | 
                            "Bad OCSP response list length");  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                throw handshakeContext.conContext.fatal(  | 
 | 
                        Alert.HANDSHAKE_FAILURE,  | 
 | 
                        "Unsupported StatusResponseType: " + statusType);  | 
 | 
            }  | 
 | 
            messageLength = messageLength();  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public SSLHandshake handshakeType() { | 
 | 
            return SSLHandshake.CERTIFICATE_STATUS;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public int messageLength() { | 
 | 
            int len = 1;  | 
 | 
 | 
 | 
            if (messageLength == -1) { | 
 | 
                if (statusType == CertStatusRequestType.OCSP) { | 
 | 
                    len += encodedResponsesLen;  | 
 | 
                } else if (statusType == CertStatusRequestType.OCSP_MULTI) { | 
 | 
                    len += 3 + encodedResponsesLen;  | 
 | 
                }  | 
 | 
                messageLength = len;  | 
 | 
            }  | 
 | 
 | 
 | 
            return messageLength;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void send(HandshakeOutStream s) throws IOException { | 
 | 
            s.putInt8(statusType.id);  | 
 | 
            if (statusType == CertStatusRequestType.OCSP) { | 
 | 
                s.putBytes24(encodedResponses.get(0));  | 
 | 
            } else if (statusType == CertStatusRequestType.OCSP_MULTI) { | 
 | 
                s.putInt24(encodedResponsesLen);  | 
 | 
                for (byte[] respBytes : encodedResponses) { | 
 | 
                    if (respBytes != null) { | 
 | 
                        s.putBytes24(respBytes);  | 
 | 
                    } else { | 
 | 
                        s.putBytes24(null);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                // It is highly unlikely that we will fall into this section  | 
 | 
                  | 
 | 
                throw new SSLHandshakeException("Unsupported status_type: " + | 
 | 
                        statusType.id);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
            StringBuilder sb = new StringBuilder();  | 
 | 
 | 
 | 
              | 
 | 
            for (byte[] respDER : encodedResponses) { | 
 | 
                if (respDER.length > 0) { | 
 | 
                    try { | 
 | 
                        OCSPResponse oResp = new OCSPResponse(respDER);  | 
 | 
                        sb.append(oResp.toString()).append("\n"); | 
 | 
                    } catch (IOException ioe) { | 
 | 
                        sb.append("OCSP Response Exception: ").append(ioe) | 
 | 
                                .append("\n"); | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    sb.append("<Zero-length entry>\n"); | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            MessageFormat messageFormat = new MessageFormat(  | 
 | 
                "\"CertificateStatus\": '{'\n" + | 
 | 
                "  \"type\"                : \"{0}\",\n" + | 
 | 
                "  \"responses \"          : [\n" + "{1}\n" + "  ]\n" + | 
 | 
                "'}'",  | 
 | 
                Locale.ENGLISH);  | 
 | 
            Object[] messageFields = { | 
 | 
                statusType.name,  | 
 | 
                Utilities.indent(Utilities.indent(sb.toString()))  | 
 | 
            };  | 
 | 
 | 
 | 
            return messageFormat.format(messageFields);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final class CertificateStatusConsumer  | 
 | 
            implements SSLConsumer { | 
 | 
          | 
 | 
        private CertificateStatusConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                ByteBuffer message) throws IOException { | 
 | 
            ClientHandshakeContext chc = (ClientHandshakeContext)context;  | 
 | 
            CertificateStatusMessage cst =  | 
 | 
                    new CertificateStatusMessage(chc, message);  | 
 | 
 | 
 | 
              | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                        "Consuming server CertificateStatus handshake message",  | 
 | 
                        cst);  | 
 | 
            }  | 
 | 
 | 
 | 
            // Pin the received responses to the SSLSessionImpl.  It will  | 
 | 
            // be retrieved by the X509TrustManagerImpl during the certificate  | 
 | 
              | 
 | 
            chc.handshakeSession.setStatusResponses(cst.encodedResponses);  | 
 | 
 | 
 | 
              | 
 | 
            T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);  | 
 | 
 | 
 | 
            // Update the handshake consumers to remove this message, indicating  | 
 | 
              | 
 | 
            chc.handshakeConsumers.remove(SSLHandshake.CERTIFICATE_STATUS.id);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final class CertificateStatusProducer  | 
 | 
            implements HandshakeProducer { | 
 | 
          | 
 | 
        private CertificateStatusProducer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public byte[] produce(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            ServerHandshakeContext shc = (ServerHandshakeContext)context;  | 
 | 
 | 
 | 
            // If stapling is not active, immediately return without producing  | 
 | 
              | 
 | 
            if (!shc.staplingActive) { | 
 | 
                return null;  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            CertificateStatusMessage csm = new CertificateStatusMessage(shc);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                    "Produced server CertificateStatus handshake message", csm);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            csm.write(shc.handshakeOutput);  | 
 | 
            shc.handshakeOutput.flush();  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private static final class CertificateStatusAbsence  | 
 | 
            implements HandshakeAbsence { | 
 | 
          | 
 | 
        private CertificateStatusAbsence() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void absent(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
            ClientHandshakeContext chc = (ClientHandshakeContext)context;  | 
 | 
 | 
 | 
              | 
 | 
            if (chc.staplingActive) { | 
 | 
                // Because OCSP stapling is active, it means two things  | 
 | 
                // if we're here: 1) The server hello asserted the  | 
 | 
                // status_request[_v2] extension.  2) The CertificateStatus  | 
 | 
                // message was not sent.  This means that cert path checking  | 
 | 
                  | 
 | 
                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                    SSLLogger.fine("Server did not send CertificateStatus, " + | 
 | 
                            "checking cert chain without status info.");  | 
 | 
                }  | 
 | 
                T12CertificateConsumer.checkServerCerts(chc, chc.deferredCerts);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  | 
 | 
 |