android: Add local certificate store

The class manages certificates stored in files within the app's
private data directory.
This commit is contained in:
Tobias Brunner 2014-05-30 13:28:16 +02:00
parent 463a6cd005
commit 275888d255
1 changed files with 230 additions and 0 deletions

View File

@ -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;
}
}