Newer
Older
/**
* @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.Arrays;
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 {
/**
* Extension file for certificate
private static final String CERT_EXT = "crt";
* Extension file for digests
private static final String DIGESTS_EXT = "dgs";
* Excluded files while building the lists of certificates/private keys
private static final String[] EXCLUDED_PATTERNS = new String[] { /*for debug: */".svn", "._.DS_Store", ".DS_Store"};
* Full path to access certificate files
private String _fullPathCerts;
/**
* Memory cache for the certificates
*/
private Map<String, byte[]> _cachedCertificates;
private Map<String, byte[]> _cachedCertificatesDigest;
private Map<String, String> _cachedReverseCertificatesDigest;
/**
* Memory cache for the signing private keys
*/
private Map<String, byte[]> _cachedSigningPrivateKey;
/**
* Memory cache for the encrypt private keys
*/
private Map<String, byte[]> _cachedEncryptPrivateKey;
/**
* Set to true if loadCertificates was already called
*/
private boolean _areCertificatesLoaded = false;
/**
* Default constructor
*/
public CertificatesIO() {
_cachedCertificates = new ConcurrentHashMap<String, byte[]>();
_cachedCertificatesDigest = new ConcurrentHashMap<String, byte[]>();
_cachedSigningPrivateKey = new ConcurrentHashMap<String, byte[]>();
_cachedEncryptPrivateKey = new ConcurrentHashMap<String, byte[]>();
_cachedReverseCertificatesDigest = new ConcurrentHashMap<String, String>();
} // 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 p_rootDirectory, final String p_configId) { // E.g. <rootDirectory path>, cfg01
TERFactory.getInstance().logDebug(">>> CertificatesIO.loadCertificates: '" + p_rootDirectory + "', '" + p_configId + "' - " + _areCertificatesLoaded);
// Sanity check
if (_areCertificatesLoaded) {
return true;
}
if ((p_rootDirectory == null) || (p_rootDirectory.length() == 0)) {
_fullPathCerts = System.getProperty("user.dir").replace("\\", "/");
_fullPathCerts = p_rootDirectory.replace("\\", "/");
if (!_fullPathCerts.endsWith("/")) {
_fullPathCerts += "/";
File certsPath = new File(_fullPathCerts);
if (!certsPath.exists()) {
TERFactory.getInstance().logError("CertificatesIO.loadCertificates: path '" + _fullPathCerts + "' does not found");
_areCertificatesLoaded = loadMemoryCache(certsPath); // Load certificates and keys and return
if ((p_configId != null) && (p_configId.length() != 0)) {
String path = new String(_fullPathCerts + "/" + p_configId);
certsPath = new File(path);
if (!certsPath.exists()) {
TERFactory.getInstance().logError("CertificatesIO.loadCertificates: path '" + path + "' does not found");
return false;
}
loadMemoryCache(certsPath); // Load certificates and keys and return
}
return _areCertificatesLoaded;
}
/**
* @desc Unload from memory cache the certificates available
* @return true on success, false otherwise
*/
@Override
public boolean unloadCertificates() {
_areCertificatesLoaded = false;
_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) {
// TERFactory.getInstance().logDebug(">>> CertificatesIO.readCertificate: " + key);
String certKey;
if (_cachedReverseCertificatesDigest.containsKey(key)) {
certKey = _cachedReverseCertificatesDigest.get(key);
}else{
certKey = key;
if (_cachedCertificates.containsKey(certKey)) {
try {
certificate.write(_cachedCertificates.get(certKey));
return true;
} catch (IOException e) {
e.printStackTrace();
}
}else{
TERFactory.getInstance().logError("CertificatesIO.readCertificate: key '" + key + "' not found");
}
return false;
}
@Override
public boolean readCertificateDigest(final String certificateId, final ByteArrayOutputStream digest) {
// TERFactory.getInstance().logDebug(">>> CertificatesIO.readCertificateDigest: " + certificateId);
if (!_cachedCertificatesDigest.containsKey(certificateId)) {
TERFactory.getInstance().logError("CertificatesIO.readCertificateDigest: key '" + certificateId + "' not found");
return false;
}
try {
digest.write(_cachedCertificatesDigest.get(certificateId));
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
* @desc Read the signing private key for the specified certificate
* @param keysId the keys identifier
* @param key the signing private key
* @return true on success, false otherwise
*/
@Override
public boolean readSigningKey(final String keyName, final ByteArrayOutputStream key) {
// TERFactory.getInstance().logDebug(">>> CertificatesIO.readSigningKey: " + keyName);
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
String certKey;
if (_cachedReverseCertificatesDigest.containsKey(keyName)) {
certKey = _cachedReverseCertificatesDigest.get(keyName);
}else{
certKey = keyName;
}
if (_cachedSigningPrivateKey.containsKey(certKey)) {
key.write(_cachedSigningPrivateKey.get(certKey));
return true;
}
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* @desc Read the encrypting private key for the specified certificate
* @param keysId the keys identifier
* @param key the signing private key
* @return true on success, false otherwise
*/
@Override
public boolean readEncryptingKey(final String keyName, final ByteArrayOutputStream key) {
String certKey;
try {
if (_cachedReverseCertificatesDigest.containsKey(keyName)) {
certKey = _cachedReverseCertificatesDigest.get(keyName);
}else{
certKey = keyName;
}
if (_cachedEncryptPrivateKey.containsKey(certKey)) {
key.write(_cachedEncryptPrivateKey.get(certKey));
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. <path>/keys, <path>/certs
// Retrieve the list of the files in the p_keysPath
List<File> files = Helpers.getInstance().getFileListing(p_keysPath, CERT_EXT, EXCLUDED_PATTERNS);
// Create the memory cache
for (File file : files) {
try {
addCertItem(file);
}catch(FileNotFoundException e){}
} // End of 'for' statement
} catch (IOException e) {
e.printStackTrace();
}
// load digests
try {
List<File> files = Helpers.getInstance().getFileListing(p_keysPath, DIGESTS_EXT, EXCLUDED_PATTERNS);
// Create the memory cache
for (File file : files) {
try {
addDigestItem(file);
}catch(FileNotFoundException e){}
} // End of 'for' statement
} catch (IOException e) {
e.printStackTrace();
}
private void addDigestItem(final File p_file) throws FileNotFoundException, IOException {
String filename = p_file.getName();
String certName = filename.substring(0, filename.lastIndexOf(".")).toUpperCase();
// Load certificate
byte bytes[] = new byte[64];
FileInputStream fs = new FileInputStream(p_file);
int n = fs.read(bytes);
fs.close();
if(n == 32){
// take last 8 bytes
bytes = Arrays.copyOfRange(bytes, 24, 32);
}else if(n >= 64){
bytes = ByteHelper.hexStringToByteArray(new String(bytes).substring(48, 64));
}else if(n >= 16){
bytes = ByteHelper.hexStringToByteArray(new String(bytes).substring(0, 16));
}else if(n == 8){
bytes = Arrays.copyOfRange(bytes, 0, 8);
}else{
TERFactory.getInstance().logError("CertificatesIO: " + filename + ": wrong digest file length\n");
_cachedCertificatesDigest.put(certName, bytes);
// TERFactory.getInstance().logDebug("CertificatesIO.addDigestItem: Store digest: " + ByteHelper.byteArrayToString(bytes) + " - " + certName);
}
private void addCertItem(final File p_certFile) throws FileNotFoundException, IOException {
// TERFactory.getInstance().logDebug(">>> CertificatesIO.addItem: " + p_certFile);
// Load the keys file name
String filename = p_certFile.getName();
String certName = filename.substring(0, filename.lastIndexOf(".")).toUpperCase();
// Load certificate
byte bytes[] = new byte[(int) p_certFile.length()];
FileInputStream fsKeys = new FileInputStream(p_certFile);
fsKeys.read(bytes);
fsKeys.close();
bytes = ByteHelper.hexStringToByteArray(new String(bytes));
_cachedCertificates.put(certName, bytes);
// TERFactory.getInstance().logDebug("CertificatesIO.addItem: Store cert " + certName + " - " + ByteHelper.byteArrayToString(bytes));
// calculate digest
bytes = calculateDigestFromCertificate(bytes);
_cachedCertificatesDigest.put(certName, bytes);
// TERFactory.getInstance().logDebug("CertificatesIO.addItem: Store digest: " + ByteHelper.byteArrayToString(bytes) + " - " + certName);
_cachedReverseCertificatesDigest.put(ByteHelper.byteArrayToString(bytes), certName);
// TERFactory.getInstance().logDebug("CertificatesIO.addItem: Store reverse digest " + ByteHelper.byteArrayToString(bytes) + " - " + certName);
// Load Private Keys
filename = p_certFile.getPath();
filename = filename.substring(0, filename.lastIndexOf("."));
try {
File f = new File(filename+".vkey");
if(f.exists()){
long l = f.length();
if(l == 32 || l == 64){
bytes = new byte[64];
fsKeys = new FileInputStream(f);
l = fsKeys.read(bytes);
fsKeys.close();
if(l == 64){
bytes = ByteHelper.hexStringToByteArray(new String(bytes));
l = 32;
}
if(l != 32){
TERFactory.getInstance().logError("CertificatesIO: " + f.getName() + ": wrong data length[" + l + "\n");
}
if (!_cachedSigningPrivateKey.containsKey(certName)) {
_cachedSigningPrivateKey.put(certName, bytes);
}
}else{
TERFactory.getInstance().logError("CertificatesIO: " + f.getName() + ": wrong key file length\n");
}
}
}catch(FileNotFoundException e){}
try {
File f = new File(filename+".ekey");
if(f.exists()){
if(f.length() == 32 || f.length() == 64){
bytes = new byte[64];
fsKeys = new FileInputStream(f);
fsKeys.read(bytes);
fsKeys.close();
if(f.length() == 64){
bytes = ByteHelper.hexStringToByteArray(new String(bytes));
}
if (!_cachedEncryptPrivateKey.containsKey(certName)) {
_cachedEncryptPrivateKey.put(certName, bytes);
}
}
}
}catch(FileNotFoundException e){}
}
@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, _cachedReverseCertificatesDigest.get(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