mirror of
https://github.com/nsacyber/HIRS.git
synced 2024-12-18 20:47:58 +00:00
Merge remote-tracking branch 'origin/main' into v3_issue_612-integrationtest
This commit is contained in:
commit
e2507d5641
19
.ci/docker/Dockerfile.ubuntu22ci
Normal file
19
.ci/docker/Dockerfile.ubuntu22ci
Normal file
@ -0,0 +1,19 @@
|
||||
FROM ubuntu:22.04
|
||||
|
||||
RUN apt-get update -y && apt-get upgrade -y && apt-get clean -y
|
||||
|
||||
# Install packages for building HIRS ACA
|
||||
RUN apt-get -y install openjdk-17-jdk mariadb-server
|
||||
RUN apt-get -y install git curl nano cron
|
||||
|
||||
# Ports needed for system-level tests
|
||||
EXPOSE 8080
|
||||
EXPOSE 8443
|
||||
|
||||
# Checkout HIRS main branch and run gradlew to install gradlew dependencies, then delete HIRS
|
||||
# Use '--depth=1' so as to not download the history of all commits
|
||||
RUN git clone -b main --depth=1 https://github.com/nsacyber/HIRS.git /hirsTemp
|
||||
WORKDIR "/hirsTemp"
|
||||
RUN /bin/bash -c './gradlew clean build'
|
||||
WORKDIR "/"
|
||||
RUN rm -rf /hirsTemp
|
69
.github/workflows/hirs_package_linux.yml
vendored
Normal file
69
.github/workflows/hirs_package_linux.yml
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
name: HIRS build and packages for Linux
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*v3*'
|
||||
- 'main'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
# run the package script for HIRS ACA, Provisioners, tcg_rim_tool, and tcg_eventlog_tool
|
||||
Package_linux:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
|
||||
settings-path: ${{ github.workspace }} # location for the settings.xml file
|
||||
- name: directory setup
|
||||
run: |
|
||||
mkdir -p artifacts/jars
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install git curl nano cron mariadb-server
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Execute Gradle build
|
||||
run: |
|
||||
./gradlew build;
|
||||
./gradlew bootWar;
|
||||
./gradlew buildDeb;
|
||||
./gradlew buildRpm;
|
||||
cp HIRS_AttestationCAPortal/build/libs/*.jar artifacts/jars/.
|
||||
cp HIRS_AttestationCA/build/libs/*.jar artifacts/jars/.
|
||||
cp HIRS_Utils/build/libs/*.jar artifacts/jars/.
|
||||
cp HIRS_Structs/build/libs/*.jar artifacts/jars/.
|
||||
- name: Archive RPM files
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: RPM_Files
|
||||
path: HIRS_AttestationCAPortal/build/distributions/*.rpm
|
||||
if-no-files-found: error
|
||||
- name: Archive DEB files
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: DEB_Files
|
||||
path: HIRS_AttestationCAPortal/build/distributions/*.deb
|
||||
if-no-files-found: error
|
||||
- name: War files
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: WAR_Files
|
||||
path: HIRS_AttestationCAPortal/build/libs/HIRS_AttestationCAPortal.war
|
||||
if-no-files-found: error
|
||||
- name: JAR_Files
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: JAR_Files
|
||||
path: artifacts/jars/
|
||||
if-no-files-found: error
|
||||
|
@ -37,6 +37,7 @@ dependencies {
|
||||
implementation libs.jakarta.api
|
||||
implementation libs.jakarta.xml
|
||||
implementation libs.hibernate.core
|
||||
implementation libs.pci
|
||||
implementation libs.guava
|
||||
implementation libs.jackson.core
|
||||
implementation libs.jackson.databind
|
||||
|
@ -8,9 +8,8 @@ import hirs.attestationca.persist.entity.manager.PolicyRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
|
||||
import hirs.attestationca.persist.entity.manager.TPM2ProvisionerStateRepository;
|
||||
import hirs.attestationca.persist.provision.CertificateRequestHandler;
|
||||
import hirs.attestationca.persist.provision.IdentityClaimHandler;
|
||||
import hirs.attestationca.persist.provision.IdentityRequestHandler;
|
||||
import hirs.attestationca.persist.provision.CertificateRequestProcessor;
|
||||
import hirs.attestationca.persist.provision.IdentityClaimProcessor;
|
||||
import hirs.attestationca.persist.service.SupplyChainValidationService;
|
||||
import hirs.structs.converters.StructConverter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
@ -62,9 +61,8 @@ public abstract class AttestationCertificateAuthority {
|
||||
private final PolicyRepository policyRepository;
|
||||
private final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository;
|
||||
|
||||
private CertificateRequestHandler certificateRequestHandler;
|
||||
private IdentityClaimHandler identityClaimHandler;
|
||||
private IdentityRequestHandler identityRequestHandler;
|
||||
private CertificateRequestProcessor certificateRequestHandler;
|
||||
private IdentityClaimProcessor identityClaimHandler;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -109,19 +107,13 @@ public abstract class AttestationCertificateAuthority {
|
||||
this.policyRepository = policyRepository;
|
||||
this.tpm2ProvisionerStateRepository = tpm2ProvisionerStateRepository;
|
||||
|
||||
this.certificateRequestHandler = new CertificateRequestHandler(supplyChainValidationService,
|
||||
this.certificateRequestHandler = new CertificateRequestProcessor(supplyChainValidationService,
|
||||
certificateRepository, deviceRepository,
|
||||
privateKey, acaCertificate, validDays, tpm2ProvisionerStateRepository);
|
||||
this.identityClaimHandler = new IdentityClaimHandler(supplyChainValidationService,
|
||||
this.identityClaimHandler = new IdentityClaimProcessor(supplyChainValidationService,
|
||||
certificateRepository, referenceManifestRepository,
|
||||
referenceDigestValueRepository,
|
||||
deviceRepository, tpm2ProvisionerStateRepository, policyRepository);
|
||||
this.identityRequestHandler = new IdentityRequestHandler(structConverter, certificateRepository,
|
||||
deviceRepository, supplyChainValidationService, privateKey, validDays, acaCertificate);
|
||||
}
|
||||
|
||||
byte[] processIdentityRequest(final byte[] identityRequest) {
|
||||
return this.identityRequestHandler.processIdentityRequest(identityRequest);
|
||||
}
|
||||
|
||||
byte[] processIdentityClaimTpm2(final byte[] identityClaim) {
|
||||
|
@ -1,223 +0,0 @@
|
||||
package hirs.attestationca.persist;
|
||||
|
||||
import hirs.attestationca.persist.entity.userdefined.PolicySettings;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
/**
|
||||
* The class handles the flags that ignore certain PCRs for validation.
|
||||
*/
|
||||
@Log4j2
|
||||
@NoArgsConstructor
|
||||
public class PCRQuoteValidator {
|
||||
|
||||
/**
|
||||
* Minimum possible value for a PCR ID. This is 0.
|
||||
*/
|
||||
public static final int MIN_PCR_ID = 0;
|
||||
|
||||
/**
|
||||
* Maximum possible value for a PCR ID. This is 23.
|
||||
*/
|
||||
public static final int MAX_PCR_ID = 23;
|
||||
|
||||
private static final int NUM_TO_SKIP = 1;
|
||||
private static final int NUM_OF_TBOOT_PCR = 3;
|
||||
// PCR 5-16
|
||||
private static final int PXE_PCR_START = 5;
|
||||
private static final int PXE_PCR_END = 16;
|
||||
// PCR 10
|
||||
private static final int IMA_PCR = 10;
|
||||
// PCR 17-19
|
||||
private static final int TBOOT_PCR_START = 17;
|
||||
private static final int TBOOT_PCR_END = 19;
|
||||
// PCR 5
|
||||
private static final int GPT_PCR = 5;
|
||||
private static final int IMA_MASK = 0xfffbff;
|
||||
|
||||
// Event Log Event Types
|
||||
private static final String EVT_EFI_BOOT = "EV_EFI_BOOT_SERVICES_APPLICATION";
|
||||
private static final String EVT_EFI_VAR = "EV_EFI_VARIABLE_BOOT";
|
||||
private static final String EVT_EFI_GPT = "EV_EFI_GPT_EVENT";
|
||||
private static final String EVT_EFI_CFG = "EV_EFI_VARIABLE_DRIVER_CONFIG";
|
||||
|
||||
private String[] baselinePCRS = new String[MAX_PCR_ID + 1];
|
||||
@Getter
|
||||
@Setter
|
||||
private PolicySettings settings;
|
||||
|
||||
/**
|
||||
* Constructor to parse PCR values.
|
||||
* @param pcrValues pcrValues RIM provided baseline PCRs
|
||||
* @param settings settings for the supply chain portal settings for provisioning
|
||||
*/
|
||||
public PCRQuoteValidator(final String[] pcrValues,
|
||||
final PolicySettings settings) {
|
||||
if (pcrValues != null) {
|
||||
baselinePCRS = new String[MAX_PCR_ID + 1];
|
||||
for (int i = 0; i <= MAX_PCR_ID; i++) {
|
||||
baselinePCRS[i] = pcrValues[i];
|
||||
}
|
||||
}
|
||||
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the array of baseline PCRs.
|
||||
* @return instance of the PCRs.
|
||||
*/
|
||||
public String[] getBaselinePCRS() {
|
||||
return baselinePCRS.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the array of baseline PCRs.
|
||||
* @param baselinePCRS instance of the PCRs.
|
||||
*/
|
||||
public void setBaselinePCRS(final String[] baselinePCRS) {
|
||||
this.baselinePCRS = baselinePCRS.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the baseline pcr list and the quote pcr list. If the
|
||||
* ignore flags are set, 10 and 17-19 will be skipped for comparison.
|
||||
*
|
||||
* @param storedPCRS non-baseline pcr list
|
||||
* @return a StringBuilder that is empty if everything passes.
|
||||
*/
|
||||
public StringBuilder validatePCRS(final String[] storedPCRS) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String failureMsg = "PCR %d does not match%n";
|
||||
if (storedPCRS[0] == null || storedPCRS[0].isEmpty()) {
|
||||
sb.append("failureMsg");
|
||||
} else {
|
||||
for (int i = 0; i <= MAX_PCR_ID; i++) {
|
||||
if (settings.isIgnoreImaEnabled() && i == IMA_PCR) {
|
||||
log.info("PCR Policy IMA Ignore enabled.");
|
||||
i += NUM_TO_SKIP;
|
||||
}
|
||||
|
||||
if (settings.isIgnoretBootEnabled() && i == TBOOT_PCR_START) {
|
||||
log.info("PCR Policy TBoot Ignore enabled.");
|
||||
i += NUM_OF_TBOOT_PCR;
|
||||
}
|
||||
|
||||
if (settings.isIgnoreGptEnabled() && i == GPT_PCR) {
|
||||
log.info("PCR Policy GPT Ignore enabled.");
|
||||
i += NUM_TO_SKIP;
|
||||
}
|
||||
|
||||
if (!baselinePCRS[i].equals(storedPCRS[i])) {
|
||||
//error
|
||||
log.error(String.format("%s =/= %s", baselinePCRS[i], storedPCRS[i]));
|
||||
sb.append(String.format(failureMsg, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the expected FM events occurring. There are policy options that
|
||||
* will ignore certain PCRs, Event Types and Event Variables present.
|
||||
* @param tcgMeasurementLog Measurement log from the client
|
||||
* @param eventValueMap The events stored as baseline to compare
|
||||
* @return the events that didn't pass
|
||||
*/
|
||||
// public List<TpmPcrEvent> validateTpmEvents(final TCGEventLog tcgMeasurementLog,
|
||||
// final Map<String, ReferenceDigestValue> eventValueMap) {
|
||||
// List<TpmPcrEvent> tpmPcrEvents = new LinkedList<>();
|
||||
// for (TpmPcrEvent tpe : tcgMeasurementLog.getEventList()) {
|
||||
// if (enableIgnoreIma && tpe.getPcrIndex() == IMA_PCR) {
|
||||
// log.info(String.format("IMA Ignored -> %s", tpe));
|
||||
// } else if (enableIgnoretBoot && (tpe.getPcrIndex() >= TBOOT_PCR_START
|
||||
// && tpe.getPcrIndex() <= TBOOT_PCR_END)) {
|
||||
// log.info(String.format("TBOOT Ignored -> %s", tpe));
|
||||
// } else if (enableIgnoreOsEvt && (tpe.getPcrIndex() >= PXE_PCR_START
|
||||
// && tpe.getPcrIndex() <= PXE_PCR_END)) {
|
||||
// log.info(String.format("OS Evt Ignored -> %s", tpe));
|
||||
// } else {
|
||||
// if (enableIgnoreGpt && tpe.getEventTypeStr().contains(EVT_EFI_GPT)) {
|
||||
// log.info(String.format("GPT Ignored -> %s", tpe));
|
||||
// } else if (enableIgnoreOsEvt && (tpe.getEventTypeStr().contains(EVT_EFI_BOOT)
|
||||
// || tpe.getEventTypeStr().contains(EVT_EFI_VAR))) {
|
||||
// log.info(String.format("OS Evt Ignored -> %s", tpe));
|
||||
// } else if (enableIgnoreOsEvt && (tpe.getEventTypeStr().contains(EVT_EFI_CFG)
|
||||
// && tpe.getEventContentStr().contains("SecureBoot"))) {
|
||||
// log.info(String.format("OS Evt Config Ignored -> %s", tpe));
|
||||
// } else {
|
||||
// if (!eventValueMap.containsKey(tpe.getEventDigestStr())) {
|
||||
// tpmPcrEvents.add(tpe);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return tpmPcrEvents;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Compares hashes to validate the quote from the client.
|
||||
*
|
||||
* @param tpmQuote the provided quote
|
||||
* @param storedPCRS values from the RIM file
|
||||
* @return true if validated, false if not
|
||||
*/
|
||||
// public boolean validateQuote(final byte[] tpmQuote, final String[] storedPCRS) {
|
||||
// System.out.println("Validating quote from associated device.");
|
||||
// boolean validated = false;
|
||||
// short localityAtRelease = 0;
|
||||
// String quoteString = new String(tpmQuote, StandardCharsets.UTF_8);
|
||||
// int pcrMaskSelection = PcrSelection.ALL_PCRS_ON;
|
||||
//
|
||||
// if (enableIgnoreIma) {
|
||||
// pcrMaskSelection = IMA_MASK;
|
||||
// }
|
||||
//
|
||||
// ArrayList<TPMMeasurementRecord> measurements = new ArrayList<>();
|
||||
//
|
||||
// try {
|
||||
// for (int i = 0; i < storedPcrs.length; i++) {
|
||||
// if (i == IMA_PCR && enableIgnoreIma) {
|
||||
// log.info("Ignore IMA PCR policy is enabled.");
|
||||
// } else {
|
||||
// measurements.add(new TPMMeasurementRecord(i, storedPcrs[i]));
|
||||
// }
|
||||
// }
|
||||
// } catch (DecoderException deEx) {
|
||||
// //error
|
||||
// System.out.println(deEx);
|
||||
// }
|
||||
//
|
||||
// PcrSelection pcrSelection = new PcrSelection(pcrMaskSelection);
|
||||
// PcrComposite pcrComposite = new PcrComposite(pcrSelection);
|
||||
// PcrInfoShort pcrInfoShort = new PcrInfoShort(pcrSelection,
|
||||
// localityAtRelease,
|
||||
// tpmQuote, pcrComposite);
|
||||
//
|
||||
// try {
|
||||
// /**
|
||||
// * The calculated string is being used in the contains method
|
||||
// * because the TPM Quote's hash isn't just for PCR values,
|
||||
// * it contains the calculated digest of the PCRs, along with
|
||||
// * other information.
|
||||
// */
|
||||
// String calculatedString = Hex.encodeHexString(
|
||||
// pcrInfoShort.getCalculatedDigest());
|
||||
// validated = quoteString.contains(calculatedString);
|
||||
// if (!validated) {
|
||||
// // warn
|
||||
// System.out.println(calculatedString + " not found in " + quoteString);
|
||||
// }
|
||||
// } catch (NoSuchAlgorithmException naEx) {
|
||||
// // error
|
||||
// System.out.println(naEx);
|
||||
// }
|
||||
//
|
||||
// return validated;
|
||||
// }
|
||||
}
|
@ -71,28 +71,6 @@ public class RestfulAttestationCertificateAuthority extends AttestationCertifica
|
||||
referenceDigestValueRepository, policyRepository, tpm2ProvisionerStateRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a given IdentityRequestEnvelope and
|
||||
* generates a IdentityResponseEnvelope. In most cases,
|
||||
* a client will generate the request using the TPM "Collate Identity" process.
|
||||
*
|
||||
* Wrap the {@link AttestationCertificateAuthority#processIdentityRequest(byte[])}
|
||||
* with a Spring {@link org.springframework.web.bind.annotation.RequestMapping}. Effectively, this method then will allow spring to
|
||||
* serialize and deserialize the request and responses on method invocation and
|
||||
* return, respectively.
|
||||
*
|
||||
* @param identityRequest generated during the collate identity process with a Tpm
|
||||
* @return response for the request
|
||||
*/
|
||||
@Override
|
||||
@ResponseBody
|
||||
@RequestMapping(value = "/identity-request/process",
|
||||
method = RequestMethod.POST,
|
||||
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
public byte[] processIdentityRequest(@RequestBody final byte[] identityRequest) {
|
||||
return super.processIdentityRequest(identityRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for identity requests from TPM 2.0 provisioning.
|
||||
*
|
||||
|
@ -5,8 +5,6 @@ package hirs.attestationca.persist;
|
||||
*/
|
||||
public interface RestfulInterface {
|
||||
|
||||
byte[] processIdentityRequest(byte[] identityRequest);
|
||||
|
||||
byte[] processIdentityClaimTpm2(byte[] identityClaim);
|
||||
|
||||
byte[] processCertificateRequest(byte[] certificateRequest);
|
||||
|
@ -40,7 +40,7 @@ public abstract class AbstractEntity implements Serializable {
|
||||
@Column (name = "create_time")
|
||||
@ColumnDefault(value = "CURRENT_TIMESTAMP")
|
||||
@Generated(GenerationTime.INSERT)
|
||||
private Date createTime;
|
||||
private Date createTime = new Date();
|
||||
|
||||
/**
|
||||
* Default empty constructor is required for Hibernate. It is protected to
|
||||
@ -67,6 +67,9 @@ public abstract class AbstractEntity implements Serializable {
|
||||
* @return creation time
|
||||
*/
|
||||
public Date getCreateTime() {
|
||||
if (createTime == null) {
|
||||
createTime = new Date();
|
||||
}
|
||||
return (Date) createTime.clone();
|
||||
}
|
||||
|
||||
|
@ -17,4 +17,5 @@ public interface CACredentialRepository extends JpaRepository<CertificateAuthori
|
||||
List<CertificateAuthorityCredential> findBySubject(String subject);
|
||||
List<CertificateAuthorityCredential> findBySubjectSorted(String subject);
|
||||
CertificateAuthorityCredential findBySubjectKeyIdentifier(byte[] subjectKeyIdentifier);
|
||||
CertificateAuthorityCredential findBySubjectKeyIdString(String subjectKeyIdString);
|
||||
}
|
||||
|
@ -11,12 +11,9 @@ import java.util.UUID;
|
||||
@Repository
|
||||
public interface ReferenceDigestValueRepository extends JpaRepository<ReferenceDigestValue, UUID> {
|
||||
|
||||
@Query(value = "SELECT * FROM ReferenceDigestValue", nativeQuery = true)
|
||||
List<ReferenceDigestValue> listAll();
|
||||
List<ReferenceDigestValue> findByModel(String model);
|
||||
List<ReferenceDigestValue> findByManufacturer(String manufacturer);
|
||||
@Query(value = "SELECT * FROM ReferenceDigestValue WHERE baseRimId = '?1' OR supportRimId = '?1'", nativeQuery = true)
|
||||
List<ReferenceDigestValue> getValuesByRimId(UUID associatedRimId);
|
||||
List<ReferenceDigestValue> findValuesByBaseRimId(UUID associatedRimId);
|
||||
List<ReferenceDigestValue> findBySupportRimId(UUID supportRimId);
|
||||
List<ReferenceDigestValue> findBySupportRimHash(String supportRimHash);
|
||||
List<ReferenceDigestValue> findByManufacturerAndModel(String manufacturer, String model);
|
||||
|
@ -39,4 +39,6 @@ public interface ReferenceManifestRepository extends JpaRepository<ReferenceMani
|
||||
EventLogMeasurements byMeasurementDeviceName(String deviceName);
|
||||
@Query(value = "SELECT * FROM ReferenceManifest WHERE platformManufacturer = ?1 AND platformModel = ?2 AND rimType = 'Support'", nativeQuery = true)
|
||||
List<SupportReferenceManifest> getSupportByManufacturerModel(String manufacturer, String model);
|
||||
@Query(value = "SELECT * FROM ReferenceManifest WHERE platformModel = ?1 AND DTYPE = 'EventLogMeasurements'", nativeQuery = true)
|
||||
EventLogMeasurements getLogByModel(String model);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import java.util.Date;
|
||||
@NoArgsConstructor
|
||||
@Entity
|
||||
public class TPM2ProvisionerState {
|
||||
private static final int MAX_BLOB_SIZE = 65535;
|
||||
private static final int MAX_BLOB_SIZE = 16777215;
|
||||
|
||||
@Id
|
||||
private Long firstPartOfNonce;
|
||||
@ -88,7 +88,7 @@ public class TPM2ProvisionerState {
|
||||
/**
|
||||
* Convenience method for finding the {@link TPM2ProvisionerState} associated with the nonce.
|
||||
*
|
||||
* @param TPM2ProvisionerStateRepository the {@link TPM2ProvisionerStateRepository} to use when looking for the
|
||||
* @param tpm2ProvisionerStateRepository the {@link TPM2ProvisionerStateRepository} to use when looking for the
|
||||
* {@link TPM2ProvisionerState}
|
||||
* @param nonce the nonce to use as the key for the {@link TPM2ProvisionerState}
|
||||
* @return the {@link TPM2ProvisionerState} associated with the nonce;
|
||||
|
@ -58,6 +58,16 @@ public class Device extends AbstractEntity {
|
||||
@Column(name = "summary_id")
|
||||
private String summaryId;
|
||||
|
||||
public Device(final DeviceInfoReport deviceInfoReport) {
|
||||
super();
|
||||
if (deviceInfoReport != null) {
|
||||
this.name = deviceInfoReport.getNetworkInfo().getHostname();
|
||||
this.deviceInfo = deviceInfoReport;
|
||||
} else {
|
||||
name = "";
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return String.format("Device Name: %s%nStatus: %s%nSummary: %s",
|
||||
name, healthStatus.getStatus(),
|
||||
|
@ -176,6 +176,8 @@ public class PlatformCredential extends DeviceAssociatedCertificate {
|
||||
|
||||
@Column(length = MAX_MESSAGE_LENGTH)
|
||||
private String componentFailures = Strings.EMPTY;
|
||||
@Column(length = MAX_MESSAGE_LENGTH)
|
||||
private String componentFailureMessage = Strings.EMPTY;
|
||||
|
||||
@Transient
|
||||
private EndorsementCredential endorsementCredential = null;
|
||||
|
@ -30,7 +30,7 @@ public class ComponentClass {
|
||||
private static final String TCG_COMPONENT_REGISTRY = "2.23.133.18.3.1";
|
||||
private static final String SMBIOS_COMPONENT_REGISTRY = "2.23.133.18.3.3";
|
||||
private static final Path JSON_PATH = FileSystems.getDefault()
|
||||
.getPath("/etc", "hirs/aca", "default-properties", "component-class.json");
|
||||
.getPath("/etc", "hirs", "aca", "default-properties", "component-class.json");
|
||||
|
||||
private static final String OTHER_STRING = "Other";
|
||||
private static final String UNKNOWN_STRING = "Unknown";
|
||||
|
@ -60,5 +60,4 @@ public class AppraisalStatus {
|
||||
this.message = message;
|
||||
this.additionalInfo = additionalInfo;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ import java.util.List;
|
||||
|
||||
@Log4j2
|
||||
@NoArgsConstructor
|
||||
public class AbstractRequestHandler {
|
||||
public class AbstractProcessor {
|
||||
|
||||
@Getter
|
||||
private int validDays;
|
||||
@ -51,8 +51,8 @@ public class AbstractRequestHandler {
|
||||
@Getter
|
||||
private PolicyRepository policyRepository;
|
||||
|
||||
public AbstractRequestHandler(final PrivateKey privateKey,
|
||||
final int validDays) {
|
||||
public AbstractProcessor(final PrivateKey privateKey,
|
||||
final int validDays) {
|
||||
this.privateKey = privateKey;
|
||||
this.validDays = validDays;
|
||||
}
|
||||
@ -137,7 +137,8 @@ public class AbstractRequestHandler {
|
||||
if (identityClaim.hasEndorsementCredential()) {
|
||||
endorsementCredential = CredentialManagementHelper.storeEndorsementCredential(
|
||||
certificateRepository,
|
||||
identityClaim.getEndorsementCredential().toByteArray());
|
||||
identityClaim.getEndorsementCredential().toByteArray(),
|
||||
identityClaim.getDv().getNw().getHostname());
|
||||
} else if (ekPub != null) {
|
||||
log.warn("Endorsement Cred was not in the identity claim from the client."
|
||||
+ " Checking for uploads.");
|
||||
@ -233,8 +234,8 @@ public class AbstractRequestHandler {
|
||||
final Device device) {
|
||||
IssuedAttestationCertificate issuedAc;
|
||||
boolean generateCertificate = true;
|
||||
PolicyRepository scp = this.getPolicyRepository();
|
||||
PolicySettings policySettings = scp.findByName("Default");
|
||||
PolicyRepository scp = getPolicyRepository();
|
||||
PolicySettings policySettings;
|
||||
Date currentDate = new Date();
|
||||
int days;
|
||||
try {
|
||||
@ -243,6 +244,7 @@ public class AbstractRequestHandler {
|
||||
derEncodedAttestationCertificate, endorsementCredential, platformCredentials);
|
||||
|
||||
if (scp != null) {
|
||||
policySettings = scp.findByName("Default");
|
||||
issuedAc = certificateRepository.findByDeviceId(device.getId());
|
||||
|
||||
generateCertificate = policySettings.isIssueAttestationCertificate();
|
||||
@ -260,6 +262,7 @@ public class AbstractRequestHandler {
|
||||
}
|
||||
}
|
||||
if (generateCertificate) {
|
||||
attCert.setDeviceId(device.getId());
|
||||
attCert.setDeviceName(device.getName());
|
||||
certificateRepository.save(attCert);
|
||||
}
|
@ -27,7 +27,7 @@ import java.security.interfaces.RSAPublicKey;
|
||||
import java.util.List;
|
||||
|
||||
@Log4j2
|
||||
public class CertificateRequestHandler extends AbstractRequestHandler {
|
||||
public class CertificateRequestProcessor extends AbstractProcessor {
|
||||
|
||||
private SupplyChainValidationService supplyChainValidationService;
|
||||
private CertificateRepository certificateRepository;
|
||||
@ -42,13 +42,13 @@ public class CertificateRequestHandler extends AbstractRequestHandler {
|
||||
* @param validDays int for the time in which a certificate is valid.
|
||||
* @param tpm2ProvisionerStateRepository db connector for provisioner state.
|
||||
*/
|
||||
public CertificateRequestHandler(final SupplyChainValidationService supplyChainValidationService,
|
||||
final CertificateRepository certificateRepository,
|
||||
final DeviceRepository deviceRepository,
|
||||
final PrivateKey privateKey,
|
||||
final X509Certificate acaCertificate,
|
||||
final int validDays,
|
||||
final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository) {
|
||||
public CertificateRequestProcessor(final SupplyChainValidationService supplyChainValidationService,
|
||||
final CertificateRepository certificateRepository,
|
||||
final DeviceRepository deviceRepository,
|
||||
final PrivateKey privateKey,
|
||||
final X509Certificate acaCertificate,
|
||||
final int validDays,
|
||||
final TPM2ProvisionerStateRepository tpm2ProvisionerStateRepository) {
|
||||
super(privateKey, validDays);
|
||||
this.supplyChainValidationService = supplyChainValidationService;
|
||||
this.certificateRepository = certificateRepository;
|
||||
@ -198,6 +198,7 @@ public class CertificateRequestHandler extends AbstractRequestHandler {
|
||||
* @return the {@link AppraisalStatus} of the supply chain validation
|
||||
*/
|
||||
private AppraisalStatus.Status doQuoteValidation(final Device device) {
|
||||
log.info("Beginning Quote Validation...");
|
||||
// perform supply chain validation
|
||||
SupplyChainValidationSummary scvs = supplyChainValidationService.validateQuote(
|
||||
device);
|
@ -57,7 +57,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Log4j2
|
||||
public class IdentityClaimHandler extends AbstractRequestHandler {
|
||||
public class IdentityClaimProcessor extends AbstractProcessor {
|
||||
private static final String PCR_QUOTE_MASK = "0,1,2,3,4,5,6,7,8,9,10,11,12,13,"
|
||||
+ "14,15,16,17,18,19,20,21,22,23";
|
||||
|
||||
@ -78,7 +78,7 @@ public class IdentityClaimHandler extends AbstractRequestHandler {
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public IdentityClaimHandler(
|
||||
public IdentityClaimProcessor(
|
||||
final SupplyChainValidationService supplyChainValidationService,
|
||||
final CertificateRepository certificateRepository,
|
||||
final ReferenceManifestRepository referenceManifestRepository,
|
||||
@ -105,7 +105,7 @@ public class IdentityClaimHandler extends AbstractRequestHandler {
|
||||
* @return an identity claim response for the specified request containing a wrapped blob
|
||||
*/
|
||||
public byte[] processIdentityClaimTpm2(final byte[] identityClaim) {
|
||||
log.error("Identity Claim received...");
|
||||
log.info("Identity Claim received...");
|
||||
|
||||
if (ArrayUtils.isEmpty(identityClaim)) {
|
||||
log.error("Identity claim empty throwing exception.");
|
||||
@ -124,6 +124,7 @@ public class IdentityClaimHandler extends AbstractRequestHandler {
|
||||
try {
|
||||
validationResult = doSupplyChainValidation(claim, ekPub);
|
||||
} catch (Exception ex) {
|
||||
log.error(ex.getMessage());
|
||||
for (StackTraceElement ste : ex.getStackTrace()) {
|
||||
log.error(ste.toString());
|
||||
}
|
||||
@ -191,12 +192,15 @@ public class IdentityClaimHandler extends AbstractRequestHandler {
|
||||
// this is to check what is in the platform object and pull
|
||||
// additional information from the DB if information exists
|
||||
if (platformCredentials.size() == 1) {
|
||||
List<PlatformCredential> tempList = new LinkedList<>();
|
||||
for (PlatformCredential pc : platformCredentials) {
|
||||
if (pc != null && pc.getPlatformSerial() != null) {
|
||||
platformCredentials.addAll(certificateRepository
|
||||
tempList.addAll(certificateRepository
|
||||
.byBoardSerialNumber(pc.getPlatformSerial()));
|
||||
}
|
||||
}
|
||||
|
||||
platformCredentials.addAll(tempList);
|
||||
}
|
||||
// perform supply chain validation
|
||||
SupplyChainValidationSummary summary = supplyChainValidationService.validateSupplyChain(
|
||||
@ -227,6 +231,9 @@ public class IdentityClaimHandler extends AbstractRequestHandler {
|
||||
log.info("Processing Device Info Report");
|
||||
// store device and device info report.
|
||||
Device device = this.deviceRepository.findByName(deviceInfoReport.getNetworkInfo().getHostname());
|
||||
if (device == null) {
|
||||
device = new Device(deviceInfoReport);
|
||||
}
|
||||
device.setDeviceInfo(deviceInfoReport);
|
||||
return this.deviceRepository.save(device);
|
||||
}
|
||||
@ -457,8 +464,8 @@ public class IdentityClaimHandler extends AbstractRequestHandler {
|
||||
if (baseRim != null) {
|
||||
// pull the base versions of the swidtag and rimel and set the
|
||||
// event log hash for use during provision
|
||||
SupportReferenceManifest sBaseRim = (SupportReferenceManifest) referenceManifestRepository
|
||||
.findByBase64Hash(baseRim.getBase64Hash());
|
||||
SupportReferenceManifest sBaseRim = referenceManifestRepository
|
||||
.getSupportRimEntityById(baseRim.getAssociatedRim());
|
||||
baseRim.setEventLogHash(temp.getHexDecHash());
|
||||
sBaseRim.setEventLogHash(temp.getHexDecHash());
|
||||
referenceManifestRepository.save(baseRim);
|
@ -1,345 +0,0 @@
|
||||
package hirs.attestationca.persist.provision;
|
||||
|
||||
import hirs.attestationca.persist.entity.manager.CertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.DeviceRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.Device;
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
|
||||
import hirs.attestationca.persist.enums.AppraisalStatus;
|
||||
import hirs.attestationca.persist.exceptions.IdentityProcessingException;
|
||||
import hirs.attestationca.persist.provision.helper.CredentialManagementHelper;
|
||||
import hirs.attestationca.persist.provision.helper.ProvisionUtils;
|
||||
import hirs.attestationca.persist.service.SupplyChainValidationService;
|
||||
import hirs.structs.converters.SimpleStructBuilder;
|
||||
import hirs.structs.converters.StructConverter;
|
||||
import hirs.structs.elements.aca.IdentityRequestEnvelope;
|
||||
import hirs.structs.elements.aca.IdentityResponseEnvelope;
|
||||
import hirs.structs.elements.aca.SymmetricAttestation;
|
||||
import hirs.structs.elements.tpm.EncryptionScheme;
|
||||
import hirs.structs.elements.tpm.IdentityProof;
|
||||
import hirs.structs.elements.tpm.IdentityRequest;
|
||||
import hirs.structs.elements.tpm.SymmetricKey;
|
||||
import hirs.structs.elements.tpm.SymmetricKeyParams;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.SerializationUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@Log4j2
|
||||
public class IdentityRequestHandler extends AbstractRequestHandler {
|
||||
|
||||
/**
|
||||
* Container wired ACA private key.
|
||||
*/
|
||||
private final PrivateKey privateKey;
|
||||
private int validDays;
|
||||
private StructConverter structConverter;
|
||||
private CertificateRepository certificateRepository;
|
||||
private DeviceRepository deviceRepository;
|
||||
private SupplyChainValidationService supplyChainValidationService;
|
||||
private X509Certificate acaCertificate;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param structConverter the struct converter
|
||||
* @param certificateRepository
|
||||
* @param deviceRepository
|
||||
* @param supplyChainValidationService the supply chain service
|
||||
* @param privateKey
|
||||
* @param validDays int for the time in which a certificate is valid.
|
||||
* @param acaCertificate object holding the x509 certificate
|
||||
*/
|
||||
public IdentityRequestHandler(final StructConverter structConverter,
|
||||
final CertificateRepository certificateRepository,
|
||||
final DeviceRepository deviceRepository,
|
||||
final SupplyChainValidationService supplyChainValidationService,
|
||||
final PrivateKey privateKey,
|
||||
final int validDays, final X509Certificate acaCertificate) {
|
||||
super(privateKey, validDays);
|
||||
this.structConverter = structConverter;
|
||||
this.certificateRepository = certificateRepository;
|
||||
this.deviceRepository = deviceRepository;
|
||||
this.supplyChainValidationService = supplyChainValidationService;
|
||||
this.privateKey = privateKey;
|
||||
this.acaCertificate = acaCertificate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic implementation of the ACA processIdentityRequest method.
|
||||
*
|
||||
* @param identityRequest cannot be null
|
||||
* @return an identity response for the specified request
|
||||
*/
|
||||
public byte[] processIdentityRequest(final byte[] identityRequest) {
|
||||
log.info("Identity Request Received...");
|
||||
if (ArrayUtils.isEmpty(identityRequest)) {
|
||||
throw new IllegalArgumentException("The IdentityRequest sent by the client"
|
||||
+ " cannot be null or empty.");
|
||||
}
|
||||
|
||||
log.debug("received request to process identity request");
|
||||
|
||||
// translate the bytes into the challenge
|
||||
IdentityRequestEnvelope challenge =
|
||||
structConverter.convert(identityRequest, IdentityRequestEnvelope.class);
|
||||
|
||||
byte[] identityProof = unwrapIdentityRequest(structConverter.convert(challenge.getRequest(),
|
||||
IdentityRequest.class));
|
||||
// the decrypted symmetric blob should be in the format of an IdentityProof. Use the
|
||||
// struct converter to generate it.
|
||||
IdentityProof proof = structConverter.convert(identityProof, IdentityProof.class);
|
||||
|
||||
// convert the credential into an actual key.
|
||||
log.debug("assembling public endorsement key");
|
||||
PublicKey ekPublicKey = null;
|
||||
|
||||
// attempt to find an endorsement credential to validate
|
||||
EndorsementCredential endorsementCredential = null;
|
||||
|
||||
// first check the identity request for the endorsement credential
|
||||
byte[] ecBytesFromIdentityRequest = proof.getEndorsementCredential();
|
||||
if (ArrayUtils.isNotEmpty(ecBytesFromIdentityRequest)) {
|
||||
endorsementCredential = CredentialManagementHelper.storeEndorsementCredential(
|
||||
this.certificateRepository, ecBytesFromIdentityRequest);
|
||||
try {
|
||||
BigInteger publicKeyModulus = Certificate.getPublicKeyModulus(
|
||||
endorsementCredential.getX509Certificate());
|
||||
if (publicKeyModulus != null) {
|
||||
ekPublicKey = ProvisionUtils.assemblePublicKey(publicKeyModulus.toByteArray());
|
||||
} else {
|
||||
throw new IdentityProcessingException("TPM 1.2 Provisioning requires EK "
|
||||
+ "Credentials to be created with RSA");
|
||||
}
|
||||
} catch (IOException ioEx) {
|
||||
log.error("Could not retrieve the public key modulus from the EK cert");
|
||||
}
|
||||
} else if (ArrayUtils.isNotEmpty(challenge.getEndorsementCredentialModulus())) {
|
||||
log.warn("EKC was not in the identity proof from the client. Checking for uploads.");
|
||||
// Check if the EC was uploaded
|
||||
ekPublicKey =
|
||||
ProvisionUtils.assemblePublicKey(new String(challenge.getEndorsementCredentialModulus()));
|
||||
endorsementCredential = getEndorsementCredential(ekPublicKey);
|
||||
} else {
|
||||
log.warn("Zero-length endorsement credential received in identity request.");
|
||||
}
|
||||
|
||||
// get platform credential from the identity request
|
||||
List<PlatformCredential> platformCredentials = new LinkedList<>();
|
||||
byte[] pcBytesFromIdentityRequest = proof.getPlatformCredential();
|
||||
if (ArrayUtils.isNotEmpty(pcBytesFromIdentityRequest)) {
|
||||
platformCredentials.add(CredentialManagementHelper.storePlatformCredential(
|
||||
this.certificateRepository, pcBytesFromIdentityRequest));
|
||||
} else if (endorsementCredential != null) {
|
||||
// if none in the identity request, look for uploaded platform credentials
|
||||
log.warn("PC was not in the identity proof from the client. Checking for uploads.");
|
||||
platformCredentials.addAll(getPlatformCredentials(endorsementCredential));
|
||||
} else {
|
||||
// if none in the identity request, look for uploaded platform credentials
|
||||
log.warn("Zero-length platform credential received in identity request.");
|
||||
}
|
||||
|
||||
log.debug("Processing serialized device info report structure of length {}",
|
||||
challenge.getDeviceInfoReportLength());
|
||||
|
||||
DeviceInfoReport deviceInfoReport = (DeviceInfoReport)
|
||||
SerializationUtils.deserialize(challenge.getDeviceInfoReport());
|
||||
|
||||
if (deviceInfoReport == null) {
|
||||
log.error("Failed to deserialize Device Info Report");
|
||||
throw new IdentityProcessingException("Device Info Report failed to deserialize "
|
||||
+ "from Identity Request");
|
||||
}
|
||||
|
||||
log.info("Processing Device Info Report");
|
||||
// store device and device info report.
|
||||
String deviceName = deviceInfoReport.getNetworkInfo().getHostname();
|
||||
Device device = this.deviceRepository.findByName(deviceName);
|
||||
device.setDeviceInfo(deviceInfoReport);
|
||||
|
||||
// perform supply chain validation. Note: It's possible that this should be done earlier
|
||||
// in this method.
|
||||
SupplyChainValidationSummary summary =
|
||||
supplyChainValidationService.validateSupplyChain(endorsementCredential,
|
||||
platformCredentials, device);
|
||||
|
||||
// update the validation result in the device
|
||||
device.setSupplyChainValidationStatus(summary.getOverallValidationResult());
|
||||
deviceRepository.save(device);
|
||||
// check if supply chain validation succeeded.
|
||||
// If it did not, do not provide the IdentityResponseEnvelope
|
||||
if (summary.getOverallValidationResult() == AppraisalStatus.Status.PASS) {
|
||||
IdentityResponseEnvelope identityResponse =
|
||||
generateIdentityResponseEnvelopeAndStoreIssuedCert(challenge,
|
||||
ekPublicKey, endorsementCredential, platformCredentials, device);
|
||||
|
||||
return structConverter.convert(identityResponse);
|
||||
} else {
|
||||
log.error("Supply chain validation did not succeed. Result is: "
|
||||
+ summary.getOverallValidationResult());
|
||||
return new byte[]{};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a successful supply chain validation, generate an Identity Response envelope and
|
||||
* the issued certificate. The issued cert is stored in the database. The identity response
|
||||
* envelope is returned, and sent back to the client using the struct converter.
|
||||
* @param challenge the identity request envelope
|
||||
* @param ekPublicKey the EK public key
|
||||
* @param endorsementCredential the endorsement credential
|
||||
* @param platformCredentials the set of platform credentials
|
||||
* @param device the device associated
|
||||
* @return the identity response envelope
|
||||
*/
|
||||
private IdentityResponseEnvelope generateIdentityResponseEnvelopeAndStoreIssuedCert(
|
||||
final IdentityRequestEnvelope challenge, final PublicKey ekPublicKey,
|
||||
final EndorsementCredential endorsementCredential,
|
||||
final List<PlatformCredential> platformCredentials, final Device device) {
|
||||
// decrypt the asymmetric / symmetric blobs
|
||||
log.debug("unwrapping identity request");
|
||||
byte[] identityProof = unwrapIdentityRequest(
|
||||
structConverter.convert(challenge.getRequest(), IdentityRequest.class));
|
||||
|
||||
// the decrypted symmetric blob should be in the format of an IdentityProof. Use the
|
||||
// struct converter to generate it.
|
||||
IdentityProof proof = structConverter.convert(identityProof, IdentityProof.class);
|
||||
|
||||
// generate a session key and convert to byte array
|
||||
log.debug("generating symmetric key for response");
|
||||
SymmetricKey sessionKey = ProvisionUtils.generateSymmetricKey();
|
||||
|
||||
// generate the asymmetric contents for the identity response
|
||||
log.debug("generating asymmetric contents for response");
|
||||
byte[] asymmetricContents = ProvisionUtils.generateAsymmetricContents(
|
||||
structConverter.convert(proof.getIdentityKey()),
|
||||
structConverter.convert(sessionKey), ekPublicKey);
|
||||
|
||||
// generate the identity credential
|
||||
log.debug("generating credential from identity proof");
|
||||
|
||||
// transform the public key struct into a public key
|
||||
PublicKey publicKey = ProvisionUtils.assemblePublicKey(proof.getIdentityKey().getStorePubKey().getKey());
|
||||
X509Certificate credential = generateCredential(publicKey, endorsementCredential,
|
||||
platformCredentials, device.getDeviceInfo()
|
||||
.getNetworkInfo()
|
||||
.getIpAddress()
|
||||
.getHostName(), acaCertificate);
|
||||
|
||||
// generate the attestation using the credential and the key for this session
|
||||
log.debug("generating symmetric response");
|
||||
SymmetricAttestation attestation = ProvisionUtils.generateAttestation(credential, sessionKey);
|
||||
|
||||
// construct the response with the both the asymmetric contents and the CA attestation
|
||||
IdentityResponseEnvelope identityResponse =
|
||||
new SimpleStructBuilder<>(IdentityResponseEnvelope.class)
|
||||
.set("asymmetricContents", asymmetricContents)
|
||||
.set("symmetricAttestation", attestation).build();
|
||||
|
||||
// save new attestation certificate
|
||||
byte[] derEncodedAttestationCertificate = ProvisionUtils.getDerEncodedCertificate(credential);
|
||||
saveAttestationCertificate(this.certificateRepository, derEncodedAttestationCertificate,
|
||||
endorsementCredential, platformCredentials, device);
|
||||
|
||||
return identityResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps a given identityRequest. That is to say, decrypt the asymmetric portion of a data
|
||||
* structure to determine the method to decrypt the symmetric portion.
|
||||
*
|
||||
* @param request
|
||||
* to be decrypted
|
||||
* @return the decrypted symmetric portion of an identity request.
|
||||
*/
|
||||
private byte[] unwrapIdentityRequest(final IdentityRequest request) {
|
||||
// in case the TPM did not specify the IV, it must be extracted from the symmetric blob.
|
||||
// the IV will then be the the first block of the cipher text.
|
||||
final byte[] iv;
|
||||
SymmetricKeyParams symmetricKeyParams = request.getSymmetricAlgorithm();
|
||||
if (symmetricKeyParams != null && symmetricKeyParams.getParams() != null) {
|
||||
iv = symmetricKeyParams.getParams().getIv();
|
||||
} else {
|
||||
iv = ProvisionUtils.extractInitialValue(request);
|
||||
}
|
||||
|
||||
// determine the encryption scheme from the algorithm
|
||||
EncryptionScheme asymmetricScheme =
|
||||
EncryptionScheme.fromInt(request.getAsymmetricAlgorithm().getEncryptionScheme());
|
||||
|
||||
// decrypt the asymmetric blob
|
||||
byte[] decryptedAsymmetricBlob =
|
||||
ProvisionUtils.decryptAsymmetricBlob(request.getAsymmetricBlob(), asymmetricScheme, getPrivateKey());
|
||||
|
||||
// construct our symmetric key structure from the decrypted asymmetric blob
|
||||
SymmetricKey symmetricKey =
|
||||
structConverter.convert(decryptedAsymmetricBlob, SymmetricKey.class);
|
||||
|
||||
byte[] decryptedSymmetricBlob =
|
||||
ProvisionUtils.decryptSymmetricBlob(request.getSymmetricBlob(), symmetricKey.getKey(), iv,
|
||||
"AES/CBC/PKCS5Padding");
|
||||
|
||||
// decrypt the symmetric blob
|
||||
return decryptedSymmetricBlob;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Endorsement Credential from the DB given the EK public key.
|
||||
* @param ekPublicKey the EK public key
|
||||
* @return the Endorsement credential, if found, otherwise null
|
||||
*/
|
||||
private EndorsementCredential getEndorsementCredential(final PublicKey ekPublicKey) {
|
||||
log.debug("Searching for endorsement credential based on public key: " + ekPublicKey);
|
||||
|
||||
if (ekPublicKey == null) {
|
||||
throw new IllegalArgumentException("Cannot look up an EC given a null public key");
|
||||
}
|
||||
|
||||
EndorsementCredential credential = null;
|
||||
|
||||
try {
|
||||
credential = certificateRepository.findByPublicKeyModulusHexValue(Certificate
|
||||
.getPublicKeyModulus(ekPublicKey)
|
||||
.toString());
|
||||
} catch (IOException ioEx) {
|
||||
log.error("Could not extract public key modulus", ioEx);
|
||||
}
|
||||
|
||||
if (credential == null) {
|
||||
log.warn("Unable to find endorsement credential for public key.");
|
||||
} else {
|
||||
log.debug("Endorsement credential found.");
|
||||
}
|
||||
|
||||
return credential;
|
||||
}
|
||||
|
||||
private List<PlatformCredential> getPlatformCredentials(final EndorsementCredential ec) {
|
||||
List<PlatformCredential> credentials = null;
|
||||
|
||||
if (ec == null) {
|
||||
log.warn("Cannot look for platform credential(s). Endorsement credential was null.");
|
||||
} else {
|
||||
log.debug("Searching for platform credential(s) based on holder serial number: "
|
||||
+ ec.getSerialNumber());
|
||||
credentials = this.certificateRepository.getByHolderSerialNumber(ec.getSerialNumber());
|
||||
if (credentials == null || credentials.isEmpty()) {
|
||||
log.warn("No platform credential(s) found");
|
||||
} else {
|
||||
log.debug("Platform Credential(s) found: " + credentials.size());
|
||||
}
|
||||
}
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
}
|
@ -27,12 +27,13 @@ public final class CredentialManagementHelper {
|
||||
* it is unarchived.
|
||||
* @param certificateRepository the certificate manager used for storage
|
||||
* @param endorsementBytes the raw EK bytes used for parsing
|
||||
* @param deviceName the host name
|
||||
* @return the parsed, valid EK
|
||||
* @throws IllegalArgumentException if the provided bytes are not a valid EK.
|
||||
*/
|
||||
public static EndorsementCredential storeEndorsementCredential(
|
||||
final CertificateRepository certificateRepository,
|
||||
final byte[] endorsementBytes) throws IllegalArgumentException {
|
||||
final byte[] endorsementBytes, final String deviceName) throws IllegalArgumentException {
|
||||
|
||||
if (certificateRepository == null) {
|
||||
throw new IllegalArgumentException("null certificate manager");
|
||||
@ -64,6 +65,7 @@ public final class CredentialManagementHelper {
|
||||
.findByCertificateHash(certificateHash);
|
||||
if (existingCredential == null) {
|
||||
log.info("No Endorsement Credential found with hash: " + certificateHash);
|
||||
endorsementCredential.setDeviceName(deviceName);
|
||||
return (EndorsementCredential) certificateRepository.save(endorsementCredential);
|
||||
} else if (existingCredential.isArchived()) {
|
||||
// if the EK is stored in the DB and it's archived, unarchive.
|
||||
|
@ -1,30 +1,266 @@
|
||||
package hirs.attestationca.persist.service;
|
||||
|
||||
import hirs.attestationca.persist.DBManagerException;
|
||||
import hirs.attestationca.persist.entity.ArchivableEntity;
|
||||
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
|
||||
import hirs.attestationca.persist.entity.manager.CertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
|
||||
import hirs.attestationca.persist.entity.manager.PolicyRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
|
||||
import hirs.attestationca.persist.entity.manager.SupplyChainValidationRepository;
|
||||
import hirs.attestationca.persist.entity.manager.SupplyChainValidationSummaryRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Device;
|
||||
import hirs.attestationca.persist.entity.userdefined.PolicySettings;
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest;
|
||||
import hirs.attestationca.persist.enums.AppraisalStatus;
|
||||
import hirs.attestationca.persist.validation.PcrValidator;
|
||||
import hirs.attestationca.persist.validation.SupplyChainCredentialValidator;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL;
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS;
|
||||
|
||||
@Log4j2
|
||||
@Service
|
||||
public class SupplyChainValidationService {
|
||||
|
||||
private CACredentialRepository caCredentialRepository;
|
||||
private PolicyRepository policyRepository;
|
||||
private ReferenceManifestRepository referenceManifestRepository;
|
||||
private ReferenceDigestValueRepository referenceDigestValueRepository;
|
||||
private ComponentResultRepository componentResultRepository;
|
||||
private CertificateRepository certificateRepository;
|
||||
private SupplyChainValidationRepository supplyChainValidationRepository;
|
||||
private SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository;
|
||||
|
||||
/**
|
||||
* Interface defining a component that will perform supply chain validations, which yields a
|
||||
* {@link SupplyChainValidationSummary}.
|
||||
*/
|
||||
public interface SupplyChainValidationService {
|
||||
/**
|
||||
* The "main" method of supply chain validation. Takes the credentials from an identity
|
||||
* request and validates the supply chain in accordance to the current supply chain
|
||||
* policy.
|
||||
* Constructor.
|
||||
*
|
||||
* @param ec The endorsement credential from the identity request.
|
||||
* @param pc The set of platform credentials from the identity request.
|
||||
* @param device The device to be validated.
|
||||
* @return True if validation is successful, false otherwise.
|
||||
* @param caCredentialRepository ca credential repository
|
||||
* @param policyRepository the policy manager
|
||||
* @param certificateRepository the cert manager
|
||||
* @param componentResultRepository the comp result manager
|
||||
* @param referenceManifestRepository the RIM manager
|
||||
* @param supplyChainValidationRepository the scv manager
|
||||
* @param supplyChainValidationSummaryRepository the summary manager
|
||||
* @param referenceDigestValueRepository the even manager
|
||||
*/
|
||||
SupplyChainValidationSummary validateSupplyChain(EndorsementCredential ec,
|
||||
List<PlatformCredential> pc,
|
||||
Device device);
|
||||
@Autowired
|
||||
@SuppressWarnings("ParameterNumberCheck")
|
||||
public SupplyChainValidationService(
|
||||
final CACredentialRepository caCredentialRepository,
|
||||
final PolicyRepository policyRepository,
|
||||
final CertificateRepository certificateRepository,
|
||||
final ComponentResultRepository componentResultRepository,
|
||||
final ReferenceManifestRepository referenceManifestRepository,
|
||||
final SupplyChainValidationRepository supplyChainValidationRepository,
|
||||
final SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository,
|
||||
final ReferenceDigestValueRepository referenceDigestValueRepository) {
|
||||
this.caCredentialRepository = caCredentialRepository;
|
||||
this.policyRepository = policyRepository;
|
||||
this.certificateRepository = certificateRepository;
|
||||
this.componentResultRepository = componentResultRepository;
|
||||
this.referenceManifestRepository = referenceManifestRepository;
|
||||
this.supplyChainValidationRepository = supplyChainValidationRepository;
|
||||
this.supplyChainValidationSummaryRepository = supplyChainValidationSummaryRepository;
|
||||
this.referenceDigestValueRepository = referenceDigestValueRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* The "main" method of supply chain validation. Takes the credentials from
|
||||
* an identity request and validates the supply chain in accordance to the
|
||||
* current supply chain policy.
|
||||
*
|
||||
* @param ec The endorsement credential from the identity request.
|
||||
* @param pcs The platform credentials from the identity request.
|
||||
* @param device The device to be validated.
|
||||
* @return A summary of the validation results.
|
||||
*/
|
||||
@SuppressWarnings("methodlength")
|
||||
public SupplyChainValidationSummary validateSupplyChain(final EndorsementCredential ec,
|
||||
final List<PlatformCredential> pcs,
|
||||
final Device device) {
|
||||
boolean acceptExpiredCerts = getPolicySettings().isExpiredCertificateValidationEnabled();
|
||||
PlatformCredential baseCredential = null;
|
||||
SupplyChainValidation platformScv = null;
|
||||
SupplyChainValidation basePlatformScv = null;
|
||||
boolean chkDeltas = false;
|
||||
String pcErrorMessage = "";
|
||||
List<SupplyChainValidation> validations = new LinkedList<>();
|
||||
Map<PlatformCredential, SupplyChainValidation> deltaMapping = new HashMap<>();
|
||||
SupplyChainValidation.ValidationType platformType = SupplyChainValidation
|
||||
.ValidationType.PLATFORM_CREDENTIAL;
|
||||
log.info("Beginning Supply Chain Validation...");
|
||||
|
||||
// Validate the Endorsement Credential
|
||||
if (getPolicySettings().isEcValidationEnabled()) {
|
||||
log.info("Beginning Endorsement Credential Validation...");
|
||||
validations.add(ValidationService.evaluateEndorsementCredentialStatus(ec, this.caCredentialRepository, acceptExpiredCerts));
|
||||
// store the device with the credential
|
||||
if (ec != null) {
|
||||
ec.setDeviceId(device.getId());
|
||||
ec.setDeviceName(device.getDeviceInfo().getNetworkInfo().getHostname());
|
||||
this.certificateRepository.save(ec);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate Platform Credential signatures
|
||||
if (getPolicySettings().isPcValidationEnabled()) {
|
||||
log.info("Beginning Platform Credential Validation...");
|
||||
// Ensure there are platform credentials to validate
|
||||
if (pcs == null || pcs.isEmpty()) {
|
||||
log.error("There were no Platform Credentials to validate.");
|
||||
pcErrorMessage = "Platform credential(s) missing\n";
|
||||
} else {
|
||||
for (PlatformCredential pc : pcs) {
|
||||
KeyStore trustedCa = ValidationService.getCaChain(pc, caCredentialRepository);
|
||||
platformScv = ValidationService.evaluatePlatformCredentialStatus(
|
||||
pc, trustedCa, acceptExpiredCerts);
|
||||
|
||||
if (platformScv.getValidationResult() == AppraisalStatus.Status.FAIL) {
|
||||
pcErrorMessage = String.format("%s%s%n", pcErrorMessage,
|
||||
platformScv.getMessage());
|
||||
}
|
||||
// set the base credential
|
||||
if (pc.isPlatformBase()) {
|
||||
baseCredential = pc;
|
||||
basePlatformScv = platformScv;
|
||||
} else {
|
||||
chkDeltas = true;
|
||||
deltaMapping.put(pc, null);
|
||||
}
|
||||
pc.setEndorsementCredential(ec);
|
||||
pc.setDeviceId(device.getId());
|
||||
pc.setDeviceName(device.getDeviceInfo().getNetworkInfo().getHostname());
|
||||
this.certificateRepository.save(pc);
|
||||
}
|
||||
|
||||
// check that the delta certificates validity date is after
|
||||
// the base
|
||||
if (baseCredential != null) {
|
||||
for (PlatformCredential pc : pcs) {
|
||||
int result = baseCredential.getBeginValidity()
|
||||
.compareTo(pc.getBeginValidity());
|
||||
if (!pc.isPlatformBase() && (result > 0)) {
|
||||
pcErrorMessage = String.format("%s%s%n", pcErrorMessage,
|
||||
"Delta Certificate's validity "
|
||||
+ "date is not after Base");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// we don't have a base cert, fail
|
||||
pcErrorMessage = String.format("%s%s%n", pcErrorMessage,
|
||||
"Base Platform credential missing");
|
||||
}
|
||||
}
|
||||
|
||||
if (pcErrorMessage.isEmpty()) {
|
||||
validations.add(platformScv);
|
||||
} else {
|
||||
if (pcs == null) {
|
||||
validations.add(new SupplyChainValidation(platformType,
|
||||
AppraisalStatus.Status.FAIL, new ArrayList<>(), pcErrorMessage));
|
||||
} else {
|
||||
validations.add(new SupplyChainValidation(platformType,
|
||||
AppraisalStatus.Status.FAIL, new ArrayList<>(pcs), pcErrorMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate Platform Credential attributes
|
||||
if (getPolicySettings().isPcAttributeValidationEnabled()
|
||||
&& pcErrorMessage.isEmpty()) {
|
||||
log.info("Beginning Platform Attributes Validation...");
|
||||
// Ensure there are platform credentials to validate
|
||||
SupplyChainValidation attributeScv = null;
|
||||
String attrErrorMessage = "";
|
||||
List<ArchivableEntity> aes = new ArrayList<>();
|
||||
// need to check if there are deltas, if not then just verify
|
||||
// components of the base
|
||||
if (baseCredential == null) {
|
||||
validations.add(ValidationService.buildValidationRecord(
|
||||
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
|
||||
AppraisalStatus.Status.FAIL,
|
||||
"Base Platform credential missing."
|
||||
+ " Cannot validate attributes",
|
||||
null, Level.ERROR));
|
||||
} else {
|
||||
if (chkDeltas) {
|
||||
aes.addAll(basePlatformScv.getCertificatesUsed());
|
||||
Iterator<PlatformCredential> it = pcs.iterator();
|
||||
while (it.hasNext()) {
|
||||
PlatformCredential pc = it.next();
|
||||
if (pc != null && !pc.isPlatformBase()) {
|
||||
attributeScv = ValidationService.evaluateDeltaAttributesStatus(
|
||||
pc, device.getDeviceInfo(),
|
||||
baseCredential, deltaMapping, certificateRepository);
|
||||
if (attributeScv.getValidationResult() == AppraisalStatus.Status.FAIL) {
|
||||
attrErrorMessage = String.format("%s%s%n", attrErrorMessage,
|
||||
attributeScv.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
aes.add(baseCredential);
|
||||
validations.remove(platformScv);
|
||||
// if there are no deltas, just check base credential
|
||||
platformScv = ValidationService.evaluatePCAttributesStatus(
|
||||
baseCredential, device.getDeviceInfo(), ec,
|
||||
certificateRepository, componentResultRepository);
|
||||
validations.add(new SupplyChainValidation(
|
||||
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
|
||||
platformScv.getValidationResult(), aes, platformScv.getMessage()));
|
||||
}
|
||||
}
|
||||
if (!attrErrorMessage.isEmpty()) {
|
||||
//combine platform and platform attributes
|
||||
validations.remove(platformScv);
|
||||
validations.add(new SupplyChainValidation(
|
||||
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
|
||||
attributeScv.getValidationResult(), aes, attributeScv.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
if (getPolicySettings().isFirmwareValidationEnabled()) {
|
||||
log.info("Beginning Firmware Validation...");
|
||||
// may need to associated with device to pull the correct info
|
||||
// compare tpm quote with what is pulled from RIM associated file
|
||||
validations.add(ValidationService.evaluateFirmwareStatus(device, getPolicySettings(),
|
||||
referenceManifestRepository, referenceDigestValueRepository,
|
||||
caCredentialRepository));
|
||||
}
|
||||
|
||||
log.info("The validation finished, summarizing...");
|
||||
// Generate validation summary, save it, and return it.
|
||||
SupplyChainValidationSummary summary
|
||||
= new SupplyChainValidationSummary(device, validations);
|
||||
try {
|
||||
supplyChainValidationSummaryRepository.save(summary);
|
||||
} catch (DBManagerException dbMEx) {
|
||||
log.error("Failed to save Supply Chain Summary");
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* A supplemental method that handles validating just the quote post main validation.
|
||||
@ -32,11 +268,110 @@ public interface SupplyChainValidationService {
|
||||
* @param device the associated device.
|
||||
* @return True if validation is successful, false otherwise.
|
||||
*/
|
||||
SupplyChainValidationSummary validateQuote(Device device);
|
||||
public SupplyChainValidationSummary validateQuote(final Device device) {
|
||||
SupplyChainValidation quoteScv = null;
|
||||
SupplyChainValidationSummary summary = null;
|
||||
Level level = Level.ERROR;
|
||||
AppraisalStatus fwStatus = new AppraisalStatus(FAIL,
|
||||
"Unknown exception caught during quote validation.");
|
||||
SupportReferenceManifest sRim = null;
|
||||
EventLogMeasurements eventLog = null;
|
||||
|
||||
// check if the policy is enabled
|
||||
if (getPolicySettings().isFirmwareValidationEnabled()) {
|
||||
String[] baseline = new String[Integer.SIZE];
|
||||
String deviceName = device.getDeviceInfo()
|
||||
.getNetworkInfo().getHostname();
|
||||
|
||||
try {
|
||||
List<SupportReferenceManifest> supportRims = referenceManifestRepository
|
||||
.getSupportByManufacturerModel(
|
||||
device.getDeviceInfo().getHardwareInfo().getManufacturer(),
|
||||
device.getDeviceInfo().getHardwareInfo().getProductName());
|
||||
for (SupportReferenceManifest support : supportRims) {
|
||||
if (support.isBaseSupport()) {
|
||||
sRim = support;
|
||||
}
|
||||
}
|
||||
eventLog = (EventLogMeasurements) referenceManifestRepository
|
||||
.findByHexDecHash(sRim.getEventLogHash());
|
||||
|
||||
if (sRim == null) {
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
String.format("Firmware Quote validation failed: "
|
||||
+ "No associated Support RIM file "
|
||||
+ "could be found for %s",
|
||||
deviceName));
|
||||
} else if (eventLog == null) {
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
String.format("Firmware Quote validation failed: "
|
||||
+ "No associated Client Log file "
|
||||
+ "could be found for %s",
|
||||
deviceName));
|
||||
} else {
|
||||
baseline = sRim.getExpectedPCRList();
|
||||
String[] storedPcrs = eventLog.getExpectedPCRList();
|
||||
PcrValidator pcrValidator = new PcrValidator(baseline);
|
||||
// grab the quote
|
||||
byte[] hash = device.getDeviceInfo().getTpmInfo().getTpmQuoteHash();
|
||||
if (pcrValidator.validateQuote(hash, storedPcrs, getPolicySettings())) {
|
||||
level = Level.INFO;
|
||||
fwStatus = new AppraisalStatus(PASS,
|
||||
SupplyChainCredentialValidator.FIRMWARE_VALID);
|
||||
fwStatus.setMessage("Firmware validation of TPM Quote successful.");
|
||||
} else {
|
||||
fwStatus.setMessage("Firmware validation of TPM Quote failed."
|
||||
+ "\nPCR hash and Quote hash do not match.");
|
||||
}
|
||||
eventLog.setOverallValidationResult(fwStatus.getAppStatus());
|
||||
this.referenceManifestRepository.save(eventLog);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error(ex);
|
||||
}
|
||||
|
||||
quoteScv = ValidationService.buildValidationRecord(SupplyChainValidation
|
||||
.ValidationType.FIRMWARE,
|
||||
fwStatus.getAppStatus(), fwStatus.getMessage(), eventLog, level);
|
||||
|
||||
// Generate validation summary, save it, and return it.
|
||||
List<SupplyChainValidation> validations = new ArrayList<>();
|
||||
SupplyChainValidationSummary previous
|
||||
= this.supplyChainValidationSummaryRepository.findByDevice(deviceName);
|
||||
for (SupplyChainValidation scv : previous.getValidations()) {
|
||||
if (scv.getValidationType() != SupplyChainValidation.ValidationType.FIRMWARE) {
|
||||
validations.add(ValidationService.buildValidationRecord(scv.getValidationType(),
|
||||
scv.getValidationResult(), scv.getMessage(),
|
||||
scv.getCertificatesUsed().get(0), Level.INFO));
|
||||
}
|
||||
}
|
||||
validations.add(quoteScv);
|
||||
previous.archive();
|
||||
supplyChainValidationSummaryRepository.save(previous);
|
||||
summary = new SupplyChainValidationSummary(device, validations);
|
||||
|
||||
// try removing the supply chain validation as well and resaving that
|
||||
try {
|
||||
supplyChainValidationSummaryRepository.save(summary);
|
||||
} catch (DBManagerException dbEx) {
|
||||
log.error("Failed to save Supply Chain Summary", dbEx);
|
||||
}
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows other service access to the policy information.
|
||||
* @return supply chain policy
|
||||
* Helper function to get a fresh load of the default policy from the DB.
|
||||
*
|
||||
* @return The default Supply Chain Policy
|
||||
*/
|
||||
// SupplyChainPolicy getPolicy();
|
||||
private PolicySettings getPolicySettings() {
|
||||
PolicySettings defaultSettings = this.policyRepository.findByName("Default");
|
||||
|
||||
if (defaultSettings == null) {
|
||||
defaultSettings = new PolicySettings("Default", "Settings are configured for no validation flags set.");
|
||||
}
|
||||
return defaultSettings;
|
||||
}
|
||||
}
|
||||
|
@ -1,377 +0,0 @@
|
||||
package hirs.attestationca.persist.service;
|
||||
|
||||
import hirs.attestationca.persist.entity.ArchivableEntity;
|
||||
import hirs.attestationca.persist.DBManagerException;
|
||||
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
|
||||
import hirs.attestationca.persist.entity.manager.CertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
|
||||
import hirs.attestationca.persist.entity.manager.PolicyRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
|
||||
import hirs.attestationca.persist.entity.manager.SupplyChainValidationSummaryRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.Device;
|
||||
import hirs.attestationca.persist.entity.userdefined.PolicySettings;
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.record.TPMMeasurementRecord;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest;
|
||||
import hirs.attestationca.persist.enums.AppraisalStatus;
|
||||
import hirs.attestationca.persist.validation.CredentialValidator;
|
||||
import hirs.attestationca.persist.validation.PcrValidator;
|
||||
import hirs.attestationca.persist.validation.SupplyChainCredentialValidator;
|
||||
import hirs.utils.BouncyCastleUtils;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL;
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS;
|
||||
|
||||
@Log4j2
|
||||
@Service
|
||||
public class SupplyChainValidationServiceImpl implements SupplyChainValidationService {
|
||||
|
||||
private CACredentialRepository caCredentialRepository;
|
||||
private PolicyRepository policyRepository;
|
||||
private ReferenceManifestRepository referenceManifestRepository;
|
||||
private ReferenceDigestValueRepository referenceDigestValueRepository;
|
||||
private ComponentResultRepository componentResultRepository;
|
||||
private CertificateRepository certificateRepository;
|
||||
private CredentialValidator supplyChainCredentialValidator;
|
||||
private SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository;
|
||||
|
||||
/**
|
||||
* Constructor to set just the CertificateRepository, so that cert chain validating
|
||||
* methods can be called from outside classes.
|
||||
*
|
||||
* @param certificateRepository the cert repository
|
||||
*/
|
||||
public SupplyChainValidationServiceImpl(final CertificateRepository certificateRepository) {
|
||||
this.certificateRepository = certificateRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param caCredentialRepository ca credential repository
|
||||
* @param policyRepository the policy manager
|
||||
* @param certificateRepository the cert manager
|
||||
* @param componentResultRepository the comp result manager
|
||||
* @param referenceManifestRepository the RIM manager
|
||||
* @param supplyChainValidationSummaryRepository the summary manager
|
||||
* @param supplyChainCredentialValidator the credential validator
|
||||
* @param referenceDigestValueRepository the even manager
|
||||
*/
|
||||
@Autowired
|
||||
@SuppressWarnings("ParameterNumberCheck")
|
||||
public SupplyChainValidationServiceImpl(
|
||||
final CACredentialRepository caCredentialRepository,
|
||||
final PolicyRepository policyRepository,
|
||||
final CertificateRepository certificateRepository,
|
||||
final ComponentResultRepository componentResultRepository,
|
||||
final ReferenceManifestRepository referenceManifestRepository,
|
||||
final SupplyChainValidationSummaryRepository supplyChainValidationSummaryRepository,
|
||||
final CredentialValidator supplyChainCredentialValidator,
|
||||
final ReferenceDigestValueRepository referenceDigestValueRepository) {
|
||||
this.caCredentialRepository = caCredentialRepository;
|
||||
this.policyRepository = policyRepository;
|
||||
this.certificateRepository = certificateRepository;
|
||||
this.componentResultRepository = componentResultRepository;
|
||||
this.referenceManifestRepository = referenceManifestRepository;
|
||||
this.supplyChainValidationSummaryRepository = supplyChainValidationSummaryRepository;
|
||||
this.supplyChainCredentialValidator = supplyChainCredentialValidator;
|
||||
this.referenceDigestValueRepository = referenceDigestValueRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SupplyChainValidationSummary validateSupplyChain(final EndorsementCredential ec,
|
||||
final List<PlatformCredential> pc,
|
||||
final Device device) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A supplemental method that handles validating just the quote post main validation.
|
||||
*
|
||||
* @param device the associated device.
|
||||
* @return True if validation is successful, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public SupplyChainValidationSummary validateQuote(final Device device) {
|
||||
SupplyChainValidation quoteScv = null;
|
||||
SupplyChainValidationSummary summary = null;
|
||||
Level level = Level.ERROR;
|
||||
AppraisalStatus fwStatus = new AppraisalStatus(FAIL,
|
||||
"Unknown exception caught during quote validation.");
|
||||
SupportReferenceManifest sRim = null;
|
||||
EventLogMeasurements eventLog = null;
|
||||
|
||||
// check if the policy is enabled
|
||||
if (getPolicySettings().isFirmwareValidationEnabled()) {
|
||||
String[] baseline = new String[Integer.SIZE];
|
||||
String deviceName = device.getDeviceInfo()
|
||||
.getNetworkInfo().getHostname();
|
||||
|
||||
try {
|
||||
List<SupportReferenceManifest> supportRims = referenceManifestRepository.getSupportByManufacturerModel(
|
||||
device.getDeviceInfo().getHardwareInfo().getManufacturer(),
|
||||
device.getDeviceInfo().getHardwareInfo().getProductName());
|
||||
for (SupportReferenceManifest support : supportRims) {
|
||||
if (support.isBaseSupport()) {
|
||||
sRim = support;
|
||||
}
|
||||
}
|
||||
eventLog = (EventLogMeasurements) referenceManifestRepository
|
||||
.findByHexDecHash(sRim.getEventLogHash());
|
||||
|
||||
if (sRim == null) {
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
String.format("Firmware Quote validation failed: "
|
||||
+ "No associated Support RIM file "
|
||||
+ "could be found for %s",
|
||||
deviceName));
|
||||
} else if (eventLog == null) {
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
String.format("Firmware Quote validation failed: "
|
||||
+ "No associated Client Log file "
|
||||
+ "could be found for %s",
|
||||
deviceName));
|
||||
} else {
|
||||
baseline = sRim.getExpectedPCRList();
|
||||
String[] storedPcrs = eventLog.getExpectedPCRList();
|
||||
PcrValidator pcrValidator = new PcrValidator(baseline);
|
||||
// grab the quote
|
||||
byte[] hash = device.getDeviceInfo().getTpmInfo().getTpmQuoteHash();
|
||||
if (pcrValidator.validateQuote(hash, storedPcrs, getPolicySettings())) {
|
||||
level = Level.INFO;
|
||||
fwStatus = new AppraisalStatus(PASS,
|
||||
SupplyChainCredentialValidator.FIRMWARE_VALID);
|
||||
fwStatus.setMessage("Firmware validation of TPM Quote successful.");
|
||||
} else {
|
||||
fwStatus.setMessage("Firmware validation of TPM Quote failed."
|
||||
+ "\nPCR hash and Quote hash do not match.");
|
||||
}
|
||||
eventLog.setOverallValidationResult(fwStatus.getAppStatus());
|
||||
this.referenceManifestRepository.save(eventLog);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error(ex);
|
||||
}
|
||||
|
||||
quoteScv = buildValidationRecord(SupplyChainValidation
|
||||
.ValidationType.FIRMWARE,
|
||||
fwStatus.getAppStatus(), fwStatus.getMessage(), eventLog, level);
|
||||
|
||||
// Generate validation summary, save it, and return it.
|
||||
List<SupplyChainValidation> validations = new ArrayList<>();
|
||||
SupplyChainValidationSummary previous
|
||||
= this.supplyChainValidationSummaryRepository.findByDevice(deviceName);
|
||||
for (SupplyChainValidation scv : previous.getValidations()) {
|
||||
if (scv.getValidationType() != SupplyChainValidation.ValidationType.FIRMWARE) {
|
||||
validations.add(buildValidationRecord(scv.getValidationType(),
|
||||
scv.getValidationResult(), scv.getMessage(),
|
||||
scv.getCertificatesUsed().get(0), Level.INFO));
|
||||
}
|
||||
}
|
||||
validations.add(quoteScv);
|
||||
previous.archive();
|
||||
supplyChainValidationSummaryRepository.save(previous);
|
||||
summary = new SupplyChainValidationSummary(device, validations);
|
||||
|
||||
// try removing the supply chain validation as well and resaving that
|
||||
try {
|
||||
supplyChainValidationSummaryRepository.save(summary);
|
||||
} catch (DBManagerException dbEx) {
|
||||
log.error("Failed to save Supply Chain Summary", dbEx);
|
||||
}
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a supply chain validation record and logs the validation message
|
||||
* at the specified log level.
|
||||
*
|
||||
* @param validationType the type of validation
|
||||
* @param result the appraisal status
|
||||
* @param message the validation message to include in the summary and log
|
||||
* @param archivableEntity the archivableEntity associated with the
|
||||
* validation
|
||||
* @param logLevel the log level
|
||||
* @return a SupplyChainValidation
|
||||
*/
|
||||
private SupplyChainValidation buildValidationRecord(
|
||||
final SupplyChainValidation.ValidationType validationType,
|
||||
final AppraisalStatus.Status result, final String message,
|
||||
final ArchivableEntity archivableEntity, final Level logLevel) {
|
||||
List<ArchivableEntity> aeList = new ArrayList<>();
|
||||
if (archivableEntity != null) {
|
||||
aeList.add(archivableEntity);
|
||||
}
|
||||
|
||||
log.log(logLevel, message);
|
||||
return new SupplyChainValidation(validationType, result, aeList, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the entire CA chain (up to a trusted
|
||||
* self-signed certificate) for the given certificate. This method will look
|
||||
* up CA certificates that have a matching issuer organization as the given
|
||||
* certificate, and will perform that operation recursively until all
|
||||
* certificates for all relevant organizations have been retrieved. For that
|
||||
* reason, the returned set of certificates may be larger than the the
|
||||
* single trust chain for the queried certificate, but is guaranteed to
|
||||
* include the trust chain if it exists in this class' CertificateManager.
|
||||
* Returns the certificate authority credentials in a KeyStore.
|
||||
*
|
||||
* @param credential the credential whose CA chain should be retrieved
|
||||
* @return A keystore containing all relevant CA credentials to the given
|
||||
* certificate's organization or null if the keystore can't be assembled
|
||||
*/
|
||||
public KeyStore getCaChain(final Certificate credential) {
|
||||
KeyStore caKeyStore = null;
|
||||
try {
|
||||
caKeyStore = caCertSetToKeystore(getCaChainRec(credential, Collections.emptySet()));
|
||||
} catch (KeyStoreException | IOException e) {
|
||||
log.error("Unable to assemble CA keystore", e);
|
||||
}
|
||||
return caKeyStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a recursive method which is used to retrieve the entire CA chain
|
||||
* (up to a trusted self-signed certificate) for the given certificate. This
|
||||
* method will look up CA certificates that have a matching issuer
|
||||
* organization as the given certificate, and will perform that operation
|
||||
* recursively until all certificates for all relevant organizations have
|
||||
* been retrieved. For that reason, the returned set of certificates may be
|
||||
* larger than the the single trust chain for the queried certificate, but
|
||||
* is guaranteed to include the trust chain if it exists in this class'
|
||||
* CertificateManager.
|
||||
* <p>
|
||||
* Implementation notes: 1. Queries for CA certs with a subject org matching
|
||||
* the given (argument's) issuer org 2. Add that org to
|
||||
* queriedOrganizations, so we don't search for that organization again 3.
|
||||
* For each returned CA cert, add that cert to the result set, and recurse
|
||||
* with that as the argument (to go up the chain), if and only if we haven't
|
||||
* already queried for that organization (which prevents infinite loops on
|
||||
* certs with an identical subject and issuer org)
|
||||
*
|
||||
* @param credential the credential whose CA chain should be retrieved
|
||||
* @param previouslyQueriedSubjects a list of organizations to refrain
|
||||
* from querying
|
||||
* @return a Set containing all relevant CA credentials to the given
|
||||
* certificate's organization
|
||||
*/
|
||||
private Set<CertificateAuthorityCredential> getCaChainRec(
|
||||
final Certificate credential,
|
||||
final Set<String> previouslyQueriedSubjects) {
|
||||
CertificateAuthorityCredential skiCA = null;
|
||||
List<CertificateAuthorityCredential> certAuthsWithMatchingIssuer = new LinkedList<>();
|
||||
if (credential.getAuthorityKeyIdentifier() != null
|
||||
&& !credential.getAuthorityKeyIdentifier().isEmpty()) {
|
||||
byte[] bytes = Hex.decode(credential.getAuthorityKeyIdentifier());
|
||||
// CYRUS is SKI unique?
|
||||
skiCA = caCredentialRepository.findBySubjectKeyIdentifier(bytes);
|
||||
}
|
||||
|
||||
if (skiCA == null) {
|
||||
if (credential.getIssuerSorted() == null
|
||||
|| credential.getIssuerSorted().isEmpty()) {
|
||||
certAuthsWithMatchingIssuer = caCredentialRepository.findBySubject(credential.getIssuer());
|
||||
} else {
|
||||
//Get certificates by subject organization
|
||||
certAuthsWithMatchingIssuer = caCredentialRepository.findBySubjectSorted(credential.getIssuerSorted());
|
||||
}
|
||||
} else {
|
||||
certAuthsWithMatchingIssuer.add(skiCA);
|
||||
}
|
||||
Set<String> queriedOrganizations = new HashSet<>(previouslyQueriedSubjects);
|
||||
queriedOrganizations.add(credential.getIssuer());
|
||||
|
||||
HashSet<CertificateAuthorityCredential> caCreds = new HashSet<>();
|
||||
for (CertificateAuthorityCredential cred : certAuthsWithMatchingIssuer) {
|
||||
caCreds.add(cred);
|
||||
if (!BouncyCastleUtils.x500NameCompare(cred.getIssuer(),
|
||||
cred.getSubject())) {
|
||||
caCreds.addAll(getCaChainRec(cred, queriedOrganizations));
|
||||
}
|
||||
}
|
||||
return caCreds;
|
||||
}
|
||||
|
||||
private KeyStore caCertSetToKeystore(final Set<CertificateAuthorityCredential> certs)
|
||||
throws KeyStoreException, IOException {
|
||||
KeyStore keyStore = KeyStore.getInstance("JKS");
|
||||
try {
|
||||
keyStore.load(null, "".toCharArray());
|
||||
for (Certificate cert : certs) {
|
||||
keyStore.setCertificateEntry(cert.getId().toString(), cert.getX509Certificate());
|
||||
}
|
||||
} catch (IOException | CertificateException | NoSuchAlgorithmException e) {
|
||||
throw new IOException("Could not create and populate keystore", e);
|
||||
}
|
||||
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private String[] buildStoredPcrs(final String pcrContent, final int algorithmLength) {
|
||||
// we have a full set of PCR values
|
||||
String[] pcrSet = pcrContent.split("\\n");
|
||||
String[] storedPcrs = new String[TPMMeasurementRecord.MAX_PCR_ID + 1];
|
||||
|
||||
// we need to scroll through the entire list until we find
|
||||
// a matching hash length
|
||||
int offset = 1;
|
||||
|
||||
for (int i = 0; i < pcrSet.length; i++) {
|
||||
if (pcrSet[i].contains("sha")) {
|
||||
// entered a new set, check size
|
||||
if (pcrSet[i + offset].split(":")[1].trim().length()
|
||||
== algorithmLength) {
|
||||
// found the matching set
|
||||
for (int j = 0; j <= TPMMeasurementRecord.MAX_PCR_ID; j++) {
|
||||
storedPcrs[j] = pcrSet[++i].split(":")[1].trim();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return storedPcrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get a fresh load of the default policy from the DB.
|
||||
*
|
||||
* @return The default Supply Chain Policy
|
||||
*/
|
||||
private PolicySettings getPolicySettings() {
|
||||
PolicySettings defaultSettings = this.policyRepository.findByName("Default");
|
||||
|
||||
if (defaultSettings == null) {
|
||||
defaultSettings = new PolicySettings("Default", "Settings are configured for no validation flags set.");
|
||||
}
|
||||
return defaultSettings;
|
||||
}
|
||||
}
|
@ -0,0 +1,338 @@
|
||||
package hirs.attestationca.persist.service;
|
||||
|
||||
import hirs.attestationca.persist.entity.ArchivableEntity;
|
||||
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
|
||||
import hirs.attestationca.persist.entity.manager.CertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.Device;
|
||||
import hirs.attestationca.persist.entity.userdefined.PolicySettings;
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
|
||||
import hirs.attestationca.persist.enums.AppraisalStatus;
|
||||
import hirs.attestationca.persist.validation.CertificateAttributeScvValidator;
|
||||
import hirs.attestationca.persist.validation.CredentialValidator;
|
||||
import hirs.attestationca.persist.validation.FirmwareScvValidator;
|
||||
import hirs.utils.BouncyCastleUtils;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Log4j2
|
||||
public class ValidationService {
|
||||
|
||||
public static SupplyChainValidation evaluateEndorsementCredentialStatus(
|
||||
final EndorsementCredential ec,
|
||||
final CACredentialRepository caCredentialRepository,
|
||||
final boolean acceptExpiredCerts) {
|
||||
final SupplyChainValidation.ValidationType validationType
|
||||
= SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL;
|
||||
log.info("Validating endorsement credential");
|
||||
if (ec == null) {
|
||||
log.error("No endorsement credential to validate");
|
||||
return buildValidationRecord(validationType,
|
||||
AppraisalStatus.Status.FAIL, "Endorsement credential is missing",
|
||||
null, Level.ERROR);
|
||||
}
|
||||
|
||||
KeyStore ecStore = getCaChain(ec, caCredentialRepository);
|
||||
AppraisalStatus result = CredentialValidator.
|
||||
validateEndorsementCredential(ec, ecStore, acceptExpiredCerts);
|
||||
switch (result.getAppStatus()) {
|
||||
case PASS:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.PASS,
|
||||
result.getMessage(), ec, Level.INFO);
|
||||
case FAIL:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL,
|
||||
result.getMessage(), ec, Level.WARN);
|
||||
case ERROR:
|
||||
default:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
|
||||
result.getMessage(), ec, Level.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public static SupplyChainValidation evaluatePlatformCredentialStatus(
|
||||
final PlatformCredential pc,
|
||||
final KeyStore trustedCertificateAuthority, final boolean acceptExpiredCerts) {
|
||||
final SupplyChainValidation.ValidationType validationType
|
||||
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL;
|
||||
|
||||
if (pc == null) {
|
||||
log.error("No platform credential to validate");
|
||||
return buildValidationRecord(validationType,
|
||||
AppraisalStatus.Status.FAIL, "Empty Platform credential", null, Level.ERROR);
|
||||
}
|
||||
log.info("Validating Platform Credential");
|
||||
AppraisalStatus result = CredentialValidator.validatePlatformCredential(pc,
|
||||
trustedCertificateAuthority, acceptExpiredCerts);
|
||||
switch (result.getAppStatus()) {
|
||||
case PASS:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.PASS,
|
||||
result.getMessage(), pc, Level.INFO);
|
||||
case FAIL:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL,
|
||||
result.getMessage(), pc, Level.WARN);
|
||||
case ERROR:
|
||||
default:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
|
||||
result.getMessage(), pc, Level.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public static SupplyChainValidation evaluatePCAttributesStatus(
|
||||
final PlatformCredential pc, final DeviceInfoReport deviceInfoReport,
|
||||
final EndorsementCredential ec,
|
||||
final CertificateRepository certificateRepository,
|
||||
final ComponentResultRepository componentResultRepository) {
|
||||
final SupplyChainValidation.ValidationType validationType
|
||||
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES;
|
||||
|
||||
if (pc == null) {
|
||||
log.error("No platform credential to validate");
|
||||
return buildValidationRecord(validationType,
|
||||
AppraisalStatus.Status.FAIL, "Platform credential is missing",
|
||||
null, Level.ERROR);
|
||||
}
|
||||
log.info("Validating platform credential attributes");
|
||||
AppraisalStatus result = CredentialValidator.
|
||||
validatePlatformCredentialAttributes(pc, deviceInfoReport, ec);
|
||||
switch (result.getAppStatus()) {
|
||||
case PASS:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.PASS,
|
||||
result.getMessage(), pc, Level.INFO);
|
||||
case FAIL:
|
||||
if (!result.getAdditionalInfo().isEmpty()) {
|
||||
pc.setComponentFailures(result.getAdditionalInfo());
|
||||
pc.setComponentFailureMessage(result.getMessage());
|
||||
certificateRepository.save(pc);
|
||||
for (ComponentResult componentResult
|
||||
: CertificateAttributeScvValidator.getComponentResultList()) {
|
||||
componentResultRepository.save(componentResult);
|
||||
}
|
||||
}
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL,
|
||||
result.getMessage(), pc, Level.WARN);
|
||||
case ERROR:
|
||||
default:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
|
||||
result.getMessage(), pc, Level.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public static SupplyChainValidation evaluateDeltaAttributesStatus(
|
||||
final PlatformCredential delta,
|
||||
final DeviceInfoReport deviceInfoReport,
|
||||
final PlatformCredential base,
|
||||
final Map<PlatformCredential, SupplyChainValidation> deltaMapping,
|
||||
final CertificateRepository certificateRepository) {
|
||||
final SupplyChainValidation.ValidationType validationType
|
||||
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES;
|
||||
|
||||
if (delta == null) {
|
||||
log.error("No delta certificate to validate");
|
||||
return buildValidationRecord(validationType,
|
||||
AppraisalStatus.Status.FAIL, "Delta platform certificate is missing",
|
||||
null, Level.ERROR);
|
||||
}
|
||||
log.info("Validating delta platform certificate attributes");
|
||||
AppraisalStatus result = CertificateAttributeScvValidator.
|
||||
validateDeltaPlatformCredentialAttributes(delta, deviceInfoReport,
|
||||
base, deltaMapping);
|
||||
switch (result.getAppStatus()) {
|
||||
case PASS:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.PASS,
|
||||
result.getMessage(), delta, Level.INFO);
|
||||
case FAIL:
|
||||
if (!result.getAdditionalInfo().isEmpty()) {
|
||||
base.setComponentFailures(result.getAdditionalInfo());
|
||||
base.setComponentFailureMessage(result.getMessage());
|
||||
certificateRepository.save(base);
|
||||
}
|
||||
// we are adding things to componentFailures
|
||||
certificateRepository.save(delta);
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.FAIL,
|
||||
result.getMessage(), delta, Level.WARN);
|
||||
case ERROR:
|
||||
default:
|
||||
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
|
||||
result.getMessage(), delta, Level.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public static SupplyChainValidation evaluateFirmwareStatus(
|
||||
final Device device,
|
||||
final PolicySettings policySettings, final ReferenceManifestRepository rimRepo,
|
||||
final ReferenceDigestValueRepository rdvRepo,
|
||||
final CACredentialRepository caRepo) {
|
||||
final SupplyChainValidation.ValidationType validationType
|
||||
= SupplyChainValidation.ValidationType.FIRMWARE;
|
||||
|
||||
AppraisalStatus result = FirmwareScvValidator.validateFirmware(device, policySettings,
|
||||
rimRepo, rdvRepo, caRepo);
|
||||
Level logLevel;
|
||||
|
||||
switch (result.getAppStatus()) {
|
||||
case PASS:
|
||||
logLevel = Level.INFO;
|
||||
break;
|
||||
case FAIL:
|
||||
logLevel = Level.WARN;
|
||||
break;
|
||||
case ERROR:
|
||||
default:
|
||||
logLevel = Level.ERROR;
|
||||
}
|
||||
return buildValidationRecord(validationType, result.getAppStatus(),
|
||||
result.getMessage(), null, logLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a supply chain validation record and logs the validation message
|
||||
* at the specified log level.
|
||||
*
|
||||
* @param validationType the type of validation
|
||||
* @param result the appraisal status
|
||||
* @param message the validation message to include in the summary and log
|
||||
* @param archivableEntity the archivableEntity associated with the
|
||||
* validation
|
||||
* @param logLevel the log level
|
||||
* @return a SupplyChainValidation
|
||||
*/
|
||||
public static SupplyChainValidation buildValidationRecord(
|
||||
final SupplyChainValidation.ValidationType validationType,
|
||||
final AppraisalStatus.Status result, final String message,
|
||||
final ArchivableEntity archivableEntity, final Level logLevel) {
|
||||
List<ArchivableEntity> aeList = new ArrayList<>();
|
||||
if (archivableEntity != null) {
|
||||
aeList.add(archivableEntity);
|
||||
}
|
||||
|
||||
log.log(logLevel, message);
|
||||
return new SupplyChainValidation(validationType, result, aeList, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to retrieve the entire CA chain (up to a trusted
|
||||
* self-signed certificate) for the given certificate. This method will look
|
||||
* up CA certificates that have a matching issuer organization as the given
|
||||
* certificate, and will perform that operation recursively until all
|
||||
* certificates for all relevant organizations have been retrieved. For that
|
||||
* reason, the returned set of certificates may be larger than the the
|
||||
* single trust chain for the queried certificate, but is guaranteed to
|
||||
* include the trust chain if it exists in this class' CertificateManager.
|
||||
* Returns the certificate authority credentials in a KeyStore.
|
||||
*
|
||||
* @param certificate the credential whose CA chain should be retrieved
|
||||
* @param caCredentialRepository db service to get CA Certs
|
||||
* @return A keystore containing all relevant CA credentials to the given
|
||||
* certificate's organization or null if the keystore can't be assembled
|
||||
*/
|
||||
public static KeyStore getCaChain(final Certificate certificate,
|
||||
final CACredentialRepository caCredentialRepository) {
|
||||
KeyStore caKeyStore = null;
|
||||
try {
|
||||
caKeyStore = caCertSetToKeystore(getCaChainRec(certificate, Collections.emptySet(),
|
||||
caCredentialRepository));
|
||||
} catch (KeyStoreException | IOException e) {
|
||||
log.error("Unable to assemble CA keystore", e);
|
||||
}
|
||||
return caKeyStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a recursive method which is used to retrieve the entire CA chain
|
||||
* (up to a trusted self-signed certificate) for the given certificate. This
|
||||
* method will look up CA certificates that have a matching issuer
|
||||
* organization as the given certificate, and will perform that operation
|
||||
* recursively until all certificates for all relevant organizations have
|
||||
* been retrieved. For that reason, the returned set of certificates may be
|
||||
* larger than the the single trust chain for the queried certificate, but
|
||||
* is guaranteed to include the trust chain if it exists in this class'
|
||||
* CertificateManager.
|
||||
* <p>
|
||||
* Implementation notes: 1. Queries for CA certs with a subject org matching
|
||||
* the given (argument's) issuer org 2. Add that org to
|
||||
* queriedOrganizations, so we don't search for that organization again 3.
|
||||
* For each returned CA cert, add that cert to the result set, and recurse
|
||||
* with that as the argument (to go up the chain), if and only if we haven't
|
||||
* already queried for that organization (which prevents infinite loops on
|
||||
* certs with an identical subject and issuer org)
|
||||
*
|
||||
* @param credential the credential whose CA chain should be retrieved
|
||||
* @param previouslyQueriedSubjects a list of organizations to refrain
|
||||
* from querying
|
||||
* @return a Set containing all relevant CA credentials to the given
|
||||
* certificate's organization
|
||||
*/
|
||||
public static Set<CertificateAuthorityCredential> getCaChainRec(
|
||||
final Certificate credential,
|
||||
final Set<String> previouslyQueriedSubjects,
|
||||
final CACredentialRepository caCredentialRepository) {
|
||||
CertificateAuthorityCredential skiCA = null;
|
||||
List<CertificateAuthorityCredential> certAuthsWithMatchingIssuer = new LinkedList<>();
|
||||
if (credential.getAuthorityKeyIdentifier() != null
|
||||
&& !credential.getAuthorityKeyIdentifier().isEmpty()) {
|
||||
byte[] bytes = Hex.decode(credential.getAuthorityKeyIdentifier());
|
||||
skiCA = caCredentialRepository.findBySubjectKeyIdentifier(bytes);
|
||||
}
|
||||
|
||||
if (skiCA == null) {
|
||||
if (credential.getIssuerSorted() == null
|
||||
|| credential.getIssuerSorted().isEmpty()) {
|
||||
certAuthsWithMatchingIssuer = caCredentialRepository.findBySubject(credential.getIssuer());
|
||||
} else {
|
||||
//Get certificates by subject organization
|
||||
certAuthsWithMatchingIssuer = caCredentialRepository.findBySubjectSorted(credential.getIssuerSorted());
|
||||
}
|
||||
} else {
|
||||
certAuthsWithMatchingIssuer.add(skiCA);
|
||||
}
|
||||
Set<String> queriedOrganizations = new HashSet<>(previouslyQueriedSubjects);
|
||||
queriedOrganizations.add(credential.getIssuer());
|
||||
|
||||
HashSet<CertificateAuthorityCredential> caCreds = new HashSet<>();
|
||||
for (CertificateAuthorityCredential cred : certAuthsWithMatchingIssuer) {
|
||||
caCreds.add(cred);
|
||||
if (!BouncyCastleUtils.x500NameCompare(cred.getIssuer(),
|
||||
cred.getSubject())) {
|
||||
caCreds.addAll(getCaChainRec(cred, queriedOrganizations, caCredentialRepository));
|
||||
}
|
||||
}
|
||||
return caCreds;
|
||||
}
|
||||
|
||||
public static KeyStore caCertSetToKeystore(final Set<CertificateAuthorityCredential> certs)
|
||||
throws KeyStoreException, IOException {
|
||||
KeyStore keyStore = KeyStore.getInstance("JKS");
|
||||
try {
|
||||
keyStore.load(null, "".toCharArray());
|
||||
for (Certificate cert : certs) {
|
||||
keyStore.setCertificateEntry(cert.getId().toString(), cert.getX509Certificate());
|
||||
}
|
||||
} catch (IOException | CertificateException | NoSuchAlgorithmException e) {
|
||||
throw new IOException("Could not create and populate keystore", e);
|
||||
}
|
||||
|
||||
return keyStore;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package hirs.attestationca.portal.page.utils;
|
||||
package hirs.attestationca.persist.util;
|
||||
|
||||
import com.github.marandus.pciid.model.Device;
|
||||
import com.github.marandus.pciid.model.Vendor;
|
File diff suppressed because it is too large
Load Diff
@ -1,19 +1,92 @@
|
||||
package hirs.attestationca.persist.validation;
|
||||
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
|
||||
import hirs.attestationca.persist.enums.AppraisalStatus;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.bouncycastle.cert.X509AttributeCertificateHolder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Map;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.cert.CertificateExpiredException;
|
||||
import java.security.cert.CertificateNotYetValidException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Date;
|
||||
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.ERROR;
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL;
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS;
|
||||
|
||||
@Log4j2
|
||||
public class CredentialValidator extends SupplyChainCredentialValidator {
|
||||
|
||||
/**
|
||||
* Checks if the endorsement credential is valid.
|
||||
*
|
||||
* @param ec the endorsement credential to verify.
|
||||
* @param trustStore trust store holding trusted trusted certificates.
|
||||
* @param acceptExpired whether or not to accept expired and not yet valid certificates
|
||||
* as valid.
|
||||
* @return the result of the validation.
|
||||
*/
|
||||
public static AppraisalStatus validateEndorsementCredential(final EndorsementCredential ec,
|
||||
final KeyStore trustStore,
|
||||
final boolean acceptExpired) {
|
||||
final String baseErrorMessage = "Can't validate endorsement credential attributes without ";
|
||||
String message;
|
||||
if (ec == null) {
|
||||
message = baseErrorMessage + "an endorsement credential";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
if (trustStore == null) {
|
||||
message = baseErrorMessage + "a trust store";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
|
||||
boolean keyInStore = false;
|
||||
try {
|
||||
keyInStore = trustStore.size() < 1;
|
||||
} catch (KeyStoreException ksEx) {
|
||||
log.error(ksEx.getMessage());
|
||||
}
|
||||
|
||||
if (keyInStore) {
|
||||
message = baseErrorMessage + "keys in the trust store";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
|
||||
try {
|
||||
X509Certificate verifiableCert = ec.getX509Certificate();
|
||||
|
||||
// check validity period, currently acceptExpired will also accept not yet
|
||||
// valid certificates
|
||||
if (!acceptExpired) {
|
||||
verifiableCert.checkValidity();
|
||||
}
|
||||
|
||||
if (verifyCertificate(verifiableCert, trustStore)) {
|
||||
return new AppraisalStatus(PASS, ENDORSEMENT_VALID);
|
||||
} else {
|
||||
return new AppraisalStatus(FAIL, "Endorsement credential does not have a valid "
|
||||
+ "signature chain in the trust store");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
message = "Couldn't retrieve X509 certificate from endorsement credential";
|
||||
return new AppraisalStatus(ERROR, message + " " + e.getMessage());
|
||||
} catch (SupplyChainValidatorException e) {
|
||||
message = "An error occurred indicating the credential is not valid";
|
||||
return new AppraisalStatus(ERROR, message + " " + e.getMessage());
|
||||
} catch (CertificateExpiredException e) {
|
||||
message = "The endorsement credential is expired";
|
||||
return new AppraisalStatus(FAIL, message + " " + e.getMessage());
|
||||
} catch (CertificateNotYetValidException e) {
|
||||
message = "The endorsement credential is not yet valid";
|
||||
return new AppraisalStatus(FAIL, message + " " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class used to support supply chain validation by performing the actual
|
||||
* validation of credentials.
|
||||
*/
|
||||
public interface CredentialValidator {
|
||||
/**
|
||||
* Checks if the platform credential is valid.
|
||||
*
|
||||
@ -22,47 +95,106 @@ public interface CredentialValidator {
|
||||
* @param acceptExpired whether or not to accept expired certificates as valid.
|
||||
* @return The result of the validation.
|
||||
*/
|
||||
AppraisalStatus validatePlatformCredential(PlatformCredential pc,
|
||||
KeyStore trustStore,
|
||||
boolean acceptExpired);
|
||||
public static AppraisalStatus validatePlatformCredential(final PlatformCredential pc,
|
||||
final KeyStore trustStore,
|
||||
final boolean acceptExpired) {
|
||||
final String baseErrorMessage = "Can't validate platform credential without ";
|
||||
String message;
|
||||
String certVerifyMsg;
|
||||
if (pc == null) {
|
||||
message = baseErrorMessage + "a platform credential";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
try {
|
||||
if (trustStore == null || trustStore.size() == 0) {
|
||||
message = baseErrorMessage + "an Issuer Cert in the Trust Store";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
message = baseErrorMessage + "an initialized trust store";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
|
||||
X509AttributeCertificateHolder attributeCert = null;
|
||||
try {
|
||||
attributeCert = pc.getX509AttributeCertificateHolder();
|
||||
} catch (IOException e) {
|
||||
message = "Could not retrieve X509 Attribute certificate";
|
||||
log.error(message, e);
|
||||
return new AppraisalStatus(FAIL, message + " " + e.getMessage());
|
||||
}
|
||||
|
||||
// check validity period, currently acceptExpired will also accept not yet
|
||||
// valid certificates
|
||||
if (!acceptExpired && !pc.isValidOn(new Date())) {
|
||||
message = "Platform credential has expired";
|
||||
// if not valid at the current time
|
||||
log.debug(message);
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
|
||||
// verify cert against truststore
|
||||
try {
|
||||
certVerifyMsg = verifyCertificate(attributeCert, trustStore);
|
||||
if (certVerifyMsg.isEmpty()) {
|
||||
message = PLATFORM_VALID;
|
||||
log.debug(message);
|
||||
return new AppraisalStatus(PASS, message);
|
||||
} else {
|
||||
message = String.format("Platform credential failed verification%n%s",
|
||||
certVerifyMsg);
|
||||
log.debug(message);
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
} catch (SupplyChainValidatorException scvEx) {
|
||||
message = "An error occurred indicating the credential is not valid";
|
||||
log.warn(message, scvEx);
|
||||
return new AppraisalStatus(FAIL, message + " " + scvEx.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the platform credential's attributes are valid.
|
||||
* @param pc The platform credential to verify.
|
||||
* @param deviceInfoReport Report containing the serial numbers of the platform to be validated.
|
||||
* @param ec The endorsement credential supplied from the same identity request as
|
||||
* the platform credential.
|
||||
* @return The result of the validation.
|
||||
*/
|
||||
AppraisalStatus validatePlatformCredentialAttributes(PlatformCredential pc,
|
||||
DeviceInfoReport deviceInfoReport,
|
||||
EndorsementCredential ec);
|
||||
|
||||
/**
|
||||
* Checks if the delta credential's attributes are valid.
|
||||
* @param delta the delta credential to verify
|
||||
* @param platformCredential The platform credential to verify.
|
||||
* @param deviceInfoReport The device info report containing
|
||||
* serial number of the platform to be validated.
|
||||
* @param base the base credential from the same identity request
|
||||
* as the delta credential.
|
||||
* @param deltaMapping delta certificates associated with the
|
||||
* delta supply validation.
|
||||
* @return the result of the validation.
|
||||
* @param endorsementCredential The endorsement credential supplied from the same
|
||||
* identity request as the platform credential.
|
||||
* @return The result of the validation.
|
||||
*/
|
||||
AppraisalStatus validateDeltaPlatformCredentialAttributes(PlatformCredential delta,
|
||||
DeviceInfoReport deviceInfoReport,
|
||||
PlatformCredential base,
|
||||
Map<PlatformCredential,
|
||||
SupplyChainValidation> deltaMapping);
|
||||
/**
|
||||
* Checks if the endorsement credential is valid.
|
||||
*
|
||||
* @param ec the endorsement credential to verify.
|
||||
* @param trustStore trust store holding trusted trusted certificates.
|
||||
* @param acceptExpired whether or not to accept expired certificates as valid.
|
||||
* @return the result of the validation.
|
||||
*/
|
||||
AppraisalStatus validateEndorsementCredential(EndorsementCredential ec,
|
||||
KeyStore trustStore,
|
||||
boolean acceptExpired);
|
||||
}
|
||||
public static AppraisalStatus validatePlatformCredentialAttributes(
|
||||
final PlatformCredential platformCredential,
|
||||
final DeviceInfoReport deviceInfoReport,
|
||||
final EndorsementCredential endorsementCredential) {
|
||||
final String baseErrorMessage = "Can't validate platform credential attributes without ";
|
||||
String message;
|
||||
if (platformCredential == null) {
|
||||
message = baseErrorMessage + "a platform credential";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
if (deviceInfoReport == null) {
|
||||
message = baseErrorMessage + "a device info report";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
if (endorsementCredential == null) {
|
||||
message = baseErrorMessage + "an endorsement credential";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
|
||||
// Quick, early check if the platform credential references the endorsement credential
|
||||
if (!endorsementCredential.getSerialNumber()
|
||||
.equals(platformCredential.getHolderSerialNumber())) {
|
||||
message = "Platform Credential holder serial number does not match "
|
||||
+ "the Endorsement Credential's serial number";
|
||||
return new AppraisalStatus(FAIL, message);
|
||||
}
|
||||
|
||||
String credentialType = platformCredential.getCredentialType();
|
||||
if (PlatformCredential.CERTIFICATE_TYPE_2_0.equals(credentialType)) {
|
||||
return CertificateAttributeScvValidator.validatePlatformCredentialAttributesV2p0(
|
||||
platformCredential, deviceInfoReport);
|
||||
}
|
||||
return CertificateAttributeScvValidator.validatePlatformCredentialAttributesV1p2(
|
||||
platformCredential, deviceInfoReport);
|
||||
}
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
package hirs.attestationca.persist.validation;
|
||||
|
||||
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Device;
|
||||
import hirs.attestationca.persist.entity.userdefined.PolicySettings;
|
||||
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.BaseReferenceManifest;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
|
||||
import hirs.attestationca.persist.enums.AppraisalStatus;
|
||||
import hirs.attestationca.persist.service.ValidationService;
|
||||
import hirs.utils.SwidResource;
|
||||
import hirs.utils.tpm.eventlog.TCGEventLog;
|
||||
import hirs.utils.tpm.eventlog.TpmPcrEvent;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.FAIL;
|
||||
import static hirs.attestationca.persist.enums.AppraisalStatus.Status.PASS;
|
||||
|
||||
@Log4j2
|
||||
public class FirmwareScvValidator extends SupplyChainCredentialValidator {
|
||||
|
||||
private static PcrValidator pcrValidator;
|
||||
|
||||
@SuppressWarnings("methodlength")
|
||||
public static AppraisalStatus validateFirmware(
|
||||
final Device device, final PolicySettings policySettings,
|
||||
final ReferenceManifestRepository referenceManifestRepository,
|
||||
final ReferenceDigestValueRepository referenceDigestValueRepository,
|
||||
final CACredentialRepository caCredentialRepository) {
|
||||
boolean passed = true;
|
||||
String[] baseline = new String[Integer.SIZE];
|
||||
AppraisalStatus fwStatus = null;
|
||||
String hostName = device.getDeviceInfo().getNetworkInfo().getHostname();
|
||||
String manufacturer = device.getDeviceInfo()
|
||||
.getHardwareInfo().getManufacturer();
|
||||
ReferenceManifest validationObject;
|
||||
List<BaseReferenceManifest> baseReferenceManifests = null;
|
||||
BaseReferenceManifest baseReferenceManifest = null;
|
||||
ReferenceManifest supportReferenceManifest = null;
|
||||
EventLogMeasurements measurement = null;
|
||||
|
||||
baseReferenceManifests = referenceManifestRepository.findAllBaseRims();
|
||||
|
||||
for (BaseReferenceManifest bRim : baseReferenceManifests) {
|
||||
if (bRim.getDeviceName().equals(hostName)
|
||||
&& !bRim.isSwidSupplemental() && !bRim.isSwidPatch()) {
|
||||
baseReferenceManifest = bRim;
|
||||
}
|
||||
}
|
||||
|
||||
String failedString = "";
|
||||
if (baseReferenceManifest == null) {
|
||||
failedString = "Base Reference Integrity Manifest\n";
|
||||
passed = false;
|
||||
} else {
|
||||
measurement = (EventLogMeasurements) referenceManifestRepository.findByHexDecHash(
|
||||
baseReferenceManifest.getEventLogHash());
|
||||
|
||||
if (measurement == null) {
|
||||
measurement = referenceManifestRepository.byMeasurementDeviceName(
|
||||
baseReferenceManifest.getDeviceName());
|
||||
}
|
||||
}
|
||||
|
||||
if (measurement == null) {
|
||||
failedString += "Bios measurement";
|
||||
passed = false;
|
||||
}
|
||||
validationObject = measurement;
|
||||
|
||||
if (passed) {
|
||||
List<SwidResource> resources =
|
||||
((BaseReferenceManifest) baseReferenceManifest).getFileResources();
|
||||
fwStatus = new AppraisalStatus(PASS,
|
||||
SupplyChainCredentialValidator.FIRMWARE_VALID);
|
||||
|
||||
// verify signatures
|
||||
ReferenceManifestValidator referenceManifestValidator =
|
||||
new ReferenceManifestValidator();
|
||||
referenceManifestValidator.setRim(baseReferenceManifest);
|
||||
|
||||
//Validate signing cert
|
||||
List<CertificateAuthorityCredential> allCerts = caCredentialRepository.findAll();
|
||||
CertificateAuthorityCredential signingCert = null;
|
||||
for (CertificateAuthorityCredential cert : allCerts) {
|
||||
signingCert = cert;
|
||||
KeyStore keyStore = ValidationService.getCaChain(signingCert,
|
||||
caCredentialRepository);
|
||||
if (referenceManifestValidator.validateXmlSignature(signingCert)) {
|
||||
try {
|
||||
if (!SupplyChainCredentialValidator.verifyCertificate(
|
||||
signingCert.getX509Certificate(), keyStore)) {
|
||||
passed = false;
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
"Firmware validation failed: invalid certificate path.");
|
||||
validationObject = baseReferenceManifest;
|
||||
}
|
||||
} catch (IOException ioEx) {
|
||||
log.error("Error getting X509 cert from manager: " + ioEx.getMessage());
|
||||
} catch (SupplyChainValidatorException scvEx) {
|
||||
log.error("Error validating cert against keystore: " + scvEx.getMessage());
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
"Firmware validation failed: invalid certificate path.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (SwidResource swidRes : resources) {
|
||||
supportReferenceManifest = referenceManifestRepository.findByHexDecHash(
|
||||
swidRes.getHashValue());
|
||||
if (supportReferenceManifest != null) {
|
||||
// Removed the filename check from this if statement
|
||||
referenceManifestValidator.validateSupportRimHash(
|
||||
supportReferenceManifest.getRimBytes(), swidRes.getHashValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (passed && signingCert == null) {
|
||||
passed = false;
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
"Firmware validation failed: signing cert not found.");
|
||||
}
|
||||
|
||||
if (passed && supportReferenceManifest == null) {
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
"Support Reference Integrity Manifest can not be found");
|
||||
passed = false;
|
||||
}
|
||||
|
||||
if (passed && !referenceManifestValidator.isSignatureValid()) {
|
||||
passed = false;
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
"Firmware validation failed: Signature validation "
|
||||
+ "failed for Base RIM.");
|
||||
}
|
||||
|
||||
if (passed && !referenceManifestValidator.isSupportRimValid()) {
|
||||
passed = false;
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
"Firmware validation failed: Hash validation "
|
||||
+ "failed for Support RIM.");
|
||||
}
|
||||
|
||||
if (passed) {
|
||||
TCGEventLog logProcessor;
|
||||
try {
|
||||
logProcessor = new TCGEventLog(supportReferenceManifest.getRimBytes());
|
||||
baseline = logProcessor.getExpectedPCRValues();
|
||||
} catch (CertificateException cEx) {
|
||||
log.error(cEx);
|
||||
} catch (NoSuchAlgorithmException noSaEx) {
|
||||
log.error(noSaEx);
|
||||
} catch (IOException ioEx) {
|
||||
log.error(ioEx);
|
||||
}
|
||||
|
||||
// part 1 of firmware validation check: PCR baseline match
|
||||
pcrValidator = new PcrValidator(baseline);
|
||||
|
||||
if (baseline.length > 0) {
|
||||
String pcrContent = "";
|
||||
pcrContent = new String(device.getDeviceInfo().getTpmInfo().getPcrValues());
|
||||
|
||||
if (pcrContent.isEmpty()) {
|
||||
fwStatus = new AppraisalStatus(FAIL,
|
||||
"Firmware validation failed: Client did not "
|
||||
+ "provide pcr values.");
|
||||
log.warn(String.format(
|
||||
"Firmware validation failed: Client (%s) did not "
|
||||
+ "provide pcr values.", device.getName()));
|
||||
} else {
|
||||
// we have a full set of PCR values
|
||||
//int algorithmLength = baseline[0].length();
|
||||
//String[] storedPcrs = buildStoredPcrs(pcrContent, algorithmLength);
|
||||
//pcrPolicy.validatePcrs(storedPcrs);
|
||||
|
||||
// part 2 of firmware validation check: bios measurements
|
||||
// vs baseline tcg event log
|
||||
// find the measurement
|
||||
TCGEventLog tcgMeasurementLog;
|
||||
LinkedList<TpmPcrEvent> tpmPcrEvents = new LinkedList<>();
|
||||
List<ReferenceDigestValue> eventValue;
|
||||
HashMap<String, ReferenceDigestValue> eventValueMap = new HashMap<>();
|
||||
try {
|
||||
if (measurement.getDeviceName().equals(hostName)) {
|
||||
tcgMeasurementLog = new TCGEventLog(measurement.getRimBytes());
|
||||
eventValue = referenceDigestValueRepository
|
||||
.findValuesByBaseRimId(baseReferenceManifest.getId());
|
||||
for (ReferenceDigestValue rdv : eventValue) {
|
||||
eventValueMap.put(rdv.getDigestValue(), rdv);
|
||||
}
|
||||
|
||||
tpmPcrEvents.addAll(pcrValidator.validateTpmEvents(
|
||||
tcgMeasurementLog, eventValueMap, policySettings));
|
||||
}
|
||||
} catch (CertificateException cEx) {
|
||||
log.error(cEx);
|
||||
} catch (NoSuchAlgorithmException noSaEx) {
|
||||
log.error(noSaEx);
|
||||
} catch (IOException ioEx) {
|
||||
log.error(ioEx);
|
||||
}
|
||||
|
||||
if (!tpmPcrEvents.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
validationObject = measurement;
|
||||
sb.append(String.format("%d digest(s) were not found:%n",
|
||||
tpmPcrEvents.size()));
|
||||
for (TpmPcrEvent tpe : tpmPcrEvents) {
|
||||
sb.append(String.format("PCR Index %d - %s%n",
|
||||
tpe.getPcrIndex(),
|
||||
tpe.getEventTypeStr()));
|
||||
}
|
||||
if (fwStatus.getAppStatus().equals(FAIL)) {
|
||||
fwStatus = new AppraisalStatus(FAIL, String.format("%s%n%s",
|
||||
fwStatus.getMessage(), sb.toString()));
|
||||
} else {
|
||||
fwStatus = new AppraisalStatus(FAIL, sb.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fwStatus = new AppraisalStatus(FAIL, "The RIM baseline could not be found.");
|
||||
}
|
||||
}
|
||||
|
||||
EventLogMeasurements eventLog = measurement;
|
||||
eventLog.setOverallValidationResult(fwStatus.getAppStatus());
|
||||
referenceManifestRepository.save(eventLog);
|
||||
} else {
|
||||
fwStatus = new AppraisalStatus(FAIL, String.format("Firmware Validation failed: "
|
||||
+ "%s for %s can not be found", failedString, hostName));
|
||||
if (measurement != null) {
|
||||
measurement.setOverallValidationResult(fwStatus.getAppStatus());
|
||||
referenceManifestRepository.save(measurement);
|
||||
}
|
||||
}
|
||||
|
||||
return fwStatus;
|
||||
}
|
||||
}
|
@ -226,4 +226,30 @@ public class PcrValidator {
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
public static String[] buildStoredPcrs(final String pcrContent, final int algorithmLength) {
|
||||
// we have a full set of PCR values
|
||||
String[] pcrSet = pcrContent.split("\\n");
|
||||
String[] storedPcrs = new String[TPMMeasurementRecord.MAX_PCR_ID + 1];
|
||||
|
||||
// we need to scroll through the entire list until we find
|
||||
// a matching hash length
|
||||
int offset = 1;
|
||||
|
||||
for (int i = 0; i < pcrSet.length; i++) {
|
||||
if (pcrSet[i].contains("sha")) {
|
||||
// entered a new set, check size
|
||||
if (pcrSet[i + offset].split(":")[1].trim().length()
|
||||
== algorithmLength) {
|
||||
// found the matching set
|
||||
for (int j = 0; j <= TPMMeasurementRecord.MAX_PCR_ID; j++) {
|
||||
storedPcrs[j] = pcrSet[++i].split(":")[1].trim();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return storedPcrs;
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +1,46 @@
|
||||
package hirs.attestationca.persist.validation;
|
||||
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidation;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.report.DeviceInfoReport;
|
||||
import hirs.attestationca.persist.enums.AppraisalStatus;
|
||||
import com.fasterxml.jackson.core.JsonFactory;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import hirs.attestationca.persist.entity.userdefined.info.ComponentInfo;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.cert.CertException;
|
||||
import org.bouncycastle.cert.X509AttributeCertificateHolder;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.operator.ContentVerifierProvider;
|
||||
import org.bouncycastle.operator.OperatorCreationException;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Security;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Log4j2
|
||||
@NoArgsConstructor
|
||||
public class SupplyChainCredentialValidator implements CredentialValidator {
|
||||
public class SupplyChainCredentialValidator {
|
||||
|
||||
public static final int NUC_VARIABLE_BIT = 159;
|
||||
/**
|
||||
* AppraisalStatus message for a valid endorsement credential appraisal.
|
||||
*/
|
||||
@ -39,34 +62,447 @@ public class SupplyChainCredentialValidator implements CredentialValidator {
|
||||
*/
|
||||
public static final String FIRMWARE_VALID = "Firmware validated";
|
||||
|
||||
private static List<ComponentResult> componentResultList = new LinkedList<>();
|
||||
/**
|
||||
* Ensure that BouncyCastle is configured as a javax.security.Security provider, as this
|
||||
* class expects it to be available.
|
||||
*/
|
||||
static {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppraisalStatus validatePlatformCredential(final PlatformCredential pc,
|
||||
final KeyStore trustStore,
|
||||
final boolean acceptExpired) {
|
||||
/**
|
||||
* Attempts to check if the certificate is validated by certificates in a cert chain. The cert
|
||||
* chain is expected to be stored in a non-ordered KeyStore (trust store). If the signing
|
||||
* certificate for the target cert is found, but it is an intermediate cert, the validation will
|
||||
* continue to try to find the signing cert of the intermediate cert. It will continue searching
|
||||
* until it follows the chain up to a root (self-signed) cert.
|
||||
*
|
||||
* @param cert
|
||||
* certificate to validate
|
||||
* @param trustStore
|
||||
* trust store holding trusted root certificates and intermediate certificates
|
||||
* @return the certificate chain if validation is successful
|
||||
* @throws SupplyChainValidatorException
|
||||
* if the verification is not successful
|
||||
*/
|
||||
public static String verifyCertificate(final X509AttributeCertificateHolder cert,
|
||||
final KeyStore trustStore) throws SupplyChainValidatorException {
|
||||
try {
|
||||
if (cert == null || trustStore == null) {
|
||||
throw new SupplyChainValidatorException("Certificate or trust store is null");
|
||||
} else if (trustStore.size() == 0) {
|
||||
throw new SupplyChainValidatorException("Truststore is empty");
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
log.error("Error accessing trust store: " + e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
Set<X509Certificate> trustedCerts = new HashSet<>();
|
||||
|
||||
Enumeration<String> alias = trustStore.aliases();
|
||||
|
||||
while (alias.hasMoreElements()) {
|
||||
trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement()));
|
||||
}
|
||||
|
||||
String certChainValidated = validateCertChain(cert, trustedCerts);
|
||||
if (!certChainValidated.isEmpty()) {
|
||||
log.error("Cert chain could not be validated");
|
||||
}
|
||||
return certChainValidated;
|
||||
} catch (KeyStoreException e) {
|
||||
throw new SupplyChainValidatorException("Error with the trust store", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to check if the certificate is validated by certificates in a cert chain. The cert
|
||||
* chain is expected to be stored in a non-ordered KeyStore (trust store). If the signing
|
||||
* certificate for the target cert is found, but it is an intermediate cert, the validation will
|
||||
* continue to try to find the signing cert of the intermediate cert. It will continue searching
|
||||
* until it follows the chain up to a root (self-signed) cert.
|
||||
*
|
||||
* @param cert
|
||||
* certificate to validate
|
||||
* @param trustStore
|
||||
* trust store holding trusted root certificates and intermediate certificates
|
||||
* @return the certificate chain if validation is successful
|
||||
* @throws SupplyChainValidatorException
|
||||
* if the verification is not successful
|
||||
*/
|
||||
public static boolean verifyCertificate(final X509Certificate cert,
|
||||
final KeyStore trustStore) throws SupplyChainValidatorException {
|
||||
try {
|
||||
if (cert == null || trustStore == null) {
|
||||
throw new SupplyChainValidatorException("Certificate or trust store is null");
|
||||
} else if (trustStore.size() == 0) {
|
||||
throw new SupplyChainValidatorException("Truststore is empty");
|
||||
}
|
||||
} catch (KeyStoreException e) {
|
||||
log.error("Error accessing trust store: " + e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
Set<X509Certificate> trustedCerts = new HashSet<>();
|
||||
Enumeration<String> alias = trustStore.aliases();
|
||||
|
||||
while (alias.hasMoreElements()) {
|
||||
trustedCerts.add((X509Certificate) trustStore.getCertificate(alias.nextElement()));
|
||||
}
|
||||
|
||||
return validateCertChain(cert, trustedCerts).isEmpty();
|
||||
} catch (KeyStoreException e) {
|
||||
log.error("Error accessing keystore", e);
|
||||
throw new SupplyChainValidatorException("Error with the trust store", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to check if an attribute certificate is validated by certificates in a cert chain.
|
||||
* The cert chain is represented as a Set of X509Certificates. If the signing certificate for
|
||||
* the target cert is found, but it is an intermediate cert, the validation will continue to try
|
||||
* to find the signing cert of the intermediate cert. It will continue searching until it
|
||||
* follows the chain up to a root (self-signed) cert.
|
||||
*
|
||||
* @param cert
|
||||
* certificate to validate
|
||||
* @param additionalCerts
|
||||
* Set of certs to validate against
|
||||
* @return String status of the cert chain validation -
|
||||
* blank if successful, error message otherwise
|
||||
* @throws SupplyChainValidatorException tried to validate using null certificates
|
||||
*/
|
||||
public static String validateCertChain(final X509AttributeCertificateHolder cert,
|
||||
final Set<X509Certificate> additionalCerts)
|
||||
throws SupplyChainValidatorException {
|
||||
if (cert == null || additionalCerts == null) {
|
||||
throw new SupplyChainValidatorException(
|
||||
"Certificate or validation certificates are null");
|
||||
}
|
||||
final String intCAError = "Intermediate signing cert found, check for CA cert";
|
||||
String foundRootOfCertChain = "";
|
||||
X509Certificate nextInChain = null;
|
||||
|
||||
do {
|
||||
for (X509Certificate trustedCert : additionalCerts) {
|
||||
boolean issuerMatchesSubject = false;
|
||||
boolean signatureMatchesPublicKey = false;
|
||||
if (nextInChain != null) {
|
||||
issuerMatchesSubject = issuerMatchesSubjectDN(nextInChain, trustedCert);
|
||||
signatureMatchesPublicKey = signatureMatchesPublicKey(nextInChain,
|
||||
trustedCert);
|
||||
} else {
|
||||
issuerMatchesSubject = issuerMatchesSubjectDN(cert, trustedCert);
|
||||
signatureMatchesPublicKey = signatureMatchesPublicKey(cert, trustedCert);
|
||||
}
|
||||
|
||||
if (issuerMatchesSubject && signatureMatchesPublicKey) {
|
||||
if (isSelfSigned(trustedCert)) {
|
||||
log.info("CA Root found.");
|
||||
return "";
|
||||
} else {
|
||||
foundRootOfCertChain = intCAError;
|
||||
nextInChain = trustedCert;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!issuerMatchesSubject) {
|
||||
foundRootOfCertChain = "Issuer DN does not match Subject DN";
|
||||
}
|
||||
if (!signatureMatchesPublicKey) {
|
||||
foundRootOfCertChain = "Certificate signature failed to verify";
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (foundRootOfCertChain.equals(intCAError));
|
||||
|
||||
log.error(foundRootOfCertChain);
|
||||
return foundRootOfCertChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to check if a public-key certificate is validated by certificates in a cert chain.
|
||||
* The cert chain is represented as a Set of X509Certificates. If the signing certificate for
|
||||
* the target cert is found, but it is an intermediate cert, the validation will continue to try
|
||||
* to find the signing cert of the intermediate cert. It will continue searching until it
|
||||
* follows the chain up to a root (self-signed) cert.
|
||||
*
|
||||
* @param cert
|
||||
* certificate to validate
|
||||
* @param additionalCerts
|
||||
* Set of certs to validate against
|
||||
* @return String status of the cert chain validation -
|
||||
* blank if successful, error message otherwise
|
||||
* @throws SupplyChainValidatorException tried to validate using null certificates
|
||||
*/
|
||||
public static String validateCertChain(final X509Certificate cert,
|
||||
final Set<X509Certificate> additionalCerts) throws SupplyChainValidatorException {
|
||||
if (cert == null || additionalCerts == null) {
|
||||
throw new SupplyChainValidatorException(
|
||||
"Certificate or validation certificates are null");
|
||||
}
|
||||
final String intCAError = "Intermediate signing cert found, check for CA cert";
|
||||
String foundRootOfCertChain = "";
|
||||
X509Certificate startOfChain = cert;
|
||||
|
||||
do {
|
||||
for (X509Certificate trustedCert : additionalCerts) {
|
||||
boolean issuerMatchesSubject = issuerMatchesSubjectDN(startOfChain, trustedCert);
|
||||
boolean signatureMatchesPublicKey = signatureMatchesPublicKey(startOfChain,
|
||||
trustedCert);
|
||||
if (issuerMatchesSubject && signatureMatchesPublicKey) {
|
||||
if (isSelfSigned(trustedCert)) {
|
||||
log.info("CA Root found.");
|
||||
return "";
|
||||
} else {
|
||||
foundRootOfCertChain = intCAError;
|
||||
startOfChain = trustedCert;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!issuerMatchesSubject) {
|
||||
foundRootOfCertChain = "Issuer DN does not match Subject DN";
|
||||
}
|
||||
if (!signatureMatchesPublicKey) {
|
||||
foundRootOfCertChain = "Certificate signature failed to verify";
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (foundRootOfCertChain.equals(intCAError));
|
||||
|
||||
log.warn(foundRootOfCertChain);
|
||||
return foundRootOfCertChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the output from PACCOR's allcomponents.sh script into ComponentInfo objects.
|
||||
* @param paccorOutput the output from PACCOR's allcomoponents.sh
|
||||
* @return a list of ComponentInfo objects built from paccorOutput
|
||||
* @throws java.io.IOException if something goes wrong parsing the JSON
|
||||
*/
|
||||
public static List<ComponentInfo> getComponentInfoFromPaccorOutput(final String paccorOutput)
|
||||
throws IOException {
|
||||
List<ComponentInfo> componentInfoList = new ArrayList<>();
|
||||
|
||||
if (StringUtils.isNotEmpty(paccorOutput)) {
|
||||
ObjectMapper objectMapper = new ObjectMapper(new JsonFactory());
|
||||
JsonNode rootNode = objectMapper.readTree(paccorOutput);
|
||||
Iterator<JsonNode> jsonComponentNodes
|
||||
= rootNode.findValue("COMPONENTS").elements();
|
||||
while (jsonComponentNodes.hasNext()) {
|
||||
JsonNode next = jsonComponentNodes.next();
|
||||
componentInfoList.add(new ComponentInfo(
|
||||
getJSONNodeValueAsText(next, "MANUFACTURER"),
|
||||
getJSONNodeValueAsText(next, "MODEL"),
|
||||
getJSONNodeValueAsText(next, "SERIAL"),
|
||||
getJSONNodeValueAsText(next, "REVISION")));
|
||||
}
|
||||
}
|
||||
|
||||
return componentInfoList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the output from PACCOR's allcomponents.sh script into ComponentInfo objects.
|
||||
* @param paccorOutput the output from PACCOR's allcomoponents.sh
|
||||
* @return a list of ComponentInfo objects built from paccorOutput
|
||||
* @throws IOException if something goes wrong parsing the JSON
|
||||
*/
|
||||
public static List<ComponentInfo> getV2PaccorOutput(
|
||||
final String paccorOutput) throws IOException {
|
||||
List<ComponentInfo> ciList = new LinkedList<>();
|
||||
String manufacturer, model, serial, revision;
|
||||
String componentClass = Strings.EMPTY;
|
||||
|
||||
if (StringUtils.isNotEmpty(paccorOutput)) {
|
||||
ObjectMapper objectMapper = new ObjectMapper(new JsonFactory());
|
||||
JsonNode rootNode = objectMapper.readTree(paccorOutput);
|
||||
Iterator<JsonNode> jsonComponentNodes
|
||||
= rootNode.findValue("COMPONENTS").elements();
|
||||
while (jsonComponentNodes.hasNext()) {
|
||||
JsonNode next = jsonComponentNodes.next();
|
||||
manufacturer = getJSONNodeValueAsText(next, "MANUFACTURER");
|
||||
model = getJSONNodeValueAsText(next, "MODEL");
|
||||
serial = getJSONNodeValueAsText(next, "SERIAL");
|
||||
revision = getJSONNodeValueAsText(next, "REVISION");
|
||||
List<JsonNode> compClassNodes = next.findValues("COMPONENTCLASS");
|
||||
|
||||
for (JsonNode subNode : compClassNodes) {
|
||||
componentClass = getJSONNodeValueAsText(subNode,
|
||||
"COMPONENTCLASSVALUE");
|
||||
}
|
||||
ciList.add(new ComponentInfo(manufacturer, model,
|
||||
serial, revision, componentClass));
|
||||
}
|
||||
}
|
||||
|
||||
return ciList;
|
||||
}
|
||||
|
||||
private static String getJSONNodeValueAsText(final JsonNode node, final String fieldName) {
|
||||
if (node.hasNonNull(fieldName)) {
|
||||
return node.findValue(fieldName).asText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppraisalStatus validatePlatformCredentialAttributes(final PlatformCredential pc,
|
||||
final DeviceInfoReport deviceInfoReport,
|
||||
final EndorsementCredential ec) {
|
||||
return null;
|
||||
/**
|
||||
* Checks if the issuer info of an attribute cert matches the supposed signing cert's
|
||||
* distinguished name.
|
||||
*
|
||||
* @param cert
|
||||
* the attribute certificate with the signature to validate
|
||||
* @param signingCert
|
||||
* the certificate with the public key to validate
|
||||
* @return boolean indicating if the names
|
||||
* @throws SupplyChainValidatorException tried to validate using null certificates
|
||||
*/
|
||||
public static boolean issuerMatchesSubjectDN(final X509AttributeCertificateHolder cert,
|
||||
final X509Certificate signingCert) throws SupplyChainValidatorException {
|
||||
if (cert == null || signingCert == null) {
|
||||
throw new SupplyChainValidatorException("Certificate or signing certificate is null");
|
||||
}
|
||||
String signingCertSubjectDN = signingCert.getSubjectX500Principal().getName();
|
||||
X500Name namedSubjectDN = new X500Name(signingCertSubjectDN);
|
||||
|
||||
X500Name issuerDN = cert.getIssuer().getNames()[0];
|
||||
|
||||
// equality check ignore DN component ordering
|
||||
return issuerDN.equals(namedSubjectDN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppraisalStatus validateDeltaPlatformCredentialAttributes(final PlatformCredential delta,
|
||||
final DeviceInfoReport deviceInfoReport,
|
||||
final PlatformCredential base,
|
||||
final Map<PlatformCredential, SupplyChainValidation> deltaMapping) {
|
||||
return null;
|
||||
/**
|
||||
* Checks if the issuer info of a public-key cert matches the supposed signing cert's
|
||||
* distinguished name.
|
||||
*
|
||||
* @param cert
|
||||
* the public-key certificate with the signature to validate
|
||||
* @param signingCert
|
||||
* the certificate with the public key to validate
|
||||
* @return boolean indicating if the names
|
||||
* @throws SupplyChainValidatorException tried to validate using null certificates
|
||||
*/
|
||||
public static boolean issuerMatchesSubjectDN(final X509Certificate cert,
|
||||
final X509Certificate signingCert) throws SupplyChainValidatorException {
|
||||
if (cert == null || signingCert == null) {
|
||||
throw new SupplyChainValidatorException("Certificate or signing certificate is null");
|
||||
}
|
||||
String signingCertSubjectDN = signingCert.getSubjectX500Principal().
|
||||
getName(X500Principal.RFC1779);
|
||||
X500Name namedSubjectDN = new X500Name(signingCertSubjectDN);
|
||||
|
||||
String certIssuerDN = cert.getIssuerX500Principal().getName();
|
||||
X500Name namedIssuerDN = new X500Name(certIssuerDN);
|
||||
|
||||
// equality check ignore DN component ordering
|
||||
return namedIssuerDN.equals(namedSubjectDN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppraisalStatus validateEndorsementCredential(final EndorsementCredential ec,
|
||||
final KeyStore trustStore,
|
||||
final boolean acceptExpired) {
|
||||
return null;
|
||||
/**
|
||||
* Checks if the signature of an attribute cert is validated against the signing cert's public
|
||||
* key.
|
||||
*
|
||||
* @param cert
|
||||
* the public-key certificate with the signature to validate
|
||||
* @param signingCert
|
||||
* the certificate with the public key to validate
|
||||
* @return boolean indicating if the validation passed
|
||||
* @throws SupplyChainValidatorException tried to validate using null certificates
|
||||
*/
|
||||
public static boolean signatureMatchesPublicKey(final X509Certificate cert,
|
||||
final X509Certificate signingCert) throws SupplyChainValidatorException {
|
||||
if (cert == null || signingCert == null) {
|
||||
throw new SupplyChainValidatorException("Certificate or signing certificate is null");
|
||||
}
|
||||
try {
|
||||
cert.verify(signingCert.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME);
|
||||
return true;
|
||||
} catch (InvalidKeyException e) {
|
||||
log.info("Incorrect key given to validate this cert's signature");
|
||||
} catch (CertificateException e) {
|
||||
log.info("Encoding error while validating this cert's signature");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
log.info("Unsupported signature algorithm found during validation");
|
||||
} catch (NoSuchProviderException e) {
|
||||
log.info("Incorrect provider for cert signature validation");
|
||||
} catch (SignatureException e) {
|
||||
log.info(String.format("%s.verify(%s)", cert.getSubjectX500Principal(),
|
||||
signingCert.getSubjectX500Principal()));
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the signature of a public-key cert is validated against the signing cert's public
|
||||
* key.
|
||||
*
|
||||
* @param cert
|
||||
* the attribute certificate with the signature to validate
|
||||
* @param signingCert
|
||||
* the certificate with the public key to validate
|
||||
* @return boolean indicating if the validation passed
|
||||
* @throws SupplyChainValidatorException tried to validate using null certificates
|
||||
*/
|
||||
public static boolean signatureMatchesPublicKey(final X509AttributeCertificateHolder cert,
|
||||
final X509Certificate signingCert) throws SupplyChainValidatorException {
|
||||
if (signingCert == null) {
|
||||
throw new SupplyChainValidatorException("Signing certificate is null");
|
||||
}
|
||||
return signatureMatchesPublicKey(cert, signingCert.getPublicKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an X509 Attribute Certificate is valid directly against a public key.
|
||||
*
|
||||
* @param cert
|
||||
* the attribute certificate with the signature to validate
|
||||
* @param signingKey
|
||||
* the key to use to check the attribute cert
|
||||
* @return boolean indicating if the validation passed
|
||||
* @throws SupplyChainValidatorException tried to validate using null certificates
|
||||
*/
|
||||
public static boolean signatureMatchesPublicKey(final X509AttributeCertificateHolder cert,
|
||||
final PublicKey signingKey) throws SupplyChainValidatorException {
|
||||
if (cert == null || signingKey == null) {
|
||||
throw new SupplyChainValidatorException("Certificate or signing certificate is null");
|
||||
}
|
||||
ContentVerifierProvider contentVerifierProvider;
|
||||
try {
|
||||
contentVerifierProvider =
|
||||
new JcaContentVerifierProviderBuilder().setProvider("BC").build(signingKey);
|
||||
return cert.isSignatureValid(contentVerifierProvider);
|
||||
} catch (OperatorCreationException | CertException e) {
|
||||
log.info("Exception thrown while verifying certificate", e);
|
||||
log.info(String.format("%s.isSignatureValid(%s)", cert.getSerialNumber(),
|
||||
signingKey.getFormat()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether given X.509 public-key certificate is self-signed. If the cert can be
|
||||
* verified using its own public key, that means it was self-signed.
|
||||
*
|
||||
* @param cert
|
||||
* X.509 Certificate
|
||||
* @return boolean indicating if the cert was self-signed
|
||||
*/
|
||||
private static boolean isSelfSigned(final X509Certificate cert)
|
||||
throws SupplyChainValidatorException {
|
||||
if (cert == null) {
|
||||
throw new SupplyChainValidatorException("Certificate is null");
|
||||
}
|
||||
try {
|
||||
PublicKey key = cert.getPublicKey();
|
||||
cert.verify(key);
|
||||
return true;
|
||||
} catch (SignatureException | InvalidKeyException e) {
|
||||
return false;
|
||||
} catch (CertificateException | NoSuchAlgorithmException | NoSuchProviderException e) {
|
||||
log.error("Exception occurred while checking if cert is self-signed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,16 +97,16 @@ ospackage {
|
||||
}
|
||||
|
||||
// Post Install
|
||||
postInstall 'sh /opt/hirs/aca/scripts/aca/aca_setup.sh -u'
|
||||
postInstall 'bash /opt/hirs/aca/scripts/aca/aca_setup.sh -u'
|
||||
// add chrontab to run ACA at boot
|
||||
postInstall 'echo "@reboot root /opt/hirs/aca/scripts/aca/aca_bootRun.sh -w" >> /etc/crontab'
|
||||
// run ACA after install
|
||||
postInstall '/opt/hirs/aca/scripts/aca/aca_bootRun.sh -w &'
|
||||
postInstall 'chmod +x /opt/hirs/aca/scripts/aca/*'
|
||||
postInstall 'sh /opt/hirs/aca/scripts/aca/check_for_aca.sh'
|
||||
postInstall 'bash /opt/hirs/aca/scripts/aca/check_for_aca.sh'
|
||||
|
||||
// Uninstall
|
||||
preUninstall 'sh /opt/hirs/aca/scripts/aca/aca_remove_setup.sh'
|
||||
preUninstall 'bash /opt/hirs/aca/scripts/aca/aca_remove_setup.sh'
|
||||
postUninstall 'rm -rf /etc/hirs'
|
||||
|
||||
buildRpm {
|
||||
@ -114,6 +114,7 @@ ospackage {
|
||||
}
|
||||
|
||||
buildDeb {
|
||||
packageName = 'hirs-attestationca'
|
||||
arch = 'amd64'
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ import java.util.Properties;
|
||||
@PropertySource(value = "classpath:hibernate.properties"),
|
||||
|
||||
// detects if file exists, if not, ignore errors
|
||||
@PropertySource(value = "file:/etc/hirs/aca/application.properties",
|
||||
@PropertySource(value = "file:/etc/hirs/aca/aca.properties",
|
||||
ignoreResourceNotFound = true)
|
||||
})
|
||||
@ComponentScan({"hirs.attestationca.portal", "hirs.attestationca.portal.page.controllers", "hirs.attestationca.persist", "hirs.attestationca.persist.entity", "hirs.attestationca.persist.service"})
|
||||
|
@ -234,16 +234,16 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
// Add the EndorsementCredential for each PlatformCredential based on the
|
||||
// serial number. (pc.HolderSerialNumber = ec.SerialNumber)
|
||||
if (certificateType.equals(PLATFORMCREDENTIAL)) {
|
||||
// records = OrderedListQueryDataTableAdapter.getOrderedList(
|
||||
// getCertificateClass(certificateType), platformCertificateRepository,
|
||||
// input, orderColumnName, criteriaModifier);
|
||||
FilteredRecordsList<PlatformCredential> records = new FilteredRecordsList<>();
|
||||
|
||||
org.springframework.data.domain.Page<PlatformCredential> pagedResult = this.platformCertificateRepository.findAll(paging);
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
records.addAll(pagedResult.getContent());
|
||||
records.setRecordsTotal(pagedResult.getContent().size());
|
||||
} else {
|
||||
records.setRecordsTotal(input.getLength());
|
||||
}
|
||||
records.setRecordsTotal(input.getLength());
|
||||
|
||||
records.setRecordsFiltered(platformCertificateRepository.count());
|
||||
EndorsementCredential associatedEC;
|
||||
|
||||
@ -267,31 +267,31 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
log.debug("Returning list of size: " + records.size());
|
||||
return new DataTableResponse<>(records, input);
|
||||
} else if (certificateType.equals(ENDORSEMENTCREDENTIAL)) {
|
||||
// records = OrderedListQueryDataTableAdapter.getOrderedList(
|
||||
// getCertificateClass(certificateType), endorsementCredentialRepository,
|
||||
// input, orderColumnName, criteriaModifier);
|
||||
FilteredRecordsList<EndorsementCredential> records = new FilteredRecordsList<>();
|
||||
org.springframework.data.domain.Page<EndorsementCredential> pagedResult = this.endorsementCredentialRepository.findAll(paging);
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
records.addAll(pagedResult.getContent());
|
||||
records.setRecordsTotal(pagedResult.getContent().size());
|
||||
} else {
|
||||
records.setRecordsTotal(input.getLength());
|
||||
}
|
||||
|
||||
records.setRecordsTotal(input.getLength());
|
||||
records.setRecordsFiltered(endorsementCredentialRepository.count());
|
||||
|
||||
log.debug("Returning list of size: " + records.size());
|
||||
return new DataTableResponse<>(records, input);
|
||||
} else if (certificateType.equals(TRUSTCHAIN)) {
|
||||
// records = OrderedListQueryDataTableAdapter.getOrderedList(
|
||||
// getCertificateClass(certificateType), caCredentialRepository,
|
||||
// input, orderColumnName, criteriaModifier);
|
||||
FilteredRecordsList<CertificateAuthorityCredential> records = new FilteredRecordsList<>();
|
||||
org.springframework.data.domain.Page<CertificateAuthorityCredential> pagedResult = this.caCredentialRepository.findAll(paging);
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
records.addAll(pagedResult.getContent());
|
||||
records.setRecordsTotal(pagedResult.getContent().size());
|
||||
} else {
|
||||
records.setRecordsTotal(input.getLength());
|
||||
}
|
||||
records.setRecordsTotal(input.getLength());
|
||||
|
||||
records.setRecordsFiltered(caCredentialRepository.count());
|
||||
|
||||
log.debug("Returning list of size: " + records.size());
|
||||
@ -299,10 +299,14 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
} else if (certificateType.equals(ISSUEDCERTIFICATES)) {
|
||||
FilteredRecordsList<IssuedAttestationCertificate> records = new FilteredRecordsList<>();
|
||||
org.springframework.data.domain.Page<IssuedAttestationCertificate> pagedResult = this.issuedCertificateRepository.findAll(paging);
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
records.addAll(pagedResult.getContent());
|
||||
records.setRecordsTotal(pagedResult.getContent().size());
|
||||
} else {
|
||||
records.setRecordsTotal(input.getLength());
|
||||
}
|
||||
records.setRecordsTotal(input.getLength());
|
||||
|
||||
records.setRecordsFiltered(issuedCertificateRepository.count());
|
||||
|
||||
log.debug("Returning list of size: " + records.size());
|
||||
@ -389,14 +393,14 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
for (PlatformCredential pc : sharedCertificates) {
|
||||
if (!pc.isPlatformBase()) {
|
||||
pc.archive();
|
||||
certificateRepository.delete(pc);
|
||||
certificateRepository.save(pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
certificate.archive();
|
||||
certificateRepository.delete(certificate);
|
||||
certificateRepository.save(certificate);
|
||||
|
||||
String deleteCompletedMessage = "Certificate successfully deleted";
|
||||
messages.addInfo(deleteCompletedMessage);
|
||||
@ -832,11 +836,11 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
log.error(failMessage, dEx);
|
||||
messages.addError(failMessage + dEx.getMessage());
|
||||
return null;
|
||||
} catch (IllegalArgumentException e) {
|
||||
} catch (IllegalArgumentException iaEx) {
|
||||
final String failMessage = String.format(
|
||||
"Certificate format not recognized(%s): ", fileName);
|
||||
log.error(failMessage, e);
|
||||
messages.addError(failMessage + e.getMessage());
|
||||
log.error(failMessage, iaEx);
|
||||
messages.addError(failMessage + iaEx.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -864,11 +868,11 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
existingCertificate = getCertificateByHash(
|
||||
certificateType,
|
||||
certificate.getCertificateHash());
|
||||
} catch (DBServiceException e) {
|
||||
} catch (DBServiceException dbsEx) {
|
||||
final String failMessage = "Querying for existing certificate failed ("
|
||||
+ fileName + "): ";
|
||||
messages.addError(failMessage + e.getMessage());
|
||||
log.error(failMessage, e);
|
||||
messages.addError(failMessage + dbsEx.getMessage());
|
||||
log.error(failMessage, dbsEx);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -924,11 +928,11 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
log.info(successMsg);
|
||||
return;
|
||||
}
|
||||
} catch (DBServiceException e) {
|
||||
} catch (DBServiceException dbsEx) {
|
||||
final String failMessage = String.format("Storing new certificate failed (%s): ",
|
||||
fileName);
|
||||
messages.addError(failMessage + e.getMessage());
|
||||
log.error(failMessage, e);
|
||||
messages.addError(failMessage + dbsEx.getMessage());
|
||||
log.error(failMessage, dbsEx);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -946,12 +950,12 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
log.info(successMsg);
|
||||
return;
|
||||
}
|
||||
} catch (DBServiceException e) {
|
||||
} catch (DBServiceException dbsEx) {
|
||||
final String failMessage = String.format("Found an identical"
|
||||
+ " pre-existing certificate in the "
|
||||
+ "archive, but failed to unarchive it (%s): ", fileName);
|
||||
messages.addError(failMessage + e.getMessage());
|
||||
log.error(failMessage, e);
|
||||
messages.addError(failMessage + dbsEx.getMessage());
|
||||
log.error(failMessage, dbsEx);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -84,10 +84,6 @@ public class DevicePageController extends PageController<NoPageParams> {
|
||||
|
||||
// get all the devices
|
||||
FilteredRecordsList<Device> deviceList = new FilteredRecordsList<>();
|
||||
// OrderedListQueryDataTableAdapter.getOrderedList(
|
||||
// Device.class,
|
||||
// deviceRepository,
|
||||
// input, orderColumnName);
|
||||
|
||||
int currentPage = input.getStart() / input.getLength();
|
||||
Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
@ -95,8 +91,10 @@ public class DevicePageController extends PageController<NoPageParams> {
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
deviceList.addAll(pagedResult.getContent());
|
||||
deviceList.setRecordsTotal(pagedResult.getContent().size());
|
||||
} else {
|
||||
deviceList.setRecordsTotal(input.getLength());
|
||||
}
|
||||
deviceList.setRecordsTotal(input.getLength());
|
||||
deviceList.setRecordsFiltered(deviceRepository.count());
|
||||
|
||||
FilteredRecordsList<HashMap<String, Object>> records
|
||||
@ -131,10 +129,11 @@ public class DevicePageController extends PageController<NoPageParams> {
|
||||
issuedCertificateList.addAll(issuedCertificateRepository.findByDeviceId(id));
|
||||
}
|
||||
|
||||
HashMap<String, List<Object>> certificatePropertyMap;
|
||||
// loop all the devices
|
||||
for (Device device : deviceList) {
|
||||
// hashmap containing the list of certificates based on the certificate type
|
||||
HashMap<String, List<Object>> certificatePropertyMap = new HashMap<>();
|
||||
certificatePropertyMap = new HashMap<>();
|
||||
|
||||
deviceCertMap.put("device", device);
|
||||
String deviceName;
|
||||
@ -179,8 +178,7 @@ public class DevicePageController extends PageController<NoPageParams> {
|
||||
}
|
||||
|
||||
for (IssuedAttestationCertificate ic : issuedCertificateList) {
|
||||
deviceName = deviceRepository.findById(ic.getDeviceId()).get().getName();
|
||||
|
||||
deviceName = ic.getDeviceName();
|
||||
// set the certificate if it's the same ID
|
||||
if (device.getName().equals(deviceName)) {
|
||||
String certificateId = IssuedAttestationCertificate.class.getSimpleName();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package hirs.attestationca.portal.page.controllers;
|
||||
|
||||
import hirs.attestationca.persist.DBServiceException;
|
||||
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
|
||||
import hirs.attestationca.persist.entity.manager.CertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
|
||||
@ -10,14 +11,14 @@ import hirs.attestationca.persist.entity.userdefined.rim.BaseReferenceManifest;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.EventLogMeasurements;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest;
|
||||
import hirs.attestationca.persist.service.SupplyChainValidationServiceImpl;
|
||||
import hirs.attestationca.persist.service.ValidationService;
|
||||
import hirs.attestationca.persist.validation.ReferenceManifestValidator;
|
||||
import hirs.attestationca.persist.validation.SupplyChainCredentialValidator;
|
||||
import hirs.attestationca.persist.validation.SupplyChainValidatorException;
|
||||
import hirs.attestationca.portal.page.Page;
|
||||
import hirs.attestationca.portal.page.PageController;
|
||||
import hirs.attestationca.portal.page.PageMessages;
|
||||
import hirs.attestationca.portal.page.params.ReferenceManifestDetailsPageParams;
|
||||
import hirs.attestationca.portal.page.utils.SupplyChainCredentialValidator;
|
||||
import hirs.utils.SwidResource;
|
||||
import hirs.utils.tpm.eventlog.TCGEventLog;
|
||||
import hirs.utils.tpm.eventlog.TpmPcrEvent;
|
||||
@ -31,6 +32,7 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -52,6 +54,7 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
private final ReferenceManifestRepository referenceManifestRepository;
|
||||
private final ReferenceDigestValueRepository referenceDigestValueRepository;
|
||||
private final CertificateRepository certificateRepository;
|
||||
private final CACredentialRepository caCertificateRepository;
|
||||
private static final ReferenceManifestValidator RIM_VALIDATOR
|
||||
= new ReferenceManifestValidator();
|
||||
|
||||
@ -61,15 +64,18 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
* @param referenceManifestRepository the repository for RIM.
|
||||
* @param referenceDigestValueRepository the reference event manager.
|
||||
* @param certificateRepository the certificate manager.
|
||||
* @param caCertificateRepository the CA certificate manager.
|
||||
*/
|
||||
@Autowired
|
||||
public ReferenceManifestDetailsPageController(final ReferenceManifestRepository referenceManifestRepository,
|
||||
final ReferenceDigestValueRepository referenceDigestValueRepository,
|
||||
final CertificateRepository certificateRepository) {
|
||||
final CertificateRepository certificateRepository,
|
||||
final CACredentialRepository caCertificateRepository) {
|
||||
super(Page.RIM_DETAILS);
|
||||
this.referenceManifestRepository = referenceManifestRepository;
|
||||
this.referenceDigestValueRepository = referenceDigestValueRepository;
|
||||
this.certificateRepository = certificateRepository;
|
||||
this.caCertificateRepository = caCertificateRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,7 +106,8 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
try {
|
||||
UUID uuid = UUID.fromString(params.getId());
|
||||
data.putAll(getRimDetailInfo(uuid, referenceManifestRepository,
|
||||
referenceDigestValueRepository, certificateRepository));
|
||||
referenceDigestValueRepository, certificateRepository,
|
||||
caCertificateRepository));
|
||||
} catch (IllegalArgumentException iaEx) {
|
||||
String uuidError = "Failed to parse ID from: " + params.getId();
|
||||
messages.addError(uuidError);
|
||||
@ -130,6 +137,7 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
* @param referenceManifestRepository the reference manifest manager.
|
||||
* @param referenceDigestValueRepository the reference event manager.
|
||||
* @param certificateRepository the certificate manager.
|
||||
* @param caCertificateRepository the certificate manager.
|
||||
* @return mapping of the RIM information from the database.
|
||||
* @throws java.io.IOException error for reading file bytes.
|
||||
* @throws NoSuchAlgorithmException If an unknown Algorithm is encountered.
|
||||
@ -138,7 +146,8 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
public static HashMap<String, Object> getRimDetailInfo(final UUID uuid,
|
||||
final ReferenceManifestRepository referenceManifestRepository,
|
||||
final ReferenceDigestValueRepository referenceDigestValueRepository,
|
||||
final CertificateRepository certificateRepository)
|
||||
final CertificateRepository certificateRepository,
|
||||
final CACredentialRepository caCertificateRepository)
|
||||
throws IOException,
|
||||
CertificateException, NoSuchAlgorithmException {
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
@ -146,7 +155,7 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
BaseReferenceManifest bRim = referenceManifestRepository.getBaseRimEntityById(uuid);
|
||||
|
||||
if (bRim != null) {
|
||||
data.putAll(getBaseRimInfo(bRim, referenceManifestRepository, certificateRepository));
|
||||
data.putAll(getBaseRimInfo(bRim, referenceManifestRepository, certificateRepository, caCertificateRepository));
|
||||
}
|
||||
|
||||
SupportReferenceManifest sRim = referenceManifestRepository.getSupportRimEntityById(uuid);
|
||||
@ -172,6 +181,7 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
* @param baseRim established ReferenceManifest Type.
|
||||
* @param referenceManifestRepository the reference manifest manager.
|
||||
* @param certificateRepository the certificate manager.
|
||||
* @param caCertificateRepository the certificate manager.
|
||||
* @return mapping of the RIM information from the database.
|
||||
* @throws java.io.IOException error for reading file bytes.
|
||||
* @throws NoSuchAlgorithmException If an unknown Algorithm is encountered.
|
||||
@ -180,7 +190,8 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
private static HashMap<String, Object> getBaseRimInfo(
|
||||
final BaseReferenceManifest baseRim,
|
||||
final ReferenceManifestRepository referenceManifestRepository,
|
||||
final CertificateRepository certificateRepository)
|
||||
final CertificateRepository certificateRepository,
|
||||
final CACredentialRepository caCertificateRepository)
|
||||
throws IOException, CertificateException, NoSuchAlgorithmException {
|
||||
HashMap<String, Object> data = new HashMap<>();
|
||||
|
||||
@ -256,8 +267,8 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
baseRim.setAssociatedRim(support.getId());
|
||||
}
|
||||
} else {
|
||||
support = (SupportReferenceManifest) referenceManifestRepository
|
||||
.getReferenceById(baseRim.getAssociatedRim());
|
||||
support = referenceManifestRepository
|
||||
.getSupportRimEntityById(baseRim.getAssociatedRim());
|
||||
}
|
||||
// going to have to pull the filename and grab that from the DB
|
||||
// to get the id to make the link
|
||||
@ -288,9 +299,7 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
//Report invalid signature unless RIM_VALIDATOR validates it and cert path is valid
|
||||
data.put("signatureValid", false);
|
||||
for (CertificateAuthorityCredential cert : certificates) {
|
||||
SupplyChainValidationServiceImpl scvsImpl =
|
||||
new SupplyChainValidationServiceImpl(certificateRepository);
|
||||
KeyStore keystore = scvsImpl.getCaChain(cert);
|
||||
KeyStore keystore = ValidationService.getCaChain(cert, caCertificateRepository);
|
||||
if (RIM_VALIDATOR.validateXmlSignature(cert)) {
|
||||
try {
|
||||
if (SupplyChainCredentialValidator.verifyCertificate(
|
||||
@ -298,21 +307,23 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
data.replace("signatureValid", true);
|
||||
break;
|
||||
}
|
||||
} catch (SupplyChainValidatorException e) {
|
||||
log.error("Error verifying cert chain: " + e.getMessage());
|
||||
} catch (SupplyChainValidatorException scvEx) {
|
||||
log.error("Error verifying cert chain: " + scvEx.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
data.put("skID", RIM_VALIDATOR.getSubjectKeyIdentifier());
|
||||
try {
|
||||
for (CertificateAuthorityCredential cert : certificates) {
|
||||
if (Arrays.equals(cert.getEncodedPublicKey(),
|
||||
RIM_VALIDATOR.getPublicKey().getEncoded())) {
|
||||
data.put("issuerID", cert.getId().toString());
|
||||
if (RIM_VALIDATOR.getPublicKey() != null) {
|
||||
for (CertificateAuthorityCredential cert : certificates) {
|
||||
if (Arrays.equals(cert.getEncodedPublicKey(),
|
||||
RIM_VALIDATOR.getPublicKey().getEncoded())) {
|
||||
data.put("issuerID", cert.getId().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
log.warn("Unable to link signing certificate: " + e.getMessage());
|
||||
} catch (NullPointerException npEx) {
|
||||
log.warn("Unable to link signing certificate: " + npEx.getMessage());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
@ -1,18 +1,15 @@
|
||||
package hirs.attestationca.portal.page.controllers;
|
||||
|
||||
import hirs.attestationca.persist.CriteriaModifier;
|
||||
import hirs.attestationca.persist.DBManagerException;
|
||||
import hirs.attestationca.persist.FilteredRecordsList;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceDigestValueRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ReferenceManifestRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.ReferenceManifest;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.BaseReferenceManifest;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.ReferenceDigestValue;
|
||||
import hirs.attestationca.persist.entity.userdefined.rim.SupportReferenceManifest;
|
||||
import hirs.attestationca.portal.datatables.DataTableInput;
|
||||
import hirs.attestationca.portal.datatables.DataTableResponse;
|
||||
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
|
||||
import hirs.attestationca.portal.page.Page;
|
||||
import hirs.attestationca.portal.page.PageController;
|
||||
import hirs.attestationca.portal.page.PageMessages;
|
||||
@ -20,13 +17,9 @@ import hirs.attestationca.portal.page.params.NoPageParams;
|
||||
import hirs.utils.tpm.eventlog.TCGEventLog;
|
||||
import hirs.utils.tpm.eventlog.TpmPcrEvent;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.hibernate.Session;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@ -45,7 +38,6 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Reference;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@ -123,36 +115,27 @@ public class ReferenceManifestPageController extends PageController<NoPageParams
|
||||
|
||||
String orderColumnName = input.getOrderColumnName();
|
||||
log.info("Ordering on column: " + orderColumnName);
|
||||
|
||||
// check that the alert is not archived and that it is in the specified report
|
||||
CriteriaModifier criteriaModifier = new CriteriaModifier() {
|
||||
@Override
|
||||
public void modify(final CriteriaQuery criteriaQuery) {
|
||||
Session session = entityManager.unwrap(Session.class);
|
||||
CriteriaBuilder cb = session.getCriteriaBuilder();
|
||||
Root<ReferenceManifest> rimRoot = criteriaQuery.from(Reference.class);
|
||||
|
||||
criteriaQuery.select(rimRoot).distinct(true).where(cb.isNull(rimRoot.get(Certificate.ARCHIVE_FIELD)));
|
||||
}
|
||||
};
|
||||
|
||||
log.info("Querying with the following dataTableInput: " + input.toString());
|
||||
|
||||
FilteredRecordsList<ReferenceManifest> records = new FilteredRecordsList<>();
|
||||
int currentPage = input.getStart() / input.getLength();
|
||||
Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
org.springframework.data.domain.Page<ReferenceManifest> pagedResult = referenceManifestRepository.findAll(paging);
|
||||
int rimCount = 0;
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
records.addAll(pagedResult.getContent());
|
||||
for (ReferenceManifest manifest : pagedResult.getContent()) {
|
||||
if (!manifest.getRimType().equals(ReferenceManifest.MEASUREMENT_RIM)) {
|
||||
records.add(manifest);
|
||||
rimCount++;
|
||||
}
|
||||
}
|
||||
records.setRecordsTotal(rimCount);
|
||||
} else {
|
||||
records.setRecordsTotal(input.getLength());
|
||||
}
|
||||
records.setRecordsTotal(input.getLength());
|
||||
|
||||
records.setRecordsFiltered(referenceManifestRepository.count());
|
||||
// FilteredRecordsList<ReferenceManifest> records
|
||||
// = OrderedListQueryDataTableAdapter.getOrderedList(
|
||||
// ReferenceManifest.class,
|
||||
// this.referenceManifestRepository,
|
||||
// input, orderColumnName, criteriaModifier);
|
||||
|
||||
log.debug("Returning list of size: " + records.size());
|
||||
return new DataTableResponse<>(records, input);
|
||||
|
@ -1,35 +0,0 @@
|
||||
package hirs.attestationca.portal.page.controllers;
|
||||
|
||||
/**
|
||||
* Restful implementation of the {@link }.
|
||||
* Exposes the ACA methods as REST endpoints.
|
||||
*/
|
||||
//@RestController
|
||||
//@RequestMapping("/")
|
||||
public class RestfulAttestationCertificateAuthority {
|
||||
// private final ReferenceManifestRepository referenceManifestRepository;
|
||||
// private final ReferenceDigestValueRepository referenceDigestValueRepository;
|
||||
//
|
||||
// @Autowired
|
||||
// public RestfulAttestationCertificateAuthority(
|
||||
// final ReferenceManifestRepository referenceManifestRepository,
|
||||
// final ReferenceDigestValueRepository referenceDigestValueRepository) {
|
||||
//
|
||||
// this.referenceManifestRepository = referenceManifestRepository;
|
||||
// this.referenceDigestValueRepository = referenceDigestValueRepository;
|
||||
//
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @ResponseBody
|
||||
// @RequestMapping(value = "/upload-swidtag", method = RequestMethod.POST, consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
// public byte[] uploadSwidtag(@RequestBody final byte[] request) {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// @ResponseBody
|
||||
// @RequestMapping(value = "/upload-rimel", method = RequestMethod.POST, consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||
// public byte[] uploadRimel(@RequestBody final byte[] request) {
|
||||
// return null;
|
||||
// }
|
||||
}
|
@ -116,15 +116,12 @@ public class RimDatabasePageController extends PageController<NoPageParams> {
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
referenceDigestValues.addAll(pagedResult.getContent());
|
||||
referenceDigestValues.setRecordsTotal(pagedResult.getContent().size());
|
||||
} else {
|
||||
referenceDigestValues.setRecordsTotal(input.getLength());
|
||||
}
|
||||
referenceDigestValues.setRecordsTotal(input.getLength());
|
||||
referenceDigestValues.setRecordsFiltered(referenceDigestValueRepository.count());
|
||||
|
||||
// FilteredRecordsList<ReferenceDigestValue> referenceDigestValues =
|
||||
// OrderedListQueryDataTableAdapter.getOrderedList(
|
||||
// referenceDigestValueRepository,
|
||||
// input, orderColumnName, criteriaModifier, entityManager);
|
||||
|
||||
// might be able to get rid of this, maybe right a query that looks for not updated
|
||||
SupportReferenceManifest support;
|
||||
for (ReferenceDigestValue rdv : referenceDigestValues) {
|
||||
|
@ -2,13 +2,11 @@ package hirs.attestationca.portal.page.controllers;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import hirs.attestationca.persist.CriteriaModifier;
|
||||
import hirs.attestationca.persist.FilteredRecordsList;
|
||||
import hirs.attestationca.persist.entity.manager.CertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.DeviceRepository;
|
||||
import hirs.attestationca.persist.entity.manager.PlatformCertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.SupplyChainValidationSummaryRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.Device;
|
||||
import hirs.attestationca.persist.entity.userdefined.SupplyChainValidationSummary;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
@ -16,18 +14,13 @@ import hirs.attestationca.persist.entity.userdefined.certificate.attributes.Comp
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.V2.ComponentIdentifierV2;
|
||||
import hirs.attestationca.portal.datatables.DataTableInput;
|
||||
import hirs.attestationca.portal.datatables.DataTableResponse;
|
||||
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
|
||||
import hirs.attestationca.portal.page.Page;
|
||||
import hirs.attestationca.portal.page.PageController;
|
||||
import hirs.attestationca.portal.page.params.NoPageParams;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.hibernate.Session;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@ -43,7 +36,6 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.lang.ref.Reference;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
@ -52,7 +44,6 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -128,20 +119,6 @@ public class ValidationReportsPageController extends PageController<NoPageParams
|
||||
String orderColumnName = input.getOrderColumnName();
|
||||
log.debug("Ordering on column: " + orderColumnName);
|
||||
|
||||
// define an alias so the composite object, device, can be used by the
|
||||
// datatables / query. This is necessary so the device.name property can
|
||||
// be used.
|
||||
CriteriaModifier criteriaModifier = new CriteriaModifier() {
|
||||
@Override
|
||||
public void modify(final CriteriaQuery criteriaQuery) {
|
||||
Session session = entityManager.unwrap(Session.class);
|
||||
CriteriaBuilder cb = session.getCriteriaBuilder();
|
||||
Root<Certificate> scvRoot = criteriaQuery.from(Reference.class);
|
||||
|
||||
criteriaQuery.select(scvRoot).distinct(true).where(cb.isNull(scvRoot.get(Certificate.ARCHIVE_FIELD)));
|
||||
}
|
||||
};
|
||||
|
||||
FilteredRecordsList<SupplyChainValidationSummary> records = new FilteredRecordsList<>();
|
||||
int currentPage = input.getStart() / input.getLength();
|
||||
Pageable paging = PageRequest.of(currentPage, input.getLength(), Sort.by(orderColumnName));
|
||||
@ -149,15 +126,12 @@ public class ValidationReportsPageController extends PageController<NoPageParams
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
records.addAll(pagedResult.getContent());
|
||||
records.setRecordsTotal(pagedResult.getContent().size());
|
||||
} else {
|
||||
records.setRecordsTotal(input.getLength());
|
||||
}
|
||||
records.setRecordsTotal(input.getLength());
|
||||
records.setRecordsFiltered(supplyChainValidatorSummaryRepository.count());
|
||||
|
||||
// FilteredRecordsList<SupplyChainValidationSummary> records =
|
||||
// OrderedListQueryDataTableAdapter.getOrderedList(
|
||||
// SupplyChainValidationSummary.class,
|
||||
// supplyChainValidatorSummaryRepository, input, orderColumnName,
|
||||
// criteriaModifier);
|
||||
records.setRecordsFiltered(supplyChainValidatorSummaryRepository.count());
|
||||
|
||||
return new DataTableResponse<>(records, input);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredent
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.PlatformConfiguration;
|
||||
import hirs.utils.BouncyCastleUtils;
|
||||
import hirs.attestationca.persist.util.PciIds;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
@ -19,6 +20,7 @@ import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -148,15 +150,14 @@ public final class CertificateStringMapBuilder {
|
||||
final Certificate certificate,
|
||||
final CertificateRepository certificateRepository,
|
||||
final CACredentialRepository caCredentialRepository) {
|
||||
List<CertificateAuthorityCredential> issuerCertificates = new LinkedList<>();
|
||||
List<CertificateAuthorityCredential> issuerCertificates = new ArrayList<>();
|
||||
CertificateAuthorityCredential skiCA = null;
|
||||
String issuerResult;
|
||||
|
||||
//Check if there is a subject organization
|
||||
if (certificate.getAuthorityKeyIdentifier() != null
|
||||
&& !certificate.getAuthorityKeyIdentifier().isEmpty()) {
|
||||
byte[] bytes = Hex.decode(certificate.getAuthorityKeyIdentifier());
|
||||
skiCA = caCredentialRepository.findBySubjectKeyIdentifier(bytes);
|
||||
skiCA = caCredentialRepository.findBySubjectKeyIdString(certificate.getAuthorityKeyIdentifier());
|
||||
} else {
|
||||
log.error(String.format("Certificate (%s) for %s has no authority key identifier.",
|
||||
certificate.getClass().toString(), certificate.getSubject()));
|
||||
@ -184,7 +185,7 @@ public final class CertificateStringMapBuilder {
|
||||
if (issuerResult.isEmpty()) {
|
||||
//Check if it's root certificate
|
||||
if (BouncyCastleUtils.x500NameCompare(issuerCert.getIssuerSorted(),
|
||||
issuerCert.getSubject())) {
|
||||
issuerCert.getSubjectSorted())) {
|
||||
return null;
|
||||
}
|
||||
return containsAllChain(issuerCert, certificateRepository, caCredentialRepository);
|
||||
|
@ -52,13 +52,7 @@
|
||||
data: 'deviceName',
|
||||
render: function (data, type, full, meta) {
|
||||
// if there's a device, display its name, otherwise
|
||||
// display nothing
|
||||
if (full.device) {
|
||||
// TODO render a link to a device details page,
|
||||
// passing the device.id
|
||||
return full.deviceName;
|
||||
}
|
||||
return '';
|
||||
return full.deviceName;
|
||||
}
|
||||
},
|
||||
{data: 'issuer'},
|
||||
|
@ -1,3 +1,15 @@
|
||||
<div class="container">
|
||||
Page Not Found! <a href="/devices">Devices</a>
|
||||
</div>
|
||||
<%@page contentType="text/html" pageEncoding="UTF-8"%>
|
||||
|
||||
<%-- JSP TAGS --%>
|
||||
<%@taglib prefix="c" uri="jakarta.tags.core" %>
|
||||
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
|
||||
|
||||
<%-- CONTENT --%>
|
||||
<my:page>
|
||||
<jsp:attribute name="pageHeaderTitle">Error - 404</jsp:attribute>
|
||||
|
||||
<jsp:body>
|
||||
<!--<div> Exception Message: <c:out value="${exception}"</c:out></div>
|
||||
<div> from URL -> <span th:text="${url}"</span></div>-->
|
||||
</jsp:body>
|
||||
</my:page>
|
@ -10,12 +10,16 @@
|
||||
<jsp:body>
|
||||
<h3 class="content-subhead" id="alerttype">Documentation</h3>
|
||||
|
||||
<!--
|
||||
<ul>
|
||||
<c:forEach items="${docs}" var="doc">
|
||||
<li><a href="${baseURL}/docs/${doc.name}">${doc.name}</a></li>
|
||||
</c:forEach>
|
||||
</ul>
|
||||
-->
|
||||
|
||||
<p>For more documentation on the project, you may visit the wiki section of our code repository.</p>
|
||||
<p>
|
||||
For more documentation on the project, you may visit the wiki section of our <a href="https://github.com/nsacyber/HIRS/wiki">code repository</a>.
|
||||
</p>
|
||||
</jsp:body>
|
||||
</my:page>
|
@ -48,12 +48,7 @@
|
||||
render: function (data, type, full, meta) {
|
||||
// if there's a device, display its name, otherwise
|
||||
// display nothing
|
||||
if (full.device) {
|
||||
// TODO render a link to a device details page,
|
||||
// passing the device.id
|
||||
return full.deviceName;
|
||||
}
|
||||
return '';
|
||||
return full.deviceName;
|
||||
}
|
||||
},
|
||||
{data: 'issuer'},
|
||||
|
@ -57,13 +57,7 @@
|
||||
data: 'deviceName',
|
||||
render: function (data, type, full, meta) {
|
||||
// if there's a device, display its name, otherwise
|
||||
// display nothing
|
||||
if (full.device) {
|
||||
// TODO render a link to a device details page,
|
||||
// passing the device.id
|
||||
return full.deviceName;
|
||||
}
|
||||
return '';
|
||||
return full.deviceName;
|
||||
}
|
||||
},
|
||||
{data: 'issuer'},
|
||||
|
@ -68,17 +68,17 @@
|
||||
|
||||
// create status icon
|
||||
var result = full.overallValidationResult;
|
||||
var ovallMessage = full.message;
|
||||
var overallMessage = full.message;
|
||||
if (result) {
|
||||
switch (result) {
|
||||
case "PASS":
|
||||
html += '<img src="${passIcon}" title="${passText}"/>';
|
||||
break;
|
||||
case "FAIL":
|
||||
html += '<img src="${failIcon}" title="' + ovallMessage + '"/>';
|
||||
html += '<img src="${failIcon}" title="' + overallMessage + '"/>';
|
||||
break;
|
||||
case "ERROR":
|
||||
html += '<img src="${errorIcon}" title="' + ovallMessage + '"/>';
|
||||
html += '<img src="${errorIcon}" title="' + overallMessage + '"/>';
|
||||
break;
|
||||
default:
|
||||
html += unknownStatus;
|
||||
@ -183,7 +183,7 @@
|
||||
// the validation_type.
|
||||
for (var i = 0; i < full.validations.length; i++) {
|
||||
var curValidation = full.validations[i];
|
||||
var curResult = curValidation.result;
|
||||
var curResult = curValidation.validationResult;
|
||||
var curMessage = curValidation.message;
|
||||
|
||||
if (curValidation.validationType === validation_type) {
|
||||
|
@ -36,8 +36,8 @@ public final class BouncyCastleUtils {
|
||||
X500Name x500Name2;
|
||||
|
||||
try {
|
||||
x500Name1 = new X500Name(nameValue1.replace(SEPARATOR_PLUS, SEPARATOR_COMMA));
|
||||
x500Name2 = new X500Name(nameValue2.replace(SEPARATOR_PLUS, SEPARATOR_COMMA));
|
||||
x500Name1 = new X500Name(nameValue1);
|
||||
x500Name2 = new X500Name(nameValue2);
|
||||
result = x500Name1.equals(x500Name2);
|
||||
} catch (IllegalArgumentException iaEx) {
|
||||
log.error(iaEx.toString());
|
||||
|
@ -23,8 +23,8 @@ public class UefiGuid {
|
||||
*/
|
||||
private static final int UUID_EPOCH_DIVISOR = 10000;
|
||||
|
||||
private static final Path JSON_PATH = FileSystems.getDefault().getPath("/opt",
|
||||
"hirs/aca", "default-properties", "vendor-table.json");
|
||||
private static final Path JSON_PATH = FileSystems.getDefault().getPath("/etc",
|
||||
"hirs", "aca", "default-properties", "vendor-table.json");
|
||||
private JsonObject uefiVendorRef;
|
||||
/**
|
||||
* guid byte array.
|
||||
@ -175,10 +175,7 @@ public class UefiGuid {
|
||||
* @return true if the uuid is the Empty UUID, false if not
|
||||
*/
|
||||
public boolean isUnknownUUID() {
|
||||
if (getVendorTableReference().equals("Unknown GUID reference")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return getVendorTableReference().equals("Unknown GUID reference");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,7 +37,15 @@ DB_SRV_CONF="/etc/my.cnf.d/mariadb-server.cnf"
|
||||
DB_CLIENT_CONF="/etc/my.cnf.d/client.cnf"
|
||||
ALL_CHECKS_PASSED=true
|
||||
ALL_CERTS_PASSED=true
|
||||
|
||||
source $SCRIPT_DIR/../db/mysql_util.sh
|
||||
source /etc/os-release
|
||||
|
||||
# Setup distro specifc paths and variables
|
||||
if [ $ID = "ubuntu" ]; then
|
||||
DB_SRV_CONF="/etc/mysql/mariadb.conf.d/50-server.cnf"
|
||||
DB_CLIENT_CONF="/etc/mysql/mariadb.conf.d/50-client.cnf"
|
||||
fi
|
||||
|
||||
# Check for Admin privileges
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
@ -69,16 +77,24 @@ done
|
||||
echo "Checking HIRS ACA Setup on this device..."
|
||||
# Check if aca setup was performed
|
||||
# Check is RPM was installed via RPM package
|
||||
rpm -q --quiet HIRS_AttestationCA
|
||||
if [ $ID = "rhel" ]; then
|
||||
echo "RHEL distro detected"
|
||||
rpm -q --quiet HIRS_AttestationCA
|
||||
elif [ $ID = 'ubuntu' ]; then
|
||||
echo "Ubuntu distro detected"
|
||||
dpkg -l "hirs-attestationca" > /dev/null
|
||||
else
|
||||
echo "Unsupported OS Distro encountered"
|
||||
fi
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "HIRS ACA was installed via rpm package on this device"
|
||||
echo "HIRS ACA was installed via an OS package on this device"
|
||||
if [[ $(cat /etc/crontab | grep -c hirs/aca) > 0 ]]; then
|
||||
echo " HIRS ACA is set to start on boot via crontab file"
|
||||
else
|
||||
echo " HIRS ACA is NOT set to start on boot via crontab file"
|
||||
fi
|
||||
else
|
||||
echo "HIRS ACA was NOT installed via rpm package on this device"
|
||||
echo "HIRS ACA was NOT installed via an OS package on this device"
|
||||
fi
|
||||
|
||||
# Check install setup pki files
|
||||
@ -92,7 +108,6 @@ echo "Checking HIRS ACA Setup on this device..."
|
||||
|
||||
source /etc/hirs/aca/aca.properties;
|
||||
|
||||
|
||||
check_pwds () {
|
||||
|
||||
PRESENT=true
|
||||
@ -121,13 +136,13 @@ check_mysql_setup () {
|
||||
# make sure mysql is running and restart if its not...
|
||||
check_mysql
|
||||
# Check DB server/client TLS setup.
|
||||
if [[ $(cat "$DB_SRV_CONF" | grep -c "ssl") < 1 ]]; then
|
||||
if [[ $(cat "$DB_SRV_CONF" | grep -c "HIRS") < 1 ]]; then
|
||||
echo " Mysql server ($DB_SRV_CONF) is NOT configured for Server Side TLS"
|
||||
ALL_CHECKS_PASSED=false
|
||||
else
|
||||
echo " Mysql server ($DB_SRV_CONF) is configured for Server Side TLS"
|
||||
fi
|
||||
if [[ $(cat "$DB_CLIENT_CONF" | grep -c "ssl") < 1 ]]; then
|
||||
if [[ $(cat "$DB_CLIENT_CONF" | grep -c "HIRS") < 1 ]]; then
|
||||
echo " Mysql client ($DB_CLIENT_CONF)is NOT configured for command line use of TLS without provding key/cert ino the commandline"
|
||||
ALL_CHECKS_PASSED=false
|
||||
else
|
||||
@ -240,12 +255,18 @@ check_db () {
|
||||
mysql -u hirs_db --password=$hirs_db_password -e "SHOW DATABASES;";
|
||||
echo "Privileges for the hirs_db user:"
|
||||
mysql -u hirs_db --password=$hirs_db_password -e "SHOW GRANTS FOR 'hirs_db'@'localhost'"
|
||||
echo "MYSQL Log:"
|
||||
mysql -u root --password=$mysql_admin_password -e "SHOW GLOBAL VARIABLES LIKE 'log_error'"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
# Check selinux status and files that require specific contexts
|
||||
check_selinux () {
|
||||
if [ $ID = "ubuntu" ]; then
|
||||
echo "Skipping selinux check on ubuntu"
|
||||
return
|
||||
fi
|
||||
SELINUXSTATUS=$(getenforce)
|
||||
DB_SRV_CONTEXT=$(ls -Z $DB_SRV_CONF)
|
||||
DB_CLIENT_CONTEXT=$(ls -Z $DB_CLIENT_CONF)
|
||||
@ -283,4 +304,4 @@ if [ $ALL_CHECKS_PASSED = true ]; then
|
||||
echo "ACA setup checks passed!"
|
||||
else
|
||||
echo "ACA setup checks failed."
|
||||
fi
|
||||
fi
|
||||
|
@ -22,7 +22,7 @@ check_mysql_root
|
||||
|
||||
# remove the hrs-db and hirs_db user
|
||||
pushd $SCRIPT_DIR/../db/ &>/dev/null
|
||||
sh db_drop.sh $DB_ADMIN_PWD
|
||||
./db_drop.sh $DB_ADMIN_PWD
|
||||
popd &>/dev/null
|
||||
|
||||
# remove pki files and config files if not installed by rpm
|
||||
@ -36,4 +36,4 @@ echo "Removing the ACA crontab"
|
||||
sed -i '/aca_bootRun.sh/d' /etc/crontab
|
||||
echo "Shutting down the aca..."
|
||||
ps axf | grep HIRS_AttestationCAPortal.war | grep -v grep | awk '{print "kill " $1}' | sh >/dev/null 2>&1
|
||||
echo "ACA setup removal complete."
|
||||
echo "ACA setup removal complete."
|
||||
|
@ -25,7 +25,7 @@ help () {
|
||||
echo " -u | --unattended Run unattended"
|
||||
echo " -h | --help Print this Help."
|
||||
echo " -sp | --skip-pki run the setup without pki setup."
|
||||
echo " -sb | --skip-db run the setup without database setup."
|
||||
echo " -sd | --skip-db run the setup without database setup."
|
||||
echo
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ if [ -z $HIRS_PKI_PWD ]; then
|
||||
fi
|
||||
|
||||
if [ -z "${ARG_SKIP_PKI}" ]; then
|
||||
sh ../pki/pki_setup.sh $LOG_FILE $PKI_PASS $ARG_UNATTEND
|
||||
../pki/pki_setup.sh $LOG_FILE $PKI_PASS $ARG_UNATTEND
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "ACA PKI setup complete" | tee -a "$LOG_FILE"
|
||||
else
|
||||
@ -109,7 +109,7 @@ if [ -z "${ARG_SKIP_PKI}" ]; then
|
||||
fi
|
||||
|
||||
if [ -z "${ARG_SKIP_DB}" ]; then
|
||||
sh ../db/db_create.sh $LOG_FILE $PKI_PASS $ARG_UNATTEND
|
||||
../db/db_create.sh $LOG_FILE $PKI_PASS $ARG_UNATTEND
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "ACA database setup complete" | tee -a "$LOG_FILE"
|
||||
else
|
||||
@ -122,4 +122,4 @@ fi
|
||||
|
||||
echo "ACA setup complete" | tee -a "$LOG_FILE"
|
||||
|
||||
popd &>/dev/null
|
||||
popd &>/dev/null
|
||||
|
@ -8,6 +8,7 @@
|
||||
################################################################################
|
||||
|
||||
LOG_FILE=$1
|
||||
DB_LOG_FILE="/var/log/mariadb/mariadb.log"
|
||||
PKI_PASS=$2
|
||||
UNATTENDED=$3
|
||||
RSA_PATH=rsa_3k_sha384_certs
|
||||
@ -17,7 +18,7 @@ SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; )
|
||||
SPRING_PROP_FILE="/etc/hirs/aca/application.properties"
|
||||
ACA_PROP_FILE="/etc/hirs/aca/aca.properties"
|
||||
DB_ADMIN_PWD=""
|
||||
# Db Configuration files
|
||||
# Db Configuration fileis, use RHELpaths as default
|
||||
DB_SRV_CONF="/etc/my.cnf.d/mariadb-server.cnf"
|
||||
DB_CLIENT_CONF="/etc/my.cnf.d/client.cnf"
|
||||
# Default Server Side Certificates
|
||||
@ -29,16 +30,29 @@ SSL_DB_CLIENT_CHAIN="/etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_rsa_3k
|
||||
SSL_DB_CLIENT_CERT="/etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_db_client_rsa_3k_sha384.pem";
|
||||
SSL_DB_CLIENT_KEY="/etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_db_client_rsa_3k_sha384.key";
|
||||
|
||||
touch $ACA_PROP_FILE
|
||||
touch $LOG_FILE
|
||||
touch $DB_SRV_CONF
|
||||
|
||||
# Make sure required paths exist
|
||||
mkdir -p /etc/hirs/aca/
|
||||
mkdir -p /var/log/hirs/
|
||||
|
||||
source $SCRIPT_DIR/mysql_util.sh
|
||||
source $ACA_PROP_FILE
|
||||
source /etc/os-release
|
||||
|
||||
# Setup distro specifc paths and variables
|
||||
if [ $ID = "ubuntu" ]; then
|
||||
DB_SRV_CONF="/etc/mysql/mariadb.conf.d/50-server.cnf"
|
||||
DB_CLIENT_CONF="/etc/mysql/mariadb.conf.d/50-client.cnf"
|
||||
mkdir -p /var/log/mariadb >> /dev/null
|
||||
if [[ $(cat "$DB_SRV_CONF" | grep -c "log-error") < 1 ]]; then
|
||||
echo "log_error=/var/log/mariadb/mariadb.log" >> $DB_SRV_CONF
|
||||
echo "tls_version = TLSv1.2,TLSv1.3" >> $DB_SRV_CONF
|
||||
fi
|
||||
fi
|
||||
|
||||
touch $ACA_PROP_FILE
|
||||
touch $LOG_FILE
|
||||
touch $DB_SRV_CONF
|
||||
touch $DB_LOG_FILE
|
||||
|
||||
check_mysql_root_pwd () {
|
||||
|
||||
@ -90,8 +104,8 @@ check_mysql_root_pwd () {
|
||||
}
|
||||
|
||||
set_mysql_server_tls () {
|
||||
# Check DB server setup. If ssl params dont exist then we need to add them.
|
||||
if [[ $(cat "$DB_SRV_CONF" | grep -c "ssl") < 1 ]]; then
|
||||
# Check DB server setup. If HIRS ssl params dont exist then we need to add them.
|
||||
if [[ $(cat "$DB_SRV_CONF" | grep -c "HIRS") < 1 ]]; then
|
||||
# Add TLS files to my.cnf
|
||||
echo "Updating $DB_SRV_CONF with ssl parameters..." | tee -a "$LOG_FILE"
|
||||
echo "ssl_ca=$SSL_DB_SRV_CHAIN" >> "$DB_SRV_CONF"
|
||||
@ -100,10 +114,12 @@ set_mysql_server_tls () {
|
||||
# Make sure mysql can access them
|
||||
chown mysql:mysql $SSL_DB_SRV_CHAIN $SSL_DB_SRV_CERT $SSL_DB_SRV_KEY
|
||||
# Make selinux contexts for config files, if selinux is enabled
|
||||
selinuxenabled
|
||||
if [ $? -eq 0 ]; then
|
||||
semanage fcontext -a -t mysqld_etc_t $DB_SRV_CONF > /dev/null #adds the context type to file
|
||||
restorecon -v -F $DB_SRV_CONF > /dev/null # changes the file's context type
|
||||
if [ $ID = "rhel" ]; then
|
||||
selinuxenabled
|
||||
if [ $? -eq 0 ]; then
|
||||
semanage fcontext -a -t mysqld_etc_t $DB_SRV_CONF > /dev/null #adds the context type to file
|
||||
restorecon -v -F $DB_SRV_CONF > /dev/null # changes the file's context type
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "mysql.cnf contians existing entry for ssl, skipping..." | tee -a "$LOG_FILE"
|
||||
@ -112,17 +128,19 @@ set_mysql_server_tls () {
|
||||
|
||||
set_mysql_client_tls () {
|
||||
# Update ACA property file with client cert info, if not there already
|
||||
if [[ $(cat "$DB_CLIENT_CONF" | grep -c "ssl") < 1 ]]; then
|
||||
if [[ $(cat "$DB_CLIENT_CONF" | grep -c "HIRS") < 1 ]]; then
|
||||
echo "Updating $DB_CLIENT_CONF with ssl parameters..." | tee -a "$LOG_FILE"
|
||||
echo "ssl_ca=$SSL_DB_CLIENT_CHAIN" >> $DB_CLIENT_CONF
|
||||
echo "ssl_cert=$SSL_DB_CLIENT_CERT" >> $DB_CLIENT_CONF
|
||||
echo "ssl_key=$SSL_DB_CLIENT_KEY" >> $DB_CLIENT_CONF
|
||||
chown mysql:mysql $SSL_DB_CLIENT_CHAIN $SSL_DB_CLIENT_CERT $SSL_DB_CLIENT_KEY
|
||||
# Make selinux contexts for config files, if selinux is enabled
|
||||
selinuxenabled
|
||||
if [ $? -eq 0 ]; then
|
||||
semanage fcontext -a -t mysqld_etc_t $DB_CLIENT_CONFf > /dev/null #adds the context type to file
|
||||
restorecon -F $DB_CLIENT_CONF > /dev/null #changes the file's context type
|
||||
if [ $ID = "rhel" ]; then
|
||||
selinuxenabled
|
||||
if [ $? -eq 0 ]; then
|
||||
semanage fcontext -a -t mysqld_etc_t $DB_CLIENT_CONFf > /dev/null #adds the context type to file
|
||||
restorecon -F $DB_CLIENT_CONF > /dev/null #changes the file's context type
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
@ -130,7 +148,8 @@ fi
|
||||
# Process HIRS DB USER
|
||||
set_hirs_db_pwd () {
|
||||
|
||||
RESULT="$(mysql -u root --password=$DB_ADMIN_PWD -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'hirs_db')")"
|
||||
RESULT="$(mysql -u root --password=$DB_ADMIN_PWD -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = 'hirs_db')")"
|
||||
|
||||
if [ "$RESULT" = 1 ]; then
|
||||
echo "hirs-db user exists"
|
||||
HIRS_DB_PWD=$hirs_db_password
|
||||
@ -157,7 +176,8 @@ create_hirs_db_with_tls () {
|
||||
else
|
||||
mysql -u root --password=$DB_ADMIN_PWD < $MYSQL_DIR/db_create.sql
|
||||
mysql -u root --password=$DB_ADMIN_PWD < $MYSQL_DIR/secure_mysql.sql
|
||||
mysql -u root --password=$DB_ADMIN_PWD -e "ALTER USER 'hirs_db'@'localhost' IDENTIFIED BY '"$HIRS_DB_PWD"'; FLUSH PRIVILEGES;";
|
||||
# mysql -u root --password=$DB_ADMIN_PWD -e "ALTER USER 'hirs_db'@'localhost' IDENTIFIED BY '"$HIRS_DB_PWD"'; FLUSH PRIVILEGES;";
|
||||
mysql -u root --password=$DB_ADMIN_PWD -e "SET PASSWORD FOR 'hirs_db'@'localhost' = PASSWORD('"$HIRS_DB_PWD"'); FLUSH PRIVILEGES;";
|
||||
fi
|
||||
}
|
||||
|
||||
@ -196,6 +216,7 @@ check_for_container -p
|
||||
set_mysql_server_tls
|
||||
set_mysql_client_tls
|
||||
start_mysqlsd
|
||||
check_mysql
|
||||
check_mysql_root_pwd
|
||||
set_hirs_db_pwd
|
||||
create_hirs_db_with_tls
|
||||
|
@ -1,13 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
SRV_CNF=/etc/my.cnf.d/mariadb-server.cnf
|
||||
CLIENT_CNF=/etc/my.cnf.d/client.cnf
|
||||
DB_SRV_CONF=/etc/my.cnf.d/mariadb-server.cnf
|
||||
DB_CLIENT_CONF=/etc/my.cnf.d/client.cnf
|
||||
SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )";)
|
||||
LOG_FILE=/dev/null
|
||||
DB_ADMIN_PWD=$1
|
||||
|
||||
#source /etc/hirs/aca/aca.properties;
|
||||
source $SCRIPT_DIR/mysql_util.sh
|
||||
source /etc/os-release
|
||||
|
||||
# Check for sudo or root user, not actually needed but a good idea
|
||||
if [ "$EUID" -ne 0 ]
|
||||
@ -15,6 +16,12 @@ if [ "$EUID" -ne 0 ]
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup distro specifc paths and variables
|
||||
if [ $ID = "ubuntu" ]; then
|
||||
DB_SRV_CONF="/etc/mysql/mariadb.conf.d/50-server.cnf"
|
||||
DB_CLIENT_CONF="/etc/mysql/mariadb.conf.d/50-client.cnf"
|
||||
fi
|
||||
|
||||
if [ -d /opt/hirs/scripts/db ]; then
|
||||
MYSQL_DIR="/opt/hirs/scripts/db"
|
||||
else
|
||||
@ -44,8 +51,8 @@ fi
|
||||
# Remove key , cert and truststore entries from client.cnf andf mariadb.cnf
|
||||
|
||||
echo "Removing hirs cert references from mariadb configuration files"
|
||||
grep -v "hirs" $SRV_CNF > tmpfile && mv tmpfile $SRV_CNF
|
||||
grep -v "hirs" $CLIENT_CNF > tmpfile && mv tmpfile $CLIENT_CNF
|
||||
grep -v "hirs" $DB_SRV_CONF > tmpfile && mv tmpfile $DB_SRV_CONF
|
||||
grep -v "hirs" $DB_CLIENT_CONF > tmpfile && mv tmpfile $DB_CLIENT_CONF
|
||||
|
||||
echo "restarting mariadb"
|
||||
|
||||
|
@ -12,7 +12,7 @@ SQL_SERVICE="mariadb"
|
||||
check_for_container () {
|
||||
PRINT_STATUS=$1
|
||||
# Check if we're in a Docker container
|
||||
if [[ $(cat /proc/1/sched | head -n 1) == *"bash"* ]]; then
|
||||
if [[ $(cat /proc/1/cgroup | head -n 1) == *"docker"* ]]; then
|
||||
#if [ -f /.dockerenv ]; then
|
||||
DOCKER_CONTAINER=true
|
||||
if [[ $PRINT_STATUS == "-p" ]]; then echo "ACA is running in a container..." | tee -a "$LOG_FILE"; fi
|
||||
@ -40,8 +40,13 @@ check_mariadb_install () {
|
||||
# Starts mariadb during intial install
|
||||
start_mysqlsd () {
|
||||
PRINT_STATUS=$1
|
||||
PROCESS="mysqld"
|
||||
source /etc/os-release
|
||||
if [ $ID = "ubuntu" ]; then
|
||||
PROCESS="mariadb"
|
||||
fi
|
||||
# Check if mysql is already running, if not initialize
|
||||
if [[ $(pgrep -c -u mysql mysqld) -eq 0 ]]; then
|
||||
if [[ $(pgrep -c -u mysql $PROCESS) -eq 0 ]]; then
|
||||
# Check if running in a container
|
||||
if [ $DOCKER_CONTAINER = true ]; then
|
||||
# if in Docker container, avoid services that invoke the D-Bus
|
||||
@ -52,13 +57,12 @@ start_mysqlsd () {
|
||||
chown -R mysql:mysql /var/lib/mysql/ >> "$LOG_FILE"
|
||||
fi
|
||||
if [[ $PRINT_STATUS == "-p" ]]; then echo "Starting mysql..."; fi
|
||||
touch /var/log/mariadb/mariadb.log
|
||||
chown mysql:mysql /var/log/mariadb/mariadb.log >> "$LOG_FILE";
|
||||
/usr/bin/mysqld_safe & >> "$LOG_FILE";
|
||||
echo "Attempting to start mariadb"
|
||||
/usr/bin/mysqld_safe --skip-syslog >> "$LOG_FILE" &
|
||||
chown -R mysql:mysql /var/lib/mysql/ >> "$LOG_FILE"
|
||||
echo "Attempting to start mariadb"
|
||||
else #not a container
|
||||
systemctl enable $SQL_SERVICE & >> "$LOG_FILE";
|
||||
systemctl start $SQL_SERVICE & >> "$LOG_FILE";
|
||||
systemctl start $SQL_SERVICE & >> "$LOG_FILE";
|
||||
fi
|
||||
else # mysql process is running
|
||||
# check if mysql service is running
|
||||
@ -70,22 +74,22 @@ start_mysqlsd () {
|
||||
fi
|
||||
fi # non contanier mysql start
|
||||
fi
|
||||
# Wait for mysql to start before continuing.
|
||||
if [[ $PRINT_STATUS == "-p" ]]; then echo "Checking mysqld status..."| tee -a "$LOG_FILE"; fi
|
||||
while ! mysqladmin ping -h "$localhost" --silent; do
|
||||
sleep 1;
|
||||
done
|
||||
|
||||
if [[ $PRINT_STATUS == "-p" ]]; then echo "mysqld is running."| tee -a "$LOG_FILE"; fi
|
||||
}
|
||||
|
||||
# Basic check for marai db status, attempts restart if not running
|
||||
check_mysql () {
|
||||
PROCESS="mysqld"
|
||||
source /etc/os-release
|
||||
if [ $ID = "ubuntu" ]; then
|
||||
PROCESS="mariadb"
|
||||
fi
|
||||
|
||||
echo "Checking mysqld status..."
|
||||
if [ $DOCKER_CONTAINER = true ]; then
|
||||
if [[ $(pgrep -c -u mysql mysqld) -eq 0 ]]; then
|
||||
if [[ $(pgrep -c -u mysql $PROCESS ) -eq 0 ]]; then
|
||||
echo "mariadb not running , attempting to restart"
|
||||
/usr/bin/mysqld_safe & >> "$LOG_FILE"
|
||||
chown mysql:mysql /var/log/mariadb/mariadb.log >> "$LOG_FILE";
|
||||
/usr/bin/mysqld_safe --skip-syslog >> "$LOG_FILE" &
|
||||
fi
|
||||
else # not in a contianer
|
||||
DB_STATUS=$(systemctl status mysql |grep 'running' | wc -l )
|
||||
@ -95,12 +99,24 @@ check_mysql () {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Wait for mysql to start before continuing.
|
||||
|
||||
while ! mysqladmin ping -h "$localhost" --silent; do
|
||||
sleep 1;
|
||||
# Wait for mysql to start before continuing.
|
||||
count=1;
|
||||
if [[ $PRINT_STATUS == "-p" ]]; then echo "Testing mysqld connection..."| tee -a "$LOG_FILE"; fi
|
||||
|
||||
until mysqladmin ping -h "localhost" --silent ; do
|
||||
((count++))
|
||||
if [[ $count -gt 20 ]]; then
|
||||
break;
|
||||
fi
|
||||
sleep 1;
|
||||
done
|
||||
echo " Mariadb is running."
|
||||
if [[ $count -gt 20 ]]; then
|
||||
echo "Timed out waiting for Mariadb to respond"
|
||||
exit 1;
|
||||
else
|
||||
echo "Mariadb started"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check for mysql root password , abort if not available
|
||||
@ -163,5 +179,5 @@ mysqld_reboot () {
|
||||
mysql -u root --password=$DB_ADMIN_PWD -e "SHUTDOWN"
|
||||
sleep 2
|
||||
check_for_container
|
||||
start_mysqlsd
|
||||
}
|
||||
start_mysqlsd >> "$LOG_FILE";
|
||||
}
|
||||
|
@ -225,6 +225,9 @@ create_cert_chain () {
|
||||
-srcstorepass $PASS -destkeystore $DB_CLIENT.jks -deststoretype JKS -deststorepass $PASS >> "$LOG_FILE" 2>&1
|
||||
}
|
||||
|
||||
# Needed for older versions of openssl
|
||||
#openssl rand -writerand .rnd
|
||||
|
||||
if [ "$ASYM_ALG" == "rsa" ]; then
|
||||
# Create Root CA key pair and self signed cert
|
||||
echo "Generating RSA Root CA ...." | tee -a "$LOG_FILE"
|
||||
@ -255,4 +258,4 @@ if [ "$ASYM_ALG" == "ecc" ]; then
|
||||
add_to_stores $PKI_ROOT
|
||||
# Create an intermediate CA, 2 Leaf CAs, and Signer Certs
|
||||
create_cert_chain
|
||||
fi
|
||||
fi
|
||||
|
@ -42,8 +42,8 @@ if [ -z "$PKI_PASS" ]; then
|
||||
fi
|
||||
|
||||
# Check for sudo or root user
|
||||
if [ "$EUID" -ne 0 ]
|
||||
then echo "This script requires root. Please run as root" | tee -a "$LOG_FILE"
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "This script requires root. Please run as root" | tee -a "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@ -61,8 +61,8 @@ if [ ! -d "/etc/hirs/certificates" ]; then
|
||||
|
||||
pushd /etc/hirs/certificates/ &> /dev/null
|
||||
cp $PKI_SETUP_DIR/ca.conf .
|
||||
sh $PKI_SETUP_DIR/pki_chain_gen.sh "HIRS" "rsa" "3072" "sha384" "$PKI_PASS" "$LOG_FILE"
|
||||
sh $PKI_SETUP_DIR/pki_chain_gen.sh "HIRS" "ecc" "512" "sha384" "$PKI_PASS" "$LOG_FILE"
|
||||
$PKI_SETUP_DIR/pki_chain_gen.sh "HIRS" "rsa" "3072" "sha384" "$PKI_PASS" "$LOG_FILE"
|
||||
$PKI_SETUP_DIR/pki_chain_gen.sh "HIRS" "ecc" "512" "sha384" "$PKI_PASS" "$LOG_FILE"
|
||||
popd &> /dev/null
|
||||
|
||||
echo "hirs_pki_password="$PKI_PASS >> $ACA_PROP
|
||||
|
Loading…
Reference in New Issue
Block a user