From: Tobias Brunner Date: Fri, 30 May 2014 11:28:16 +0000 (+0200) Subject: android: Add local certificate store X-Git-Tag: 5.2.1dr1~117^2~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=275888d255e2d0620758c9159be54ee5128f62e2;p=thirdparty%2Fstrongswan.git android: Add local certificate store The class manages certificates stored in files within the app's private data directory. --- 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 index 0000000000..cec5c603d8 --- /dev/null +++ b/src/frontends/android/src/org/strongswan/android/security/LocalCertificateStore.java @@ -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 . + * + * 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 aliases() + { + ArrayList list = new ArrayList(); + 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; + } +}