• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package org.apache.harmony.xnet.provider.jsse;
18 
19 import java.io.IOException;
20 import java.security.Principal;
21 import java.security.cert.Certificate;
22 import java.security.cert.CertificateEncodingException;
23 import java.security.cert.X509Certificate;
24 import java.util.HashMap;
25 import java.util.Map;
26 import javax.net.ssl.SSLPeerUnverifiedException;
27 import javax.net.ssl.SSLSession;
28 import javax.net.ssl.SSLSessionBindingEvent;
29 import javax.net.ssl.SSLSessionBindingListener;
30 import javax.net.ssl.SSLSessionContext;
31 import javax.security.cert.CertificateException;
32 
33 /**
34  * Implementation of the class OpenSSLSessionImpl
35  * based on OpenSSL.
36  */
37 public class OpenSSLSessionImpl implements SSLSession {
38 
39     private long creationTime = 0;
40     long lastAccessedTime = 0;
41     final X509Certificate[] localCertificates;
42     final X509Certificate[] peerCertificates;
43 
44     private boolean isValid = true;
45     private final Map<String, Object> values = new HashMap<String, Object>();
46     private volatile javax.security.cert.X509Certificate[] peerCertificateChain;
47     protected int sslSessionNativePointer;
48     private String peerHost;
49     private int peerPort = -1;
50     private String cipherSuite;
51     private String protocol;
52     private String compressionMethod;
53     private AbstractSessionContext sessionContext;
54     private byte[] id;
55 
56     /**
57      * Class constructor creates an SSL session context given the appropriate
58      * SSL parameters.
59      */
OpenSSLSessionImpl(int sslSessionNativePointer, X509Certificate[] localCertificates, X509Certificate[] peerCertificates, String peerHost, int peerPort, AbstractSessionContext sessionContext)60     protected OpenSSLSessionImpl(int sslSessionNativePointer, X509Certificate[] localCertificates,
61             X509Certificate[] peerCertificates, String peerHost, int peerPort,
62             AbstractSessionContext sessionContext) {
63         this.sslSessionNativePointer = sslSessionNativePointer;
64         this.localCertificates = localCertificates;
65         this.peerCertificates = peerCertificates;
66         this.peerHost = peerHost;
67         this.peerPort = peerPort;
68         this.sessionContext = sessionContext;
69     }
70 
71     /**
72      * Constructs a session from a byte[] containing DER data. This
73      * allows loading the saved session.
74      * @throws IOException
75      */
OpenSSLSessionImpl(byte[] derData, String peerHost, int peerPort, X509Certificate[] peerCertificates, AbstractSessionContext sessionContext)76     OpenSSLSessionImpl(byte[] derData,
77             String peerHost, int peerPort,
78             X509Certificate[] peerCertificates,
79             AbstractSessionContext sessionContext)
80             throws IOException {
81         this(NativeCrypto.d2i_SSL_SESSION(derData),
82              null,
83              peerCertificates,
84              peerHost,
85              peerPort,
86              sessionContext);
87         // TODO move this check into native code so we can throw an error with more information
88         if (this.sslSessionNativePointer == 0) {
89             throw new IOException("Invalid session data");
90         }
91     }
92 
93     /**
94      * Gets the identifier of the actual SSL session
95      * @return array of sessions' identifiers.
96      */
getId()97     public byte[] getId() {
98         if (id == null) {
99             resetId();
100         }
101         return id;
102     }
103 
104     /**
105      * Reset the id field to the current value found in the native
106      * SSL_SESSION. It can change during the lifetime of the session
107      * because while a session is created during initial handshake,
108      * with handshake_cutthrough, the SSL_do_handshake may return
109      * before we have read the session ticket from the server side and
110      * therefore have computed no id based on the SHA of the ticket.
111      */
resetId()112     void resetId() {
113         id = NativeCrypto.SSL_SESSION_session_id(sslSessionNativePointer);
114     }
115 
116     /**
117      * Get the session object in DER format. This allows saving the session
118      * data or sharing it with other processes.
119      */
getEncoded()120     byte[] getEncoded() {
121         return NativeCrypto.i2d_SSL_SESSION(sslSessionNativePointer);
122     }
123 
124     /**
125      * Gets the creation time of the SSL session.
126      * @return the session's creation time in milliseconds since the epoch
127      */
getCreationTime()128     public long getCreationTime() {
129         if (creationTime == 0) {
130             creationTime = NativeCrypto.SSL_SESSION_get_time(sslSessionNativePointer);
131         }
132         return creationTime;
133     }
134 
135     /**
136      * Returns the last time this concrete SSL session was accessed. Accessing
137      * here is to mean that a new connection with the same SSL context data was
138      * established.
139      *
140      * @return the session's last access time in milliseconds since the epoch
141      */
getLastAccessedTime()142     public long getLastAccessedTime() {
143         return (lastAccessedTime == 0) ? getCreationTime() : lastAccessedTime;
144     }
145 
146     /**
147      * Returns the largest buffer size for the application's data bound to this
148      * concrete SSL session.
149      * @return the largest buffer size
150      */
getApplicationBufferSize()151     public int getApplicationBufferSize() {
152         return SSLRecordProtocol.MAX_DATA_LENGTH;
153     }
154 
155     /**
156      * Returns the largest SSL/TLS packet size one can expect for this concrete
157      * SSL session.
158      * @return the largest packet size
159      */
getPacketBufferSize()160     public int getPacketBufferSize() {
161         return SSLRecordProtocol.MAX_SSL_PACKET_SIZE;
162     }
163 
164     /**
165      * Returns the principal (subject) of this concrete SSL session used in the
166      * handshaking phase of the connection.
167      * @return a X509 certificate or null if no principal was defined
168      */
getLocalPrincipal()169     public Principal getLocalPrincipal() {
170         if (localCertificates != null && localCertificates.length > 0) {
171             return localCertificates[0].getSubjectX500Principal();
172         } else {
173             return null;
174         }
175     }
176 
177     /**
178      * Returns the certificate(s) of the principal (subject) of this concrete SSL
179      * session used in the handshaking phase of the connection. The OpenSSL
180      * native method supports only RSA certificates.
181      * @return an array of certificates (the local one first and then eventually
182      *         that of the certification authority) or null if no certificate
183      *         were used during the handshaking phase.
184      */
getLocalCertificates()185     public Certificate[] getLocalCertificates() {
186         return localCertificates;
187     }
188 
189     /**
190      * Returns the certificate(s) of the peer in this SSL session
191      * used in the handshaking phase of the connection.
192      * Please notice hat this method is superseded by
193      * <code>getPeerCertificates()</code>.
194      * @return an array of X509 certificates (the peer's one first and then
195      *         eventually that of the certification authority) or null if no
196      *         certificate were used during the SSL connection.
197      * @throws <code>SSLPeerUnverifiedCertificateException</code> if either a
198      *         not X509 certificate was used (i.e. Kerberos certificates) or the
199      *         peer could not be verified.
200      */
getPeerCertificateChain()201     public javax.security.cert.X509Certificate[] getPeerCertificateChain()
202             throws SSLPeerUnverifiedException {
203         checkPeerCertificatesPresent();
204         javax.security.cert.X509Certificate[] result = peerCertificateChain;
205         if (result == null) {
206             // single-check idiom
207             peerCertificateChain = result = createPeerCertificateChain();
208         }
209         return result;
210     }
211 
212     /**
213      * Provide a value to initialize the volatile peerCertificateChain
214      * field based on the native SSL_SESSION
215      */
createPeerCertificateChain()216     private javax.security.cert.X509Certificate[] createPeerCertificateChain()
217             throws SSLPeerUnverifiedException {
218         try {
219             javax.security.cert.X509Certificate[] chain
220                     = new javax.security.cert.X509Certificate[peerCertificates.length];
221 
222             for (int i = 0; i < peerCertificates.length; i++) {
223                 byte[] encoded = peerCertificates[i].getEncoded();
224                 chain[i] = javax.security.cert.X509Certificate.getInstance(encoded);
225             }
226             return chain;
227         } catch (CertificateEncodingException e) {
228             SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
229             exception.initCause(exception);
230             throw exception;
231         } catch (CertificateException e) {
232             SSLPeerUnverifiedException exception = new SSLPeerUnverifiedException(e.getMessage());
233             exception.initCause(exception);
234             throw exception;
235         }
236     }
237 
238     /**
239      * Return the identity of the peer in this SSL session
240      * determined via certificate(s).
241      * @return an array of X509 certificates (the peer's one first and then
242      *         eventually that of the certification authority) or null if no
243      *         certificate were used during the SSL connection.
244      * @throws <code>SSLPeerUnverifiedException</code> if either a not X509
245      *         certificate was used (i.e. Kerberos certificates) or the peer
246      *         could not be verified.
247      */
getPeerCertificates()248     public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
249         checkPeerCertificatesPresent();
250         return peerCertificates;
251     }
252 
253     /**
254      * Throw SSLPeerUnverifiedException on null or empty peerCertificates array
255      */
checkPeerCertificatesPresent()256     private void checkPeerCertificatesPresent() throws SSLPeerUnverifiedException {
257         if (peerCertificates == null || peerCertificates.length == 0) {
258             throw new SSLPeerUnverifiedException("No peer certificates");
259         }
260     }
261 
262     /**
263      * The identity of the principal that was used by the peer during the SSL
264      * handshake phase is returned by this method.
265      * @return a X500Principal of the last certificate for X509-based
266      *         cipher suites.
267      * @throws <code>SSLPeerUnverifiedException</code> if either a not X509
268      *         certificate was used (i.e. Kerberos certificates) or the
269      *         peer does not exist.
270      *
271      */
getPeerPrincipal()272     public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
273         checkPeerCertificatesPresent();
274         return peerCertificates[0].getSubjectX500Principal();
275     }
276 
277     /**
278      * The peer's host name used in this SSL session is returned. It is the host
279      * name of the client for the server; and that of the server for the client.
280      * It is not a reliable way to get a fully qualified host name: it is mainly
281      * used internally to implement links for a temporary cache of SSL sessions.
282      *
283      * @return the host name of the peer, or null if no information is
284      *         available.
285      *
286      */
getPeerHost()287     public String getPeerHost() {
288         return peerHost;
289     }
290 
291     /**
292      * Returns the peer's port number for the actual SSL session. It is the port
293      * number of the client for the server; and that of the server for the
294      * client. It is not a reliable way to get a peer's port number: it is
295      * mainly used internally to implement links for a temporary cache of SSL
296      * sessions.
297      * @return the peer's port number, or -1 if no one is available.
298      *
299      */
getPeerPort()300     public int getPeerPort() {
301         return peerPort;
302     }
303 
304     /**
305      * Returns a string identifier of the crypto tools used in the actual SSL
306      * session. For example AES_256_WITH_MD5.
307      */
getCipherSuite()308     public String getCipherSuite() {
309         if (cipherSuite == null) {
310             String name = NativeCrypto.SSL_SESSION_cipher(sslSessionNativePointer);
311             cipherSuite = NativeCrypto.OPENSSL_TO_STANDARD_CIPHER_SUITES.get(name);
312             if (cipherSuite == null) {
313                 cipherSuite = name;
314             }
315         }
316         return cipherSuite;
317     }
318 
319     /**
320      * Returns the standard version name of the SSL protocol used in all
321      * connections pertaining to this SSL session.
322      */
getProtocol()323     public String getProtocol() {
324         if (protocol == null) {
325             protocol = NativeCrypto.SSL_SESSION_get_version(sslSessionNativePointer);
326         }
327         return protocol;
328     }
329 
330     /**
331      * Returns the compression method name used in all connections
332      * pertaining to this SSL session.
333      */
getCompressionMethod()334     public String getCompressionMethod() {
335         if (compressionMethod == null) {
336             compressionMethod
337                     = NativeCrypto.SSL_SESSION_compress_meth(sessionContext.sslCtxNativePointer,
338                                                              sslSessionNativePointer);
339         }
340         return compressionMethod;
341     }
342 
343     /**
344      * Returns the context to which the actual SSL session is bound. A SSL
345      * context consists of (1) a possible delegate, (2) a provider and (3) a
346      * protocol.
347      * @return the SSL context used for this session, or null if it is
348      * unavailable.
349      */
getSessionContext()350     public SSLSessionContext getSessionContext() {
351         return sessionContext;
352     }
353 
354     /**
355      * Returns a boolean flag signaling whether a SSL session is valid
356      * and available for resuming or joining or not.
357      *
358      * @return true if this session may be resumed.
359      */
isValid()360     public boolean isValid() {
361         SSLSessionContext context = sessionContext;
362         if (isValid
363                 && context != null
364                 && context.getSessionTimeout() != 0
365                 && getCreationTime() + (context.getSessionTimeout() * 1000)
366                     < System.currentTimeMillis()) {
367             isValid = false;
368         }
369         return isValid;
370     }
371 
372     /**
373      * It invalidates a SSL session forbidding any resumption.
374      */
invalidate()375     public void invalidate() {
376         isValid = false;
377         sessionContext = null;
378     }
379 
380     /**
381      * Returns the object which is bound to the the input parameter name.
382      * This name is a sort of link to the data of the SSL session's application
383      * layer, if any exists. The search for this link is monitored, as a matter
384      * of security, by the full machinery of the <code>AccessController</code>
385      * class.
386      *
387      * @param name the name of the binding to find.
388      * @return the value bound to that name, or null if the binding does not
389      *         exist.
390      * @throws <code>IllegalArgumentException</code> if the argument is null.
391      */
getValue(String name)392     public Object getValue(String name) {
393         if (name == null) {
394             throw new IllegalArgumentException("name == null");
395         }
396         return values.get(name);
397     }
398 
399     /**
400      * Returns an array with the names (sort of links) of all the data
401      * objects of the application layer bound into the SSL session. The search
402      * for this link is monitored, as a matter of security, by the full
403      * machinery of the <code>AccessController</code> class.
404      *
405      * @return a non-null (possibly empty) array of names of the data objects
406      *         bound to this SSL session.
407      */
getValueNames()408     public String[] getValueNames() {
409         return values.keySet().toArray(new String[values.size()]);
410     }
411 
412     /**
413      * A link (name) with the specified value object of the SSL session's
414      * application layer data is created or replaced. If the new (or existing)
415      * value object implements the <code>SSLSessionBindingListener</code>
416      * interface, that object will be notified in due course. These links-to
417      * -data bounds are monitored, as a matter of security, by the full
418      * machinery of the <code>AccessController</code> class.
419      *
420      * @param name the name of the link (no null are
421      *            accepted!)
422      * @param value data object that shall be bound to
423      *            name.
424      * @throws <code>IllegalArgumentException</code> if one or both
425      *             argument(s) is null.
426      */
putValue(String name, Object value)427     public void putValue(String name, Object value) {
428         if (name == null || value == null) {
429             throw new IllegalArgumentException("name == null || value == null");
430         }
431         Object old = values.put(name, value);
432         if (value instanceof SSLSessionBindingListener) {
433             ((SSLSessionBindingListener) value)
434                     .valueBound(new SSLSessionBindingEvent(this, name));
435         }
436         if (old instanceof SSLSessionBindingListener) {
437             ((SSLSessionBindingListener) old)
438                     .valueUnbound(new SSLSessionBindingEvent(this, name));
439         }
440     }
441 
442     /**
443      * Removes a link (name) with the specified value object of the SSL
444      * session's application layer data.
445      *
446      * <p>If the value object implements the <code>SSLSessionBindingListener</code>
447      * interface, the object will receive a <code>valueUnbound</code> notification.
448      *
449      * <p>These links-to -data bounds are
450      * monitored, as a matter of security, by the full machinery of the
451      * <code>AccessController</code> class.
452      *
453      * @param name the name of the link (no null are
454      *            accepted!)
455      * @throws <code>IllegalArgumentException</code> if the argument is null.
456      */
removeValue(String name)457     public void removeValue(String name) {
458         if (name == null) {
459             throw new IllegalArgumentException("name == null");
460         }
461         Object old = values.remove(name);
462         if (old instanceof SSLSessionBindingListener) {
463             SSLSessionBindingListener listener = (SSLSessionBindingListener) old;
464             listener.valueUnbound(new SSLSessionBindingEvent(this, name));
465         }
466     }
467 
finalize()468     @Override protected void finalize() throws Throwable {
469         try {
470             NativeCrypto.SSL_SESSION_free(sslSessionNativePointer);
471         } finally {
472             super.finalize();
473         }
474     }
475 }
476