OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / core / java / android / net / http / CertificateValidatorCache.java
1 /*
2  * Copyright (C) 2008 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 android.net.http;
18
19 import android.os.SystemClock;
20
21 import android.security.Sha1MessageDigest;
22
23 import java.security.cert.Certificate;
24 import java.security.cert.CertificateFactory;
25 import java.security.cert.CertPath;
26 import java.security.GeneralSecurityException;
27
28 import java.util.Arrays;
29 import java.util.HashMap;
30 import java.util.Random;
31
32
33 /**
34  * Validator cache used to speed-up certificate chain validation. The idea is
35  * to keep each secure domain name associated with a cryptographically secure
36  * hash of the certificate chain successfully used to validate the domain. If
37  * we establish connection with the domain more than once and each time receive
38  * the same list of certificates, we do not have to re-validate.
39  * 
40  * {@hide}
41  */
42 class CertificateValidatorCache {
43
44     // TODO: debug only!
45     public static long mSave = 0;
46     public static long mCost = 0;
47     // TODO: debug only!
48
49     /**
50      * The cache-entry lifetime in milliseconds (here, 10 minutes)
51      */
52     private static final long CACHE_ENTRY_LIFETIME = 10 * 60 * 1000;
53
54     /**
55      * The certificate factory
56      */
57     private static CertificateFactory sCertificateFactory;
58
59     /**
60      * The certificate validator cache map (domain to a cache entry)
61      */
62     private HashMap<Integer, CacheEntry> mCacheMap;
63
64     /**
65      * Random salt
66      */
67     private int mBigScrew;
68
69     /**
70      * @param certificate The array of server certificates to compute a
71      * secure hash from
72      * @return The secure hash computed from server certificates
73      */
74     public static byte[] secureHash(Certificate[] certificates) {
75         byte[] secureHash = null;
76
77         // TODO: debug only!
78         long beg = SystemClock.uptimeMillis();
79         // TODO: debug only!
80
81         if (certificates != null && certificates.length != 0) {
82             byte[] encodedCertPath = null;
83             try {
84                 synchronized (CertificateValidatorCache.class) {
85                     if (sCertificateFactory == null) {
86                         try {
87                             sCertificateFactory =
88                                 CertificateFactory.getInstance("X.509");
89                         } catch(GeneralSecurityException e) {
90                             if (HttpLog.LOGV) {
91                                 HttpLog.v("CertificateValidatorCache:" +
92                                           " failed to create the certificate factory");
93                             }
94                         }
95                     }
96                 }
97
98                 CertPath certPath =
99                     sCertificateFactory.generateCertPath(Arrays.asList(certificates));
100                 if (certPath != null) {
101                     encodedCertPath = certPath.getEncoded();
102                     if (encodedCertPath != null) {
103                       Sha1MessageDigest messageDigest =
104                           new Sha1MessageDigest();
105                       secureHash = messageDigest.digest(encodedCertPath);
106                     }
107                 }
108             } catch (GeneralSecurityException e) {}
109         }
110
111         // TODO: debug only!
112         long end = SystemClock.uptimeMillis();
113         mCost += (end - beg);
114         // TODO: debug only!
115
116         return secureHash;
117     }
118
119     /**
120      * Creates a new certificate-validator cache
121      */
122     public CertificateValidatorCache() {
123         Random random = new Random();
124         mBigScrew = random.nextInt();
125
126         mCacheMap = new HashMap<Integer, CacheEntry>();
127     }
128
129      /**
130      * @param domain The domain to check against
131      * @param secureHash The secure hash to check against
132      * @return True iff there is a valid (not expired) cache entry
133      * associated with the domain and the secure hash
134      */
135     public boolean has(String domain, byte[] secureHash) {
136         boolean rval = false;
137
138         if (domain != null && domain.length() != 0) {
139             if (secureHash != null && secureHash.length != 0) {
140                 CacheEntry cacheEntry = (CacheEntry)mCacheMap.get(
141                     new Integer(mBigScrew ^ domain.hashCode()));
142                 if (cacheEntry != null) {
143                     if (!cacheEntry.expired()) {
144                         rval = cacheEntry.has(domain, secureHash);
145                         // TODO: debug only!
146                         if (rval) {
147                             mSave += cacheEntry.mSave;
148                         }
149                         // TODO: debug only!
150                     } else {
151                         mCacheMap.remove(cacheEntry);
152                     }
153                 }
154             }
155         }
156
157         return rval;
158     }
159
160     /**
161      * Adds the (domain, secureHash) tuple to the cache
162      * @param domain The domain to be added to the cache
163      * @param secureHash The secure hash to be added to the cache
164      * @return True iff succeeds
165      */
166     public boolean put(String domain, byte[] secureHash, long save) {
167         if (domain != null && domain.length() != 0) {
168             if (secureHash != null && secureHash.length != 0) {
169                 mCacheMap.put(
170                     new Integer(mBigScrew ^ domain.hashCode()),
171                     new CacheEntry(domain, secureHash, save));
172
173                 return true;
174             }
175         }
176
177         return false;
178     }
179
180     /**
181      * Certificate-validator cache entry. We have one per domain
182      */
183     private class CacheEntry {
184
185         /**
186          * The hash associated with this cache entry
187          */
188         private byte[] mHash;
189
190         /**
191          * The time associated with this cache entry
192          */
193         private long mTime;
194
195         // TODO: debug only!
196         public long mSave;
197         // TODO: debug only!
198
199         /**
200          * The host associated with this cache entry
201          */
202         private String mDomain;
203
204         /**
205          * Creates a new certificate-validator cache entry
206          * @param domain The domain to be associated with this cache entry
207          * @param secureHash The secure hash to be associated with this cache
208          * entry
209          */
210         public CacheEntry(String domain, byte[] secureHash, long save) {
211             mDomain = domain;
212             mHash = secureHash;
213             // TODO: debug only!
214             mSave = save;
215             // TODO: debug only!
216             mTime = SystemClock.uptimeMillis();
217         }
218
219         /**
220          * @return True iff the cache item has expired
221          */
222         public boolean expired() {
223             return CACHE_ENTRY_LIFETIME < SystemClock.uptimeMillis() - mTime;
224         }
225
226         /**
227          * @param domain The domain to check
228          * @param secureHash The secure hash to check
229          * @return True iff the given domain and hash match those associated
230          * with this entry
231          */
232         public boolean has(String domain, byte[] secureHash) {
233             if (domain != null && 0 < domain.length()) {
234                 if (!mDomain.equals(domain)) {
235                     return false;
236                 }
237             }
238
239             if (secureHash != null) {
240                 int hashLength = secureHash.length;
241                 if (0 < hashLength) {
242                     if (hashLength == mHash.length) {
243                         for (int i = 0; i < hashLength; ++i) {
244                             if (secureHash[i] != mHash[i]) {
245                                 return false;
246                             }
247                         }
248                         return true;
249                     }
250                 }
251             }
252
253             return false;
254         }
255     }
256 };