/** * @author ETSI / STF481 / Yann Garcia * @version $URL$ * $Id$ */ package org.etsi.certificates.io; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.etsi.certificates.Helpers; import org.etsi.common.ByteHelper; import de.fraunhofer.sit.c2x.CryptoLib; public class CertificatesIO implements ICertificatesIO { /** * Relative path for to extract certificates */ private static final String CERTS_PATH = "/certs_store"; /** * Extension file for private keys */ private static final String KEYS_EXT = "bin"; /** * Relative path for to extract private keys */ private static final String KEYS_PATH = "/priv_keys"; /** * Full path to access certificate files */ private String _fullPathCerts; /** * Full path to access key files */ private String _fullPathKeys; /** * Memory cache for the certificates */ private Map _cachedCertificates; private Map _cachedCertificatesDigest; private Map _cachedReverseCertificatesDigest; /** * Memory cache for the signing private keys */ private Map _cachedSigningPrivateKey; /** * Memory cache for the encrypt private keys */ private Map _cachedEncryptPrivateKey; /** * Default constructor */ public CertificatesIO() { _cachedCertificates = new ConcurrentHashMap(); _cachedCertificatesDigest = new ConcurrentHashMap(); _cachedSigningPrivateKey = new ConcurrentHashMap(); _cachedEncryptPrivateKey = new ConcurrentHashMap(); _cachedReverseCertificatesDigest = new ConcurrentHashMap(); } // End of Constructor /** * @desc Load in memory cache the certificates available in the specified directory * @param rootDirectory Root directory to access to the certificates identified by the certificate ID * @param configId A configuration identifier * @return true on success, false otherwise */ @Override public boolean loadCertificates(final String rootDirectory, final String configId) { // E.g. , cfg01 // Initialise the memory caches unloadCertificates(); // Build full path if ((rootDirectory == null) || (rootDirectory.length() == 0)) { _fullPathCerts = System.getProperty("user.dir").replace("\\", "/"); } else { _fullPathCerts = rootDirectory.replace("\\", "/"); } if (!_fullPathCerts.endsWith("/")) { _fullPathCerts += "/"; } if ((configId != null) && (configId.length() != 0)) { _fullPathCerts += configId.replace('.', '/'); } _fullPathCerts = _fullPathCerts.toLowerCase(); _fullPathKeys = _fullPathCerts + KEYS_PATH; _fullPathCerts += CERTS_PATH; // Check the paths File keysPath = new File(_fullPathKeys); if (!keysPath.exists()) { System.err.println("CertificatesIO.readCertificate: path '" + _fullPathKeys + "' does not found"); return false; } File certsPath = new File(_fullPathCerts); if (!certsPath.exists()) { System.err.println("CertificatesIO.readCertificate: path '" + _fullPathCerts + "' does not found"); return false; } return loadMemoryCache(keysPath); // Load certificates and keys and return } /** * @desc Unload from memory cache the certificates available * @return true on success, false otherwise */ @Override public boolean unloadCertificates() { _fullPathKeys = null; _fullPathCerts = null; _cachedCertificates.clear(); _cachedCertificatesDigest.clear(); _cachedSigningPrivateKey.clear(); _cachedEncryptPrivateKey.clear(); _cachedReverseCertificatesDigest.clear(); return true; } /** * @desc Read the specified certificate * @param certificateId the certificate identifier * @param certificate the expected certificate * @return true on success, false otherwise */ @Override public boolean readCertificate(final String key, final ByteArrayOutputStream certificate) { // Sanity check if (!_cachedCertificates.containsKey(key)) { System.err.println("CertificatesIO.readCertificate: key '" + key + "' not found"); return false; } try { certificate.write(_cachedCertificates.get(key)); return true; } catch (IOException e) { e.printStackTrace(); } return false; } @Override public boolean readCertificateDigest(final String certificateId, final ByteArrayOutputStream digest) { // Sanity check String key = certificateId + ".DIGEST"; if (!_cachedCertificatesDigest.containsKey(key)) { System.err.println("CertificatesIO.readCertificate: key '" + key + "' not found"); return false; } try { digest.write(_cachedCertificatesDigest.get(key)); return true; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; } /** * @desc Read the private keys for the specified certificate * @param keysId the keys identifier * @param signingPrivateKey the signing private key * @param encryptPrivateKey the encrypt private key * @return true on success, false otherwise */ @Override public boolean readPrivateKeys(final String key, final ByteArrayOutputStream signingPrivateKey, final ByteArrayOutputStream encryptPrivateKey) { // Sanity check if (!_cachedSigningPrivateKey.containsKey(key) || !_cachedEncryptPrivateKey.containsKey(key)) { System.err.println("CertificatesIO.readPrivateKeys: key '" + key + "' not found"); return false; } try { signingPrivateKey.write(_cachedSigningPrivateKey.get(key)); encryptPrivateKey.write(_cachedEncryptPrivateKey.get(key)); return true; } catch (IOException e) { e.printStackTrace(); } return false; } /** * @desc Load certificates based on existing keys file, excluding xxx_at.bin files * @param p_keysPath path for private key files * @param p_certsPath Path for certificate files * @return true on success, false otherwise */ private boolean loadMemoryCache(final File p_keysPath) { // E.g. /keys, /certs // Retrieve the list of the files in the p_keysPath try { List files = Helpers.getInstance().getFileListing(p_keysPath, KEYS_EXT, new String[] { "_at", "_aa", /*for debug: */".svn", "._.DS_Store", ".DS_Store"}); // Create the memory cache for (File file : files) { addItem(file); } // End of 'for' statement return true; } catch (IOException e) { e.printStackTrace(); } return false; } private void addItem(final File p_keysFile) throws FileNotFoundException, IOException { // Sanity checks if (p_keysFile.length() != 32) { // FIXME Should have both encrypt and singing keys return; } // Load the keys file name String filename = p_keysFile.getName().toLowerCase(); String keysFileName = filename.substring(0, filename.indexOf("_ca")); File caFile = new File(_fullPathCerts + "/" + keysFileName + "_ca.crt"); File aaFile = new File(_fullPathCerts + "/" + keysFileName + "_aa.crt"); File atFile = new File(_fullPathCerts + "/" + keysFileName + "_at.crt"); String key = keysFileName.toUpperCase(); if (!_cachedCertificates.containsKey(key)) { // Open CA keys file FileInputStream fsKeys = new FileInputStream(p_keysFile.getCanonicalPath()); // Load keys byte bytes[] = new byte[(int) p_keysFile.length()]; fsKeys.read(bytes); _cachedSigningPrivateKey.put(key + ".CA_PRIVATE_KEYS", ByteHelper.extract(bytes, 0, 32)); // FIXME Should have both encrypt and singing keys _cachedEncryptPrivateKey.put(key + ".PRIVATE_KEYS", ByteHelper.extract(bytes, 32, 32)); _cachedEncryptPrivateKey.put(key + ".CA_PRIVATE_KEYS", ByteHelper.extract(bytes, 0, 32)); // Close file stream fsKeys.close(); // Open CA keys file fsKeys = new FileInputStream(p_keysFile.getCanonicalPath().toLowerCase().replaceAll("_ca", "_at")); // Load keys bytes = new byte[(int) p_keysFile.length()]; fsKeys.read(bytes); _cachedSigningPrivateKey.put(key + ".AT_PRIVATE_KEYS", ByteHelper.extract(bytes, 0, 32)); // FIXME Should have both encrypt and singing keys _cachedEncryptPrivateKey.put(key + ".PRIVATE_KEYS", ByteHelper.extract(bytes, 32, 32)); _cachedEncryptPrivateKey.put(key + ".AT_PRIVATE_KEYS", ByteHelper.extract(bytes, 0, 32)); // Close file stream fsKeys.close(); // Open CA certificate FileInputStream fsCaFile = new FileInputStream(caFile.getCanonicalPath()); bytes = new byte[(int) caFile.length()]; fsCaFile.read(bytes); _cachedCertificates.put(key + ".CA_CERT", bytes); // Close file streams fsCaFile.close(); // Open AA certificate FileInputStream fsAaFile = new FileInputStream(aaFile.getCanonicalPath()); bytes = new byte[(int) aaFile.length()]; fsAaFile.read(bytes); _cachedCertificates.put(key + ".AA_CERT", bytes); // Close file streams fsAaFile.close(); // Open AT certificate FileInputStream fsAtFile = new FileInputStream(atFile.getCanonicalPath()); bytes = new byte[(int) atFile.length()]; fsAtFile.read(bytes); _cachedCertificates.put(key + ".AT_CERT", bytes); byte[] digest = calculateDigestFromCertificate(bytes); _cachedCertificatesDigest.put(key + ".AT_CERT.DIGEST", digest); _cachedReverseCertificatesDigest.put(ByteHelper.byteArrayToString(digest), key + ".AT_CERT.DIGEST"); // Close file streams fsAtFile.close(); } // else, ignore it } @Override public String getKeyIdFromHashedId8(byte[] p_hashedId8ToBeUsed) { String key = ByteHelper.byteArrayToString(p_hashedId8ToBeUsed); if (!_cachedReverseCertificatesDigest.containsKey(key)) { return null; } return _cachedReverseCertificatesDigest.get(key).substring(0, key.length() - 7/*.DIGEST*/); } private byte[] calculateDigestFromCertificate(final byte[] p_toBeHashedData) { byte[] hash = CryptoLib.hashWithSha256(p_toBeHashedData); return ByteHelper.extract(hash, hash.length - 8, 8); } } // End of class CertificatesIO