/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.xnet.provider.jsse; import java.util.*; import java.util.logging.Level; import java.io.*; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSessionContext; import javax.security.cert.X509Certificate; import javax.security.cert.CertificateEncodingException; import javax.security.cert.CertificateException; /** * Supports SSL session caches. */ abstract class AbstractSessionContext implements SSLSessionContext { volatile int maximumSize; volatile int timeout; final SSLParameters parameters; /** Identifies OpenSSL sessions. */ static final int OPEN_SSL = 1; /** * Constructs a new session context. * * @param parameters * @param maximumSize of cache * @param timeout for cache entries */ AbstractSessionContext(SSLParameters parameters, int maximumSize, int timeout) { this.parameters = parameters; this.maximumSize = maximumSize; this.timeout = timeout; } /** * Returns the collection of sessions ordered by least-recently-used first. */ abstract Iterator<SSLSession> sessionIterator(); public final Enumeration getIds() { final Iterator<SSLSession> iterator = sessionIterator(); return new Enumeration<byte[]>() { public boolean hasMoreElements() { return iterator.hasNext(); } public byte[] nextElement() { return iterator.next().getId(); } }; } public final int getSessionCacheSize() { return maximumSize; } public final int getSessionTimeout() { return timeout; } /** * Makes sure cache size is < maximumSize. */ abstract void trimToSize(); public final void setSessionCacheSize(int size) throws IllegalArgumentException { if (size < 0) { throw new IllegalArgumentException("size < 0"); } int oldMaximum = maximumSize; maximumSize = size; // Trim cache to size if necessary. if (size < oldMaximum) { trimToSize(); } } /** * Converts the given session to bytes. * * @return session data as bytes or null if the session can't be converted */ byte[] toBytes(SSLSession session) { // TODO: Support SSLSessionImpl, too. if (!(session instanceof OpenSSLSessionImpl)) { return null; } OpenSSLSessionImpl sslSession = (OpenSSLSessionImpl) session; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream daos = new DataOutputStream(baos); daos.writeInt(OPEN_SSL); // session type ID // Session data. byte[] data = sslSession.getEncoded(); daos.writeInt(data.length); daos.write(data); // Certificates. X509Certificate[] certs = session.getPeerCertificateChain(); daos.writeInt(certs.length); // TODO: Call nativegetpeercertificates() for (X509Certificate cert : certs) { data = cert.getEncoded(); daos.writeInt(data.length); daos.write(data); } return baos.toByteArray(); } catch (IOException e) { log(e); return null; } catch (CertificateEncodingException e) { log(e); return null; } } /** * Creates a session from the given bytes. * * @return a session or null if the session can't be converted */ SSLSession toSession(byte[] data, String host, int port) { ByteArrayInputStream bais = new ByteArrayInputStream(data); DataInputStream dais = new DataInputStream(bais); try { int type = dais.readInt(); if (type != OPEN_SSL) { log(new AssertionError("Unexpected type ID: " + type)); return null; } int length = dais.readInt(); byte[] sessionData = new byte[length]; dais.readFully(sessionData); int count = dais.readInt(); X509Certificate[] certs = new X509Certificate[count]; for (int i = 0; i < count; i++) { length = dais.readInt(); byte[] certData = new byte[length]; dais.readFully(certData); certs[i] = X509Certificate.getInstance(certData); } return new OpenSSLSessionImpl(sessionData, parameters, host, port, certs, this); } catch (IOException e) { log(e); return null; } catch (CertificateException e) { log(e); return null; } } static void log(Throwable t) { java.util.logging.Logger.global.log(Level.WARNING, "Error converting session.", t); } /** * Byte array wrapper. Implements equals() and hashCode(). */ static class ByteArray { private final byte[] bytes; ByteArray(byte[] bytes) { this.bytes = bytes; } @Override public int hashCode() { return Arrays.hashCode(bytes); } @Override @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") public boolean equals(Object o) { ByteArray other = (ByteArray) o; return Arrays.equals(bytes, other.bytes); } } }