1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package org.conscrypt; 18 19import java.io.IOException; 20import java.security.cert.X509Certificate; 21import java.util.Collections; 22import java.util.HashMap; 23import java.util.List; 24import java.util.Map; 25import javax.net.ssl.SSLPeerUnverifiedException; 26import javax.net.ssl.SSLSessionBindingEvent; 27import javax.net.ssl.SSLSessionBindingListener; 28 29/** 30 * Implementation of the class OpenSSLSessionImpl 31 * based on BoringSSL. 32 * 33 * @hide 34 */ 35@Internal 36public class OpenSSLSessionImpl extends AbstractOpenSSLSession { 37 private long creationTime = 0; 38 long lastAccessedTime = 0; 39 final X509Certificate[] localCertificates; 40 final X509Certificate[] peerCertificates; 41 42 private final Map<String, Object> values = new HashMap<String, Object>(); 43 private byte[] peerCertificateOcspData; 44 private byte[] peerTlsSctData; 45 protected long sslSessionNativePointer; 46 private String peerHost; 47 private int peerPort = -1; 48 private String cipherSuite; 49 private String protocol; 50 private byte[] id; 51 52 /** 53 * Class constructor creates an SSL session context given the appropriate 54 * SSL parameters. 55 */ 56 protected OpenSSLSessionImpl(long sslSessionNativePointer, X509Certificate[] localCertificates, 57 X509Certificate[] peerCertificates, byte[] peerCertificateOcspData, 58 byte[] peerTlsSctData, String peerHost, int peerPort, 59 AbstractSessionContext sessionContext) { 60 super(sessionContext); 61 this.sslSessionNativePointer = sslSessionNativePointer; 62 this.localCertificates = localCertificates; 63 this.peerCertificates = peerCertificates; 64 this.peerCertificateOcspData = peerCertificateOcspData; 65 this.peerTlsSctData = peerTlsSctData; 66 this.peerHost = peerHost; 67 this.peerPort = peerPort; 68 } 69 70 /** 71 * Constructs a session from a byte[] containing an SSL session serialized with DER encoding. 72 * This allows loading of a previously saved OpenSSLSessionImpl. 73 * 74 * @throws IOException if the serialized session data can not be parsed 75 */ 76 OpenSSLSessionImpl(byte[] derData, String peerHost, int peerPort, 77 X509Certificate[] peerCertificates, byte[] peerCertificateOcspData, 78 byte[] peerTlsSctData, AbstractSessionContext sessionContext) 79 throws IOException { 80 this(NativeCrypto.d2i_SSL_SESSION(derData), null, peerCertificates, 81 peerCertificateOcspData, peerTlsSctData, peerHost, peerPort, sessionContext); 82 } 83 84 /** 85 * Gets the identifier of the actual SSL session 86 * @return array of sessions' identifiers. 87 */ 88 @Override 89 public byte[] getId() { 90 if (id == null) { 91 resetId(); 92 } 93 return id; 94 } 95 96 /** 97 * Reset the id field to the current value found in the native 98 * SSL_SESSION. It can change during the lifetime of the session 99 * because while a session is created during initial handshake, 100 * with handshake_cutthrough, the SSL_do_handshake may return 101 * before we have read the session ticket from the server side and 102 * therefore have computed no id based on the SHA of the ticket. 103 */ 104 @Override 105 void resetId() { 106 id = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer); 107 } 108 109 /** 110 * Get the session object in DER format. This allows saving the session 111 * data or sharing it with other processes. 112 */ 113 public byte[] getEncoded() { 114 return NativeCrypto.i2d_SSL_SESSION(sslSessionNativePointer); 115 } 116 117 /** 118 * Gets the creation time of the SSL session. 119 * @return the session's creation time in milliseconds since the epoch 120 */ 121 @Override 122 public long getCreationTime() { 123 if (creationTime == 0) { 124 creationTime = NativeCrypto.SSL_SESSION_get_time(sslSessionNativePointer); 125 } 126 return creationTime; 127 } 128 129 /** 130 * Returns the last time this concrete SSL session was accessed. Accessing 131 * here is to mean that a new connection with the same SSL context data was 132 * established. 133 * 134 * @return the session's last access time in milliseconds since the epoch 135 */ 136 @Override 137 public long getLastAccessedTime() { 138 return (lastAccessedTime == 0) ? getCreationTime() : lastAccessedTime; 139 } 140 141 @Override 142 public void setLastAccessedTime(long accessTimeMillis) { 143 lastAccessedTime = accessTimeMillis; 144 } 145 146 @Override 147 protected X509Certificate[] getX509LocalCertificates() { 148 return localCertificates; 149 } 150 151 @Override 152 protected X509Certificate[] getX509PeerCertificates() throws SSLPeerUnverifiedException { 153 if (peerCertificates == null || peerCertificates.length == 0) { 154 throw new SSLPeerUnverifiedException("No peer certificates"); 155 } 156 return peerCertificates; 157 } 158 159 /** 160 * The peer's host name used in this SSL session is returned. It is the host 161 * name of the client for the server; and that of the server for the client. 162 * It is not a reliable way to get a fully qualified host name: it is mainly 163 * used internally to implement links for a temporary cache of SSL sessions. 164 * 165 * @return the host name of the peer, or {@code null} if no information is 166 * available. 167 */ 168 @Override 169 public String getPeerHost() { 170 return peerHost; 171 } 172 173 /** 174 * Returns the peer's port number for the actual SSL session. It is the port 175 * number of the client for the server; and that of the server for the 176 * client. It is not a reliable way to get a peer's port number: it is 177 * mainly used internally to implement links for a temporary cache of SSL 178 * sessions. 179 * 180 * @return the peer's port number, or {@code -1} if no one is available. 181 */ 182 @Override 183 public int getPeerPort() { 184 return peerPort; 185 } 186 187 /** 188 * Returns a string identifier of the crypto tools used in the actual SSL 189 * session. For example AES_256_WITH_MD5. 190 */ 191 @Override 192 public String getCipherSuite() { 193 if (cipherSuite == null) { 194 String name = NativeCrypto.SSL_SESSION_cipher(sslSessionNativePointer); 195 cipherSuite = NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.get(name); 196 if (cipherSuite == null) { 197 cipherSuite = name; 198 } 199 } 200 return cipherSuite; 201 } 202 203 /** 204 * Returns the standard version name of the SSL protocol used in all 205 * connections pertaining to this SSL session. 206 */ 207 @Override 208 public String getProtocol() { 209 if (protocol == null) { 210 protocol = NativeCrypto.SSL_SESSION_get_version(sslSessionNativePointer); 211 } 212 return protocol; 213 } 214 215 /** 216 * Returns the object which is bound to the the input parameter name. 217 * This name is a sort of link to the data of the SSL session's application 218 * layer, if any exists. 219 * 220 * @param name the name of the binding to find. 221 * @return the value bound to that name, or null if the binding does not 222 * exist. 223 * @throws IllegalArgumentException if the argument is null. 224 */ 225 @Override 226 public Object getValue(String name) { 227 if (name == null) { 228 throw new IllegalArgumentException("name == null"); 229 } 230 return values.get(name); 231 } 232 233 /** 234 * Returns an array with the names (sort of links) of all the data 235 * objects of the application layer bound into the SSL session. 236 * 237 * @return a non-null (possibly empty) array of names of the data objects 238 * bound to this SSL session. 239 */ 240 @Override 241 public String[] getValueNames() { 242 return values.keySet().toArray(new String[values.size()]); 243 } 244 245 /** 246 * A link (name) with the specified value object of the SSL session's 247 * application layer data is created or replaced. If the new (or existing) 248 * value object implements the <code>SSLSessionBindingListener</code> 249 * interface, that object will be notified in due course. 250 * 251 * @param name the name of the link (no null are 252 * accepted!) 253 * @param value data object that shall be bound to 254 * name. 255 * @throws IllegalArgumentException if one or both argument(s) is null. 256 */ 257 @Override 258 public void putValue(String name, Object value) { 259 if (name == null || value == null) { 260 throw new IllegalArgumentException("name == null || value == null"); 261 } 262 Object old = values.put(name, value); 263 if (value instanceof SSLSessionBindingListener) { 264 ((SSLSessionBindingListener) value) 265 .valueBound(new SSLSessionBindingEvent(this, name)); 266 } 267 if (old instanceof SSLSessionBindingListener) { 268 ((SSLSessionBindingListener) old) 269 .valueUnbound(new SSLSessionBindingEvent(this, name)); 270 } 271 } 272 273 /** 274 * Removes a link (name) with the specified value object of the SSL 275 * session's application layer data. 276 * 277 * <p>If the value object implements the <code>SSLSessionBindingListener</code> 278 * interface, the object will receive a <code>valueUnbound</code> notification. 279 * 280 * @param name the name of the link (no null are 281 * accepted!) 282 * @throws IllegalArgumentException if the argument is null. 283 */ 284 @Override 285 public void removeValue(String name) { 286 if (name == null) { 287 throw new IllegalArgumentException("name == null"); 288 } 289 Object old = values.remove(name); 290 if (old instanceof SSLSessionBindingListener) { 291 SSLSessionBindingListener listener = (SSLSessionBindingListener) old; 292 listener.valueUnbound(new SSLSessionBindingEvent(this, name)); 293 } 294 } 295 296 /** 297 * Returns the name requested by the SNI extension. 298 */ 299 @Override 300 public String getRequestedServerName() { 301 return NativeCrypto.get_SSL_SESSION_tlsext_hostname(sslSessionNativePointer); 302 } 303 304 /** 305 * Returns the OCSP stapled response. 306 */ 307 @Override 308 public List<byte[]> getStatusResponses() { 309 if (peerCertificateOcspData == null) { 310 return Collections.<byte[]>emptyList(); 311 } 312 313 return Collections.singletonList(peerCertificateOcspData.clone()); 314 } 315 316 @Override 317 public byte[] getTlsSctData() { 318 if (peerTlsSctData == null) { 319 return null; 320 } 321 return peerTlsSctData.clone(); 322 } 323 324 @Override 325 protected void finalize() throws Throwable { 326 try { 327 // The constructor can throw an exception if this object is constructed from invalid 328 // saved session data. 329 if (sslSessionNativePointer != 0) { 330 NativeCrypto.SSL_SESSION_free(sslSessionNativePointer); 331 } 332 } finally { 333 super.finalize(); 334 } 335 } 336} 337