95fdecf14d1757a436a201fc7e6e49c640f76001
[strongswan.git] / src / frontends / android / src / org / strongswan / android / logic / TrustedCertificateManager.java
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 package org.strongswan.android.logic;
19
20 import java.security.KeyStore;
21 import java.security.KeyStoreException;
22 import java.security.cert.Certificate;
23 import java.security.cert.X509Certificate;
24 import java.util.Enumeration;
25 import java.util.Hashtable;
26 import java.util.concurrent.locks.ReentrantReadWriteLock;
27
28 import android.util.Log;
29
30 public class TrustedCertificateManager
31 {
32 private static final String TAG = TrustedCertificateManager.class.getSimpleName();
33 private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
34 private Hashtable<String, X509Certificate> mCACerts = new Hashtable<String, X509Certificate>();
35 private boolean mLoaded;
36
37 /**
38 * Private constructor to prevent instantiation from other classes.
39 */
40 private TrustedCertificateManager()
41 {
42 }
43
44 /**
45 * This is not instantiated until the first call to getInstance()
46 */
47 private static class Singleton {
48 public static final TrustedCertificateManager mInstance = new TrustedCertificateManager();
49 }
50
51 /**
52 * Get the single instance of the CA certificate manager.
53 * @return CA certificate manager
54 */
55 public static TrustedCertificateManager getInstance()
56 {
57 return Singleton.mInstance;
58 }
59
60 /**
61 * Forces a load/reload of the cached CA certificates.
62 * As this takes a while it should be called asynchronously.
63 * @return reference to itself
64 */
65 public TrustedCertificateManager reload()
66 {
67 Log.d(TAG, "Force reload of cached CA certificates");
68 this.mLock.writeLock().lock();
69 loadCertificates();
70 this.mLock.writeLock().unlock();
71 return this;
72 }
73
74 /**
75 * Ensures that the certificates are loaded but does not force a reload.
76 * As this takes a while if the certificates are not loaded yet it should
77 * be called asynchronously.
78 * @return reference to itself
79 */
80 public TrustedCertificateManager load()
81 {
82 Log.d(TAG, "Ensure cached CA certificates are loaded");
83 this.mLock.writeLock().lock();
84 if (!this.mLoaded)
85 {
86 loadCertificates();
87 }
88 this.mLock.writeLock().unlock();
89 return this;
90 }
91
92 /**
93 * Opens the CA certificate KeyStore and loads the cached certificates.
94 * The lock must be locked when calling this method.
95 */
96 private void loadCertificates()
97 {
98 Log.d(TAG, "Load cached CA certificates");
99 try
100 {
101 KeyStore store = KeyStore.getInstance("AndroidCAStore");
102 store.load(null, null);
103 this.mCACerts = fetchCertificates(store);
104 this.mLoaded = true;
105 Log.d(TAG, "Cached CA certificates loaded");
106 }
107 catch (Exception ex)
108 {
109 ex.printStackTrace();
110 this.mCACerts = new Hashtable<String, X509Certificate>();
111 }
112 }
113
114 /**
115 * Load all X.509 certificates from the given KeyStore.
116 * @param store KeyStore to load certificates from
117 * @return Hashtable mapping aliases to certificates
118 */
119 private Hashtable<String, X509Certificate> fetchCertificates(KeyStore store)
120 {
121 Hashtable<String, X509Certificate> certs = new Hashtable<String, X509Certificate>();
122 try
123 {
124 Enumeration<String> aliases = store.aliases();
125 while (aliases.hasMoreElements())
126 {
127 String alias = aliases.nextElement();
128 Certificate cert;
129 cert = store.getCertificate(alias);
130 if (cert != null && cert instanceof X509Certificate)
131 {
132 certs.put(alias, (X509Certificate)cert);
133 }
134 }
135 }
136 catch (KeyStoreException ex)
137 {
138 ex.printStackTrace();
139 }
140 return certs;
141 }
142
143 /**
144 * Retrieve the CA certificate with the given alias.
145 * @param alias alias of the certificate to get
146 * @return the certificate, null if not found
147 */
148 public X509Certificate getCACertificateFromAlias(String alias)
149 {
150 X509Certificate certificate = null;
151
152 if (this.mLock.readLock().tryLock())
153 {
154 certificate = this.mCACerts.get(alias);
155 this.mLock.readLock().unlock();
156 }
157 else
158 { /* if we cannot get the lock load it directly from the KeyStore,
159 * should be fast for a single certificate */
160 try
161 {
162 KeyStore store = KeyStore.getInstance("AndroidCAStore");
163 store.load(null, null);
164 Certificate cert = store.getCertificate(alias);
165 if (cert != null && cert instanceof X509Certificate)
166 {
167 certificate = (X509Certificate)cert;
168 }
169 }
170 catch (Exception e)
171 {
172 e.printStackTrace();
173 }
174
175 }
176 return certificate;
177 }
178
179 /**
180 * Get all CA certificates (from the system and user keystore).
181 * @return Hashtable mapping aliases to certificates
182 */
183 @SuppressWarnings("unchecked")
184 public Hashtable<String, X509Certificate> getAllCACertificates()
185 {
186 Hashtable<String, X509Certificate> certs;
187 this.mLock.readLock().lock();
188 certs = (Hashtable<String, X509Certificate>)this.mCACerts.clone();
189 this.mLock.readLock().unlock();
190 return certs;
191 }
192
193 /**
194 * Get only the system-wide CA certificates.
195 * @return Hashtable mapping aliases to certificates
196 */
197 public Hashtable<String, X509Certificate> getSystemCACertificates()
198 {
199 Hashtable<String, X509Certificate> certs = new Hashtable<String, X509Certificate>();
200 this.mLock.readLock().lock();
201 for (String alias : this.mCACerts.keySet())
202 {
203 if (alias.startsWith("system:"))
204 {
205 certs.put(alias, this.mCACerts.get(alias));
206 }
207 }
208 this.mLock.readLock().unlock();
209 return certs;
210 }
211
212 /**
213 * Get only the CA certificates installed by the user.
214 * @return Hashtable mapping aliases to certificates
215 */
216 public Hashtable<String, X509Certificate> getUserCACertificates()
217 {
218 Hashtable<String, X509Certificate> certs = new Hashtable<String, X509Certificate>();
219 this.mLock.readLock().lock();
220 for (String alias : this.mCACerts.keySet())
221 {
222 if (alias.startsWith("user:"))
223 {
224 certs.put(alias, this.mCACerts.get(alias));
225 }
226 }
227 this.mLock.readLock().unlock();
228 return certs;
229 }
230 }