Commit c89d62ed authored by berge's avatar berge
Browse files

Merged revision(s) 1840-1916 from branches/Security

parent d0339b89
...@@ -27,6 +27,8 @@ import org.etsi.its.adapter.ports.FsapPort; ...@@ -27,6 +27,8 @@ import org.etsi.its.adapter.ports.FsapPort;
import org.etsi.its.adapter.ports.GnPort; import org.etsi.its.adapter.ports.GnPort;
import org.etsi.ttcn.tci.CharstringValue; import org.etsi.ttcn.tci.CharstringValue;
import de.fraunhofer.sit.c2x.CryptoLib;
/** /**
* This class is used to centralise test adapter configuration and execution parameters * This class is used to centralise test adapter configuration and execution parameters
* All settings are component specific (multiton) * All settings are component specific (multiton)
...@@ -411,8 +413,9 @@ public class Management implements IManagementTA, IManagementLayers { ...@@ -411,8 +413,9 @@ public class Management implements IManagementTA, IManagementLayers {
// Extract public keys // Extract public keys
atCertificate = certificate.toByteArray(); atCertificate = certificate.toByteArray();
// System.out.println("Management.setupSecuredModeFromTaConfig: certificate=" + ByteHelper.byteArrayToString(value)); // System.out.println("Management.setupSecuredModeFromTaConfig: certificate=" + ByteHelper.byteArrayToString(value));
atCertificateDigest = new byte[8]; // Compute AT certificate digest
System.arraycopy(atCertificate, 3, atCertificateDigest, 0, 8); byte[] atHash = CryptoLib.hashWithSha256(atCertificate);
atCertificateDigest = ByteHelper.extract(atHash, atHash.length - 8, 8);
// System.out.println("Management.setupSecuredModeFromTaConfig: atCertificateDigest=" + ByteHelper.byteArrayToString(atCertificateDigest)); // System.out.println("Management.setupSecuredModeFromTaConfig: atCertificateDigest=" + ByteHelper.byteArrayToString(atCertificateDigest));
int offset = 18; int offset = 18;
// KeyX // KeyX
......
...@@ -7,7 +7,7 @@ package org.etsi.its.adapter; ...@@ -7,7 +7,7 @@ package org.etsi.its.adapter;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import org.etsi.ttcn.common.ByteHelper; import org.etsi.common.ByteHelper;
public class TlsHelper { public class TlsHelper {
......
...@@ -242,7 +242,7 @@ public class GnPort extends ProtocolPort implements Runnable, IEthernetSpecific ...@@ -242,7 +242,7 @@ public class GnPort extends ProtocolPort implements Runnable, IEthernetSpecific
@Override @Override
public boolean send(byte[] message) { public boolean send(byte[] message) {
HashMap<String, Object> params = new HashMap<String, Object>(); HashMap<String, Object> params = new HashMap<String, Object>();
ByteHelper.dump(">>> GnPort.send", message); // ByteHelper.dump(">>> GnPort.send", message);
byte[] destMacAddress = ByteHelper.extract(message, message.length - 6, 6); byte[] destMacAddress = ByteHelper.extract(message, message.length - 6, 6);
message = ByteHelper.extract(message, 0, message.length - 6); message = ByteHelper.extract(message, 0, message.length - 6);
...@@ -250,7 +250,7 @@ public class GnPort extends ProtocolPort implements Runnable, IEthernetSpecific ...@@ -250,7 +250,7 @@ public class GnPort extends ProtocolPort implements Runnable, IEthernetSpecific
if (management.isSecuredModeSet()) { // Secure mode disabled if (management.isSecuredModeSet()) { // Secure mode disabled
message = createSecuredMessage(message); message = createSecuredMessage(message);
} }
ByteHelper.dump("GnPort.send", message); // ByteHelper.dump("GnPort.send", message);
return send(message, params); return send(message, params);
} }
......
...@@ -77,7 +77,7 @@ public abstract class ProtocolPort extends Layer implements IPort, IObservable { ...@@ -77,7 +77,7 @@ public abstract class ProtocolPort extends Layer implements IPort, IObservable {
public boolean send(byte[] message) { public boolean send(byte[] message) {
HashMap<String, Object> params = new HashMap<String, Object>(); HashMap<String, Object> params = new HashMap<String, Object>();
ByteHelper.dump("ProtocolPortLayer.send", message); // ByteHelper.dump("ProtocolPortLayer.send", message);
return send(message, params); return send(message, params);
} }
......
...@@ -5,11 +5,6 @@ import org.etsi.certificates.io.ICertificatesIO; ...@@ -5,11 +5,6 @@ import org.etsi.certificates.io.ICertificatesIO;
public class CertificatesIOFactory { public class CertificatesIOFactory {
/**
* The single instance of this class.
*/
private static CertificatesIOFactory _instance = new CertificatesIOFactory();
/** /**
* The single instance of the class CertificatesIO. * The single instance of the class CertificatesIO.
*/ */
...@@ -20,7 +15,7 @@ public class CertificatesIOFactory { ...@@ -20,7 +15,7 @@ public class CertificatesIOFactory {
* @return The single instance of this class. * @return The single instance of this class.
*/ */
public static ICertificatesIO getInstance() { public static ICertificatesIO getInstance() {
return _instance._certIO; return _certIO;
} }
/** /**
......
...@@ -7,6 +7,7 @@ package org.etsi.certificates; ...@@ -7,6 +7,7 @@ package org.etsi.certificates;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
...@@ -68,32 +69,46 @@ public final class Helpers { ...@@ -68,32 +69,46 @@ public final class Helpers {
/** /**
* Recursively walk a directory tree and return a List of all Files found; the List is sorted using File.compareTo(). * Recursively walk a directory tree and return a List of all Files found; the List is sorted using File.compareTo().
* @param startingDir is a valid directory, which can be read. * @param p_startingDir The valid directory, which can be read.
* @param p_extension The file extension, in lower case
* @param p_excludedPatterns The pattern which shall be excluded, in lower case
*/ */
public List<File> getFileListing(File startingDir) throws FileNotFoundException { public List<File> getFileListing(File p_startingDir, final String p_extension, final String[] p_excludedPatterns) throws FileNotFoundException {
validateDirectory(startingDir); validateDirectory(p_startingDir);
List<File> result = getFileListingNoSort(startingDir); List<File> result = getFileListingNoSort(p_startingDir, p_extension, p_excludedPatterns);
Collections.sort(result); Collections.sort(result);
return result; return result;
} }
private List<File> getFileListingNoSort(File startingDir) throws FileNotFoundException { private List<File> getFileListingNoSort(final File p_startingDir, final String p_extension, final String[] p_excludedPatterns) throws FileNotFoundException {
List<File> result = new ArrayList<File>(); List<File> result = new ArrayList<File>();
File[] filesAndDirs = startingDir.listFiles(); FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(final File p_dirName, final String p_fileName) {
String name = p_fileName.toLowerCase();
if (!p_extension.isEmpty() && !name.endsWith(p_extension)) {
return false;
}
if (p_excludedPatterns != null) {
for (String excludePattern : p_excludedPatterns) {
if (name.indexOf(excludePattern) != -1) {
return false;
}
}
}
return true;
}
};
File[] filesAndDirs = p_startingDir.listFiles(filter);
List<File> filesDirs = Arrays.asList(filesAndDirs); List<File> filesDirs = Arrays.asList(filesAndDirs);
for (File file : filesDirs) { for (File file : filesDirs) {
if (file.isDirectory() && ((file.getName().indexOf(".svn") != -1)
|| (file.getName().indexOf("._.DS_Store") != -1)
|| (file.getName().indexOf(".DS_Store") != -1))) { // For debug purpose only
// Skip svn files
continue;
}
result.add(file); // always add, even if directory result.add(file); // always add, even if directory
if (!file.isFile()) { if (!file.isFile()) {
//must be a directory //must be a directory
//recursive call! //recursive call!
List<File> deeperList = getFileListingNoSort(file); List<File> deeperList = getFileListingNoSort(file, p_extension, p_excludedPatterns);
result.addAll(deeperList); result.addAll(deeperList);
} }
} }
......
...@@ -13,64 +13,48 @@ import java.io.IOException; ...@@ -13,64 +13,48 @@ import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.etsi.certificates.Helpers; import org.etsi.certificates.Helpers;
import org.etsi.common.ByteHelper; import org.etsi.common.ByteHelper;
import de.fraunhofer.sit.c2x.CryptoLib;
public class CertificatesIO implements ICertificatesIO { public class CertificatesIO implements ICertificatesIO {
/** /**
* Regex pattern to extract signing private key * Relative path for to extract certificates
*/
private static final String SIGNING_PRIVATE_KEY_PATTERN = "^SigningPrivate key\\s*=\\s*([A-Fa-f0-9]+)$";
/**
* Regex pattern to extract encrypt private key
*/
private static final String ENCRYPT_PRIVATE_KEY_PATTERN = "^EncryptPrivate key\\s*=\\s*([A-Fa-f0-9]+)$";
/**
* Regex pattern to extract AA certificate
*/ */
private static final String CERT_AA_PATTERN = "^AUTHORIZATION_AUTHORITY.dump\\s*=\\s*([A-Fa-f0-9]+)$"; private static final String CERTS_PATH = "/certs_store";
/** /**
* Regex pattern to extract AT certificate * Extension file for private keys
*/ */
private static final String CERT_AT_PATTERN = "^AUTHORIZATION_TICKET.dump\\s*=\\s*([A-Fa-f0-9]+)$"; private static final String KEYS_EXT = "bin";
/** /**
* Regex compiler to extract signing private key * Relative path for to extract private keys
*/ */
private Pattern signingPrivateKeyPattern = Pattern.compile(SIGNING_PRIVATE_KEY_PATTERN, Pattern.MULTILINE); private static final String KEYS_PATH = "/priv_keys";
/** /**
* Regex compiler to extract encrypt private key * Full path to access certificate files
*/
private Pattern encryptPrivateKeyPattern = Pattern.compile(ENCRYPT_PRIVATE_KEY_PATTERN, Pattern.MULTILINE);
/**
* Regex compiler to extract AA certificate
*/ */
private Pattern certAaPattern = Pattern.compile(CERT_AA_PATTERN, Pattern.MULTILINE); private String _fullPathCerts;
/** /**
* Regex compiler to extract AT certificate * Full path to access key files
*/ */
private Pattern certAtPattern = Pattern.compile(CERT_AT_PATTERN, Pattern.MULTILINE); private String _fullPathKeys;
/**
* Full path to access certificate files
*/
private String _fullPath;
/** /**
* Memory cache for the certificates * Memory cache for the certificates
*/ */
private Map<String, byte[]> _cachedCertificates; private Map<String, byte[]> _cachedCertificates;
private Map<String, byte[]> _cachedCertificatesDigest;
private Map<String, String> _cachedReverseCertificatesDigest;
/** /**
* Memory cache for the signing private keys * Memory cache for the signing private keys
*/ */
...@@ -86,8 +70,10 @@ public class CertificatesIO implements ICertificatesIO { ...@@ -86,8 +70,10 @@ public class CertificatesIO implements ICertificatesIO {
*/ */
public CertificatesIO() { public CertificatesIO() {
_cachedCertificates = new ConcurrentHashMap<String, byte[]>(); _cachedCertificates = new ConcurrentHashMap<String, byte[]>();
_cachedCertificatesDigest = new ConcurrentHashMap<String, byte[]>();
_cachedSigningPrivateKey = new ConcurrentHashMap<String, byte[]>(); _cachedSigningPrivateKey = new ConcurrentHashMap<String, byte[]>();
_cachedEncryptPrivateKey = new ConcurrentHashMap<String, byte[]>(); _cachedEncryptPrivateKey = new ConcurrentHashMap<String, byte[]>();
_cachedReverseCertificatesDigest = new ConcurrentHashMap<String, String>();
} // End of Constructor } // End of Constructor
/** /**
...@@ -99,31 +85,36 @@ public class CertificatesIO implements ICertificatesIO { ...@@ -99,31 +85,36 @@ public class CertificatesIO implements ICertificatesIO {
@Override @Override
public boolean loadCertificates(final String rootDirectory, final String configId) { // E.g. <rootDirectory path>, cfg01 public boolean loadCertificates(final String rootDirectory, final String configId) { // E.g. <rootDirectory path>, cfg01
// Initialise the memory caches // Initialise the memory caches
_cachedCertificates.clear(); unloadCertificates();
_cachedSigningPrivateKey.clear();
_cachedEncryptPrivateKey.clear();
// Build full path // Build full path
if ((rootDirectory == null) || (rootDirectory.length() == 0)) { if ((rootDirectory == null) || (rootDirectory.length() == 0)) {
_fullPath = System.getProperty("user.dir").replace("\\", "/"); _fullPathCerts = System.getProperty("user.dir").replace("\\", "/");
} else { } else {
_fullPath = rootDirectory.replace("\\", "/"); _fullPathCerts = rootDirectory.replace("\\", "/");
} }
if (!_fullPath.endsWith("/")) { if (!_fullPathCerts.endsWith("/")) {
_fullPath += "/"; _fullPathCerts += "/";
} }
if ((configId != null) && (configId.length() != 0)) { if ((configId != null) && (configId.length() != 0)) {
_fullPath += configId.replace('.', '/'); _fullPathCerts += configId.replace('.', '/');
} }
_fullPath = _fullPath.toLowerCase(); _fullPathCerts = _fullPathCerts.toLowerCase();
// Check the path _fullPathKeys = _fullPathCerts + KEYS_PATH;
File path = new File(_fullPath); _fullPathCerts += CERTS_PATH;
if (!path.exists()) { // Check the paths
System.err.println("CertificatesIO.readCertificate: path '" + _fullPath + "' does not found"); 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 false;
} }
return loadMemoryCache(path); // Load certificates and keys and return return loadMemoryCache(keysPath); // Load certificates and keys and return
} }
/** /**
...@@ -132,9 +123,13 @@ public class CertificatesIO implements ICertificatesIO { ...@@ -132,9 +123,13 @@ public class CertificatesIO implements ICertificatesIO {
*/ */
@Override @Override
public boolean unloadCertificates() { public boolean unloadCertificates() {
_fullPath = null; _fullPathKeys = null;
_fullPathCerts = null;
_cachedCertificates.clear();
_cachedCertificatesDigest.clear();
_cachedSigningPrivateKey.clear(); _cachedSigningPrivateKey.clear();
_cachedEncryptPrivateKey.clear(); _cachedEncryptPrivateKey.clear();
_cachedReverseCertificatesDigest.clear();
return true; return true;
} }
...@@ -163,6 +158,26 @@ public class CertificatesIO implements ICertificatesIO { ...@@ -163,6 +158,26 @@ public class CertificatesIO implements ICertificatesIO {
return false; 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 * @desc Read the private keys for the specified certificate
* @param keysId the keys identifier * @param keysId the keys identifier
...@@ -189,10 +204,16 @@ public class CertificatesIO implements ICertificatesIO { ...@@ -189,10 +204,16 @@ public class CertificatesIO implements ICertificatesIO {
return false; return false;
} }
private boolean loadMemoryCache(final File path) { // E.g. <path>/ta_config_a.txt /**
// Retrieve the list of the files in the _fullpath * @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
try { try {
List<File> files = Helpers.getInstance().getFileListing(path); List<File> files = Helpers.getInstance().getFileListing(p_keysPath, KEYS_EXT, new String[] { "_at", "_aa", /*for debug: */".svn", "._.DS_Store", ".DS_Store"});
// Create the memory cache // Create the memory cache
for (File file : files) { for (File file : files) {
addItem(file); addItem(file);
...@@ -205,47 +226,87 @@ public class CertificatesIO implements ICertificatesIO { ...@@ -205,47 +226,87 @@ public class CertificatesIO implements ICertificatesIO {
return false; return false;
} }
private void addItem(final File file) throws FileNotFoundException, IOException { private void addItem(final File p_keysFile) throws FileNotFoundException, IOException {
String key = file.getName().substring(0, file.getName().indexOf('.')).toUpperCase(); // 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)) { if (!_cachedCertificates.containsKey(key)) {
FileInputStream fs = new FileInputStream(file.getCanonicalPath()); // Open CA keys file
byte bytes[] = new byte[(int) file.length()]; FileInputStream fsKeys = new FileInputStream(p_keysFile.getCanonicalPath());
fs.read(bytes); // Load keys
String content = new String(bytes); 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();
// Extract SigningPrivateKey. Key identifier is key.PRIVATE_KEYS // Open CA certificate
// E.g. SigningPrivate key=68E45C8BBE0ABFA80188E62D082D7A1E5FD4D637D99A5826781280F3865B5819 FileInputStream fsCaFile = new FileInputStream(caFile.getCanonicalPath());
Matcher matcher = signingPrivateKeyPattern.matcher(content); bytes = new byte[(int) caFile.length()];
if (matcher.find()) { fsCaFile.read(bytes);
// System.out.println("CertificatesIO.addItem: Add key '" + key + ".PRIVATE_KEYS" + "'"); _cachedCertificates.put(key + ".CA_CERT", bytes);
_cachedSigningPrivateKey.put(key + ".PRIVATE_KEYS", ByteHelper.hexStringToByteArray(matcher.group(1))); // Close file streams
} fsCaFile.close();
// Extract EncryptPrivate. Key identifier is key.PRIVATE_KEYS
// E.g. EncryptPrivate key=00B65233550BE5A9866CF871D7712691CC47532A08C620D20579FB02BEFA3D1C5F
matcher = encryptPrivateKeyPattern.matcher(content);
if (matcher.find()) {
// System.out.println("CertificatesIO.addItem: Add key '" + key + ".PRIVATE_KEYS" + "'");
_cachedEncryptPrivateKey.put(key + ".PRIVATE_KEYS", ByteHelper.hexStringToByteArray(matcher.group(1)));
}
// Extract AUTHORIZATION_AUTHORITY.dump. Key identifier is key.AA_CERT // Open AA certificate
// E.g. AUTHORIZATION_AUTHORITY.dump=02015791FDC6CE82402F0210455453495F506C7567746573745F414180910000040E5F7F47D7A165E2DC403F1E8BAA14E6FF04ADE282CB22212056444CE0F193B01493338B6A8A5D36B2BE9CD3D1F5C708862BCD4539F9D99A0D9F7347066A92C001010004402A571AF5293651B3E2A931CEB1009805EB92E44288D08510E67BA629F14D61402A571AF5293651B3E2A931CEB1009805EB92E44288D08510E67BA629F14D6102202006C04080C040812401146757031A5617030303181DB9CF7C052616001DB9566E0526872A1D53F0D0052783500000A3EECD601A7D17D4C1EB4B8205EEFAE45E170872D49741941B5C9AEDEAA76E38938A67F98DCD46D390B9CF4C16AD324B9E6520565994790029A841E4C820BE4D FileInputStream fsAaFile = new FileInputStream(aaFile.getCanonicalPath());
matcher = certAaPattern.matcher(content); bytes = new byte[(int) aaFile.length()];
if (matcher.find()) { fsAaFile.read(bytes);
// System.out.println("CertificatesIO.addItem: Add key '" + key + ".AA_CERT" + "'"); _cachedCertificates.put(key + ".AA_CERT", bytes);
_cachedCertificates.put(key + ".AA_CERT", ByteHelper.hexStringToByteArray(matcher.group(1))); // Close file streams
} fsAaFile.close();
// Extract AUTHORIZATION_TICKET.dump. Key identifier is key.AT_CERT // Open AT certificate
// E.g. AUTHORIZATION_TICKET.dump=0201CDF3567352CD8B4401008095000004DCBD7FBD344A653D42420350D3CB84E68C1E618ACC16FF4BA31D9D2913530A1ADED6F691D23476E779CCA54C63327F28E9E943929CF5C459DDA01E459279CAA0010100041D2EE0E362BF2798C5148BB198B2EB3718CB0B8714580A70A7161A5550D71F364FF1D5098A18E2B9CE142A17B5558A9E54438823D146CC24A372E7CE1761AF100220210AC040800100C0408101002401146757031A5617030303181DB9CF7C052616001DB9566E0526872A1D53F0D0052783500000AE35B36EA820B183AD0AD732656E2606A8C373C95DFA580FF4C049C66FEFD76F0B558C16932B4DC2BA0D52966AC3D56AC20E606DEDE63B625B4B80FDE1269335 FileInputStream fsAtFile = new FileInputStream(atFile.getCanonicalPath());
matcher = certAtPattern.matcher(content); bytes = new byte[(int) atFile.length()];
if (matcher.find()) { fsAtFile.read(bytes);
// System.out.println("CertificatesIO.addItem: Add key '" + key + ".AT_CERT" + "'"); _cachedCertificates.put(key + ".AT_CERT", bytes);
_cachedCertificates.put(key + ".AT_CERT", ByteHelper.hexStringToByteArray(matcher.group(1))); 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();