mirror of
https://github.com/nsacyber/HIRS.git
synced 2024-12-18 20:47:58 +00:00
merging any updates in main into this branc
gerge branch 'main' into v3_issue_768_Part2-eventProcessing
This commit is contained in:
commit
f995f0fc1c
110
.ci/docker/Dockerfile.aca-rocky-ci
Normal file
110
.ci/docker/Dockerfile.aca-rocky-ci
Normal file
@ -0,0 +1,110 @@
|
||||
FROM rockylinux:9
|
||||
LABEL org.opencontainers.image.vendor NSA Laboratory for Advanced Cybersecurity Research
|
||||
LABEL org.opencontainers.image.source https://github.com/nsacyber/hirs
|
||||
LABEL org.opencontainers.image.description NSA\'s HIRS Attestation Certificate Authority. Expose port 8443 to access the portal from outside the container.
|
||||
|
||||
# Purpose: This image is designed for HIRS ACA testing on Rocky 9. It is meant to be used in
|
||||
# the .ci, and does not automatically start the ACA upon running the container.
|
||||
# Date Modified: 5/3/24
|
||||
# Notes: The image to be built should be named "aca-rocky-ci:latest".
|
||||
# For local image build, use this command from the /HIRS/ directory to build the image:
|
||||
# $ docker build -f ./.ci/docker/Dockerfile.aca-rocky-ci -t aca-rocky-ci:latest .
|
||||
# For manually setting up and starting the ACA in the container, uncomment the CMD
|
||||
# statement at the end of this Dockerfile, allowing those scripts to be executed upon
|
||||
# running the container.
|
||||
|
||||
# REF can be specified as a docker run environment variable to select the HIRS branch to work with
|
||||
ARG REF=main
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
# Rocky 9 has a different channel for some apps
|
||||
RUN dnf install -y 'dnf-command(config-manager)' && dnf config-manager --set-enabled crb
|
||||
|
||||
# Update and install OS-dependencies
|
||||
RUN dnf update -y
|
||||
# Dependencies were selected for these reasons:
|
||||
# OS setup/Unknown direct impact for HIRS
|
||||
ENV HIRS_DNF_OS_SETUP="initscripts firewalld policycoreutils policycoreutils-python-utils net-tools"
|
||||
# OS tools
|
||||
ENV HIRS_DNF_OS_TOOLS="git sudo vim wget"
|
||||
# ACA compile
|
||||
ENV HIRS_DNF_ACA_COMPILE="java-17-openjdk-devel"
|
||||
# ACA run
|
||||
ENV HIRS_DNF_ACA_RUN="mariadb-server"
|
||||
# IBM TPM simulator compile
|
||||
ENV HIRS_DNF_TPM_COMPILE="tpm2-tools gcc cmake openssl-devel"
|
||||
# IBM TSS compile
|
||||
ENV HIRS_DNF_TSS_COMPILE="autoconf automake libtool"
|
||||
# Download and install all dependencies at one time
|
||||
RUN dnf -y install $(echo "$HIRS_DNF_OS_SETUP") $(echo "$HIRS_DNF_OS_TOOLS") $(echo "$HIRS_DNF_ACA_COMPILE") $(echo "$HIRS_DNF_ACA_RUN") $(echo "$HIRS_DNF_TPM_COMPILE") $(echo "$HIRS_DNF_TSS_COMPILE")
|
||||
|
||||
# Build IBM TPM Simulator
|
||||
RUN git clone https://github.com/kgoldman/ibmswtpm2 /ibmswtpm2
|
||||
WORKDIR /ibmswtpm2/src
|
||||
RUN make
|
||||
|
||||
# Build IBM TPM tools
|
||||
RUN git clone https://github.com/kgoldman/ibmtss /ibmtss
|
||||
WORKDIR /ibmtss/utils
|
||||
RUN make -f makefiletpmc
|
||||
|
||||
# The following script tests that the SW TPM and TSS were compiled in the docker image. And documents how to start the SW TPM after container launch using both IBM's tss and TPM2-TOOLS.
|
||||
RUN echo "#!/bin/bash" > /tmp/tpm_config && \
|
||||
echo "/ibmswtpm2/src/tpm_server &" >> /tmp/tpm_config && \
|
||||
echo "sleep 5" >> /tmp/tpm_config && \
|
||||
echo "/ibmtss/utils/startup -c" >> /tmp/tpm_config && \
|
||||
echo "tpm2_shutdown" >> /tmp/tpm_config && \
|
||||
echo "tpm2_startup -c" >> /tmp/tpm_config && \
|
||||
echo "/ibmtss/utils/shutdown -c" >> /tmp/tpm_config && \
|
||||
bash /tmp/tpm_config && \
|
||||
rm -rf /tmp/tpm_config
|
||||
|
||||
EXPOSE 8443
|
||||
|
||||
# Checkout HIRS
|
||||
RUN git clone -b ${REF} https://github.com/nsacyber/HIRS.git /repo
|
||||
|
||||
# Defensive copy of the repo so it's easy to start fresh if needed
|
||||
RUN mkdir /hirs
|
||||
WORKDIR /repo
|
||||
RUN cp -r . /hirs
|
||||
|
||||
# Run bootwar to cache build artifacts
|
||||
WORKDIR /hirs
|
||||
RUN ./gradlew bootWar
|
||||
|
||||
# Add ACA TLS certification path to container OS
|
||||
# Allows the curl command in the HEALTHCHECK to work with TLS
|
||||
# These commands are placed into a script that can be run after aca_setup.sh on container launch.
|
||||
RUN echo "#!/bin/bash" > /tmp/hirs_add_aca_tls_path_to_os.sh && \
|
||||
echo "cp /etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_intermediate_ca_rsa_3k_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \
|
||||
echo "cp /etc/hirs/certificates/HIRS/ecc_512_sha384_certs/HIRS_intermediate_ca_ecc_512_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \
|
||||
echo "cp /etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_root_ca_rsa_3k_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \
|
||||
echo "cp /etc/hirs/certificates/HIRS/ecc_512_sha384_certs/HIRS_root_ca_ecc_512_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \
|
||||
echo "cp /etc/hirs/certificates/HIRS/rsa_3k_sha384_certs/HIRS_leaf_ca3_rsa_3k_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \
|
||||
echo "cp /etc/hirs/certificates/HIRS/ecc_512_sha384_certs/HIRS_leaf_ca3_ecc_512_sha384.pem /etc/pki/ca-trust/source/anchors/" >> /tmp/hirs_add_aca_tls_path_to_os.sh && \
|
||||
echo "update-ca-trust" >> /tmp/hirs_add_aca_tls_path_to_os.sh
|
||||
RUN chmod +x /tmp/hirs_add_aca_tls_path_to_os.sh
|
||||
|
||||
# The container will report a health state based on when embedded tomcat finishes loading. If the ACA isn't loaded after the timeout, the container will report that it is unhealthy.
|
||||
HEALTHCHECK --start-period=50s --interval=1s --timeout=90s CMD curl -f https://localhost:8443/HIRS_AttestationCAPortal/portal/index
|
||||
|
||||
# The following script will clone and copy the referenced branch of HIRS off GitHub
|
||||
# If configured, run bootwar to cache build artifacts
|
||||
RUN echo "#!/bin/bash" > /tmp/auto_clone_branch && \
|
||||
echo "cd /hirs" >> /tmp/auto_clone_branch && \
|
||||
echo "git fetch origin && git pull origin main && git reset --hard" >> /tmp/auto_clone_branch && \
|
||||
echo 'git checkout $1 && git reset --hard' >> /tmp/auto_clone_branch && \
|
||||
echo 'if [ -n "${2}" ]; then ./gradlew bootWar; fi' >> /tmp/auto_clone_branch && \
|
||||
echo "cd HIRS_Provisioner.NET/hirs" >> /tmp/auto_clone_branch && \
|
||||
echo 'if [ -n "${2}" ]; then dotnet deb -r linux-x64 -c Release && dotnet rpm -r linux-x64 -c Release; fi' >> /tmp/auto_clone_branch && \
|
||||
chmod 755 /tmp/auto_clone_branch
|
||||
|
||||
# Reset working directory
|
||||
WORKDIR /hirs
|
||||
|
||||
# On container launch, the database will be set up. Then bootRun should utilize build artifacts stored in the image.
|
||||
# NOTE: Uncomment the CMD command below before running this container locally or outside of CI, which will allow the ACA
|
||||
# to automatically set up and start upon running the container.
|
||||
# CMD ["bash", "-c", "/hirs/package/linux/aca/aca_setup.sh --unattended && /tmp/hirs_add_aca_tls_path_to_os.sh && /hirs/package/linux/aca/aca_bootRun.sh"]
|
@ -3,11 +3,15 @@ LABEL org.opencontainers.image.vendor NSA Laboratory for Advanced Cybersecurity
|
||||
LABEL org.opencontainers.image.source https://github.com/nsacyber/hirs
|
||||
LABEL org.opencontainers.image.description Tools for testing the build and deployment of HIRS projects.
|
||||
|
||||
# Purpose: This image is designed for HIRS Provisioner.Net testing on Rocky 9.
|
||||
# Purpose: This image is designed for HIRS Provisioner.Net testing on Rocky 9. It is meant to be used in
|
||||
# the .ci, and does not automatically start the Provisioner upon running the container.
|
||||
# Date Modified: 4/15/24
|
||||
# Notes: The image to be built should be named "tpm2provisioner-dotnet-ci:latest".
|
||||
# For local build, use this command from the /HIRS/ directory to build the image:
|
||||
# $ docker build -f ./.ci/docker/Dockerfile.tpm2provisioner_dotnet -t tpm2provisioner-dotnet-ci:latest .
|
||||
# For local image build, use this command from the /HIRS/ directory to build the image:
|
||||
# $ docker build -f ./.ci/docker/Dockerfile.tpm2provisioner_dotnet -t tpm2provisioner-dotnet-ci:latest .
|
||||
# For manually running the Provisioner.Net using the TPM Simulator, first ensure that an ACA
|
||||
# is running on port 8443. Then, run the setup script within this container using this command:
|
||||
# $ ./.ci/setup/container/setup_tpm2provisioner_dotnet.sh
|
||||
|
||||
# REF can be specified as a docker run environment variable to select the HIRS branch to work with
|
||||
ENV REF=main
|
@ -1,9 +1,10 @@
|
||||
services:
|
||||
aca:
|
||||
image: ghcr.io/nsacyber/hirs/aca-rocky:5445278
|
||||
image: ghcr.io/nsacyber/hirs/aca-rocky-ci:latest
|
||||
container_name: hirs-aca1
|
||||
volumes:
|
||||
- ../../:/HIRS
|
||||
command: ["bash", "-c", "tail -f /dev/null;"]
|
||||
ports:
|
||||
- "${HIRS_ACA_PORTAL_PORT}:${HIRS_ACA_PORTAL_CONTAINER_PORT}"
|
||||
hostname: ${HIRS_ACA_HOSTNAME}
|
||||
|
@ -15,8 +15,16 @@ tpm2_container=hirs-provisioner1-tpm2
|
||||
echo "******** Setting up for HIRS System Tests for TPM 2.0 ******** "
|
||||
docker compose -f ./.ci/docker/docker-compose-system-test.yml up -d
|
||||
|
||||
# Switching to current/desired branch
|
||||
docker exec $tpm2_container sh -c "cd / && ./tmp/auto_clone_branch $1 1> /dev/null && cd hirs"
|
||||
# Setting up and Starting ACA + Switching to current/desired branch in ACA Container
|
||||
docker exec $aca_container sh -c "cd / && ./tmp/auto_clone_branch $1 > /dev/null 2>&1 \
|
||||
&& cd hirs && echo 'ACA Container Current Branch: ' && git branch \
|
||||
&& cd / && ./hirs/package/linux/aca/aca_setup.sh --unattended 1> /dev/null \
|
||||
&& ./tmp/hirs_add_aca_tls_path_to_os.sh 1> /dev/null \
|
||||
&& cd hirs && ./package/linux/aca/aca_bootRun.sh 1> /dev/null" &
|
||||
|
||||
# Switching to current/desired branch in Provisioner Container
|
||||
docker exec $tpm2_container sh -c "cd / && ./tmp/auto_clone_branch $1 > /dev/null 2>&1 \
|
||||
&& cd hirs && echo 'Provisioner Container Current Branch: ' && git branch"
|
||||
|
||||
# Install HIRS Provisioner.Net and setup tpm2 simulator.
|
||||
# In doing so, tests a single provision between Provisioner.Net and ACA.
|
||||
|
4
.github/workflows/system_test.yml
vendored
4
.github/workflows/system_test.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: ACA TPM2 Tests
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
@ -30,7 +30,7 @@ jobs:
|
||||
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
bash .ci/system-tests/run_system_tests.sh ${GITHUB_REF#refs/heads/}
|
||||
- name: Archive System Test Log files
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: System_Test_Log_Files
|
||||
path: logs/
|
||||
|
@ -91,7 +91,7 @@ public class BaseReferenceManifest extends ReferenceManifest {
|
||||
* @param rimBytes - the file content of the uploaded file.
|
||||
* @throws IOException - thrown if the file is invalid.
|
||||
*/
|
||||
public BaseReferenceManifest(final byte[] rimBytes) throws IOException {
|
||||
public BaseReferenceManifest(final byte[] rimBytes) throws UnmarshalException {
|
||||
this("", rimBytes);
|
||||
}
|
||||
|
||||
@ -104,7 +104,8 @@ public class BaseReferenceManifest extends ReferenceManifest {
|
||||
* @throws IOException if unable to unmarshal the string
|
||||
*/
|
||||
@SuppressWarnings("checkstyle:AvoidInlineConditionals")
|
||||
public BaseReferenceManifest(final String fileName, final byte[] rimBytes) throws IOException {
|
||||
public BaseReferenceManifest(final String fileName, final byte[] rimBytes)
|
||||
throws UnmarshalException {
|
||||
super(rimBytes);
|
||||
this.setRimType(BASE_RIM);
|
||||
this.setFileName(fileName);
|
||||
@ -116,10 +117,14 @@ public class BaseReferenceManifest extends ReferenceManifest {
|
||||
|
||||
// begin parsing valid swid tag
|
||||
if (document != null) {
|
||||
softwareIdentity = (Element) document.getElementsByTagName(SwidTagConstants.SOFTWARE_IDENTITY).item(0);
|
||||
entity = (Element) document.getElementsByTagName(SwidTagConstants.ENTITY).item(0);
|
||||
link = (Element) document.getElementsByTagName(SwidTagConstants.LINK).item(0);
|
||||
meta = (Element) document.getElementsByTagName(SwidTagConstants.META).item(0);
|
||||
softwareIdentity = (Element) document.getElementsByTagNameNS(
|
||||
SwidTagConstants.SWIDTAG_NAMESPACE, SwidTagConstants.SOFTWARE_IDENTITY).item(0);
|
||||
entity = (Element) document.getElementsByTagNameNS(
|
||||
SwidTagConstants.SWIDTAG_NAMESPACE, SwidTagConstants.ENTITY).item(0);
|
||||
link = (Element) document.getElementsByTagNameNS(
|
||||
SwidTagConstants.SWIDTAG_NAMESPACE, SwidTagConstants.LINK).item(0);
|
||||
meta = (Element) document.getElementsByTagNameNS(
|
||||
SwidTagConstants.SWIDTAG_NAMESPACE, SwidTagConstants.META).item(0);
|
||||
setTagId(softwareIdentity.getAttribute(SwidTagConstants.TAGID));
|
||||
this.swidName = softwareIdentity.getAttribute(SwidTagConstants.NAME);
|
||||
this.swidCorpus = Boolean.parseBoolean(softwareIdentity.getAttribute(SwidTagConstants.CORPUS)) ? 1 : 0;
|
||||
@ -215,15 +220,24 @@ public class BaseReferenceManifest extends ReferenceManifest {
|
||||
* @param byteArrayInputStream the location of the file to be validated
|
||||
*/
|
||||
private Element getDirectoryTag(final ByteArrayInputStream byteArrayInputStream) {
|
||||
Document document = unmarshallSwidTag(byteArrayInputStream);
|
||||
Element softwareIdentity =
|
||||
(Element) document.getElementsByTagName("SoftwareIdentity").item(0);
|
||||
if (softwareIdentity != null) {
|
||||
Element directory = (Element) document.getElementsByTagName("Directory").item(0);
|
||||
Document document = null;
|
||||
try {
|
||||
document = unmarshallSwidTag(byteArrayInputStream);
|
||||
} catch (UnmarshalException e) {
|
||||
log.error("Error while parsing Directory tag: " + e.getMessage());
|
||||
}
|
||||
if (document != null) {
|
||||
Element softwareIdentity =
|
||||
(Element) document.getElementsByTagNameNS(
|
||||
SwidTagConstants.SWIDTAG_NAMESPACE, "SoftwareIdentity").item(0);
|
||||
if (softwareIdentity != null) {
|
||||
Element directory = (Element) document.getElementsByTagNameNS(
|
||||
SwidTagConstants.SWIDTAG_NAMESPACE, "Directory").item(0);
|
||||
|
||||
return directory;
|
||||
} else {
|
||||
log.error("Invalid xml for validation, please verify ");
|
||||
return directory;
|
||||
} else {
|
||||
log.error("Invalid xml for validation, please verify ");
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
@ -268,7 +282,8 @@ public class BaseReferenceManifest extends ReferenceManifest {
|
||||
* @param byteArrayInputStream to the input swidtag
|
||||
* @return the Document element at the root of the swidtag
|
||||
*/
|
||||
private Document unmarshallSwidTag(final ByteArrayInputStream byteArrayInputStream) {
|
||||
private Document unmarshallSwidTag(final ByteArrayInputStream byteArrayInputStream)
|
||||
throws UnmarshalException {
|
||||
InputStream is = null;
|
||||
Document document = null;
|
||||
Unmarshaller unmarshaller = null;
|
||||
@ -288,7 +303,7 @@ public class BaseReferenceManifest extends ReferenceManifest {
|
||||
} catch (SAXException e) {
|
||||
log.error("Error setting schema for validation!");
|
||||
} catch (UnmarshalException e) {
|
||||
log.error("Error validating swidtag file!");
|
||||
throw new UnmarshalException("Error validating swidtag file");
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("Input file empty.");
|
||||
} catch (JAXBException e) {
|
||||
|
@ -41,6 +41,7 @@ import hirs.utils.SwidResource;
|
||||
import hirs.utils.enums.DeviceInfoEnums;
|
||||
import hirs.utils.tpm.eventlog.TCGEventLog;
|
||||
import hirs.utils.tpm.eventlog.TpmPcrEvent;
|
||||
import jakarta.xml.bind.UnmarshalException;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
@ -420,8 +421,8 @@ public class IdentityClaimProcessor extends AbstractProcessor {
|
||||
}
|
||||
}
|
||||
tagId = dbBaseRim.getTagId();
|
||||
} catch (IOException ioEx) {
|
||||
log.error(ioEx);
|
||||
} catch (UnmarshalException e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -20,6 +20,12 @@ java {
|
||||
}
|
||||
}
|
||||
|
||||
bootRun {
|
||||
if (project.hasProperty('debug')) {
|
||||
jvmArgs project.debug
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
compileOnly {
|
||||
extendsFrom annotationProcessor
|
||||
@ -41,6 +47,8 @@ dependencies {
|
||||
implementation libs.bouncycastle
|
||||
implementation libs.guava
|
||||
implementation libs.jakarta.servlet
|
||||
implementation libs.jakarta.api
|
||||
implementation libs.jakarta.xml
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
|
@ -35,13 +35,16 @@ import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -303,6 +306,16 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
for (Certificate certificate : certificates) {
|
||||
caCert = (CertificateAuthorityCredential) certificate;
|
||||
KeyStore keystore = ValidationService.getCaChain(caCert, caCertificateRepository);
|
||||
try {
|
||||
List<X509Certificate> truststore =
|
||||
convertCACsToX509Certificates(ValidationService.getCaChainRec(caCert,
|
||||
Collections.emptySet(),
|
||||
caCertificateRepository));
|
||||
RIM_VALIDATOR.setTrustStore(truststore);
|
||||
} catch (IOException e) {
|
||||
log.error("Error building CA chain for " + caCert.getSubjectKeyIdentifier() + ": "
|
||||
+ e.getMessage());
|
||||
}
|
||||
if (RIM_VALIDATOR.validateXmlSignature(caCert.getX509Certificate().getPublicKey(),
|
||||
caCert.getSubjectKeyIdString(), caCert.getEncodedPublicKey())) {
|
||||
try {
|
||||
@ -333,6 +346,20 @@ public class ReferenceManifestDetailsPageController extends PageController<Refer
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts a Set<CertificateAuthorityCredential> to a List<X509Certificate>.
|
||||
* @param set of CACs to convert
|
||||
* @return list of X509Certificates
|
||||
*/
|
||||
private static List<X509Certificate> convertCACsToX509Certificates(Set<CertificateAuthorityCredential> set)
|
||||
throws IOException {
|
||||
ArrayList<X509Certificate> certs = new ArrayList<>(set.size());
|
||||
for (CertificateAuthorityCredential cac : set) {
|
||||
certs.add(cac.getX509Certificate());
|
||||
}
|
||||
return certs;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes the place of an entire class for a string builder.
|
||||
* Gathers all information and returns it for displays.
|
||||
|
@ -19,6 +19,7 @@ import hirs.utils.tpm.eventlog.TpmPcrEvent;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.xml.bind.UnmarshalException;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
@ -61,7 +62,8 @@ import java.util.zip.ZipOutputStream;
|
||||
@RequestMapping("/HIRS_AttestationCAPortal/portal/reference-manifests")
|
||||
public class ReferenceManifestPageController extends PageController<NoPageParams> {
|
||||
|
||||
private static final String LOG_FILE_PATTERN = "([^\\s]+(\\.(?i)(rimpcr|rimel|bin|log))$)";
|
||||
private static final String BASE_RIM_FILE_PATTERN = "([^\\s]+(\\.(?i)swidtag)$)";
|
||||
private static final String SUPPORT_RIM_FILE_PATTERN = "([^\\s]+(\\.(?i)(rimpcr|rimel|bin|log))$)";
|
||||
|
||||
@Autowired(required = false)
|
||||
private EntityManager entityManager;
|
||||
@ -155,21 +157,34 @@ public class ReferenceManifestPageController extends PageController<NoPageParams
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
PageMessages messages = new PageMessages();
|
||||
String fileName;
|
||||
Pattern logPattern = Pattern.compile(LOG_FILE_PATTERN);
|
||||
Pattern baseRimPattern = Pattern.compile(BASE_RIM_FILE_PATTERN);
|
||||
Pattern supportRimPattern = Pattern.compile(SUPPORT_RIM_FILE_PATTERN);
|
||||
Matcher matcher;
|
||||
boolean supportRIM = false;
|
||||
List<BaseReferenceManifest> baseRims = new ArrayList<>();
|
||||
List<SupportReferenceManifest> supportRims = new ArrayList<>();
|
||||
log.info(String.format("Processing %s uploaded files", files.length));
|
||||
|
||||
// loop through the files
|
||||
for (MultipartFile file : files) {
|
||||
boolean isBaseRim;
|
||||
boolean isSupportRim = false;
|
||||
fileName = file.getOriginalFilename();
|
||||
matcher = logPattern.matcher(fileName);
|
||||
supportRIM = matcher.matches();
|
||||
|
||||
//Parse reference manifests
|
||||
parseRIM(file, supportRIM, messages, baseRims, supportRims);
|
||||
matcher = baseRimPattern.matcher(fileName);
|
||||
isBaseRim = matcher.matches();
|
||||
if (!isBaseRim) {
|
||||
matcher = supportRimPattern.matcher(fileName);
|
||||
isSupportRim = matcher.matches();
|
||||
}
|
||||
if (isBaseRim || isSupportRim) {
|
||||
parseRIM(file, isSupportRim, messages, baseRims, supportRims);
|
||||
} else {
|
||||
String errorString = "The file extension of " + fileName + " was not recognized." +
|
||||
" Base RIMs support the extension \".swidtag\", and support RIMs support " +
|
||||
"\".rimpcr\", \".rimel\", \".bin\", and \".log\". " +
|
||||
"Please verify your upload and retry.";
|
||||
log.error("File extension in " + fileName + " not recognized as base or support RIM.");
|
||||
messages.addError(errorString);
|
||||
}
|
||||
}
|
||||
baseRims.stream().forEach((rim) -> {
|
||||
log.info(String.format("Storing swidtag %s", rim.getFileName()));
|
||||
@ -393,23 +408,33 @@ public class ReferenceManifestPageController extends PageController<NoPageParams
|
||||
try {
|
||||
if (supportRIM) {
|
||||
supportRim = new SupportReferenceManifest(fileName, fileBytes);
|
||||
if (referenceManifestRepository.findByHexDecHashAndRimType(supportRim.getHexDecHash(),
|
||||
supportRim.getRimType()) == null) {
|
||||
if (referenceManifestRepository.findByHexDecHashAndRimType(
|
||||
supportRim.getHexDecHash(), supportRim.getRimType()) == null) {
|
||||
supportRims.add(supportRim);
|
||||
messages.addInfo("Saved Reference Manifest " + fileName);
|
||||
messages.addInfo("Saved support RIM " + fileName);
|
||||
}
|
||||
} else {
|
||||
baseRim = new BaseReferenceManifest(fileName, fileBytes);
|
||||
if (referenceManifestRepository.findByHexDecHashAndRimType(baseRim.getHexDecHash(),
|
||||
baseRim.getRimType()) == null) {
|
||||
if (referenceManifestRepository.findByHexDecHashAndRimType(
|
||||
baseRim.getHexDecHash(), baseRim.getRimType()) == null) {
|
||||
baseRims.add(baseRim);
|
||||
messages.addInfo("Saved base RIM " + fileName);
|
||||
}
|
||||
}
|
||||
} catch (IOException | NullPointerException ioEx) {
|
||||
final String failMessage
|
||||
= String.format("Failed to parse uploaded file (%s): ", fileName);
|
||||
= String.format("Failed to parse support RIM file (%s): ", fileName);
|
||||
log.error(failMessage, ioEx);
|
||||
messages.addError(failMessage + ioEx.getMessage());
|
||||
} catch (UnmarshalException e) {
|
||||
final String failMessage
|
||||
= String.format("Failed to parse base RIM file (%s): ", fileName);
|
||||
log.error(failMessage, e);
|
||||
messages.addError(failMessage + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
final String failMessage
|
||||
= String.format("Failed to parse (%s): ", fileName);
|
||||
log.error(failMessage, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
<jsp:attribute name="pageHeaderTitle">Error - 404</jsp:attribute>
|
||||
|
||||
<jsp:body>
|
||||
<!--<div> Exception Message: <c:out value="${exception}"</c:out></div>
|
||||
<!--<div> Exception Message: <c:out value="${exception}"></c:out></div>
|
||||
<div> from URL -> <span th:text="${url}"</span></div>-->
|
||||
</jsp:body>
|
||||
</my:page>
|
@ -8,6 +8,7 @@ import jakarta.xml.bind.Unmarshaller;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.X509;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
@ -23,6 +24,7 @@ import javax.xml.crypto.KeySelectorResult;
|
||||
import javax.xml.crypto.MarshalException;
|
||||
import javax.xml.crypto.XMLCryptoContext;
|
||||
import javax.xml.crypto.XMLStructure;
|
||||
import javax.xml.crypto.dsig.Reference;
|
||||
import javax.xml.crypto.dsig.XMLSignature;
|
||||
import javax.xml.crypto.dsig.XMLSignatureException;
|
||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||
@ -48,7 +50,15 @@ import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.*;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyException;
|
||||
import java.security.MessageDigest;
|
||||
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.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
@ -154,12 +164,20 @@ public class ReferenceManifestValidator {
|
||||
|
||||
/**
|
||||
* Setter for the truststore file path.
|
||||
* @param trustStoreFile the truststore
|
||||
* @param trustStoreFile the file path to reference
|
||||
*/
|
||||
public void setTrustStoreFile(String trustStoreFile) {
|
||||
this.trustStoreFile = trustStoreFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for truststore
|
||||
* @param trustStore the List of X509Certificates
|
||||
*/
|
||||
public void setTrustStore(List<X509Certificate> trustStore) {
|
||||
this.trustStore = trustStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for rimel file path.
|
||||
* @param rimEventLog the rimel file
|
||||
@ -174,6 +192,7 @@ public class ReferenceManifestValidator {
|
||||
*/
|
||||
public ReferenceManifestValidator() {
|
||||
try {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
InputStream is = ReferenceManifestValidator.class
|
||||
.getClassLoader().getResourceAsStream(SCHEMA_URL);
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(SCHEMA_LANGUAGE);
|
||||
@ -220,8 +239,7 @@ public class ReferenceManifestValidator {
|
||||
X509Certificate embeddedCert = parseCertFromPEMString(
|
||||
certElement.item(0).getTextContent());
|
||||
if (embeddedCert != null) {
|
||||
if (Arrays.equals(embeddedCert.getPublicKey().getEncoded(),
|
||||
encodedPublicKey)) {
|
||||
if (isCertChainValid(embeddedCert)) {
|
||||
context = new DOMValidateContext(new X509KeySelector(), nodes.item(0));
|
||||
}
|
||||
}
|
||||
@ -252,7 +270,8 @@ public class ReferenceManifestValidator {
|
||||
* @return true if both the file element and signature are valid, false otherwise
|
||||
*/
|
||||
public boolean validateRim(String signingCertPath) {
|
||||
Element fileElement = (Element) rim.getElementsByTagName("File").item(0);
|
||||
Element fileElement = (Element) rim.getElementsByTagNameNS(
|
||||
SwidTagConstants.SWIDTAG_NAMESPACE, "File").item(0);
|
||||
X509Certificate signingCert = parseCertificatesFromPem(signingCertPath).get(0);
|
||||
if (signingCert == null) {
|
||||
return failWithError("Unable to parse the signing cert from " + signingCertPath);
|
||||
@ -374,7 +393,12 @@ public class ReferenceManifestValidator {
|
||||
try {
|
||||
XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
|
||||
XMLSignature signature = sigFactory.unmarshalXMLSignature(context);
|
||||
return signature.validate(context);
|
||||
boolean isValid = signature.validate(context);
|
||||
if (isValid) {
|
||||
return true;
|
||||
} else {
|
||||
whySignatureInvalid(signature, context);
|
||||
}
|
||||
} catch (MarshalException e) {
|
||||
log.warn("Error while unmarshalling XML signature: " + e.getMessage());
|
||||
} catch (XMLSignatureException e) {
|
||||
@ -384,6 +408,147 @@ public class ReferenceManifestValidator {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method attempts to gather information on why a signature failed to validate.
|
||||
* The <SignatureValue> and each <Reference> is cryptographically validated and the
|
||||
* results are logged.
|
||||
*
|
||||
* @param signature the signature that failed to validate
|
||||
* @param context the context used for validation
|
||||
* @throws XMLSignatureException
|
||||
*/
|
||||
private void whySignatureInvalid(final XMLSignature signature, final DOMValidateContext context)
|
||||
throws XMLSignatureException{
|
||||
boolean cryptoValidity = signature.getSignatureValue().validate(context);
|
||||
if (cryptoValidity) {
|
||||
log.error("Signature value is valid.");
|
||||
} else {
|
||||
log.error("Signature value is invalid!");
|
||||
}
|
||||
Iterator itr = signature.getSignedInfo().getReferences().iterator();
|
||||
while (itr.hasNext()) {
|
||||
Reference reference = (Reference) itr.next();
|
||||
boolean refValidity = reference.validate(context);
|
||||
String refUri = reference.getURI();
|
||||
if (refUri.isEmpty()) {
|
||||
refUri = "whole document";
|
||||
}
|
||||
if (refValidity) {
|
||||
log.error("Reference for " + refUri + " is valid.");
|
||||
} else {
|
||||
log.error("Reference for " + refUri + " is invalid!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates the cert chain for a given certificate. The truststore is iterated
|
||||
* over until a root CA is found, otherwise an error is returned.
|
||||
* @param cert the certificate at the start of the chain
|
||||
* @return true if the chain is valid
|
||||
* @throws Exception if a valid chain is not found in the truststore
|
||||
*/
|
||||
private boolean isCertChainValid(final X509Certificate cert)
|
||||
throws Exception {
|
||||
if (cert == null || trustStore == null) {
|
||||
throw new Exception("Null certificate or truststore received");
|
||||
} else if (trustStore.size() == 0) {
|
||||
throw new Exception("Truststore is empty");
|
||||
}
|
||||
|
||||
String errorMessage = "";
|
||||
X509Certificate chainCert = cert;
|
||||
boolean isChainCertValid;
|
||||
do {
|
||||
isChainCertValid = false;
|
||||
log.info("Validating " + chainCert.getSubjectX500Principal().getName());
|
||||
for (X509Certificate trustedCert : trustStore) {
|
||||
boolean isIssuer = areYouMyIssuer(chainCert, trustedCert);
|
||||
boolean isSigner = areYouMySigner(chainCert, trustedCert);
|
||||
if (isIssuer && isSigner) {
|
||||
if (isSelfSigned(trustedCert)) {
|
||||
log.info("Root CA found.");
|
||||
return true;
|
||||
} else {
|
||||
chainCert = trustedCert;
|
||||
isChainCertValid = true;
|
||||
log.info("Intermediate CA found.");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!isIssuer) {
|
||||
errorMessage = "Issuer cert not found";
|
||||
} else if (!isSigner) {
|
||||
errorMessage = "Signing cert not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (isChainCertValid);
|
||||
|
||||
log.error("CA chain validation failed to validate "
|
||||
+ chainCert.getSubjectX500Principal().getName() + ", " + errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if cert's issuerDN matches issuer's subjectDN.
|
||||
* @param cert the signed certificate
|
||||
* @param issuer the signing certificate
|
||||
* @return true if they match, false if not
|
||||
* @throws Exception if either argument is null
|
||||
*/
|
||||
private boolean areYouMyIssuer(final X509Certificate cert, final X509Certificate issuer)
|
||||
throws Exception {
|
||||
if (cert == null || issuer == null) {
|
||||
throw new Exception("Cannot verify issuer, null certificate received");
|
||||
}
|
||||
X500Principal issuerDN = new X500Principal(cert.getIssuerX500Principal().getName());
|
||||
return issuer.getSubjectX500Principal().equals(issuerDN);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if cert's signature matches signer's public key.
|
||||
* @param cert the signed certificate
|
||||
* @param signer the signing certificate
|
||||
* @return true if they match
|
||||
* @throws Exception if an error occurs or there is no match
|
||||
*/
|
||||
private boolean areYouMySigner(final X509Certificate cert, final X509Certificate signer)
|
||||
throws Exception {
|
||||
if (cert == null || signer == null) {
|
||||
throw new Exception("Cannot verify signature, null certificate received");
|
||||
}
|
||||
try {
|
||||
cert.verify(signer.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME);
|
||||
return true;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new Exception("Signing algorithm in signing cert not supported");
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new Exception("Signing certificate key does not match signature");
|
||||
} catch (NoSuchProviderException e) {
|
||||
throw new Exception("Error with BouncyCastleProvider: " + e.getMessage());
|
||||
} catch (SignatureException e) {
|
||||
String error = "Error with signature: " + e.getMessage()
|
||||
+ System.lineSeparator()
|
||||
+ "Certificate needed for verification is missing: "
|
||||
+ signer.getSubjectX500Principal().getName();
|
||||
log.info(error);
|
||||
} catch (CertificateException e) {
|
||||
throw new Exception("Encoding error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a given certificate is self signed or not.
|
||||
* @param cert the cert to check
|
||||
* @return true if self signed, false if not
|
||||
*/
|
||||
private boolean isSelfSigned(final X509Certificate cert) {
|
||||
return cert.getIssuerX500Principal().equals(cert.getSubjectX500Principal());
|
||||
}
|
||||
|
||||
/**
|
||||
* This internal class handles selecting an X509 certificate embedded in a KeyInfo element.
|
||||
* It is passed as a parameter to a DOMValidateContext that uses it to validate
|
||||
@ -473,106 +638,6 @@ public class ReferenceManifestValidator {
|
||||
&& name.equalsIgnoreCase("RSA");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates the cert chain for a given certificate. The truststore is iterated
|
||||
* over until a root CA is found, otherwise an error is returned.
|
||||
* @param cert the certificate at the start of the chain
|
||||
* @return true if the chain is valid
|
||||
* @throws Exception if a valid chain is not found in the truststore
|
||||
*/
|
||||
private boolean isCertChainValid(final X509Certificate cert)
|
||||
throws Exception {
|
||||
if (cert == null || trustStore == null) {
|
||||
throw new Exception("Null certificate or truststore received");
|
||||
} else if (trustStore.size() == 0) {
|
||||
throw new Exception("Truststore is empty");
|
||||
}
|
||||
|
||||
final String INT_CA_ERROR = "Intermediate CA found, searching for root CA";
|
||||
String errorMessage = "";
|
||||
X509Certificate startOfChain = cert;
|
||||
do {
|
||||
for (X509Certificate trustedCert : trustStore) {
|
||||
boolean isIssuer = areYouMyIssuer(startOfChain, trustedCert);
|
||||
boolean isSigner = areYouMySigner(startOfChain, trustedCert);
|
||||
if (isIssuer && isSigner) {
|
||||
if (isSelfSigned(trustedCert)) {
|
||||
return true;
|
||||
} else {
|
||||
startOfChain = trustedCert;
|
||||
errorMessage = INT_CA_ERROR;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!isIssuer) {
|
||||
errorMessage = "Issuer cert not found";
|
||||
} else if (!isSigner) {
|
||||
errorMessage = "Signing cert not found";
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (errorMessage.equals(INT_CA_ERROR));
|
||||
|
||||
throw new Exception("Error while validating cert chain: " + errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if cert's issuerDN matches issuer's subjectDN.
|
||||
* @param cert the signed certificate
|
||||
* @param issuer the signing certificate
|
||||
* @return true if they match, false if not
|
||||
* @throws Exception if either argument is null
|
||||
*/
|
||||
private boolean areYouMyIssuer(final X509Certificate cert, final X509Certificate issuer)
|
||||
throws Exception {
|
||||
if (cert == null || issuer == null) {
|
||||
throw new Exception("Cannot verify issuer, null certificate received");
|
||||
}
|
||||
X500Principal issuerDN = new X500Principal(cert.getIssuerX500Principal().getName());
|
||||
return issuer.getSubjectX500Principal().equals(issuerDN);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if cert's signature matches signer's public key.
|
||||
* @param cert the signed certificate
|
||||
* @param signer the signing certificate
|
||||
* @return true if they match
|
||||
* @throws Exception if an error occurs or there is no match
|
||||
*/
|
||||
private boolean areYouMySigner(final X509Certificate cert, final X509Certificate signer)
|
||||
throws Exception {
|
||||
if (cert == null || signer == null) {
|
||||
throw new Exception("Cannot verify signature, null certificate received");
|
||||
}
|
||||
try {
|
||||
cert.verify(signer.getPublicKey(), BouncyCastleProvider.PROVIDER_NAME);
|
||||
return true;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new Exception("Signing algorithm in signing cert not supported");
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new Exception("Signing certificate key does not match signature");
|
||||
} catch (NoSuchProviderException e) {
|
||||
throw new Exception("Error with BouncyCastleProvider: " + e.getMessage());
|
||||
} catch (SignatureException e) {
|
||||
String error = "Error with signature: " + e.getMessage()
|
||||
+ System.lineSeparator()
|
||||
+ "Certificate needed for verification is missing: "
|
||||
+ signer.getSubjectX500Principal().getName();
|
||||
throw new Exception(error);
|
||||
} catch (CertificateException e) {
|
||||
throw new Exception("Encoding error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a given certificate is self signed or not.
|
||||
* @param cert the cert to check
|
||||
* @return true if self signed, false if not
|
||||
*/
|
||||
private boolean isSelfSigned(final X509Certificate cert) {
|
||||
return cert.getIssuerX500Principal().equals(cert.getSubjectX500Principal());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method compares a public key against those in the truststore.
|
||||
* @param pk a public key
|
||||
|
@ -22,6 +22,7 @@ public class SwidTagConstants {
|
||||
public static final String SCHEMA_PACKAGE = "hirs.swid.xjc";
|
||||
public static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI;
|
||||
public static final String SCHEMA_URL = "swid_schema.xsd";
|
||||
public static final String SWIDTAG_NAMESPACE = "http://standards.iso.org/iso/19770/-2/2015/schema.xsd";
|
||||
|
||||
public static final String SOFTWARE_IDENTITY = "SoftwareIdentity";
|
||||
public static final String ENTITY = "Entity";
|
||||
|
@ -66,6 +66,8 @@ public class ObjectFactory {
|
||||
private final static QName _SoftwareIdentityLink_QNAME = new QName("http://standards.iso.org/iso/19770/-2/2015/schema.xsd", "Link");
|
||||
private final static QName _SoftwareIdentityEvidence_QNAME = new QName("http://standards.iso.org/iso/19770/-2/2015/schema.xsd", "Evidence");
|
||||
private final static QName _SoftwareIdentityPayload_QNAME = new QName("http://standards.iso.org/iso/19770/-2/2015/schema.xsd", "Payload");
|
||||
private final static QName _PayloadDirectory_QNAME = new QName("http://standards.iso.org/iso/19770/-2/2015/schema.xsd", "Directory");
|
||||
private final static QName _DirectoryFile_QNAME = new QName("http://standards.iso.org/iso/19770/-2/2015/schema.xsd", "File");
|
||||
private final static QName _SoftwareIdentityEntity_QNAME = new QName("http://standards.iso.org/iso/19770/-2/2015/schema.xsd", "Entity");
|
||||
private final static QName _SoftwareIdentityMeta_QNAME = new QName("http://standards.iso.org/iso/19770/-2/2015/schema.xsd", "Meta");
|
||||
private final static QName _SignatureMethodTypeHMACOutputLength_QNAME = new QName("http://www.w3.org/2000/09/xmldsig#", "HMACOutputLength");
|
||||
@ -666,6 +668,24 @@ public class ObjectFactory {
|
||||
return new JAXBElement<ResourceCollection>(_SoftwareIdentityPayload_QNAME, ResourceCollection.class, SoftwareIdentity.class, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link JAXBElement }{@code <}{@link FilesystemItem }{@code >}}
|
||||
*
|
||||
*/
|
||||
@XmlElementDecl(namespace = "http://standards.iso.org/iso/19770/-2/2015/schema.xsd", name = "Directory", scope = ResourceCollection.class)
|
||||
public JAXBElement<FilesystemItem> createPayloadDirectory(FilesystemItem value) {
|
||||
return new JAXBElement<FilesystemItem>(_PayloadDirectory_QNAME, FilesystemItem.class, ResourceCollection.class, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link JAXBElement }{@code <}{@link FilesystemItem }{@code >}}
|
||||
*
|
||||
*/
|
||||
@XmlElementDecl(namespace = "http://standards.iso.org/iso/19770/-2/2015/schema.xsd", name = "File", scope = ResourceCollection.class)
|
||||
public JAXBElement<FilesystemItem> createDirectoryFile(FilesystemItem value) {
|
||||
return new JAXBElement<FilesystemItem>(_DirectoryFile_QNAME, FilesystemItem.class, ResourceCollection.class, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of {@link JAXBElement }{@code <}{@link Entity }{@code >}}
|
||||
*
|
||||
|
@ -14,6 +14,7 @@ SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; )
|
||||
LOG_FILE=/dev/null
|
||||
GRADLE_WRAPPER="./gradlew"
|
||||
DEPLOYED_WAR=false
|
||||
DEBUG_OPTIONS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:9123"
|
||||
|
||||
# Check for sudo or root user
|
||||
if [ "$EUID" -ne 0 ]
|
||||
@ -27,6 +28,7 @@ help () {
|
||||
echo " options:"
|
||||
echo " -p | --path Path to the HIRS_AttestationCAPortal.war file"
|
||||
echo " -w | --war Use deployed war file"
|
||||
echo " -d | --debug Launch the JVM with a debug port open"
|
||||
echo " -h | --help Print this help"
|
||||
echo
|
||||
}
|
||||
@ -49,6 +51,10 @@ while [[ $# -gt 0 ]]; do
|
||||
WAR_PATH="/opt/hirs/aca/HIRS_AttestationCAPortal.war"
|
||||
DEPLOYED_WAR=true
|
||||
;;
|
||||
-d|--debug)
|
||||
DEBUG_ACA=YES
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
help
|
||||
exit 0
|
||||
@ -127,11 +133,19 @@ WEB_TLS_PARAMS="--server.ssl.key-store-password=$hirs_pki_password \
|
||||
|
||||
if [ -z "$USE_WAR" ]; then
|
||||
echo "Booting the ACA from local build..."
|
||||
# ./gradlew bootRun --args="$CONNECTOR_PARAMS$WEB_TLS_PARAMS"
|
||||
./gradlew bootRun --args="--spring.config.location=$SPRING_PROP_FILE"
|
||||
if [ "$DEBUG_ACA" == YES ]; then
|
||||
echo "... in debug"
|
||||
./gradlew bootRun --args="--spring.config.location=$SPRING_PROP_FILE" -Pdebug="$DEBUG_OPTIONS"
|
||||
else
|
||||
./gradlew bootRun --args="--spring.config.location=$SPRING_PROP_FILE"
|
||||
fi
|
||||
else
|
||||
echo "Booting the ACA from a war file..."
|
||||
# java -jar $WAR_PATH $CONNECTOR_PARAMS$WEB_TLS_PARAMS &
|
||||
java -jar $WAR_PATH --spring.config.location=$SPRING_PROP_FILE &
|
||||
exit 0
|
||||
if [ "$DEBUG_ACA" == YES ]; then
|
||||
echo "... in debug"
|
||||
java $DEBUG_OPTIONS -jar $WAR_PATH --spring.config.location=$SPRING_PROP_FILE &
|
||||
else
|
||||
java -jar $WAR_PATH --spring.config.location=$SPRING_PROP_FILE &
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
@ -1,6 +1,7 @@
|
||||
param (
|
||||
[string]$p, [string]$path = $null,
|
||||
[switch]$w, [switch]$war = $false,
|
||||
[switch]$d, [switch]$debug = $false,
|
||||
[switch]$h, [switch]$help = $false
|
||||
)
|
||||
|
||||
@ -9,6 +10,7 @@ $ACA_COMMON_SCRIPT=(Join-Path $APP_HOME 'aca_common.ps1')
|
||||
$ALG="RSA" # or "ECC"
|
||||
$GRADLE_WRAPPER='./gradlew'
|
||||
$DEPLOYED_WAR=$null
|
||||
$DEBUG_OPTIONS='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:9123'
|
||||
|
||||
# Load other scripts
|
||||
. $ACA_COMMON_SCRIPT
|
||||
@ -34,6 +36,7 @@ if ($p) {
|
||||
$path = $p
|
||||
}
|
||||
$war = $w -or $war
|
||||
$debug = $d -or $debug
|
||||
$help = $h -or $help
|
||||
|
||||
if(!(New-Object Security.Principal.WindowsPrincipal(
|
||||
@ -49,6 +52,7 @@ if ($help) {
|
||||
echo " options:"
|
||||
echo " -p | --path Path to the HIRS_AttestationCAPortal.war file"
|
||||
echo " -w | --war Use deployed war file"
|
||||
echo " -d | --debug Launch the JVM with a debug port open"
|
||||
echo " -h | --help Print this help"
|
||||
exit 1
|
||||
}
|
||||
@ -73,8 +77,19 @@ if (!$DEPLOYED_WAR) {
|
||||
$SPRING_PROP_FILE_FORWARDSLASHES=($global:HIRS_DATA_SPRING_PROP_FILE | ChangeBackslashToForwardSlash)
|
||||
if ($w -or $war) {
|
||||
echo "Booting the ACA from a war file..." | WriteAndLog
|
||||
java -jar $DEPLOYED_WAR --spring.config.location=$SPRING_PROP_FILE_FORWARDSLASHES
|
||||
if ($d -or $debug) {
|
||||
echo "... in debug"
|
||||
java $DEBUG_OPTIONS -jar $DEPLOYED_WAR --spring.config.location=$SPRING_PROP_FILE_FORWARDSLASHES
|
||||
} else {
|
||||
java -jar $DEPLOYED_WAR --spring.config.location=$SPRING_PROP_FILE_FORWARDSLASHES
|
||||
}
|
||||
} else {
|
||||
echo "Booting the ACA from local build..." | WriteAndLog
|
||||
./gradlew bootRun --args="--spring.config.location=$SPRING_PROP_FILE_FORWARDSLASHES"
|
||||
if ($d -or $debug) {
|
||||
echo "... in debug"
|
||||
./gradlew bootRun --args="--spring.config.location=$SPRING_PROP_FILE_FORWARDSLASHES" -Pdebug="$$DEBUG_OPTIONS"
|
||||
} else {
|
||||
./gradlew bootRun --args="--spring.config.location=$SPRING_PROP_FILE_FORWARDSLASHES"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ public class SwidTagConstants {
|
||||
public static final String SCHEMA_PACKAGE = "hirs.utils.xjc";
|
||||
public static final String SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI;
|
||||
public static final String SCHEMA_URL = "swid_schema.xsd";
|
||||
public static final String SWIDTAG_NAMESPACE = "http://standards.iso.org/iso/19770/-2/2015/schema.xsd";
|
||||
|
||||
public static final String SOFTWARE_IDENTITY = "SoftwareIdentity";
|
||||
public static final String ENTITY = "Entity";
|
||||
|
@ -3,22 +3,24 @@ package hirs.swid;
|
||||
import hirs.swid.utils.HashSwid;
|
||||
import hirs.utils.xjc.Directory;
|
||||
import hirs.utils.xjc.Entity;
|
||||
import hirs.utils.xjc.FilesystemItem;
|
||||
import hirs.utils.xjc.Link;
|
||||
import hirs.utils.xjc.ObjectFactory;
|
||||
import hirs.utils.xjc.ResourceCollection;
|
||||
import hirs.utils.xjc.SoftwareIdentity;
|
||||
import hirs.utils.xjc.SoftwareMeta;
|
||||
import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import jakarta.xml.bind.Marshaller;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonException;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
import jakarta.xml.bind.JAXBContext;
|
||||
import jakarta.xml.bind.JAXBElement;
|
||||
import jakarta.xml.bind.JAXBException;
|
||||
import jakarta.xml.bind.Marshaller;
|
||||
import javax.xml.crypto.MarshalException;
|
||||
import javax.xml.crypto.XMLStructure;
|
||||
import javax.xml.crypto.dom.DOMStructure;
|
||||
@ -41,6 +43,7 @@ import javax.xml.crypto.dsig.keyinfo.X509Data;
|
||||
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
|
||||
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
@ -91,6 +94,8 @@ public class SwidTagGateway {
|
||||
private String timestampFormat;
|
||||
private String timestampArgument;
|
||||
private String errorRequiredFields;
|
||||
private DocumentBuilderFactory dbf;
|
||||
private DocumentBuilder builder;
|
||||
|
||||
/**
|
||||
* Default constructor initializes jaxbcontext, marshaller, and unmarshaller
|
||||
@ -107,8 +112,15 @@ public class SwidTagGateway {
|
||||
timestampFormat = "";
|
||||
timestampArgument = "";
|
||||
errorRequiredFields = "";
|
||||
dbf = DocumentBuilderFactory.newInstance();
|
||||
dbf.setNamespaceAware(true);
|
||||
builder = dbf.newDocumentBuilder();
|
||||
} catch (JAXBException e) {
|
||||
System.out.println("Error initializing jaxbcontext: " + e.getMessage());
|
||||
} catch (ParserConfigurationException e) {
|
||||
System.out.println("Error instantiating Document object for parsing swidtag: "
|
||||
+ e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,49 +210,62 @@ public class SwidTagGateway {
|
||||
* @param filename
|
||||
*/
|
||||
public void generateSwidTag(final String filename) {
|
||||
SoftwareIdentity swidTag = null;
|
||||
Document swidtag = builder.newDocument();
|
||||
SoftwareIdentity softwareIdentity = null;
|
||||
try {
|
||||
InputStream is = new FileInputStream(attributesFile);
|
||||
JsonReader reader = Json.createReader(is);
|
||||
JsonObject configProperties = reader.readObject();
|
||||
reader.close();
|
||||
//SoftwareIdentity
|
||||
swidTag = createSwidTag(
|
||||
softwareIdentity = createSwidTag(
|
||||
configProperties.getJsonObject(SwidTagConstants.SOFTWARE_IDENTITY));
|
||||
//Entity
|
||||
JAXBElement<Entity> entity = objectFactory.createSoftwareIdentityEntity(
|
||||
createEntity(configProperties.getJsonObject(SwidTagConstants.ENTITY)));
|
||||
swidTag.getEntityOrEvidenceOrLink().add(entity);
|
||||
softwareIdentity.getEntityOrEvidenceOrLink().add(entity);
|
||||
//Link
|
||||
JAXBElement<Link> link = objectFactory.createSoftwareIdentityLink(
|
||||
createLink(configProperties.getJsonObject(SwidTagConstants.LINK)));
|
||||
swidTag.getEntityOrEvidenceOrLink().add(link);
|
||||
softwareIdentity.getEntityOrEvidenceOrLink().add(link);
|
||||
//Meta
|
||||
JAXBElement<SoftwareMeta> meta = objectFactory.createSoftwareIdentityMeta(
|
||||
createSoftwareMeta(configProperties.getJsonObject(SwidTagConstants.META)));
|
||||
swidTag.getEntityOrEvidenceOrLink().add(meta);
|
||||
//Payload
|
||||
ResourceCollection payload = createPayload(
|
||||
configProperties.getJsonObject(SwidTagConstants.PAYLOAD));
|
||||
//Directory
|
||||
Directory directory = createDirectory(
|
||||
configProperties.getJsonObject(SwidTagConstants.PAYLOAD)
|
||||
.getJsonObject(SwidTagConstants.DIRECTORY));
|
||||
softwareIdentity.getEntityOrEvidenceOrLink().add(meta);
|
||||
|
||||
swidtag = convertToDocument(objectFactory.createSoftwareIdentity(softwareIdentity));
|
||||
Element rootElement = swidtag.getDocumentElement();
|
||||
|
||||
//File
|
||||
hirs.utils.xjc.File file = createFile(
|
||||
configProperties.getJsonObject(SwidTagConstants.PAYLOAD)
|
||||
.getJsonObject(SwidTagConstants.DIRECTORY)
|
||||
.getJsonObject(SwidTagConstants.FILE));
|
||||
//Nest File in Directory in Payload
|
||||
directory.getDirectoryOrFile().add(file);
|
||||
payload.getDirectoryOrFileOrProcess().add(directory);
|
||||
JAXBElement<FilesystemItem> jaxbFile = objectFactory.createDirectoryFile(file);
|
||||
Document fileDoc = convertToDocument(jaxbFile);
|
||||
//Directory
|
||||
Directory directory = createDirectory(
|
||||
configProperties.getJsonObject(SwidTagConstants.PAYLOAD)
|
||||
.getJsonObject(SwidTagConstants.DIRECTORY));
|
||||
JAXBElement<FilesystemItem> jaxbDirectory = objectFactory.createPayloadDirectory(directory);
|
||||
Document dirDoc = convertToDocument(jaxbDirectory);
|
||||
Node fileNode = dirDoc.importNode(fileDoc.getDocumentElement(), true);
|
||||
dirDoc.getDocumentElement().appendChild(fileNode);
|
||||
//Payload
|
||||
ResourceCollection payload = createPayload(
|
||||
configProperties.getJsonObject(SwidTagConstants.PAYLOAD));
|
||||
JAXBElement<ResourceCollection> jaxbPayload =
|
||||
objectFactory.createSoftwareIdentityPayload(payload);
|
||||
swidTag.getEntityOrEvidenceOrLink().add(jaxbPayload);
|
||||
Document payloadDoc = convertToDocument(jaxbPayload);
|
||||
Node dirNode = payloadDoc.importNode(dirDoc.getDocumentElement(), true);
|
||||
payloadDoc.getDocumentElement().appendChild(dirNode);
|
||||
|
||||
Node payloadNode = swidtag.importNode(payloadDoc.getDocumentElement(), true);
|
||||
rootElement.appendChild(payloadNode);
|
||||
|
||||
//Signature
|
||||
if (errorRequiredFields.isEmpty()) {
|
||||
Document signedSoftwareIdentity = signXMLDocument(
|
||||
objectFactory.createSoftwareIdentity(swidTag));
|
||||
Document signedSoftwareIdentity = signXMLDocument(swidtag);
|
||||
writeSwidTagFile(signedSoftwareIdentity, filename);
|
||||
} else {
|
||||
System.out.println("The following fields cannot be empty or null: "
|
||||
@ -545,23 +570,31 @@ public class SwidTagGateway {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method signs a SoftwareIdentity with an xmldsig in compatibility mode.
|
||||
* Current assumptions: digest method SHA256, signature method SHA256, enveloped signature
|
||||
* This method converts a JAXBElement object generated from the hirs.utils.xjc package into
|
||||
* a Document object.
|
||||
*
|
||||
* @param element to convert
|
||||
* @return a Document object
|
||||
*/
|
||||
private Document signXMLDocument(JAXBElement<SoftwareIdentity> swidTag) {
|
||||
private Document convertToDocument(JAXBElement element) {
|
||||
Document doc = null;
|
||||
try {
|
||||
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
|
||||
marshaller.marshal(swidTag, doc);
|
||||
} catch (ParserConfigurationException e) {
|
||||
System.out.println("Error instantiating Document object for parsing swidtag: "
|
||||
+ e.getMessage());
|
||||
System.exit(1);
|
||||
doc = builder.newDocument();
|
||||
marshaller.marshal(element, doc);
|
||||
} catch (JAXBException e) {
|
||||
System.out.println("Error while marshaling swidtag: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method signs a SoftwareIdentity with an xmldsig in compatibility mode.
|
||||
* Current assumptions: digest method SHA256, signature method SHA256, enveloped signature
|
||||
*/
|
||||
private Document signXMLDocument(Document doc) {
|
||||
XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
|
||||
List xmlObjectList = null;
|
||||
String signatureId = null;
|
||||
@ -679,12 +712,13 @@ public class SwidTagGateway {
|
||||
* @return an XMLObject containing the timestamp element
|
||||
*/
|
||||
private XMLObject createXmlTimestamp(Document doc, XMLSignatureFactory sigFactory) {
|
||||
Element timeStampElement = doc.createElement("TimeStamp");
|
||||
Element timeStampElement = null;
|
||||
switch (timestampFormat.toUpperCase()) {
|
||||
case "RFC3852":
|
||||
try {
|
||||
byte[] counterSignature = Base64.getEncoder().encode(
|
||||
Files.readAllBytes(Paths.get(timestampArgument)));
|
||||
timeStampElement = doc.createElementNS(SwidTagConstants.RFC3852_NS, "TimeStamp");
|
||||
timeStampElement.setAttributeNS("http://www.w3.org/2000/xmlns/",
|
||||
"xmlns:" + SwidTagConstants.RFC3852_PFX,
|
||||
SwidTagConstants.RFC3852_NS);
|
||||
@ -696,6 +730,7 @@ public class SwidTagGateway {
|
||||
}
|
||||
break;
|
||||
case "RFC3339":
|
||||
timeStampElement = doc.createElementNS(SwidTagConstants.RFC3339_NS, "TimeStamp");
|
||||
timeStampElement.setAttributeNS("http://www.w3.org/2000/xmlns/",
|
||||
"xmlns:" + SwidTagConstants.RFC3339_PFX,
|
||||
SwidTagConstants.RFC3339_NS);
|
||||
|
Loading…
Reference in New Issue
Block a user