13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/*
23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2013 Square, Inc.
33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License.
63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at
73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software
113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and
143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License.
153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp;
183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.Util;
203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.security.Principal;
213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.security.cert.Certificate;
223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.security.cert.X509Certificate;
233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Collections;
243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List;
253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport javax.net.ssl.SSLPeerUnverifiedException;
263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport javax.net.ssl.SSLSession;
273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/**
293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * A record of a TLS handshake. For HTTPS clients, the client is <i>local</i>
303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * and the remote server is its <i>peer</i>.
313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller *
323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * <p>This value object describes a completed handshake. Use {@link
333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * javax.net.ssl.SSLSocketFactory} to set policy for new handshakes.
343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */
353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpublic final class Handshake {
363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final String cipherSuite;
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final List<Certificate> peerCertificates;
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final List<Certificate> localCertificates;
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private Handshake(
413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String cipherSuite, List<Certificate> peerCertificates, List<Certificate> localCertificates) {
423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.cipherSuite = cipherSuite;
433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.peerCertificates = peerCertificates;
443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    this.localCertificates = localCertificates;
453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static Handshake get(SSLSession session) {
483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    String cipherSuite = session.getCipherSuite();
493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (cipherSuite == null) throw new IllegalStateException("cipherSuite == null");
503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Certificate[] peerCertificates;
523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    try {
533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      peerCertificates = session.getPeerCertificates();
543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    } catch (SSLPeerUnverifiedException ignored) {
553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      peerCertificates = null;
563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    }
573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<Certificate> peerCertificatesList = peerCertificates != null
583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? Util.immutableList(peerCertificates)
593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : Collections.<Certificate>emptyList();
603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Certificate[] localCertificates = session.getLocalCertificates();
623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    List<Certificate> localCertificatesList = localCertificates != null
633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? Util.immutableList(localCertificates)
643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : Collections.<Certificate>emptyList();
653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new Handshake(cipherSuite, peerCertificatesList, localCertificatesList);
673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public static Handshake get(
703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      String cipherSuite, List<Certificate> peerCertificates, List<Certificate> localCertificates) {
713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (cipherSuite == null) throw new IllegalArgumentException("cipherSuite == null");
723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return new Handshake(cipherSuite, Util.immutableList(peerCertificates),
733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        Util.immutableList(localCertificates));
743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Returns a cipher suite name like "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA". */
773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public String cipherSuite() {
783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return cipherSuite;
793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Returns a possibly-empty list of certificates that identify the remote peer. */
823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public List<Certificate> peerCertificates() {
833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return peerCertificates;
843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Returns the remote peer's principle, or null if that peer is anonymous. */
873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public Principal peerPrincipal() {
883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return !peerCertificates.isEmpty()
893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? ((X509Certificate) peerCertificates.get(0)).getSubjectX500Principal()
903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : null;
913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Returns a possibly-empty list of certificates that identify this peer. */
943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public List<Certificate> localCertificates() {
953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return localCertificates;
963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  /** Returns the local principle, or null if this peer is anonymous. */
993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  public Principal localPrincipal() {
1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return !localCertificates.isEmpty()
1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        ? ((X509Certificate) localCertificates.get(0)).getSubjectX500Principal()
1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        : null;
1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Override public boolean equals(Object other) {
1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    if (!(other instanceof Handshake)) return false;
1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    Handshake that = (Handshake) other;
1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return cipherSuite.equals(that.cipherSuite)
1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && peerCertificates.equals(that.peerCertificates)
1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        && localCertificates.equals(that.localCertificates);
1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller
1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  @Override public int hashCode() {
1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    int result = 17;
1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    result = 31 * result + cipherSuite.hashCode();
1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    result = 31 * result + peerCertificates.hashCode();
1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    result = 31 * result + localCertificates.hashCode();
1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return result;
1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  }
1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller}
121