android: Apply configured server port
[strongswan.git] / src / frontends / android / src / org / strongswan / android / logic / TrustedCertificateManager.java
1 /*
2 * Copyright (C) 2012-2014 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.ArrayList;
25 import java.util.Enumeration;
26 import java.util.Hashtable;
27 import java.util.concurrent.locks.ReentrantReadWriteLock;
28
29 import android.util.Log;
30
31 public class TrustedCertificateManager
32 {
33 private static final String TAG = TrustedCertificateManager.class.getSimpleName();
34 private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
35 private Hashtable<String, X509Certificate> mCACerts = new Hashtable<String, X509Certificate>();
36 private volatile boolean mReload;
37 private boolean mLoaded;
38 private final ArrayList<KeyStore> mKeyStores = new ArrayList<KeyStore>();
39
40 public enum TrustedCertificateSource
41 {
42 SYSTEM("system:"),
43 USER("user:"),
44 LOCAL("local:");
45
46 private final String mPrefix;
47
48 private TrustedCertificateSource(String prefix)
49 {
50 mPrefix = prefix;
51 }
52
53 private String getPrefix()
54 {
55 return mPrefix;
56 }
57 }
58
59 /**
60 * Private constructor to prevent instantiation from other classes.
61 */
62 private TrustedCertificateManager()
63 {
64 for (String name : new String[] { "LocalCertificateStore", "AndroidCAStore" })
65 {
66 KeyStore store;
67 try
68 {
69 store = KeyStore.getInstance(name);
70 store.load(null,null);
71 mKeyStores.add(store);
72 }
73 catch (Exception e)
74 {
75 Log.e(TAG, "Unable to load KeyStore: " + name);
76 e.printStackTrace();
77 }
78 }
79 }
80
81 /**
82 * This is not instantiated until the first call to getInstance()
83 */
84 private static class Singleton {
85 public static final TrustedCertificateManager mInstance = new TrustedCertificateManager();
86 }
87
88 /**
89 * Get the single instance of the CA certificate manager.
90 * @return CA certificate manager
91 */
92 public static TrustedCertificateManager getInstance()
93 {
94 return Singleton.mInstance;
95 }
96
97 /**
98 * Invalidates the current load state so that the next call to load()
99 * will force a reload of the cached CA certificates.
100 * @return reference to itself
101 */
102 public TrustedCertificateManager reset()
103 {
104 Log.d(TAG, "Force reload of cached CA certificates on next load");
105 this.mReload = true;
106 return this;
107 }
108
109 /**
110 * Ensures that the certificates are loaded but does not force a reload.
111 * As this takes a while if the certificates are not loaded yet it should
112 * be called asynchronously.
113 * @return reference to itself
114 */
115 public TrustedCertificateManager load()
116 {
117 Log.d(TAG, "Ensure cached CA certificates are loaded");
118 this.mLock.writeLock().lock();
119 if (!this.mLoaded || this.mReload)
120 {
121 this.mReload = false;
122 loadCertificates();
123 }
124 this.mLock.writeLock().unlock();
125 return this;
126 }
127
128 /**
129 * Opens the CA certificate KeyStore and loads the cached certificates.
130 * The lock must be locked when calling this method.
131 */
132 private void loadCertificates()
133 {
134 Log.d(TAG, "Load cached CA certificates");
135 Hashtable<String, X509Certificate> certs = new Hashtable<String, X509Certificate>();
136 for (KeyStore store : this.mKeyStores)
137 {
138 fetchCertificates(certs, store);
139 }
140 this.mCACerts = certs;
141 this.mLoaded = true;
142 Log.d(TAG, "Cached CA certificates loaded");
143 }
144
145 /**
146 * Load all X.509 certificates from the given KeyStore.
147 * @param certs Hashtable to store certificates in
148 * @param store KeyStore to load certificates from
149 */
150 private void fetchCertificates(Hashtable<String, X509Certificate> certs, KeyStore store)
151 {
152 try
153 {
154 Enumeration<String> aliases = store.aliases();
155 while (aliases.hasMoreElements())
156 {
157 String alias = aliases.nextElement();
158 Certificate cert;
159 cert = store.getCertificate(alias);
160 if (cert != null && cert instanceof X509Certificate)
161 {
162 certs.put(alias, (X509Certificate)cert);
163 }
164 }
165 }
166 catch (KeyStoreException ex)
167 {
168 ex.printStackTrace();
169 }
170 }
171
172 /**
173 * Retrieve the CA certificate with the given alias.
174 * @param alias alias of the certificate to get
175 * @return the certificate, null if not found
176 */
177 public X509Certificate getCACertificateFromAlias(String alias)
178 {
179 X509Certificate certificate = null;
180
181 if (this.mLock.readLock().tryLock())
182 {
183 certificate = this.mCACerts.get(alias);
184 this.mLock.readLock().unlock();
185 }
186 else
187 { /* if we cannot get the lock load it directly from the KeyStore,
188 * should be fast for a single certificate */
189 for (KeyStore store : this.mKeyStores)
190 {
191 try
192 {
193 Certificate cert = store.getCertificate(alias);
194 if (cert != null && cert instanceof X509Certificate)
195 {
196 certificate = (X509Certificate)cert;
197 break;
198 }
199 }
200 catch (KeyStoreException e)
201 {
202 e.printStackTrace();
203 }
204 }
205 }
206 return certificate;
207 }
208
209 /**
210 * Get all CA certificates (from all keystores).
211 * @return Hashtable mapping aliases to certificates
212 */
213 @SuppressWarnings("unchecked")
214 public Hashtable<String, X509Certificate> getAllCACertificates()
215 {
216 Hashtable<String, X509Certificate> certs;
217 this.mLock.readLock().lock();
218 certs = (Hashtable<String, X509Certificate>)this.mCACerts.clone();
219 this.mLock.readLock().unlock();
220 return certs;
221 }
222
223 /**
224 * Get all certificates from the given source.
225 * @param source type to filter certificates
226 * @return Hashtable mapping aliases to certificates
227 */
228 public Hashtable<String, X509Certificate> getCACertificates(TrustedCertificateSource source)
229 {
230 Hashtable<String, X509Certificate> certs = new Hashtable<String, X509Certificate>();
231 this.mLock.readLock().lock();
232 for (String alias : this.mCACerts.keySet())
233 {
234 if (alias.startsWith(source.getPrefix()))
235 {
236 certs.put(alias, this.mCACerts.get(alias));
237 }
238 }
239 this.mLock.readLock().unlock();
240 return certs;
241 }
242 }