19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Copyright 2009 The Android Open Source Project 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.core; 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.Cursor; 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.SQLException; 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.sqlite.SQLiteDatabase; 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper; 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log; 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ContentValues; 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context; 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.apache.commons.codec.binary.Base64; 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.LinkedHashMap; 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Map; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport javax.net.ssl.SSLSession; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Hook into harmony SSL cache to persist the SSL sessions. 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Current implementation is suitable for saving a small number of hosts - 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * like google services. It can be extended with expiration and more features 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * to support more hosts. 279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide} 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class DatabaseSessionCache implements SSLClientSessionCache { 319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String TAG = "SslSessionCache"; 329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static DatabaseHelper sDefaultDatabaseHelper; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private DatabaseHelper mDatabaseHelper; 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Table where sessions are stored. 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final String SSL_CACHE_TABLE = "ssl_sessions"; 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String SSL_CACHE_ID = "_id"; 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Key is host:port - port is not optional. 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String SSL_CACHE_HOSTPORT = "hostport"; 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Base64-encoded DER value of the session. 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String SSL_CACHE_SESSION = "session"; 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Time when the record was added - should be close to the time 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * of the initial session negotiation. 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String SSL_CACHE_TIME_SEC = "time_sec"; 589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final String DATABASE_NAME = "ssl_sessions.db"; 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int DATABASE_VERSION = 1; 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** public for testing 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int SSL_CACHE_ID_COL = 0; 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int SSL_CACHE_HOSTPORT_COL = 1; 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int SSL_CACHE_SESSION_COL = 2; 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int SSL_CACHE_TIME_SEC_COL = 3; 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final String SAVE_ON_ADD = "save_on_add"; 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static boolean sHookInitializationDone = false; 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int MAX_CACHE_SIZE = 256; 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final Map<String, byte[]> mExternalCache = 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project new LinkedHashMap<String, byte[]>(MAX_CACHE_SIZE, 0.75f, true) { 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public boolean removeEldestEntry( 809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Map.Entry<String, byte[]> eldest) { 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean shouldDelete = this.size() > MAX_CACHE_SIZE; 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // TODO: delete from DB 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return shouldDelete; 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static boolean mNeedsCacheLoad = true; 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final String[] PROJECTION = new String[] { 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_ID, 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_HOSTPORT, 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_SESSION, 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_TIME_SEC 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project }; 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This class needs to be installed as a hook, if the security property 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * is set. Getting the right classloader may be fun since we don't use 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Provider to get its classloader, but in android this is in same 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * loader with AndroidHttpClient. 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This constructor will use the default database. You must 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * call init() before to specify the context used for the database and 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * check settings. 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public DatabaseSessionCache() { 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.v(TAG, "Instance created."); 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // May be null if caching is disabled - no sessions will be persisted. 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this.mDatabaseHelper = sDefaultDatabaseHelper; 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Create a SslSessionCache instance, using the specified context to 1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * initialize the database. 1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This constructor will use the default database - created the first 1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * time. 1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param activityContext 1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public DatabaseSessionCache(Context activityContext) { 1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Static init - only one initialization will happen. 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Each SslSessionCache is using the same DB. 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project init(activityContext); 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // May be null if caching is disabled - no sessions will be persisted. 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this.mDatabaseHelper = sDefaultDatabaseHelper; 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Create a SslSessionCache that uses a specific database. 1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param database 1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public DatabaseSessionCache(DatabaseHelper database) { 1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project this.mDatabaseHelper = database; 1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// public static boolean enabled(Context androidContext) { 139edc5189c33de03f3e2f5f73edc0e007992b933c9Doug Zongker// String sslCache = Settings.Secure.getString(androidContext.getContentResolver(), 140edc5189c33de03f3e2f5f73edc0e007992b933c9Doug Zongker// Settings.Secure.SSL_SESSION_CACHE); 1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// 1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// if (Log.isLoggable(TAG, Log.DEBUG)) { 1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Log.d(TAG, "enabled " + sslCache + " " + androidContext.getPackageName()); 1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// } 1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// 1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// return SAVE_ON_ADD.equals(sslCache); 1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// } 1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You must call this method to enable SSL session caching for an app. 1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public synchronized static void init(Context activityContext) { 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // It is possible that multiple provider will try to install this hook. 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // We want a single db per VM. 1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (sHookInitializationDone) { 1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// // More values can be added in future to provide different 1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// // behaviours, like 'batch save'. 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// if (enabled(activityContext)) { 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Context appContext = activityContext.getApplicationContext(); 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sDefaultDatabaseHelper = new DatabaseHelper(appContext); 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Set default SSLSocketFactory 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // The property is defined in the javadocs for javax.net.SSLSocketFactory 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // (no constant defined there) 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // This should cover all code using SSLSocketFactory.getDefault(), 1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // including native http client and apache httpclient. 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // MCS is using its own custom factory - will need special code. 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// Security.setProperty("ssl.SocketFactory.provider", 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// SslSocketFactoryWithCache.class.getName()); 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project// } 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Won't try again. 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sHookInitializationDone = true; 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void putSessionData(SSLSession session, byte[] der) { 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mDatabaseHelper == null) { 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mExternalCache.size() > MAX_CACHE_SIZE) { 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // remove oldest. 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Cursor byTime = mDatabaseHelper.getWritableDatabase().query(SSL_CACHE_TABLE, 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project PROJECTION, null, null, null, null, SSL_CACHE_TIME_SEC); 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byTime.moveToFirst(); 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // TODO: can I do byTime.deleteRow() ? 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String hostPort = byTime.getString(SSL_CACHE_HOSTPORT_COL); 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDatabaseHelper.getWritableDatabase().delete(SSL_CACHE_TABLE, 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_HOSTPORT + "= ?" , new String[] { hostPort }); 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Serialize native session to standard DER encoding 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long t0 = System.currentTimeMillis(); 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String b64 = new String(Base64.encodeBase64(der)); 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String key = session.getPeerHost() + ":" + session.getPeerPort(); 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ContentValues values = new ContentValues(); 2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(SSL_CACHE_HOSTPORT, key); 2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(SSL_CACHE_SESSION, b64); 2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project values.put(SSL_CACHE_TIME_SEC, System.currentTimeMillis() / 1000); 2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project synchronized (this.getClass()) { 2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mExternalCache.put(key, der); 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDatabaseHelper.getWritableDatabase().insert(SSL_CACHE_TABLE, null /*nullColumnHack */ , values); 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch(SQLException ex) { 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Ignore - nothing we can do to recover, and caller shouldn't 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // be affected. 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.w(TAG, "Ignoring SQL exception when caching session", ex); 2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Log.isLoggable(TAG, Log.DEBUG)) { 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long t1 = System.currentTimeMillis(); 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.d(TAG, "New SSL session " + session.getPeerHost() + 2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project " DER len: " + der.length + " " + (t1 - t0)); 2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public byte[] getSessionData(String host, int port) { 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Current (simple) implementation does a single lookup to DB, then saves 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // all entries to the cache. 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // This works for google services - i.e. small number of certs. 2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If we extend this to all processes - we should hold a separate cache 2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // or do lookups to DB each time. 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mDatabaseHelper == null) { 2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project synchronized(this.getClass()) { 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mNeedsCacheLoad) { 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Don't try to load again, if something is wrong on the first 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // request it'll likely be wrong each time. 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mNeedsCacheLoad = false; 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long t0 = System.currentTimeMillis(); 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Cursor cur = null; 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cur = mDatabaseHelper.getReadableDatabase().query(SSL_CACHE_TABLE, PROJECTION, null, 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project null, null, null, null); 2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cur.moveToFirst()) { 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project do { 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String hostPort = cur.getString(SSL_CACHE_HOSTPORT_COL); 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String value = cur.getString(SSL_CACHE_SESSION_COL); 2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (hostPort == null || value == null) { 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project continue; 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // TODO: blob support ? 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] der = Base64.decodeBase64(value.getBytes()); 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mExternalCache.put(hostPort, der); 2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } while (cur.moveToNext()); 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (SQLException ex) { 2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.d(TAG, "Error loading SSL cached entries ", ex); 2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (cur != null) { 2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cur.close(); 2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Log.isLoggable(TAG, Log.DEBUG)) { 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long t1 = System.currentTimeMillis(); 2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Log.d(TAG, "LOADED CACHED SSL " + (t1 - t0) + " ms"); 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String key = host + ":" + port; 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mExternalCache.get(key); 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public byte[] getSessionData(byte[] id) { 2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // We support client side only - the cache will do nothing on client. 2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return null; 2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** Visible for testing. 2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static class DatabaseHelper extends SQLiteOpenHelper { 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public DatabaseHelper(Context context) { 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project super(context, DATABASE_NAME, null /* factory */, DATABASE_VERSION); 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onCreate(SQLiteDatabase db) { 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project db.execSQL("CREATE TABLE " + SSL_CACHE_TABLE + " (" + 2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + 2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_HOSTPORT + " TEXT UNIQUE ON CONFLICT REPLACE," + 2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_SESSION + " TEXT," + 2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_TIME_SEC + " INTEGER" + 2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ");"); 3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project db.execSQL("CREATE INDEX ssl_sessions_idx1 ON ssl_sessions (" + 3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project SSL_CACHE_HOSTPORT + ");"); 3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project @Override 3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project db.execSQL("DROP TABLE IF EXISTS " + SSL_CACHE_TABLE ); 3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project onCreate(db); 3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 312edc5189c33de03f3e2f5f73edc0e007992b933c9Doug Zongker} 313