/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.intel.mtwilson.as.business;
import com.intel.mtwilson.tag.model.X509AttributeCertificate;
import com.intel.mtwilson.datatypes.TagDataType;
import com.intel.dcsg.cpg.crypto.Sha1Digest;
import com.intel.dcsg.cpg.io.UUID;
import com.intel.mountwilson.as.common.ASException;
import com.intel.mtwilson.ApacheHttpClient;
import com.intel.mtwilson.My;
import com.intel.mtwilson.api.ApiException;
import com.intel.mtwilson.api.ApiResponse;
import com.intel.mtwilson.as.data.MwAssetTagCertificate;
import com.intel.mtwilson.as.data.TblHosts;
import com.intel.dcsg.cpg.x509.X509Util;
import com.intel.mtwilson.datatypes.AssetTagCertAssociateRequest;
import com.intel.mtwilson.datatypes.AssetTagCertCreateRequest;
import com.intel.mtwilson.datatypes.AssetTagCertRevokeRequest;
import com.intel.mtwilson.i18n.ErrorCode;
import com.intel.mtwilson.security.http.apache.ApacheBasicHttpAuthorization;
import com.intel.dcsg.cpg.tls.policy.impl.InsecureTlsPolicy;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.X509Certificate;
import java.util.List;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.commons.io.IOUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.intel.dcsg.cpg.crypto.CryptographyException;
import java.security.cert.CertificateException;
import org.apache.commons.codec.binary.Hex;
/**
*
* @author ssbangal
*/
public class AssetTagCertBO {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AssetTagCertBO.class);
public AssetTagCertBO() {
}
// public AssetTagCertBO(PersistenceManager pm) {
// super(pm);
// }
/**
* This functions stores a new asset tag certificate that was provisioned by the Asset tag
* provisioning service for a host.This certificate would be associated to the host for
* which it was provisioned only when that host gets registered with Mt.Wilson
* @param atagObj
* @return
*/
public boolean importAssetTagCertificate(AssetTagCertCreateRequest atagObj, String uuid) {
boolean result;
X509AttributeCertificate x509AttrCert;
try {
try {
x509AttrCert = X509AttributeCertificate.valueOf(atagObj.getCertificate());
} catch (IllegalArgumentException ce) {
log.error("Error during retrieval of a new asset tag certificate. Error Details - {}.", ce.getMessage());
throw new ASException(ce, ErrorCode.AS_INVALID_ASSET_TAG_CERTIFICATE, ce.getMessage());
}
MwAssetTagCertificate atagCert = new MwAssetTagCertificate();
if (uuid != null && !uuid.isEmpty())
atagCert.setUuid_hex(uuid);
else
atagCert.setUuid_hex(new UUID().toString());
atagCert.setCertificate(atagObj.getCertificate());
atagCert.setUuid(x509AttrCert.getSubject().toLowerCase());
atagCert.setNotAfter(x509AttrCert.getNotAfter());
atagCert.setNotBefore(x509AttrCert.getNotBefore());
atagCert.setRevoked(false);
//atagCert.setSHA1Hash(Sha1Digest.digestOf(atagObj.getCertificate()).toByteArray());
atagCert.setSHA1Hash(Sha1Digest.digestOf(x509AttrCert.getEncoded()).toByteArray());
log.debug("Certificate creation time is {}", x509AttrCert.getSerialNumber());
log.debug("Certificate SHA1 is {}", Sha1Digest.digestOf(x509AttrCert.getEncoded()).toHexString());
atagCert.setCreate_time(x509AttrCert.getSerialNumber());
//atagCert.setSHA256Hash(Sha256Digest.digestOf(atagObj.getCertificate()).toByteArray()); // not used with TPM 1.2
// We are just writing some default value here, which would be changed when the host would be mapped to this
// certificate.
//atagCert.setPCREvent(Sha1Digest.digestOf(atagCert.getSHA1Hash()).toByteArray());
Sha1Digest sha1D = Sha1Digest.digestOf(atagObj.getCertificate());
Sha1Digest expectedPcr = Sha1Digest.ZERO.extend( Sha1Digest.digestOf( sha1D.toBase64().getBytes() ) );
atagCert.setPCREvent(expectedPcr.toByteArray() );
log.debug("assetTag writing cert to DB");
My.jpa().mwAssetTagCertificate().create(atagCert);
result = true;
// here we need to check a config option, mtwilson.atag.associate.hosts.auto
// now try to match a host to it
log.debug("trying to associate tag to existing host using " + Hex.encodeHexString(atagCert.getSHA1Hash()));
AssetTagCertAssociateRequest request = new AssetTagCertAssociateRequest();
request.setSha1OfAssetCert(atagCert.getSHA1Hash());
//result =
mapAssetTagCertToHost(request);
} catch (ASException ase) {
log.error("Error during creation of a new asset tag certificate. Error Details - {}:{}.", ase.getErrorCode(), ase.getErrorMessage());
throw ase;
} catch (Exception ex) {
log.error("Unexpected error during creation of a new asset tag certificate. Error Details - {}.", ex.getMessage());
throw new ASException(ex);
}
return result;
}
/**
* This function would be used to associate a asset tag certificate with the host for which it is
* provisioned for. It does not require you know the ID of the host you are associating to.
* Here you are giving the hash of the cert to the code and letting it find a matching host
* @param atagObj
* @return true if host was found, false if not
*/
public boolean mapAssetTagCertToHost(AssetTagCertAssociateRequest atagObj) {
boolean result = false;
log.debug("mapAssetTagCertToHost");
AssetTagCertAssociateRequest request = new AssetTagCertAssociateRequest();
try {
My.initDataEncryptionKey(); // needed for connection string decryption
if (atagObj.getSha1OfAssetCert() != null) {
log.debug("trying to associate tag to existing host using " + Hex.encodeHexString(atagObj.getSha1OfAssetCert()));
List<MwAssetTagCertificate> atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificateBySha1Hash(atagObj.getSha1OfAssetCert());
// below code is for debugging.. we will delete it later.
// List<MwAssetTagCertificate> atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificatesByHostUUID("494cb5dc-a3e1-4e46-9b52-e694349b1654");
if (atagCerts.isEmpty() ) {
log.error("mapAssetTagCertToHost: The asset tag certificate does not exist");
throw new ASException(ErrorCode.AS_INVALID_ASSET_TAG_CERTIFICATE_HASH);
} else if (atagCerts.size() > 1) {
log.error("mapAssetTagCertToHost: There were multiple matches for the specified hash");
throw new ASException(ErrorCode.AS_INVALID_ASSET_TAG_CERTIFICATE_HASH);
} else {
MwAssetTagCertificate atagCert = atagCerts.get(0);
request.setSha1OfAssetCert(atagCert.getSHA1Hash());
String uuid = atagCert.getUuid().toLowerCase().trim();
log.debug("searching using " + uuid);
TblHosts tblHost = My.jpa().mwHosts().findByHwUUID(uuid);
if(tblHost != null) {
log.debug("found host matching uuid of cert, going to assoicate with host id = " + tblHost.getId());
request.setHostID(tblHost.getId());
//atagObj.setHostID(tblHost.getId());
result = mapAssetTagCertToHostById(request);
}else{
log.debug("found no matching uuid of cert");
result = false;
}
}
}
}catch(IOException | ASException | CryptographyException ex){
log.error("Unexpected error during mapping of host to the asset tag certificate. Error Details - {}.", ex.getMessage());
throw new ASException(ex);
}
return result;
}
/**
* This function would be used to associate a asset tag certificate with the host for which it is
* provisioned for. It requires you know the ID of the host it is to be associated with
* @param atagObj
* @return
*/
public boolean mapAssetTagCertToHostById(AssetTagCertAssociateRequest atagObj) {
boolean result;
log.debug("mapAssetTagCertToHostById");
// Before we map the asset tag cert to the host, we first need to unmap any associations if it already exists
try {
unmapAssetTagCertFromHostById(atagObj);
log.debug("Successfully unmapped the asset tag certificate assocation with host {}. ", atagObj.getHostID());
} catch (Exception ex) {
log.error("Error during unmap of asset tag cert from host with id {}. {}", atagObj.getHostID(), ex.getMessage());
}
try {
My.initDataEncryptionKey(); // needed for connection string decryption
// Find the asset tag certificate for the specified Sha256Hash value
if (atagObj.getSha1OfAssetCert() != null) {
List<MwAssetTagCertificate> atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificateBySha1Hash(atagObj.getSha1OfAssetCert());
// below code is for debugging.. we will delete it later.
// List<MwAssetTagCertificate> atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificatesByHostUUID("494cb5dc-a3e1-4e46-9b52-e694349b1654");
if (atagCerts.isEmpty() ) {
log.error("mapAssetTagCertToHostById: The asset tag certificate does not exist");
throw new ASException(ErrorCode.AS_INVALID_ASSET_TAG_CERTIFICATE_HASH);
}
else if( atagCerts.size() > 1) {
log.error("mapAssetTagCertToHostById: There were multiple matches for the specified hash");
throw new ASException(ErrorCode.AS_INVALID_ASSET_TAG_CERTIFICATE_HASH);
} else {
// Now that we have the asset tag identified, let us update the entry with the host ID for which it has
// to be associated.
MwAssetTagCertificate atagCert = atagCerts.get(0);
atagCert.setHostID(atagObj.getHostID());
// Now that the mapping is done, we need to calculate what the expected PCR value should be and put it in
// the PCREvent column.
Sha1Digest tag = Sha1Digest.digestOf(atagCert.getCertificate());
log.debug("mapAssetTagCertToHostById : Sha1 Hash of the certificate with UUID {} is {}.", atagCert.getUuid(), tag.toString());
Sha1Digest expectedHash = Sha1Digest.ZERO.extend(tag);
log.debug("mapAssetTagCertToHostById : Final expected PCR for the certificate with UUID {} is {}.", atagCert.getUuid(), expectedHash.toString());
atagCert.setPCREvent(expectedHash.toByteArray());
My.jpa().mwAssetTagCertificate().edit(atagCert);
result = true;
}
} else {
log.error("Sha1Hash for the asset tag is not specified.");
throw new ASException(ErrorCode.AS_INVALID_ASSET_TAG_CERTIFICATE_HASH);
}
} catch (ASException ase) {
log.error("Error during mapping of host to the asset tag certificate. Error Details - {}:{}.", ase.getErrorCode(), ase.getErrorMessage());
throw ase;
} catch (Exception ex) {
log.error("Unexpected error during mapping of host by id to the asset tag certificate. Error Details - {}.", ex.getMessage());
throw new ASException(ex);
}
return result;
}
/**
* This function removes the mapping between the host and the asset tag certificate. This needs to be
* instantiated when ever the host is deleted from Mt.Wilson.
*
* For removing the mapping, the user need not specify the sha256Hash value. Only the hostID would be
* enough.
*
* @param atagObj
* @return
*/
public boolean unmapAssetTagCertFromHostById(AssetTagCertAssociateRequest atagObj) {
boolean result = false;
try {
// Find the asset tag certificate for the specified Sha256Hash value
if (atagObj.getHostID() != 0) {
List<MwAssetTagCertificate> atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificatesByHostID(atagObj.getHostID());
if (atagCerts.isEmpty()) {
// There is nothing to unmap. So, we will just return back success
log.info("The host is currently not mapped to any asset tag certificate. So, nothing to unmap.");
return true;
} else {
// Now that we have the asset tag identified, let us remove the host mapping
// to be associated.
for (MwAssetTagCertificate atagTempCert : atagCerts){
// There is no need to validate during unmapping the asset tag request
// if (validateAssetTagCert(atagTempCert)) {
atagTempCert.setHostID(null);
My.jpa().mwAssetTagCertificate().edit(atagTempCert);
log.debug("Successfully upmapped the host with id {} from the asset tag certificate.", atagObj.getHostID());
return true;
//}
}
}
} else {
log.error("Host specified for the asset tag unmap request is not valid.");
throw new ASException(ErrorCode.AS_HOST_SPECIFIED_IS_CURRENTLY_NOT_MAPPED_TO_ASSET_TAG_CERTIFICATE);
}
} catch (ASException ase) {
log.error("Error during unmapping of the host from asset tag certificate. Error Details - {}:{}.", ase.getErrorCode(), ase.getErrorMessage());
throw ase;
} catch (Exception ex) {
log.error("Unexpected error during unmapping of the host from asset tag certificate. Error Details - {}.", ex.getMessage());
throw new ASException(ex);
}
return result;
}
/**
* Updates the asset tag certificate entry and sets the revoked flag to true so that this
* asset tag certificate will not be considered during attestation of the asset tag.
* @param atagObj
* @return
*/
public boolean revokeAssetTagCertificate(AssetTagCertRevokeRequest atagObj, String uuid) {
boolean result;
List<MwAssetTagCertificate> atagCerts;
try {
// Find the asset tag certificate for the specified Sha256Hash value
if (uuid != null && !uuid.isEmpty()) {
log.debug("UUID {} is specified for revoking the asset tag certificate", uuid);
atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificatesByUuid(uuid);
} else if (atagObj.getSha1OfAssetCert() != null) {
log.error("SHA1 {} is specified for revoking the asset tag certificate", atagObj.getSha1OfAssetCert());
atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificateBySha1Hash(atagObj.getSha1OfAssetCert());
} else {
log.error("Sha1 for the asset tag is not specified.");
throw new ASException(ErrorCode.AS_INVALID_ASSET_TAG_CERTIFICATE_HASH);
}
if (atagCerts.isEmpty() || atagCerts.size() > 1) {
log.warn("Either the asset tag certificate does not exist or there were multiple matches for the specified hash.");
// throw new ASException(ErrorCode.AS_INVALID_ASSET_TAG_CERTIFICATE_HASH);
result = true;
} else {
// Now that we have the asset tag identified, set the revoked flag to true.
MwAssetTagCertificate atagCert = atagCerts.get(0);
atagCert.setRevoked(true);
My.jpa().mwAssetTagCertificate().edit(atagCert);
result = true;
}
} catch (ASException ase) {
log.error("Error during revocation of the asset tag certificate. Error Details - {}:{}.", ase.getErrorCode(), ase.getErrorMessage());
throw ase;
} catch (Exception ex) {
log.error("Unexpected error during revocation of the new asset tag certificate. Error Details - {}.", ex.getMessage());
throw new ASException(ex);
}
return result;
}
/**
* Finds a valid asset tag certificate for the specified host.
* @param uuid
* @return
*/
public MwAssetTagCertificate findValidAssetTagCertForHost(String uuid){
uuid = uuid.replace("\n", "");
try {
// Find the asset tag certificates for the specified UUID of the host. Not that this might return back multiple
// values. We need to evaluate each of the certificates to make sure that they are valid
// The below query has been modified to return back the results ordered by the insert date with the latest one first
// So if the host has been provisioned multiple times, we will pick up the latest one.
if (uuid != null && !uuid.isEmpty()) {
List<MwAssetTagCertificate> atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificatesByHostUUID(uuid.toLowerCase());
if (atagCerts.isEmpty()) {
log.info("Asset tag certificate has not been provisioned for the host with UUID : {}.", uuid);
return null;
} else {
// For each of the asset tag certs that are returned back, we need to validate the certificate first.
for (MwAssetTagCertificate atagTempCert : atagCerts){
if (validateAssetTagCert(atagTempCert)) {
log.debug("Valid asset tag certificate found for host with UUID {}.", uuid);
return atagTempCert;
}
}
log.info("No valid asset tag certificate found for host with UUID {}.", uuid);
return null;
}
} else {
log.error("UUID specified for the host is not valid.");
throw new ASException(ErrorCode.AS_HOST_NOT_FOUND);
}
} catch (ASException ase) {
log.error("Error during querying of valid asset tag certificate. Error Details - {}:{}.", ase.getErrorCode(), ase.getErrorMessage());
throw ase;
} catch (Exception ex) {
log.error("Unexpected error during querying of valid asset tag certificate. Error Details - {}.", ex.getMessage());
throw new ASException(ex);
}
}
public MwAssetTagCertificate findValidAssetTagCertForHost(Integer hostID){
try {
// Find the asset tag certificates for the specified UUID of the host. Note that this might return back multiple
// values. We need to evaluate each of the certificates to make sure that they are valid
// The below query has been modified to return back the results ordered by the insert date with the latest one first
// So if the host has been provisioned multiple times, we will pick up the latest one.
if (hostID != 0) {
List<MwAssetTagCertificate> atagCerts = My.jpa().mwAssetTagCertificate().findAssetTagCertificatesByHostID(hostID);
if (atagCerts.isEmpty()) {
log.info("Asset tag certificate has not been provisioned for the host with ID : {}.", hostID);
return null;
} else {
// For each of the asset tag certs that are returned back, we need to validate the certificate first.
// Ideally there should be only one that is valid.
for (MwAssetTagCertificate atagTempCert : atagCerts){
if (validateAssetTagCert(atagTempCert)) {
log.debug("Valid asset tag certificate found for host with ID {}.", hostID);
return atagTempCert;
}
}
log.info("No valid asset tag certificate found for host with ID {}.", hostID);
}
} else {
log.error("ID specified for the host is not valid.");
throw new ASException(ErrorCode.AS_HOST_NOT_FOUND);
}
} catch (ASException ase) {
log.error("Error during querying of valid asset tag certificate using host ID. Error Details - {}:{}.", ase.getErrorCode(), ase.getErrorMessage());
throw ase;
} catch (Exception ex) {
log.error("Unexpected error during querying of valid asset tag certificate using host ID. Error Details - {}.", ex.getMessage());
throw new ASException(ex);
}
return null;
}
/**
* Validates the asset tag certificate and returns back true/false accordingly.
*
* @param atagObj
* @return
*/
private boolean validateAssetTagCert(MwAssetTagCertificate atagObj){
boolean isValid = false;
try {
// First let us verify if the revoked flag is set
if (atagObj.getRevoked() == true)
return false;
// X509AttributeCertificate provides a helper function that validates both the dates and the signature.
// For that we need to first get the CA certificate that signed the Attribute Certificate. We need to
// extract this from the PEM file list and pass it to the helper function
X509AttributeCertificate atagAttrCertForHost = X509AttributeCertificate.valueOf(atagObj.getCertificate());
List<X509Certificate> atagCaCerts = null;
try (InputStream atagCaIn = new FileInputStream(My.configuration().getAssetTagCaCertificateFile())) {
atagCaCerts = X509Util.decodePemCertificates(IOUtils.toString(atagCaIn));
//IOUtils.closeQuietly(atagCaIn);
log.debug("Added {} certificates from AssetTagCA.pem", atagCaCerts.size());
} catch(IOException | CertificateException ex) {
log.error("Error loading the Asset Tag pem file to extract the CA certificate(s).",ex);
}
// The below isValid function verifies both the signature and the dates.
if (atagCaCerts != null ) {
for (X509Certificate atagCACert : atagCaCerts) {
if (atagAttrCertForHost.isValid(atagCACert))
return true;
}
}
} catch (Exception ex) {
throw new ASException (ex);
}
return isValid;
}
protected static final ObjectMapper mapper = new ObjectMapper();
private <T> T fromJSON(String document, Class<T> valueType) throws IOException, ApiException {
try {
return mapper.readValue(document, valueType);
}
catch(com.fasterxml.jackson.core.JsonParseException e) {
throw new ApiException("Cannot parse response", e);
}
}
public TagDataType getTagInfoByOID(String oid) throws IOException, ApiException, NoSuchAlgorithmException, KeyManagementException, SignatureException {
log.error("attempting to connect to asset tag host");
String requestURL = My.configuration().getAssetTagServerURL() + "/tags?oidEqualTo="+oid;
//1.3.6.1.4.1.99999.3";
ApacheHttpClient client = new ApacheHttpClient(My.configuration().getAssetTagServerURL(), new ApacheBasicHttpAuthorization(new UsernamePasswordCredentials(My.configuration().getAssetTagApiUsername(),My.configuration().getAssetTagApiPassword())), null, new InsecureTlsPolicy());
//ApiRequest request = new ApiRequest(MediaType., "");
ApiResponse response = client.get(requestURL);
String str = new String(response.content);
System.out.println("getTagInfoByOID response = " + str);
TagDataType[] tag = fromJSON(str, TagDataType[].class);
if(tag == null || tag[0] == null)
throw new ApiException("Error while getting tag from server");
return tag[0];
}
}