android: Add local certificate store
authorTobias Brunner <tobias@strongswan.org>
Fri, 30 May 2014 11:28:16 +0000 (13:28 +0200)
committerTobias Brunner <tobias@strongswan.org>
Tue, 22 Jul 2014 08:41:49 +0000 (10:41 +0200)
The class manages certificates stored in files within the app's
private data directory.

src/frontends/android/src/org/strongswan/android/security/LocalCertificateStore.java [new file with mode: 0644]

diff --git a/src/frontends/android/src/org/strongswan/android/security/LocalCertificateStore.java b/src/frontends/android/src/org/strongswan/android/security/LocalCertificateStore.java
new file mode 100644 (file)
index 0000000..cec5c60
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+package org.strongswan.android.security;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.regex.Pattern;
+
+import org.strongswan.android.logic.StrongSwanApplication;
+import org.strongswan.android.utils.Utils;
+
+import android.content.Context;
+
+public class LocalCertificateStore
+{
+       private static final String FILE_PREFIX = "certificate-";
+       private static final String ALIAS_PREFIX = "local:";
+       private static final Pattern ALIAS_PATTERN = Pattern.compile("^" + ALIAS_PREFIX + "[0-9a-f]{40}$");
+
+       /**
+        * Add the given certificate to the store
+        * @param cert the certificate to add
+        * @return true if successful
+        */
+       public boolean addCertificate(Certificate cert)
+       {
+               if (!(cert instanceof X509Certificate))
+               {       /* only accept X.509 certificates */
+                       return false;
+               }
+               String keyid = getKeyId(cert);
+               if (keyid == null)
+               {
+                       return false;
+               }
+               FileOutputStream out;
+               try
+               {
+                       /* we replace any existing file with the same alias */
+                       out = StrongSwanApplication.getContext().openFileOutput(FILE_PREFIX + keyid, Context.MODE_PRIVATE);
+                       try
+                       {
+                               out.write(cert.getEncoded());
+                               return true;
+                       }
+                       catch (CertificateEncodingException e)
+                       {
+                               e.printStackTrace();
+                       }
+                       catch (IOException e)
+                       {
+                               e.printStackTrace();
+                       }
+                       finally
+                       {
+                               try
+                               {
+                                       out.close();
+                               }
+                               catch (IOException e)
+                               {
+                                       e.printStackTrace();
+                               }
+                       }
+               }
+               catch (FileNotFoundException e)
+               {
+                       e.printStackTrace();
+               }
+               return false;
+       }
+
+       /**
+        * Delete the certificate with the given alias
+        * @param alias a certificate's alias
+        */
+       public void deleteCertificate(String alias)
+       {
+               if (ALIAS_PATTERN.matcher(alias).matches())
+               {
+                       alias = alias.substring(ALIAS_PREFIX.length());
+                       StrongSwanApplication.getContext().deleteFile(FILE_PREFIX + alias);
+               }
+       }
+
+       /**
+        * Retrieve the certificate with the given alias
+        * @param alias a certificate's alias
+        * @return certificate object or null
+        */
+       public X509Certificate getCertificate(String alias)
+       {
+               if (!ALIAS_PATTERN.matcher(alias).matches())
+               {
+                       return null;
+               }
+               alias = alias.substring(ALIAS_PREFIX.length());
+               try
+               {
+                       FileInputStream in = StrongSwanApplication.getContext().openFileInput(FILE_PREFIX + alias);
+                       try
+                       {
+                               CertificateFactory factory = CertificateFactory.getInstance("X.509");
+                               X509Certificate certificate = (X509Certificate)factory.generateCertificate(in);
+                               return certificate;
+                       }
+                       catch (CertificateException e)
+                       {
+                               e.printStackTrace();
+                       }
+                       finally
+                       {
+                               try
+                               {
+                                       in.close();
+                               }
+                               catch (IOException e)
+                               {
+                                       e.printStackTrace();
+                               }
+                       }
+               }
+               catch (FileNotFoundException e)
+               {
+                       e.printStackTrace();
+               }
+               return null;
+       }
+
+       /**
+        * Returns the creation date of the certificate with the given alias
+        * @param alias certificate alias
+        * @return creation date or null if not found
+        */
+       public Date getCreationDate(String alias)
+       {
+               if (!ALIAS_PATTERN.matcher(alias).matches())
+               {
+                       return null;
+               }
+               alias = alias.substring(ALIAS_PREFIX.length());
+               File file = StrongSwanApplication.getContext().getFileStreamPath(FILE_PREFIX + alias);
+               return file.exists() ? new Date(file.lastModified()) : null;
+       }
+
+       /**
+        * Returns a list of all known certificate aliases
+        * @return list of aliases
+        */
+       public ArrayList<String> aliases()
+       {
+               ArrayList<String> list = new ArrayList<String>();
+               for (String file : StrongSwanApplication.getContext().fileList())
+               {
+                       if (file.startsWith(FILE_PREFIX))
+                       {
+                               list.add(ALIAS_PREFIX + file.substring(FILE_PREFIX.length()));
+                       }
+               }
+               return list;
+       }
+
+       /**
+        * Check if the store contains a certificate with the given alias
+        * @param alias certificate alias
+        * @return true if the store contains the certificate
+        */
+       public boolean containsAlias(String alias)
+       {
+               return getCreationDate(alias) != null;
+       }
+
+       /**
+        * Returns a certificate alias based on a SHA-1 hash of the public key.
+        *
+        * @param cert certificate to get an alias for
+        * @return hex encoded alias, or null if failed
+        */
+       public String getCertificateAlias(Certificate cert)
+       {
+               String keyid = getKeyId(cert);
+               return keyid != null ? ALIAS_PREFIX + keyid : null;
+       }
+
+       /**
+        * Calculates the SHA-1 hash of the public key of the given certificate.
+        * @param cert certificate to get the key ID from
+        * @return hex encoded SHA-1 hash of the public key or null if failed
+        */
+       private String getKeyId(Certificate cert)
+       {
+               MessageDigest md;
+               try
+               {
+                       md = java.security.MessageDigest.getInstance("SHA1");
+                       byte[] hash = md.digest(cert.getPublicKey().getEncoded());
+                       return Utils.bytesToHex(hash);
+               }
+               catch (NoSuchAlgorithmException e)
+               {
+                       e.printStackTrace();
+               }
+               return null;
+       }
+}