Initial release

This commit is contained in:
apldev4 2018-09-06 09:47:33 -04:00
commit d7e44b8310
1262 changed files with 244470 additions and 0 deletions

124
.gitignore vendored Normal file
View File

@ -0,0 +1,124 @@
# compiled python for systems tests
*.pyc
# NetBeans specific #
private/
build/
dist/
.nb-gradle-properties
# Eclipse specific #
.classpath
.project
.settings/
.checkstyle
# IDEA specific #
*.iml
.idea
cmake-build-debug/
# Gradle specific #
.gradle
# Class Files #
*.class
classes/
# Package Files #
*.war
*.ear
*.rpm
*.deb
# Specific RPMs used for testing
!HIRS_Utils/src/test/resources/repository/vim-common-7.2.411-1.8.el6.x86_64.rpm
!HIRS_Utils/src/test/resources/repository/kernel-2.6.32-642.6.1.el6.x86_64.rpm
!HIRS_Utils/src/test/resources/testrepo/*
!Systems_Tests/resources/**
# DEB Sources #
DEB_SOURCES/
# RPM Files #
BUILD/
BUILDROOT/
SOURCES/
SPECS/
RPM/
SRPM/
PLUGIN_SOURCE/
# C++ Files #
*.o
# Vagrant Files #
.vagrant/
.vagrantfile.swp
vagrant/tmp/
# tpm_module #
tpm_module/tpm_module
main.o
main.d
# Misc Files #
*~
bin/
!package/extras/*/bin
*/test-output/
# MAC OSX Finder Files #
.DS_Store
# Log Files #
*.log
/.nb-gradle/
# rejected diff applications
*.rej
# cmake artifacts from manual build
cmake_install.cmake
HIRS_ProvisionerTPM2/*.cmake
HIRS_ProvisionerTPM2/HIRS_ProvisionerTPM2.cbp
HIRS_ProvisionerTPM2/CMakeCache.txt
Makefile
CMakeFiles/
HIRS_ProvisionerTPM2/DartConfiguration.tcl
HIRS_ProvisionerTPM2/lib/cpplint-download/
HIRS_ProvisionerTPM2/lib/cpplint/
HIRS_ProvisionerTPM2/lib/cpr-build/
HIRS_ProvisionerTPM2/lib/cpr-download/
HIRS_ProvisionerTPM2/lib/cpr-src/
HIRS_ProvisionerTPM2/lib/googletest-build/
HIRS_ProvisionerTPM2/lib/googletest-download/
HIRS_ProvisionerTPM2/lib/googletest-src/
HIRS_ProvisionerTPM2/lib/*.a
HIRS_ProvisionerTPM2/lib/*.so
HIRS_ProvisionerTPM2/install_manifest.txt
HIRS_ProvisionerTPM2/src/libTPM2_PROVISIONER_LIBRARY.a
HIRS_ProvisionerTPM2/test/CTestTestfile.cmake
# C++ Doxygen Documentation
HIRS_ProvisionerTPM2/docs/html/
HIRS_ProvisionerTPM2/docs/latex/
HIRS_ProvisionerTPM2/CMakeDoxyfile.in
# C++ Style Checker
HIRS_ProvisionerTPM2/lint
/*/out
HIRS_ProvisionerTPM2/cmake-build-debug
# autogenerated protobuf files
*.pb.cc
*.pb.h
HIRS_AttestationCA/src/main/java/hirs/attestationca/configuration/provisionerTpm2/ProvisionerTpm2.java
# these files are copied over by ProvisionerTPM2 CMake build
HIRS_ProvisionerTPM2/config/logging.properties
HIRS_ProvisionerTPM2/scripts/tpm_aca_provision

31
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,31 @@
# NOTE: if you are editing this, try using the lint tool to check your work before pushing: https://forge.outer.jhuapl.edu/ci/lint
before_script:
- echo "running CI jobs for HIRS"
stages:
- build
gradle_build:
stage: build
script: ./gradlew build
artifacts:
when: on_failure
untracked: true
expire_in: 3 days
rpm_build_centos6:
stage: build
script: ONLY_BUILD_EL6_RPMS=true ./package/package.centos.sh
artifacts:
paths:
- package/rpm/RPMS/
expire_in: 3 days
rpm_build_centos7:
stage: build
script: ONLY_BUILD_EL7_RPMS=true ./package/package.centos.sh
artifacts:
paths:
- package/rpm/RPMS/
expire_in: 3 days

6
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,6 @@
All contributions to this project will be released as follows:
1. If you are a U.S. government employee, then your changes are exempt from copyright in the U.S. and will be released under the [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) [Universal license](https://creativecommons.org/publicdomain/zero/1.0/legalcode) worldwide.
1. If you are a not a U.S. government employee, then your changes will be released under the [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) [Universal license](https://creativecommons.org/publicdomain/zero/1.0/legalcode) in the U.S. and worldwide.
By submitting a pull request, you are agreeing to comply with this waiver of copyright interest.

9
DISCLAIMER.md Normal file
View File

@ -0,0 +1,9 @@
## Disclaimer of Warranty
This Work is provided "as is." Any express or implied warranties, including but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the United States Government be liable for any direct, indirect, incidental, special, exemplary or consequential damages (including, but not limited to, procurement of substitute goods or services, loss of use, data or profits, or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this Guidance, even if advised of the possibility of such damage.
The User of this Work agrees to hold harmless and indemnify the United States Government, its agents and employees from every claim or liability (whether in tort or in contract), including attorneys' fees, court costs, and expenses, arising in direct consequence of Recipient's use of the item, including, but not limited to, claims or liabilities made for injury to or death of personnel of User or third parties, damage to or destruction of property of User or third parties, and infringement or other violations of intellectual property or technical data rights.
Nothing in this Work is intended to constitute an endorsement, explicit or implied, by the United States Government of any particular manufacturer's product or service.
## Disclaimer of Endorsement
Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise, in this Work does not constitute an endorsement, recommendation, or favoring by the United States Government and shall not be used for advertising or product endorsement purposes.

View File

@ -0,0 +1,83 @@
apply plugin: 'war'
apply plugin: 'checkstyle'
evaluationDependsOn(':HIRS_Utils')
sourceCompatibility = 1.8
dependencies {
compile project(':TPM_Utils')
compile project(':HIRS_Structs')
compile project(':HIRS_Utils')
compile libs.bouncy_castle
compile libs.commons_codec
compile libs.commons_lang
compile libs.spring_webmvc
compile libs.log4j2
compile libs.log4j2_web
compile libs.protobuf_java
providedCompile libs.servlet_api
testCompile project(':HIRS_Utils').sourceSets.test.output
testCompile project(':HIRS_Utils').sourceSets.test.resources
testCompile libs.commons_lang
testCompile libs.spring_test
testCompile libs.mockito
testCompile libs.testng
testCompile libs.hsqldb
}
task generateProtoBuf(type:Exec) {
workingDir 'config'
commandLine './genJavaProtoBuf.sh'
}
compileJava.dependsOn generateProtoBuf
ext.configDir = new File(projectDir, 'config')
ext.checkstyleConfigDir = "$configDir/checkstyle"
checkstyle {
toolVersion = '5.7'
configFile = checkstyleConfigFile
configProperties.put('basedir', checkstyleConfigDir)
ignoreFailures = false
showViolations = true
}
war {
archiveName = 'HIRS_AttestationCA.war'
}
publishing {
publications {
maven(MavenPublication) {
artifactId 'hirs-attestationca'
artifact jar
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.runtime.allDependencies.each {
if (it.group != null && it.name != null) {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
if (it.excludeRules.size() > 0) {
def exclusionsNode = dependencyNode.appendNode('exclusions')
it.excludeRules.each { rule ->
def exclusionNode = exclusionsNode.appendNode('exclusion')
exclusionNode.appendNode('groupId', rule.group)
exclusionNode.appendNode('artifactId', rule.module)
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress checks="MagicNumber" files=".*[/\\]src[/\\]test[/\\]+" />
<suppress checks="FinalParameters" files=".*[/\\]src[/\\]test[/\\]+" />
<suppress checks="JavadocPackage" files=".*[/\\]src[/\\]test[/\\]+" />
<suppress files="src/main/java/hirs/attestationca/configuration/provisionerTpm2/ProvisionerTpm2\.java" checks="[a-zA-Z0-9]*"/>
</suppressions>

View File

@ -0,0 +1,16 @@
#!/bin/bash
# Script to generate protobuf Java code. Called by gradle to compile the
# protobuf spec file to Java source. Generates the file
# hirs/attestationca/configuration/provisionerTpm2/ProvisionerTpm2.java.
dir=$(pwd)
# Relative paths are different when building locally versus on CI
if [[ "$dir" == *"package"* ]]; then
SRC_DIR=$dir/../../../../../../HIRS_ProvisionerTPM2/src
DEST_DIR=$dir/../src/main/java
else
SRC_DIR=../../HIRS_ProvisionerTPM2/src
DEST_DIR=../src/main/java
fi
protoc -I=$SRC_DIR --java_out=$DEST_DIR $SRC_DIR/ProvisionerTpm2.proto

View File

@ -0,0 +1,72 @@
package hirs.attestationca;
import hirs.appraiser.SupplyChainAppraiser;
import hirs.data.persist.DeviceGroup;
import hirs.data.persist.SupplyChainPolicy;
import hirs.persist.AppraiserManager;
import hirs.persist.DeviceGroupManager;
import hirs.persist.PolicyManager;
import static hirs.attestationca.AbstractAttestationCertificateAuthority.LOG;
/**
* Utility class that simply holds logic to seed the ACA's database with its
* default entries.
*/
public final class AcaDbInit {
// prevent construction
private AcaDbInit() { }
/**
* Insert the ACA's default entries into the DB. This class is invoked after successful
* install of the HIRS_AttestationCA RPM.
*
* @param appraiserManager the AppraiserManager to use to persist appraisers
* @param deviceGroupManager the DeviceGroupManager to use to persist device groups
* @param policyManager the PolicyManager to use to persist policies
*/
public static synchronized void insertDefaultEntries(
final AppraiserManager appraiserManager,
final DeviceGroupManager deviceGroupManager,
final PolicyManager policyManager
) {
LOG.info("Ensuring default ACA database entries are present.");
// Ensure the default group exists. It may have already been created by the Server RPM
DeviceGroup defaultGroup = deviceGroupManager.getDeviceGroup(DeviceGroup.DEFAULT_GROUP);
if (defaultGroup == null) {
LOG.info("Default group not found; saving...");
defaultGroup = deviceGroupManager.saveDeviceGroup(new DeviceGroup(
DeviceGroup.DEFAULT_GROUP,
"This is the default group"
));
LOG.info("Saved default group.");
}
// If the SupplyChainAppraiser exists, do not attempt to re-save the supply chain appraiser
// or SupplyChainPolicy
SupplyChainAppraiser supplyChainAppraiser = (SupplyChainAppraiser)
appraiserManager.getAppraiser(SupplyChainAppraiser.NAME);
if (supplyChainAppraiser != null) {
LOG.info("Supply chain appraiser is present; not inserting any more entries.");
LOG.info("ACA database initialization complete.");
return;
}
// Create the SupplyChainAppraiser
LOG.info("Saving supply chain appraiser...");
supplyChainAppraiser = (SupplyChainAppraiser)
appraiserManager.saveAppraiser(new SupplyChainAppraiser());
// Create the SupplyChainPolicy
LOG.info("Saving default supply chain policy...");
SupplyChainPolicy supplyChainPolicy = new SupplyChainPolicy(
SupplyChainPolicy.DEFAULT_POLICY
);
policyManager.savePolicy(supplyChainPolicy);
policyManager.setDefaultPolicy(supplyChainAppraiser, supplyChainPolicy);
policyManager.setPolicy(supplyChainAppraiser, defaultGroup, supplyChainPolicy);
LOG.info("ACA database initialization complete.");
}
}

View File

@ -0,0 +1,52 @@
package hirs.attestationca;
/**
* Defines the responsibilities of the Attestation Certificate Authority.
*/
public interface AttestationCertificateAuthority {
/**
* The default size for IV blocks.
*/
int DEFAULT_IV_SIZE = 16;
/**
* Processes a given {@link hirs.structs.elements.aca.IdentityRequestEnvelope} and
* generates a {@link hirs.structs.elements.aca.IdentityResponseEnvelope}. In most cases,
* a client will generate the request using the TPM "Collate Identity" process.
*
* @param identityRequest generated during the collate identity process with a Tpm
* @return response for the request
*/
byte[] processIdentityRequest(byte[] identityRequest);
/**
* Processes a given
* {@link hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2.IdentityClaim} and
* generates a response containing an encrypted nonce to be returned by the client in
* a future handshake request.
*
* @param identityClaim generated during the create identity claim process on a TPM2 Provisioner
* @return response for the request
*/
byte[] processIdentityClaimTpm2(byte[] identityClaim);
/**
* Processes a given
* {@link hirs.attestationca.configuration.provisionerTpm2.ProvisionerTpm2.CertificateRequest}
* and generates a response containing the signed, public certificate for
* the client's desired attestation key, if the correct nonce is supplied.
*
* @param certificateRequest request containing nonce from earlier identity
* claim handshake
* @return response for the request
*/
byte[] processCertificateRequest(byte[] certificateRequest);
/**
* Issues the PK of the ACA public/private key pair.
*
* @return public key of the attestation certificate authority
*/
byte[] getPublicKey();
}

View File

@ -0,0 +1,122 @@
package hirs.attestationca;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.persist.CertificateManager;
import hirs.persist.DBManagerException;
/**
* Utility class which includes credential management functions used by the ACA.
*/
public final class CredentialManagementHelper {
private static final Logger LOG = LogManager.getLogger(CredentialManagementHelper.class);
private CredentialManagementHelper() {
}
/**
* Parses and stores the EK in the cert manager. If the cert is already present and archived,
* it is unarchived.
* @param certificateManager the certificate manager used for storage
* @param endorsementBytes the raw EK bytes used for parsing
* @return the parsed, valid EK
* @throws IllegalArgumentException if the provided bytes are not a valid EK.
*/
public static EndorsementCredential storeEndorsementCredential(
final CertificateManager certificateManager,
final byte[] endorsementBytes) throws IllegalArgumentException {
if (null == certificateManager) {
throw new IllegalArgumentException("null certificate manager");
}
if (null == endorsementBytes) {
throw new IllegalArgumentException("null endorsement credential bytes");
}
if (endorsementBytes.length <= 1) {
throw new IllegalArgumentException(
String.format("%d-length byte array given for endorsement credential",
endorsementBytes.length)
);
}
LOG.info("Parsing Endorsement Credential of length " + endorsementBytes.length);
EndorsementCredential endorsementCredential =
EndorsementCredential.parseWithPossibleHeader(endorsementBytes);
int certificateHash = endorsementCredential.getCertificateHash();
EndorsementCredential existingCredential =
EndorsementCredential.select(certificateManager).includeArchived()
.byHashCode(certificateHash).getCertificate();
if (null == existingCredential) {
LOG.info("No Endorsement Credential found with hash: " + certificateHash);
return (EndorsementCredential) certificateManager.save(endorsementCredential);
} else if (existingCredential.isArchived()) {
// if the EK is stored in the DB and it's archived, unarchive.
LOG.info("Unarchiving credential");
existingCredential.restore();
existingCredential.resetCreateTime();
certificateManager.update(existingCredential);
}
return existingCredential;
}
/**
* Parses and stores the PC in the cert manager. If the cert is already present and archived,
* it is unarchived.
* @param certificateManager the certificate manager used for storage
* @param platformBytes the raw PC bytes used for parsing
* @return the parsed, valid PC, or null if the provided bytes are not a valid EK.
*/
public static PlatformCredential storePlatformCredential(
final CertificateManager certificateManager,
final byte[] platformBytes) {
if (null == certificateManager) {
throw new IllegalArgumentException("null certificate manager");
}
if (null == platformBytes) {
throw new IllegalArgumentException("null platform credential bytes");
}
if (platformBytes.length == 0) {
throw new IllegalArgumentException(
"zero-length byte array given for platform credential"
);
}
LOG.info("Parsing Platform Credential of length " + platformBytes.length);
try {
PlatformCredential platformCredential =
PlatformCredential.parseWithPossibleHeader(platformBytes);
if (null == platformCredential) {
return null;
}
PlatformCredential existingCredential =
PlatformCredential.select(certificateManager)
.byHashCode(platformCredential.getCertificateHash()).getCertificate();
if (null == existingCredential) {
return (PlatformCredential) certificateManager.save(platformCredential);
} else if (existingCredential.isArchived()) {
// if the PC is stored in the DB and it's archived, unarchive.
LOG.info("Unarchiving credential");
existingCredential.restore();
certificateManager.update(existingCredential);
return existingCredential;
}
return existingCredential;
} catch (DBManagerException dbe) {
LOG.error("Error retrieving or saving platform credential", dbe);
} catch (Exception e) {
LOG.error("Error parsing platform credential", e);
}
return null;
}
}

View File

@ -0,0 +1,27 @@
package hirs.attestationca;
/**
* Generic exception thrown while a {@link AttestationCertificateAuthority} is processing a newly
* submitted Identity.
*/
public class IdentityProcessingException extends RuntimeException {
/**
* Constructs a generic instance of this exception using the specified reason.
*
* @param reason for the exception
*/
public IdentityProcessingException(final String reason) {
super(reason);
}
/**
* Constructs a instance of this exception with the specified reason and backing root
* exception.
*
* @param reason for this exception
* @param rootException causing this exception
*/
public IdentityProcessingException(final String reason, final Throwable rootException) {
super(reason, rootException);
}
}

View File

@ -0,0 +1,41 @@
package hirs.attestationca;
import org.hibernate.SessionFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import hirs.persist.DBAppraiserManager;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBPolicyManager;
import hirs.persist.PersistenceConfiguration;
import hirs.utils.HIRSProfiles;
/**
* Simply holds a contextInitialized method which will be called when the web app starts.
*/
public class InitializationListener implements ServletContextListener {
@Override
public void contextInitialized(final ServletContextEvent event) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().addActiveProfile(HIRSProfiles.SERVER);
// register the database configuration and refresh the context
context.register(PersistenceConfiguration.class);
context.refresh();
// obtain reference to hibernate session factory
SessionFactory sessionFactory = context.getBean(LocalSessionFactoryBean.class).getObject();
AcaDbInit.insertDefaultEntries(
new DBAppraiserManager(sessionFactory),
new DBDeviceGroupManager(sessionFactory),
new DBPolicyManager(sessionFactory)
);
}
@Override
public void contextDestroyed(final ServletContextEvent event) {
}
}

View File

@ -0,0 +1,174 @@
package hirs.attestationca;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.GeneralNamesBuilder;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
/**
* Builds extensions based on Platform and Endorsement credentials to provide in an issued
* certificate.
*/
public final class IssuedCertificateAttributeHelper {
private static final String TPM_ID_LABEL_OID = "2.23.133.2.15";
/**
* Object Identifier TCPA at TPM ID Label.
*/
public static final ASN1ObjectIdentifier TCPA_AT_TPM_ID_LABEL =
new ASN1ObjectIdentifier(TPM_ID_LABEL_OID);
/**
* The extended key usage extension.
*/
public static final Extension EXTENDED_KEY_USAGE_EXTENSION;
private static final Logger LOG = LogManager.getLogger(IssuedCertificateAttributeHelper.class);
private static final ASN1ObjectIdentifier TCG_KP_AIK_CERTIFICATE_ATTRIBUTE =
new ASN1ObjectIdentifier("2.23.133.8.3");
static {
// Generates an extension that identifies a cert as an AIK cert
Extension extension = null;
try {
extension = new Extension(Extension.extendedKeyUsage, true,
new ExtendedKeyUsage(new KeyPurposeId[] {
KeyPurposeId.getInstance(TCG_KP_AIK_CERTIFICATE_ATTRIBUTE)}).getEncoded());
} catch (IOException e) {
LOG.error("Error generating extended key usage extension");
}
EXTENDED_KEY_USAGE_EXTENSION = extension;
}
private IssuedCertificateAttributeHelper() {
// do not construct publicly
}
/**
* Builds the subject alternative name based on the supplied certificates.
* @param endorsementCredential the endorsement credential
* @param platformCredentials the platform credentials
* @param hostName the host name
* @return the subject alternative name extension
* @throws IOException an IO exception occurs building the extension
* @throws IllegalArgumentException if the host name is null
*/
public static Extension buildSubjectAlternativeNameFromCerts(
final EndorsementCredential endorsementCredential,
final Collection<PlatformCredential> platformCredentials, final String hostName)
throws IOException, IllegalArgumentException {
if (StringUtils.isEmpty(hostName)) {
LOG.error("null host name");
throw new IllegalArgumentException("must provide host name");
}
// assemble AIK cert SAN, using info from EC and PC
X500NameBuilder nameBuilder = new X500NameBuilder();
populateEndorsementCredentialAttributes(endorsementCredential, nameBuilder);
if (!CollectionUtils.isEmpty(platformCredentials)) {
for (PlatformCredential platformCredential : platformCredentials) {
populatePlatformCredentialAttributes(platformCredential, nameBuilder);
}
}
// add the OID for the TCG-required TPM ID label
DERUTF8String idLabel = new DERUTF8String(hostName);
nameBuilder.addRDN(new AttributeTypeAndValue(TCPA_AT_TPM_ID_LABEL, idLabel));
// put everything into the SAN, usable by the certificate builder
GeneralNamesBuilder genNamesBuilder = new GeneralNamesBuilder();
genNamesBuilder.addName(new GeneralName(nameBuilder.build()));
DEROctetString sanContent =
new DEROctetString(genNamesBuilder.build().getEncoded());
Extension subjectAlternativeName = new Extension(Extension.subjectAlternativeName,
true, sanContent);
return subjectAlternativeName;
}
private static void populatePlatformCredentialAttributes(
final PlatformCredential platformCredential,
final X500NameBuilder nameBuilder) throws IOException {
if (null == platformCredential) {
return;
}
final RDN[] rdns;
try {
LOG.debug("Applying platform credential attributes to SAN");
AttributeCertificateInfo platformCredentialAttributeHolders =
platformCredential.getAttributeCertificate().getAcinfo();
rdns = ((X500Name) GeneralNames.fromExtensions(
platformCredentialAttributeHolders.getExtensions(),
Extension.subjectAlternativeName).getNames()[0].getName()).getRDNs();
} catch (IllegalArgumentException e) {
LOG.error("Unable to extract attributes from platform credential", e);
return;
}
populateRdnAttributesInNameBuilder(nameBuilder, rdns);
}
private static void populateEndorsementCredentialAttributes(
final EndorsementCredential endorsementCredential, final X500NameBuilder nameBuilder) {
if (null == endorsementCredential) {
return;
}
final RDN[] rdns;
try {
LOG.debug("Applying endorsement credential attributes to SAN");
X509Certificate endorsementX509 = endorsementCredential.getX509Certificate();
TBSCertificate tbsCertificate = TBSCertificate.getInstance(
endorsementX509.getTBSCertificate());
Extensions extensions = tbsCertificate.getExtensions();
GeneralNames names = GeneralNames.fromExtensions(extensions,
Extension.subjectAlternativeName);
if (names != null) {
X500Name x500 = (X500Name) names.getNames()[0].getName();
rdns = x500.getRDNs();
populateRdnAttributesInNameBuilder(nameBuilder, rdns);
} else {
LOG.error("No RDNs in endorsement credential attributes");
return;
}
} catch (CertificateEncodingException e) {
LOG.error("Certificate encoding exception", e);
return;
} catch (IOException e) {
LOG.error("Error creating x509 cert from endorsement credential", e);
return;
}
}
private static void populateRdnAttributesInNameBuilder(final X500NameBuilder nameBuilder,
final RDN[] rdns) {
for (final RDN rdn : rdns) {
nameBuilder.addRDN(rdn.getTypesAndValues()[0]);
}
}
}

View File

@ -0,0 +1,251 @@
package hirs.attestationca.configuration;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.X509Certificate;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBDeviceManager;
import hirs.persist.DeviceGroupManager;
import hirs.persist.DeviceManager;
import hirs.persist.HibernateConfiguration;
import hirs.structs.converters.SimpleStructConverter;
import hirs.structs.converters.StructConverter;
import hirs.utils.LogConfigurationUtil;
/**
* Provides application context configuration for the Attestation Certificate Authority
* application. The properties are processed in order and as such, the last property file read in
* will override properties that may had already been defined previously. In other words, the
* 'defaults.properties' file provides a basic standard of properties that can be overrode by the
*/
@Configuration
@PropertySources({
@PropertySource(value = "classpath:defaults.properties"),
// detects if file exists, if not, ignore errors
@PropertySource(value = "file:/etc/hirs/aca/aca.properties",
ignoreResourceNotFound = true)
})
@ComponentScan({ "hirs.attestationca", "hirs.attestationca.service", "hirs.validation",
"hirs.data.service" })
@Import(HibernateConfiguration.class)
@EnableWebMvc
public class AttestationCertificateAuthorityConfiguration extends WebMvcConfigurerAdapter {
private static final Logger LOG =
LogManager.getLogger(AttestationCertificateAuthorityConfiguration.class);
static {
try {
LogConfigurationUtil.applyConfiguration();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static final String CLIENT_FILES_PATH = "file:/etc/hirs/aca/client-files/";
@Value("${aca.directories.certificates}")
private String certificatesLocation;
@Value("${aca.keyStore.location}")
private String keyStoreLocation;
@Value("${aca.keyStore.password:''}")
private String keyStorePassword;
@Value("${aca.keyStore.alias}")
private String keyAlias;
@Autowired
private Environment environment;
@Autowired
private LocalSessionFactoryBean sessionFactory;
/**
* @return bean to resolve injected annotation.Value
* property expressions for beans.
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
/**
* Initialization of the ACA. Detects environment and runs configuration methods as required.
* This method is intended to be invoked by the Spring application context.
*/
@PostConstruct
void initialize() {
// ensure that Bouncy Castle is registered as a security provider
Security.addProvider(new BouncyCastleProvider());
// obtain path to ACA configuration
Path certificatesPath = Paths.get(certificatesLocation);
// create base directories if they do not exist
try {
Files.createDirectories(certificatesPath);
} catch (IOException e) {
throw new BeanInitializationException(
"Encountered error while initializing ACA directories: " + e.getMessage(), e);
}
// create the ACA key store if it doesn't exist
Path keyStorePath = Paths.get(keyStoreLocation);
if (!Files.exists(keyStorePath)) {
throw new IllegalStateException(
String.format("ACA Key Store not found at %s. Consult the HIRS User "
+ "Guide for ACA installation instructions.", keyStoreLocation));
}
}
/**
* @return the {@link PrivateKey} of the ACA
*/
@Bean
public PrivateKey privateKey() {
// obtain the key store
KeyStore keyStore = keyStore();
try {
// load the key from the key store
PrivateKey acaKey = (PrivateKey) keyStore.getKey(keyAlias,
keyStorePassword.toCharArray());
// break early if the certificate is not available.
if (acaKey == null) {
throw new BeanInitializationException(String.format("Key with alias "
+ "%s was not in KeyStore %s. Ensure that the KeyStore has the "
+ "specified certificate. ", keyAlias, keyStoreLocation));
}
return acaKey;
} catch (Exception e) {
throw new BeanInitializationException("Encountered error loading ACA private key "
+ "from key store: " + e.getMessage(), e);
}
}
/**
* @return the {@link X509Certificate} of the ACA
*/
@Bean
public X509Certificate acaCertificate() {
KeyStore keyStore = keyStore();
try {
X509Certificate acaCertificate = (X509Certificate) keyStore.getCertificate(keyAlias);
// break early if the certificate is not available.
if (acaCertificate == null) {
throw new BeanInitializationException(String.format("Certificate with alias "
+ "%s was not in KeyStore %s. Ensure that the KeyStore has the "
+ "specified certificate. ", keyAlias, keyStoreLocation));
}
return acaCertificate;
} catch (KeyStoreException e) {
throw new BeanInitializationException("Encountered error loading ACA certificate "
+ "from key store: " + e.getMessage(), e);
}
}
/**
* @return the {@link java.security.KeyStore} that contains the certificates for the ACA.
*/
@Bean
public KeyStore keyStore() {
Path keyStorePath = Paths.get(keyStoreLocation);
// attempt to open the key store. if that fails, log a meaningful message before failing.
try {
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(Files.newInputStream(keyStorePath), keyStorePassword.toCharArray());
return keyStore;
} catch (Exception e) {
LOG.error(String.format(
"Encountered error while loading ACA key store. The most common issue is "
+ "that configured password does not work on the configured key"
+ " store %s.", keyStorePath));
LOG.error(String.format("Exception message: %s", e.getMessage()));
throw new BeanInitializationException(e.getMessage(), e);
}
}
/**
* Prototyped {@link StructConverter}. In other words, all instances returned by this method
* will be configured identically, but subsequent invocations will return a new instance.
*
* @return ready to use {@link StructConverter}.
*/
@Bean
@Scope("prototype")
public static StructConverter structConverter() {
return new SimpleStructConverter();
}
/**
* Creates a {@link DeviceGroupManager} ready to use.
*
* @return {@link DeviceGroupManager}
*/
@Bean
public DeviceGroupManager deviceGroupManager() {
return new DBDeviceGroupManager(sessionFactory.getObject());
}
/**
* Creates a {@link DeviceManager} ready to use.
*
* @return {@link DeviceManager}
*/
@Bean
public DeviceManager deviceManager() {
return new DBDeviceManager(sessionFactory.getObject());
}
@Override
public void addResourceHandlers(final ResourceHandlerRegistry resourceHandlerRegistry) {
resourceHandlerRegistry.addResourceHandler("/client-files/**")
.addResourceLocations(CLIENT_FILES_PATH);
}
@Override
public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}

View File

@ -0,0 +1,4 @@
/**
* Includes the Spring Framework application context configuration classes.
*/
package hirs.attestationca.configuration;

View File

@ -0,0 +1,5 @@
/**
* Base package that includes common exceptions, interfaces and base implementations for and related
* to the ACA.
*/
package hirs.attestationca;

View File

@ -0,0 +1,145 @@
package hirs.attestationca.rest;
import hirs.attestationca.IdentityProcessingException;
import hirs.persist.DBManager;
import hirs.persist.TPM2ProvisionerState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import hirs.attestationca.AbstractAttestationCertificateAuthority;
import hirs.attestationca.service.SupplyChainValidationService;
import hirs.data.service.DeviceRegister;
import hirs.persist.CertificateManager;
import hirs.persist.DeviceManager;
import hirs.structs.converters.StructConverter;
/**
* Restful implementation of the {@link hirs.attestationca.AttestationCertificateAuthority}.
* Exposes the ACA methods as REST endpoints.
*/
@RestController
@RequestMapping("/")
public class RestfulAttestationCertificateAuthority
extends AbstractAttestationCertificateAuthority {
/**
* Constructor.
* @param supplyChainValidationService the supply chain service
* @param privateKey the ACA private key
* @param acaCertificate the ACA certificate
* @param structConverter the struct converter
* @param certificateManager the certificate manager
* @param deviceRegister the device register
* @param validDays the number of days issued certs are valid
* @param deviceManager the device manager
* @param tpm2ProvisionerStateDBManager the DBManager for persisting provisioner state
*/
@SuppressWarnings({ "checkstyle:parameternumber" })
@Autowired
public RestfulAttestationCertificateAuthority(
final SupplyChainValidationService supplyChainValidationService,
final PrivateKey privateKey, final X509Certificate acaCertificate,
final StructConverter structConverter,
final CertificateManager certificateManager,
final DeviceRegister deviceRegister,
final DeviceManager deviceManager,
final DBManager<TPM2ProvisionerState> tpm2ProvisionerStateDBManager,
@Value("${aca.certificates.validity}") final int validDays) {
super(supplyChainValidationService, privateKey, acaCertificate, structConverter,
certificateManager, deviceRegister, validDays, deviceManager,
tpm2ProvisionerStateDBManager);
}
/*
* (non-javadoc)
*
* Wrap the {@link AbstractAttestationCertificateAuthority#processIdentityRequest(byte[])}
* with a Spring {@link RequestMapping}. Effectively, this method then will allow spring to
* serialize and deserialize the request and responses on method invocation and
* return, respectively.
*/
@Override
@ResponseBody
@RequestMapping(value = "/identity-request/process", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public byte[] processIdentityRequest(@RequestBody final byte[] request) {
return super.processIdentityRequest(request);
}
/**
* Listener for identity requests from TPM 2.0 provisioning.
* @param request The request object from the provisioner.
* @return The response to the provisioner.
*/
@Override
@ResponseBody
@RequestMapping(value = "/identity-claim-tpm2/process",
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public byte[] processIdentityClaimTpm2(@RequestBody final byte[] request) {
return super.processIdentityClaimTpm2(request);
}
/**
* Endpoint for processing certificate requests for TPM 2.0 provisioning.
*
* @param request The credential request from the client provisioner.
* @return The response to the client provisioner.
*/
@Override
@ResponseBody
@RequestMapping(value = "/request-certificate-tpm2",
method = RequestMethod.POST,
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public byte[] processCertificateRequest(@RequestBody final byte[] request) {
return super.processCertificateRequest(request);
}
/*
* (non-javadoc)
*
* Wrap the {@link AbstractAttestationCertificateAuthority#getPublicKey()} with a Spring
* {@link RequestMapping} such that Spring can serialize the certificate to be returned to an
* HTTP Request.
*/
@Override
@ResponseBody
@RequestMapping(value = "/public-key", method = RequestMethod.GET,
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public byte[] getPublicKey() {
return super.getPublicKey();
}
/**
* Handle processing of exceptions for ACA REST API.
* @param e exception thrown during invocation of ACA REST API
* @return exception thrown during invocation of ACA REST API
*/
@ExceptionHandler
@ResponseBody
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
public Exception handleException(final Exception e) {
if (e instanceof IdentityProcessingException) {
LOG.error("Processing exception while provisioning", e.getMessage(), e);
} else {
LOG.error(String.format("Encountered unexpected error while processing identity "
+ "claim: %s", e.getMessage()), e);
}
return e;
}
}

View File

@ -0,0 +1,4 @@
/**
* RESTful implementations of the {@link hirs.attestationca.AttestationCertificateAuthority}.
*/
package hirs.attestationca.rest;

View File

@ -0,0 +1,28 @@
package hirs.attestationca.service;
import java.util.Set;
import hirs.data.persist.Device;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
/**
* 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.
*
* @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.
*/
SupplyChainValidationSummary validateSupplyChain(EndorsementCredential ec,
Set<PlatformCredential> pc,
Device device);
}

View File

@ -0,0 +1,373 @@
package hirs.attestationca.service;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.Level;
import hirs.appraiser.Appraiser;
import hirs.appraiser.SupplyChainAppraiser;
import hirs.data.persist.AppraisalStatus;
import hirs.data.persist.Device;
import hirs.data.persist.DeviceInfoReport;
import hirs.data.persist.SupplyChainPolicy;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.persist.AppraiserManager;
import hirs.persist.CertificateManager;
import hirs.persist.CertificateSelector;
import hirs.persist.CrudManager;
import hirs.persist.DBManagerException;
import hirs.persist.PersistenceConfiguration;
import hirs.persist.PolicyManager;
import hirs.validation.CredentialValidator;
/**
* The main executor of supply chain verification tasks. The AbstractAttestationCertificateAuthority
* will feed it the PC, EC, other relevant certificates, and serial numbers of the provisioning
* task, and it will then manipulate the data as necessary, retrieve useful certs, and arrange
* for actual validation by the SupplyChainValidator.
*/
@Service
@Import(PersistenceConfiguration.class)
public class SupplyChainValidationServiceImpl implements SupplyChainValidationService {
private PolicyManager policyManager;
private AppraiserManager appraiserManager;
private CertificateManager certificateManager;
private CredentialValidator supplyChainCredentialValidator;
private CrudManager<SupplyChainValidationSummary> supplyChainValidatorSummaryManager;
private static final Logger LOGGER =
LogManager.getLogger(SupplyChainValidationServiceImpl.class);
/**
* Constructor.
* @param policyManager the policy manager
* @param appraiserManager the appraiser manager
* @param certificateManager the cert manager
* @param supplyChainValidatorSummaryManager the summary manager
* @param supplyChainCredentialValidator the credential validator
*/
@Autowired
public SupplyChainValidationServiceImpl(final PolicyManager policyManager,
final AppraiserManager appraiserManager,
final CertificateManager certificateManager,
final CrudManager<SupplyChainValidationSummary> supplyChainValidatorSummaryManager,
final CredentialValidator supplyChainCredentialValidator) {
this.policyManager = policyManager;
this.appraiserManager = appraiserManager;
this.certificateManager = certificateManager;
this.supplyChainValidatorSummaryManager = supplyChainValidatorSummaryManager;
this.supplyChainCredentialValidator = supplyChainCredentialValidator;
}
/**
* 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.
*/
@Override
public SupplyChainValidationSummary validateSupplyChain(final EndorsementCredential ec,
final Set<PlatformCredential> pcs,
final Device device) {
final Appraiser supplyChainAppraiser = appraiserManager.getAppraiser(
SupplyChainAppraiser.NAME);
SupplyChainPolicy policy = (SupplyChainPolicy) policyManager.getDefaultPolicy(
supplyChainAppraiser);
boolean acceptExpiredCerts = policy.isExpiredCertificateValidationEnabled();
List<SupplyChainValidation> validations = new ArrayList<>();
// validate all supply chain pieces. Potentially, a policy setting could be made
// to dictate stopping after the first validation failure.
// Validate the Endorsement Credential
if (policy.isEcValidationEnabled()) {
validations.add(validateEndorsementCredential(ec, acceptExpiredCerts));
// store the device with the credential
if (null != ec) {
ec.setDevice(device);
this.certificateManager.update(ec);
}
}
// Validate Platform Credential signatures
if (policy.isPcValidationEnabled()) {
// Ensure there are platform credentials to validate
if (pcs == null || pcs.isEmpty()) {
LOGGER.error("There were no Platform Credentials to validate.");
validations.add(buildValidationRecord(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL,
AppraisalStatus.Status.FAIL,
"Platform credential(s) missing", null, Level.ERROR));
} else {
Iterator<PlatformCredential> it = pcs.iterator();
while (it.hasNext()) {
PlatformCredential pc = it.next();
KeyStore trustedCa = getCaChain(pc);
validations.add(validatePlatformCredential(pc, trustedCa, acceptExpiredCerts));
if (null != pc) {
pc.setDevice(device);
this.certificateManager.update(pc);
}
}
}
}
// Validate Platform Credential attributes
if (policy.isPcAttributeValidationEnabled()) {
// Ensure there are platform credentials to validate
if (pcs == null || pcs.isEmpty()) {
LOGGER.error("There were no Platform Credentials to validate attributes.");
validations.add(buildValidationRecord(
SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES,
AppraisalStatus.Status.FAIL,
"Platform credential(s) missing. Cannot validate attributes",
null, Level.ERROR));
} else {
Iterator<PlatformCredential> it = pcs.iterator();
while (it.hasNext()) {
PlatformCredential pc = it.next();
validations.add(validatePlatformCredentialAttributes(pc, device.getDeviceInfo(),
ec));
if (null != pc) {
pc.setDevice(device);
this.certificateManager.update(pc);
}
}
}
}
// Generate validation summary, save it, and return it.
SupplyChainValidationSummary summary =
new SupplyChainValidationSummary(device, validations);
try {
supplyChainValidatorSummaryManager.save(summary);
} catch (DBManagerException ex) {
LOGGER.error("Failed to save Supply chain summary", ex);
}
return summary;
}
private SupplyChainValidation validateEndorsementCredential(final EndorsementCredential ec,
final boolean acceptExpiredCerts) {
final SupplyChainValidation.ValidationType validationType
= SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL;
LOGGER.info("Validating endorsement credential");
if (ec == null) {
LOGGER.error("No endorsement credential to validate");
return buildValidationRecord(validationType,
AppraisalStatus.Status.FAIL, "Endorsement credential is missing",
null, Level.ERROR);
}
KeyStore ecStore = getCaChain(ec);
AppraisalStatus result = supplyChainCredentialValidator.
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:
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
result.getMessage(), ec, Level.ERROR);
default:
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
result.getMessage(), ec, Level.ERROR);
}
}
private SupplyChainValidation validatePlatformCredential(final PlatformCredential pc,
final KeyStore
trustedCertificateAuthority,
final boolean acceptExpiredCerts) {
final SupplyChainValidation.ValidationType validationType
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL;
if (pc == null) {
LOGGER.error("No platform credential to validate");
return buildValidationRecord(validationType,
AppraisalStatus.Status.FAIL, "Empty Platform credential", null, Level.ERROR);
}
LOGGER.info("Validating Platform Credential");
AppraisalStatus result = supplyChainCredentialValidator.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:
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
result.getMessage(), pc, Level.ERROR);
default:
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
result.getMessage(), pc, Level.ERROR);
}
}
private SupplyChainValidation validatePlatformCredentialAttributes(final PlatformCredential pc,
final DeviceInfoReport deviceInfoReport,
final EndorsementCredential ec) {
final SupplyChainValidation.ValidationType validationType
= SupplyChainValidation.ValidationType.PLATFORM_CREDENTIAL_ATTRIBUTES;
if (pc == null) {
LOGGER.error("No platform credential to validate");
return buildValidationRecord(validationType,
AppraisalStatus.Status.FAIL, "Platform credential is missing",
null, Level.ERROR);
}
LOGGER.info("Validating platform credential attributes");
AppraisalStatus result = supplyChainCredentialValidator.
validatePlatformCredentialAttributes(pc, deviceInfoReport, ec);
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:
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
result.getMessage(), pc, Level.ERROR);
default:
return buildValidationRecord(validationType, AppraisalStatus.Status.ERROR,
result.getMessage(), pc, Level.ERROR);
}
}
/**
* 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 certificate the certificate 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 Certificate certificate, final Level logLevel) {
List<Certificate> certificateList = new ArrayList<>();
if (null != certificate) {
certificateList.add(certificate);
}
LOGGER.log(logLevel, message);
return new SupplyChainValidation(validationType, result, certificateList, 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 ontaining all relevant CA credentials to the given certificate's
* organization
*/
public KeyStore getCaChain(final Certificate credential) {
KeyStore caKeyStore = null;
try {
caKeyStore = caCertSetToKeystore(getCaChainRec(credential, Collections.emptySet()));
} catch (KeyStoreException | IOException e) {
LOGGER.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.
*
* 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 previouslyQueriedOrganizations 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> previouslyQueriedOrganizations
) {
CertificateSelector<CertificateAuthorityCredential> caSelector =
CertificateAuthorityCredential.select(certificateManager)
.bySubjectOrganization(credential.getIssuerOrganization());
Set<CertificateAuthorityCredential> certAuthsWithMatchingOrg = caSelector.getCertificates();
Set<String> queriedOrganizations = new HashSet<>(previouslyQueriedOrganizations);
queriedOrganizations.add(credential.getIssuerOrganization());
HashSet<CertificateAuthorityCredential> caCreds = new HashSet<>();
for (CertificateAuthorityCredential cred : certAuthsWithMatchingOrg) {
caCreds.add(cred);
if (!queriedOrganizations.contains(cred.getIssuerOrganization())) {
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;
}
}

View File

@ -0,0 +1,5 @@
/**
* Contains the main functionality of the SupplyChainValidationService. Executes the actual
* validation based on the current supply chain policy.
*/
package hirs.attestationca.service;

View File

@ -0,0 +1,12 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- Instructs Spring to scan the ACA packages for annotations -->
<context:component-scan base-package="hirs.attestationca"/>
</beans>

View File

@ -0,0 +1,37 @@
# General notes:
# Properties are processed using an expression processor. That said, properties can inherit the
# values of other properties.
#
# In example the processor will resolve the value of aca.both as 'hello world!'.
# aca.hello = hello
# aca.world = world!
# aca.both = ${aca.hello} ${aca.world}
#
# ACA Directories
# root: the root directory of ACA related files
# certificates: the directory for ACA certificate files
aca.directories.root = /etc/hirs/aca
aca.directories.certificates = ${aca.directories.root}/certificates
# ACA certificate related properties. These are generic properties that apply to the creation of
# any certificate that the ACA is responsible for creating.
# validity: the number of days that credentials generated by the ACA are valid.
aca.certificates.validity = 3652
# ACA key store properties
# alias: the alias to reference the ACA key and certificate by
# location: the absolute path to the ACA key store.
# password: key store password
aca.keyStore.alias = HIRS_ACA_KEY
aca.keyStore.location = ${aca.directories.certificates}/keyStore.jks
aca.keyStore.password =
# ACA setup/initialization properties. These properties are used exclusively by the ACA
# initialization process. Generally these properties do not need to be modified
#
# keySize: the default key size of the ACA key pair stored within the trust store
# subjectName: the CN of the generate X509 certificate
# expiration: the number of days that the generated X509 certificate will expire
aca.setup.keyStore.keySize = 2048
aca.setup.keyStore.subjectName = HIRS_AttestationCA_Endorsement
aca.setup.keyStore.expiration = ${aca.certificates.validity}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t @ %C.%M] %-5p : %m%n"/>
</Console>
<RollingFile name="FILE" fileName="logs/HIRS_AttestationCA.log"
filePattern="logs/HIRS_AttestationCA.log-%d{yyyy-MM-dd}-%i.log" >
<PatternLayout>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%C.%M] %-5p : %m%n</pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level = "WARN">
<AppenderRef ref="STDOUT" level="WARN" />
<AppenderRef ref="FILE"/>
</Root>
</Loggers>
</Configuration>

View File

@ -0,0 +1,8 @@
#used in the initial setup of the client-files properties files upon WAR deployment
attestationca.url=https://***replace***:8443/HIRS_AttestationCA
attestationca.cert.subjectname=HIRS_AttestationCA
attestationca.cert.password=***replace***
endorsementca.cert.subjectname=HIRS_AttestationCA_Endorsement
cert.validitydays = 3652
aik.auth=0000000000000000000000000000000000000000

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5" metadata-complete="true">
<display-name>HIRS Attestation Certificate Authority</display-name>
<!-- Specify the location of the LOG4J file -->
<context-param>
<param-name>log4j.configurationFile</param-name>
<param-value>classpath:log4j2.xml</param-value>
</context-param>
<!-- Configure spring to not expose web app root system property -->
<context-param>
<param-name>log4jExposeWebAppRoot</param-name>
<param-value>false</param-value>
</context-param>
<!-- Configures the main Spring Servlet that dispatches HTTP requests to Controllers -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
</servlet>
<!-- Map all HTTP requests to the dispatcher -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>hirs.attestationca.InitializationListener</listener-class>
</listener>
</web-app>

View File

@ -0,0 +1,904 @@
package hirs.attestationca;
import com.google.protobuf.ByteString;
import hirs.utils.HexUtils;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.ArrayUtils;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.test.util.ReflectionTestUtils;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.util.Calendar;
import hirs.structs.converters.StructConverter;
import hirs.structs.elements.aca.SymmetricAttestation;
import hirs.structs.elements.tpm.AsymmetricKeyParams;
import hirs.structs.elements.tpm.AsymmetricPublicKey;
import hirs.structs.elements.tpm.EncryptionScheme;
import hirs.structs.elements.tpm.IdentityProof;
import hirs.structs.elements.tpm.IdentityRequest;
import hirs.structs.elements.tpm.StorePubKey;
import hirs.structs.elements.tpm.SymmetricKey;
import hirs.structs.elements.tpm.SymmetricKeyParams;
import hirs.structs.elements.tpm.SymmetricSubParams;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
/**
* Test suite for {@link AbstractAttestationCertificateAuthority}.
*/
public class AbstractAttestationCertificateAuthorityTest {
// object in test
private AbstractAttestationCertificateAuthority aca;
private static final String EK_PUBLIC_PATH = "/tpm2/ek.pub";
private static final String AK_PUBLIC_PATH = "/tpm2/ak.pub";
private static final String AK_NAME_PATH = "/tpm2/ak.name";
private static final String TEST_NONCE_BLOB_PATH = "test/nonce.blob";
private static final String EK_MODULUS_HEX = "a3 b5 c2 1c 57 be 40 c4 3c 78 90 0d 00 81 01 78"
+ "13 ca 02 ec b6 75 89 60 ca 60 9b 10 b6 b4 d0 0b"
+ "4d e4 68 ad 01 a6 91 e2 56 20 5e cf 16 fe 77 ae"
+ "1f 13 d7 ac a1 91 0b 68 f6 07 cf c2 4b 5e c1 2c"
+ "4c fe 3a c9 62 7e 10 02 5b 33 c8 c2 1a cd 2e 7f"
+ "dd 7c 43 ac a9 5f b1 d6 07 56 4f 72 9b 0a 00 6c"
+ "f6 8d 23 a1 84 ca c1 7f 5a 8b ef 0e 23 11 90 00"
+ "30 f2 99 e9 94 59 c6 b0 fe b2 5c 0c c7 b4 76 69"
+ "6c f1 b7 d8 e5 60 d6 61 9f ab 7c 17 ce a4 74 6d"
+ "8c cd e6 9e 6e bb 64 52 a7 c3 bf ac 07 e8 5e 3e"
+ "ae eb dc c5 95 37 26 6a 5d a6 a2 12 52 fa 03 43"
+ "b2 62 2d 87 8c a7 06 8f d6 3f 63 b6 2d 73 c4 9d"
+ "9d d6 55 0e bb db b1 eb dd c5 4b 8f c3 17 cb 3b"
+ "c3 bf f6 7f 13 44 de 8e d7 b9 f1 a7 15 56 8f 6c"
+ "cd f2 4c 86 99 39 19 88 d3 4a 2f 38 c4 c4 37 39"
+ "85 6f 41 98 19 14 a4 1f 95 bc 04 ef 74 c2 0d f3";
private static final String AK_MODULUS_HEX = "d7 c9 f0 e3 ac 1b 4a 1e 3c 9d 2d 57 02 e9 2a 93"
+ "b0 c0 e1 50 af e4 61 11 31 73 a1 96 b8 d6 d2 1c"
+ "40 40 c8 a6 46 a4 10 4b d1 06 74 32 f6 e3 8a 55"
+ "1e 03 c0 3e cc 75 04 c6 44 88 b6 ad 18 c9 45 65"
+ "0d be c5 45 22 bd 24 ad 32 8c be 83 a8 9b 1b d9"
+ "e0 c8 d9 ec 14 67 55 1b fe 68 dd c7 f7 33 e4 cd"
+ "87 bd ba 9a 07 e7 74 eb 57 ef 80 9c 6d ee f9 35"
+ "52 67 36 e2 53 98 46 a5 4e 8f 17 41 8d ff eb bb"
+ "9c d2 b4 df 57 f8 7f 31 ef 2e 2d 6e 06 7f 05 ed"
+ "3f e9 6f aa b4 b7 5a f9 6d ba ff 2b 5e f7 c1 05"
+ "90 68 1f b6 4b 38 67 f7 92 d8 73 51 6e 08 19 ad"
+ "ca 35 48 a7 c1 fb cb 01 9a 28 03 c9 fe bb 49 2f"
+ "88 3f a1 e7 a8 69 f0 f8 e8 78 db d3 6d c5 80 8d"
+ "c2 e4 8a af 4b c2 ac 48 2a 44 63 6e 39 b0 8f dd"
+ "e4 b3 a3 f9 2a b1 c8 d9 3d 6b c4 08 b0 16 c4 e7"
+ "c7 2f f5 94 c6 43 3e ee 9b 8a da e7 31 d1 54 dd";
private static final String AK_NAME_HEX = "00 0b 6e 8f 79 1c 7e 16 96 1b 11 71 65 9c e0 cd"
+ "ae 0d 4d aa c5 41 be 58 89 74 67 55 96 c2 5e 38"
+ "e2 94";
// test key pair
private KeyPair keyPair;
/**
* Registers bouncy castle as a security provider. Normally the JEE container will handle this,
* but since the tests are not instantiating a container, have the unit test runner setup the
* provider.
*/
@BeforeClass
public static void setupTests() {
Security.addProvider(new BouncyCastleProvider());
}
/**
* Instantiates an anonymous instance of the {@link AbstractAttestationCertificateAuthority}.
* This is sufficient as this class does not have any unimplemented or abstract methods.
*/
@BeforeTest
public void setup() {
aca = new AbstractAttestationCertificateAuthority(null, keyPair.getPrivate(),
null, null, null, null, 1,
null, null) {
};
}
/**
* Generates a key pair that can be used by the test suite.
*
* @throws Exception during key generation
*/
@BeforeSuite
public void suiteSetup() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#processIdentityRequest(byte[])}
* where the byte array is null. Expects an illegal argument exception to be thrown.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testProcessIdentityRequestNullRequest() {
aca.processIdentityRequest(null);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#processIdentityClaimTpm2(byte[])}
* where the byte array is null. Expects an illegal argument exception to be thrown.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testProcessIdentityClaimTpm2NullRequest() {
aca.processIdentityClaimTpm2(null);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#getPublicKey()}.
*/
@Test
public void testGetPublicKey() {
// encoded byte array to be returned by public key
byte[] encoded = new byte[]{0, 1, 0, 1, 0};
// create mocks for testing
X509Certificate acaCertificate = mock(X509Certificate.class);
PublicKey publicKey = mock(PublicKey.class);
// assign the aca certificate to the aca
ReflectionTestUtils.setField(aca, "acaCertificate", acaCertificate);
// return a mocked public key
when(acaCertificate.getPublicKey()).thenReturn(publicKey);
// return test byte array
when(publicKey.getEncoded()).thenReturn(encoded);
// assert what the ACA returns is as expected
assertEquals(aca.getPublicKey(), encoded);
// verify mock interactions
verify(acaCertificate).getPublicKey();
verify(publicKey).getEncoded();
// verify no other interactions with mocks
verifyNoMoreInteractions(acaCertificate, publicKey);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#unwrapIdentityRequest(byte[])}.
*
* @throws Exception during aca processing
*/
@Test
public void testUnwrapIdentityRequest() throws Exception {
// create a key generator to generate a "shared" secret
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// test variables
byte[] request = new byte[1];
byte[] iv = new byte[16];
byte[] asymmetricBlob = new byte[]{1, 2, 3};
byte[] symmetricBlob = new byte[]{3, 2, 1};
byte[] secretKey = keyGenerator.generateKey().getEncoded();
// fill the IV with random bytes
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.nextBytes(iv);
// encrypt the asymmetric blob
byte[] encryptedAsymmetricBlob =
encryptBlob(asymmetricBlob, EncryptionScheme.OAEP.toString());
// encrypt the symmetric blob
byte[] encryptedSymmetricBlob =
encryptBlob(symmetricBlob, secretKey, iv, "AES/CBC/PKCS5Padding");
// create some mocks for the tests
IdentityRequest identityRequest = mock(IdentityRequest.class);
AsymmetricKeyParams asymmetricKeyParams = mock(AsymmetricKeyParams.class);
SymmetricKeyParams symmetricKeyParams = mock(SymmetricKeyParams.class);
SymmetricSubParams symmetricSubParams = mock(SymmetricSubParams.class);
StructConverter structConverter = mock(StructConverter.class);
SymmetricKey symmetricKey = mock(SymmetricKey.class);
// assign the mocked struct converter to the test object
ReflectionTestUtils.setField(aca, "structConverter", structConverter);
// when converting our test request byte array, return our mocked identity request
// when converting our test asymmetric blob to a symmetric key, return the mocked key
when(structConverter.convert(request, IdentityRequest.class)).thenReturn(identityRequest);
when(structConverter.convert(asymmetricBlob, SymmetricKey.class)).thenReturn(symmetricKey);
// mock out the identity request by returning other mocks as needed
when(identityRequest.getSymmetricAlgorithm()).thenReturn(symmetricKeyParams);
when(identityRequest.getAsymmetricAlgorithm()).thenReturn(asymmetricKeyParams);
when(identityRequest.getAsymmetricBlob()).thenReturn(encryptedAsymmetricBlob);
when(identityRequest.getSymmetricBlob()).thenReturn(encryptedSymmetricBlob);
// use OAEP encryption scheme when asked
when(asymmetricKeyParams.getEncryptionScheme()).thenReturn(
(short) EncryptionScheme.OAEP_VALUE);
// use the mocked sub params when asked
when(symmetricKeyParams.getParams()).thenReturn(symmetricSubParams);
// use the test IV when asked
when(symmetricSubParams.getIv()).thenReturn(iv);
// use the test secret key when asked
when(symmetricKey.getKey()).thenReturn(secretKey);
// perform test
byte[] unwrappedRequest = aca.unwrapIdentityRequest(request);
// verify test results
assertEquals(unwrappedRequest, symmetricBlob);
// verify mock interactions
verify(structConverter).convert(request, IdentityRequest.class);
verify(structConverter).convert(asymmetricBlob, SymmetricKey.class);
verify(identityRequest).getSymmetricAlgorithm();
verify(identityRequest).getAsymmetricAlgorithm();
verify(identityRequest).getAsymmetricBlob();
verify(identityRequest).getSymmetricBlob();
verify(asymmetricKeyParams).getEncryptionScheme();
verify(symmetricKeyParams, times(2)).getParams();
verify(symmetricSubParams).getIv();
verify(symmetricKey).getKey();
verifyNoMoreInteractions(identityRequest, symmetricKeyParams, symmetricSubParams,
asymmetricKeyParams, structConverter, symmetricKey);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#unwrapIdentityRequest(byte[])}.
*
* @throws Exception during aca processing
*/
@Test
public void testUnwrapIdentityRequestNoKeyParams() throws Exception {
// create a key generator to generate a "shared" secret
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// test variables
byte[] request = new byte[1];
byte[] iv = new byte[16];
byte[] asymmetricBlob = new byte[]{1, 2, 3};
byte[] symmetricBlob = new byte[]{3, 2, 1};
byte[] secretKey = keyGenerator.generateKey().getEncoded();
// fill the IV with random bytes
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.nextBytes(iv);
// encrypt the asymmetric blob
byte[] encryptedAsymmetricBlob =
encryptBlob(asymmetricBlob, EncryptionScheme.OAEP.toString());
// encrypt the symmetric blob
byte[] encryptedSymmetricBlob =
encryptBlob(symmetricBlob, secretKey, iv, "AES/CBC/PKCS5Padding");
// since there is no symmetric key params, put the IV in front of encrypted blob.
byte[] totalBlob = ArrayUtils.addAll(iv, encryptedSymmetricBlob);
// create some mocks for the tests
IdentityRequest identityRequest = mock(IdentityRequest.class);
AsymmetricKeyParams asymmetricKeyParams = mock(AsymmetricKeyParams.class);
StructConverter structConverter = mock(StructConverter.class);
SymmetricKey symmetricKey = mock(SymmetricKey.class);
// assign the mocked struct converter to the test object
ReflectionTestUtils.setField(aca, "structConverter", structConverter);
// when converting our test request byte array, return our mocked identity request
// when converting our test asymmetric blob to a symmetric key, return the mocked key
when(structConverter.convert(request, IdentityRequest.class)).thenReturn(identityRequest);
when(structConverter.convert(asymmetricBlob, SymmetricKey.class)).thenReturn(symmetricKey);
// mock out the identity request by returning other mocks as needed
when(identityRequest.getSymmetricAlgorithm()).thenReturn(null);
when(identityRequest.getAsymmetricAlgorithm()).thenReturn(asymmetricKeyParams);
when(identityRequest.getAsymmetricBlob()).thenReturn(encryptedAsymmetricBlob);
// the first request should return IV + encrypted blob. Subsequent requests should return
// just the encrypted portion of the blob
when(identityRequest.getSymmetricBlob()).thenReturn(totalBlob)
.thenReturn(encryptedSymmetricBlob);
// use OAEP encryption scheme when asked
when(asymmetricKeyParams.getEncryptionScheme()).thenReturn(
(short) EncryptionScheme.OAEP_VALUE);
// use the test secret key when asked
when(symmetricKey.getKey()).thenReturn(secretKey);
// perform test
byte[] unwrappedRequest = aca.unwrapIdentityRequest(request);
// verify test results
assertEquals(unwrappedRequest, symmetricBlob);
// verify mock interactions
verify(structConverter).convert(request, IdentityRequest.class);
verify(structConverter).convert(asymmetricBlob, SymmetricKey.class);
verify(identityRequest).getSymmetricAlgorithm();
verify(identityRequest).getAsymmetricAlgorithm();
verify(identityRequest).getAsymmetricBlob();
verify(identityRequest, times(2)).getSymmetricBlob();
verify(identityRequest).setSymmetricBlob(encryptedSymmetricBlob);
verify(asymmetricKeyParams).getEncryptionScheme();
verify(symmetricKey).getKey();
verifyNoMoreInteractions(identityRequest, asymmetricKeyParams, structConverter,
symmetricKey);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#decryptAsymmetricBlob(byte[],
* EncryptionScheme)}.
*
* @throws Exception during aca processing
*/
@Test
public void testDecryptAsymmetricBlob() throws Exception {
// test encryption transformation
EncryptionScheme encryptionScheme = EncryptionScheme.PKCS1;
// test variables
byte[] expected = "test".getBytes();
// encrypt the expected value using same algorithm as the ACA.
byte[] encrypted = encryptBlob(expected, encryptionScheme.toString());
// perform the decryption and assert that the decrypted bytes equal the expected bytes
assertEquals(aca.decryptAsymmetricBlob(encrypted, encryptionScheme), expected);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#decryptSymmetricBlob(
* byte[], byte[], byte[], String)}.
*
* @throws Exception during aca processing
*/
@Test
public void testDecryptSymmetricBlob() throws Exception {
// test encryption transformation
String transformation = "AES/CBC/PKCS5Padding";
// test variables
byte[] expected = "test".getBytes();
// create a key generator to generate a "shared" secret
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// use some random bytes as the IV to encrypt and subsequently decrypt with
byte[] randomBytes = new byte[16];
// generate the random bytes
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.nextBytes(randomBytes);
// the shared secret
byte[] secretKey = keyGenerator.generateKey().getEncoded();
// encrypt the expected value with the private key being the shared secret
byte[] encrypted = encryptBlob(expected, secretKey, randomBytes, transformation);
// perform the decryption using the generated shared secert, random bytes as an IV, and the
// AES CBC transformation for the cipher. then assert the decrypted results are the same
// as our expected value.
assertEquals(aca.decryptSymmetricBlob(encrypted, secretKey, randomBytes, transformation),
expected);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#generateSymmetricKey()}.
*/
@Test
public void testGenerateSymmetricKey() {
// perform the test
SymmetricKey symmetricKey = aca.generateSymmetricKey();
// assert the symmetric algorithm, scheme, and key size are all set appropriately
assertTrue(symmetricKey.getAlgorithmId() == 6);
assertTrue(symmetricKey.getEncryptionScheme() == 255);
assertTrue(symmetricKey.getKeySize() == symmetricKey.getKey().length);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#generateAsymmetricContents(
* IdentityProof, SymmetricKey, PublicKey)}.
*
* @throws Exception during aca processing
*/
@Test
public void testGenerateAsymmetricContents() throws Exception {
// mocks for test
IdentityProof proof = mock(IdentityProof.class);
AsymmetricPublicKey publicKey = mock(AsymmetricPublicKey.class);
StructConverter structConverter = mock(StructConverter.class);
SymmetricKey symmetricKey = mock(SymmetricKey.class);
// assign the mocked struct converter to the test object
ReflectionTestUtils.setField(aca, "structConverter", structConverter);
// "encoded" identity proof (returned by struct converter)
byte[] identityProofEncoded = new byte[]{0, 0, 1, 1};
// generate a random session key to be used for encryption and decryption
byte[] sessionKey = new byte[16];
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.nextBytes(sessionKey);
// when requesting the identity key from the proof, return the mocked public key
when(proof.getIdentityKey()).thenReturn(publicKey);
// when requesting to convert the public key, return the encoded identity proof
when(structConverter.convert(publicKey)).thenReturn(identityProofEncoded);
when(structConverter.convert(symmetricKey)).thenReturn(sessionKey);
// perform the test
byte[] result = aca.generateAsymmetricContents(proof, symmetricKey, keyPair.getPublic());
// verify mock interactions
verify(proof).getIdentityKey();
verify(structConverter).convert(publicKey);
verify(structConverter).convert(symmetricKey);
verifyZeroInteractions(proof, structConverter, publicKey, symmetricKey);
// decrypt the result
byte[] decryptedResult = decryptBlob(result);
// create a SHA1 digest of the identity key
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(identityProofEncoded);
// generate the digest
byte[] identityDigest = md.digest();
// the decrypted asymmetric contents should be the session key and a SHA-1 hash of the
// encoded identity proof.
byte[] expected = ArrayUtils.addAll(sessionKey, identityDigest);
// compare the two byte arrays
assertEquals(decryptedResult, expected);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#generateAttestation(X509Certificate,
* SymmetricKey)}.
*
* @throws Exception during aca processing
*/
@Test
public void testGenerateAttestation() throws Exception {
// create some mocks for the unit tests
X509Certificate certificate = mock(X509Certificate.class);
SymmetricKey symmetricKey = mock(SymmetricKey.class);
// create a key generator to generate a secret key
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
// obtain the key from the generator
byte[] secretKey = keyGenerator.generateKey().getEncoded();
// use our public key for encryption
when(symmetricKey.getKey()).thenReturn(secretKey);
// just use the existing public key for the credential
when(certificate.getEncoded()).thenReturn(keyPair.getPublic().getEncoded());
// perform the actual test
SymmetricAttestation attestation = aca.generateAttestation(certificate, symmetricKey);
// validate that the attestation is not null
assertNotNull(attestation);
// validate the attestation algorithm
assertNotNull(attestation.getAlgorithm());
assertTrue(attestation.getAlgorithm().getAlgorithmId() == 6);
assertTrue(attestation.getAlgorithm().getEncryptionScheme() == 0x1);
assertTrue(attestation.getAlgorithm().getSignatureScheme() == 0);
assertTrue(attestation.getAlgorithm().getParamsSize() == 0);
// validate the attestation credential
assertNotNull(attestation.getCredential());
// validate that the credential size is the size of the actual credential block
assertTrue(attestation.getCredential().length == attestation.getCredentialSize());
// create containers for the 2 parts of the credential
byte[] iv = new byte[16];
byte[] credential = new byte[attestation.getCredential().length - iv.length];
// siphon off the first 16 bytes for the IV
System.arraycopy(attestation.getCredential(), 0, iv, 0, iv.length);
// the rest is the actual encrypted credential
System.arraycopy(attestation.getCredential(), iv.length, credential, 0, credential.length);
// decrypt the credential
byte[] decrypted = decryptBlob(credential, secretKey, iv, "AES/CBC/PKCS5Padding");
// assert that the decrypted credential is our public key
assertEquals(keyPair.getPublic().getEncoded(), decrypted);
// verify that the mocks were interacted with appropriately
verify(symmetricKey).getKey();
verify(certificate).getEncoded();
verifyNoMoreInteractions(certificate, symmetricKey);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#
* AbstractAttestationCertificateAuthority(SupplyChainValidationService, PrivateKey,
* X509Certificate, StructConverter, CertificateManager, DeviceRegister, int,
* DeviceManager, DBManager)}.
*
* @throws Exception during subject alternative name checking if cert formatting is bad
*/
@Test
public void testGenerateCredential() throws Exception {
// test variables
final String identityProofLabelString = "label";
byte[] identityProofLabel = identityProofLabelString.getBytes();
byte[] modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus().toByteArray();
X500Principal principal = new X500Principal("CN=TEST, OU=TEST, O=TEST, C=TEST");
int validDays = 1;
// create mocks for testing
IdentityProof identityProof = mock(IdentityProof.class);
AsymmetricPublicKey asymmetricPublicKey = mock(AsymmetricPublicKey.class);
StorePubKey storePubKey = mock(StorePubKey.class);
X509Certificate acaCertificate = mock(X509Certificate.class);
// assign ACA fields
ReflectionTestUtils.setField(aca, "validDays", validDays);
ReflectionTestUtils.setField(aca, "acaCertificate", acaCertificate);
// prepare identity proof interactions
when(identityProof.getLabel()).thenReturn(identityProofLabel);
// prepare other mocks
when(acaCertificate.getSubjectX500Principal()).thenReturn(principal);
when(acaCertificate.getIssuerX500Principal()).thenReturn(principal);
// perform the test
X509Certificate certificate = aca.generateCredential(keyPair.getPublic(),
null,
null,
"exampleIdLabel");
// grab the modulus from the generate certificate
byte[] resultMod = ((RSAPublicKey) certificate.getPublicKey()).getModulus().toByteArray();
// today and tomorrow, when the certificate should be valid for
Calendar today = Calendar.getInstance();
Calendar tomorrow = Calendar.getInstance();
tomorrow.add(Calendar.DATE, 1);
// validate the certificate
assertTrue(certificate.getIssuerX500Principal().toString().contains("CN=TEST"));
assertTrue(certificate.getIssuerX500Principal().toString().contains("OU=TEST"));
assertTrue(certificate.getIssuerX500Principal().toString().contains("O=TEST"));
assertTrue(certificate.getIssuerX500Principal().toString().contains("C=TEST"));
// validate the format of the subject and subject alternative name
assertEquals(certificate.getSubjectX500Principal().getName(), "");
assertEquals(((X500Name) GeneralNames.fromExtensions(((TBSCertificate.getInstance(
certificate.getTBSCertificate()).getExtensions())), Extension.
subjectAlternativeName).getNames()[0].getName()).getRDNs(
IssuedCertificateAttributeHelper.TCPA_AT_TPM_ID_LABEL)[0].getFirst()
.getValue().toString(), "exampleIdLabel");
assertEquals(resultMod, modulus);
// obtain the expiration dates from the certificate
Calendar beforeDate = Calendar.getInstance();
Calendar afterDate = Calendar.getInstance();
beforeDate.setTime(certificate.getNotBefore());
afterDate.setTime(certificate.getNotAfter());
// assert the dates are set correctly
assertEquals(beforeDate.get(Calendar.DATE), today.get(Calendar.DATE));
assertEquals(afterDate.get(Calendar.DATE), tomorrow.get(Calendar.DATE));
// validate mock interactions
verify(acaCertificate).getSubjectX500Principal();
verifyNoMoreInteractions(identityProof, asymmetricPublicKey, storePubKey, acaCertificate);
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#assemblePublicKey(byte[])}.
*/
@Test
public void testAssemblePublicKeyUsingByteArray() {
// obtain the expected modulus from the existing public key
final BigInteger modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus();
// perform test
RSAPublicKey publicKey = (RSAPublicKey) aca.assemblePublicKey(modulus.toByteArray());
// assert that the exponent and the modulus are the same. the exponents should be the well
// known prime, 101
assertTrue(publicKey.getPublicExponent().equals(new BigInteger("010001", 16)));
assertTrue(publicKey.getModulus().equals(modulus));
}
/**
* Tests {@link AbstractAttestationCertificateAuthority#assemblePublicKey(String)}.
*/
@Test
public void testAssemblePublicKeyUsingHexEncodedString() {
// obtain the expected modulus from the existing public key
final BigInteger modulus = ((RSAPublicKey) keyPair.getPublic()).getModulus();
// encode our existing public key into hex
final String modulusString = Hex.encodeHexString(
((RSAPublicKey) keyPair.getPublic()).getModulus().toByteArray());
// perform test
RSAPublicKey publicKey = (RSAPublicKey) aca.assemblePublicKey(modulusString);
// assert that the exponent and the modulus are the same. the exponents should be the well
// known prime, 101.
assertTrue(publicKey.getPublicExponent().equals(new BigInteger("010001", 16)));
assertTrue(publicKey.getModulus().equals(modulus));
}
/**
* Tests parsing the EK from the TPM2 output file.
* @throws URISyntaxException incorrect resource path
* @throws IOException unable to read from file
*/
@Test
public void testParseEk() throws URISyntaxException, IOException {
Path ekPath = Paths.get(getClass().getResource(
EK_PUBLIC_PATH).toURI());
byte[] ekFile = Files.readAllBytes(ekPath);
RSAPublicKey ek = aca.parsePublicKey(ekFile);
assertTrue(ek.getPublicExponent().equals(new BigInteger("010001", 16)));
byte[] mod = ek.getModulus().toByteArray();
// big integer conversion is signed so it can add a 0 byte
if (mod[0] == 0) {
byte[] tmp = new byte[mod.length - 1];
System.arraycopy(mod, 1, tmp, 0, mod.length - 1);
mod = tmp;
}
String hex = HexUtils.byteArrayToHexString(mod);
String realMod = EK_MODULUS_HEX.replaceAll("\\s+", "");
assertEquals(hex, realMod);
}
/**
* Tests parsing the AK public key from the TPM2 output file.
* @throws URISyntaxException incorrect resource path
* @throws IOException unable to read from file
*/
@Test
public void testParseAk() throws URISyntaxException, IOException {
Path akPath = Paths.get(getClass().getResource(
AK_PUBLIC_PATH).toURI());
byte[] akFile = Files.readAllBytes(akPath);
RSAPublicKey ak = aca.parsePublicKey(akFile);
assertTrue(ak.getPublicExponent().equals(new BigInteger("010001", 16)));
byte[] mod = ak.getModulus().toByteArray();
// big integer conversion is signed so it can add a 0 byte
if (mod[0] == 0) {
byte[] tmp = new byte[mod.length - 1];
System.arraycopy(mod, 1, tmp, 0, mod.length - 1);
mod = tmp;
}
String hex = HexUtils.byteArrayToHexString(mod);
String realMod = AK_MODULUS_HEX.replaceAll("\\s+", "");
assertEquals(hex, realMod);
}
/**
* Tests parsing the AK name from the TPM2 output file.
* @throws URISyntaxException incorrect resource path
* @throws IOException unable to read from file
* @throws NoSuchAlgorithmException inavlid algorithm
*/
@Test
public void testGenerateAkName() throws URISyntaxException, IOException,
NoSuchAlgorithmException {
Path akNamePath = Paths.get(getClass().getResource(
AK_NAME_PATH).toURI());
byte[] akNameFileBytes = Files.readAllBytes(akNamePath);
String realHex = HexUtils.byteArrayToHexString(akNameFileBytes);
String realMod = AK_MODULUS_HEX.replaceAll("\\s+", "");
byte[] akName = aca.generateAkName(HexUtils.hexStringToByteArray(realMod));
String hex = HexUtils.byteArrayToHexString(akName);
String realName = AK_NAME_HEX.replaceAll("\\s+", "");
assertEquals(hex, realName);
assertEquals(hex, realHex);
}
/**
* Method to generate a make credential output file for use in manual testing. Feed to
* a TPM 2.0 or emulator using the activate credential command to ensure proper parsing.
* Must be performed manually. To use, copy the TPM's ek and ak into
* HIRS_AttestationCA/src/test/resources/tpm2/test/ and ensure the variables akPubPath
* and ekPubPath are correct. Your output file will be
* HIRS_AttestationCA/src/test/resources/tpm2/test/make.blob and the nonce used will be
* output as HIRS_AttestationCA/src/test/resources/tpm2/test/secret.blob
* @throws URISyntaxException invalid file path
* @throws IOException unable to read file
*/
@Test(enabled = false)
public void testMakeCredential() throws URISyntaxException, IOException {
Path akPubPath = Paths.get(getClass().getResource(
AK_PUBLIC_PATH).toURI());
Path ekPubPath = Paths.get(getClass().getResource(
EK_PUBLIC_PATH).toURI());
byte[] ekPubFile = Files.readAllBytes(ekPubPath);
byte[] akPubFile = Files.readAllBytes(akPubPath);
RSAPublicKey ekPub = aca.parsePublicKey(ekPubFile);
RSAPublicKey akPub = aca.parsePublicKey(akPubFile);
// prepare the nonce and wrap it with keys
byte[] nonce = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
ByteString blob = aca.tpm20MakeCredential(ekPub, akPub, nonce);
Path resources = Paths.get(getClass().getResource(
"/").toURI()).getParent().getParent().getParent().getParent();
Path makeBlob = resources.resolve("src/test/resources/tpm2/test/make.blob");
Files.write(makeBlob, blob.toByteArray());
Path secretPath = resources.resolve("src/test/resources/tpm2/test/secret.blob");
Files.write(secretPath, nonce);
}
/**
* Test helper method that encrypts a blob using the specified transformation and the test key
* pair public key.
*
* @param blob to be encrypted
* @param transformation used by a cipher to encrypt
* @return encrypted blob
* @throws Exception during the encryption process
*/
private byte[] encryptBlob(byte[] blob, String transformation) throws Exception {
// initialize a cipher using the specified transformation
Cipher cipher = Cipher.getInstance(transformation);
// use our generated public key to encrypt
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
// return the cipher text
return cipher.doFinal(blob);
}
/**
* Test helper method that encrypts a blob using a shared key and IV using the specified
* transformation.
*
* @param blob to be encrypted
* @param key shared key
* @param iv to encrypt with
* @param transformation of the encryption cipher
* @return encrypted blob
* @throws Exception
*/
private byte[] encryptBlob(byte[] blob, byte[] key, byte[] iv, String transformation)
throws Exception {
// initialize a cipher using the specified transformation
Cipher cipher = Cipher.getInstance(transformation);
// generate a secret key specification using the key and AES.
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// create IV parameter for key specification
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// encrypt using the key specification with the generated IV
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);
// return the cipher text
return cipher.doFinal(blob);
}
/**
* Test helper method to decrypt blobs.
*
* @param blob to be decrypted
* @return decrypted blob
* @throws Exception
*/
private byte[] decryptBlob(byte[] blob) throws Exception {
// initialize a cipher using the specified transformation
Cipher cipher = Cipher.getInstance(EncryptionScheme.OAEP.toString());
OAEPParameterSpec spec = new OAEPParameterSpec("Sha1", "MGF1",
MGF1ParameterSpec.SHA1, new PSource.PSpecified("TCPA".getBytes()));
// use our generated public key to encrypt
cipher.init(Cipher.PRIVATE_KEY, keyPair.getPrivate(), spec);
// return the cipher text
return cipher.doFinal(blob);
}
/**
* Test helper method that decrypts a blob using a shared key and IV using the specified.
* transformation.
*
* @param blob to be decrypted
* @param key shared key
* @param iv to decrypt with
* @param transformation of the decryption cipher
* @return decrypted blob
* @throws Exception
*/
private byte[] decryptBlob(byte[] blob, byte[] key, byte[] iv, String transformation)
throws Exception {
// initialize a cipher using the specified transformation
Cipher cipher = Cipher.getInstance(transformation);
// generate a secret key specification using the key and AES.
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// create IV parameter for key specification
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// encrypt using the key specification with the generated IV
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
// return the cipher text
return cipher.doFinal(blob);
}
}

View File

@ -0,0 +1,104 @@
package hirs.attestationca;
import org.apache.commons.io.IOUtils;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.FileInputStream;
import java.io.IOException;
import hirs.data.persist.certificate.Certificate;
import hirs.persist.CertificateManager;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Unit tests for {@see CredentialManagementHelper}.
*/
public class CredentialManagementHelperTest {
private CertificateManager certMan;
private static final String EK_HEADER_TRUNCATED
= "/certificates/nuc-1/ek_cert_7_byte_header_removed.cer";
private static final String EK_UNTOUCHED
= "/certificates/nuc-1/ek_cert_untouched.cer";
/**
* Setup mocks.
*/
@BeforeMethod
public void setUp() {
certMan = mock(CertificateManager.class);
}
/**
* Tests exception generated if providing a null cert manager.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processNullCertMan() {
CredentialManagementHelper.storeEndorsementCredential(null, null);
}
/**
* Tests exception generated when providing a null EK byte array.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processNullEndorsementCredential() {
CredentialManagementHelper.storeEndorsementCredential(certMan, null);
}
/**
* Tests exception generated when providing an empty array of bytes as the EK.
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processEmptyEndorsementCredential() {
CredentialManagementHelper.storeEndorsementCredential(certMan, new byte[0]);
}
/**
* Tests processing an invalid EK (too small of an array).
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processInvalidEndorsementCredentialCase1() {
byte[] ekBytes = new byte[] {1};
CredentialManagementHelper.storeEndorsementCredential(certMan, ekBytes);
}
/**
* Tests processing an invalid EK (garbage bytes of a reasonable length).
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void processInvalidEndorsementCredentialCase2() {
byte[] ekBytes = new byte[] {1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0};
CredentialManagementHelper.storeEndorsementCredential(certMan, ekBytes);
}
/**
* Tests processing a valid EK with the 7 byte header in tact.
* @throws IOException if an IO error occurs
*/
@Test
public void parseUntouchedEndorsementCredential() throws IOException {
String path = CredentialManagementHelperTest.class.getResource(EK_UNTOUCHED).getPath();
byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path));
CredentialManagementHelper.storeEndorsementCredential(certMan, ekBytes);
verify(certMan).save(any(Certificate.class));
}
/**
* Tests processing a valid EK with the 7 byte header already stripped.
* @throws IOException if an IO error occurs
*/
@Test
public void parseHeaderTruncatedEndorsementCredential() throws IOException {
String path = CredentialManagementHelperTest.class.getResource(EK_HEADER_TRUNCATED)
.getPath();
byte[] ekBytes = IOUtils.toByteArray(new FileInputStream(path));
CredentialManagementHelper.storeEndorsementCredential(certMan, ekBytes);
verify(certMan).save(any(Certificate.class));
}
}

View File

@ -0,0 +1,204 @@
package hirs.attestationca;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.x509.Extension;
import org.testng.annotations.Test;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
/**
* Tests for {@see IssuedCertificateAttributeHelper}.
*/
public class IssuedCertificateAttributeHelperTest {
private static final String NUC1_EC = "/certificates/nuc-1/tpmcert.pem";
private static final String INTEL_PC = "/certificates/platform_certs_2/"
+ "Intel_pc.pem";
private static final String TEST_HOSTNAME = "box1";
private static final String TPM_MANUFACTURER = "2.23.133.2.1";
private static final String TPM_MODEL = "2.23.133.2.2";
private static final String TPM_VERSION = "2.23.133.2.3";
private static final String TPM_ID_LABEL_OID = "2.23.133.2.15";
private static final String PLATFORM_MANUFACTURER = "2.23.133.2.4";
private static final String PLATFORM_MODEL = "2.23.133.2.5";
private static final String PLATFORM_VERSION = "2.23.133.2.6";
/**
* Test that provide a null host name and is rejected.
* @throws IOException an IO error occurs
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void rejectNullHostName() throws IOException {
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(null, null, "");
}
/**
* Test that subject alt name can be built without an EC or PC.
* @throws IOException an IO error occurs
*/
@Test
public void buildAttributesNoEndorsementNoPlatform() throws IOException {
Extension subjectAlternativeName =
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
null, null, TEST_HOSTNAME);
Map<String, String> subjectAlternativeNameAttrMap = getSubjectAlternativeNameAttributes(
subjectAlternativeName);
assertNull(subjectAlternativeNameAttrMap.get(TPM_MANUFACTURER));
assertNull(subjectAlternativeNameAttrMap.get(TPM_MODEL));
assertNull(subjectAlternativeNameAttrMap.get(TPM_VERSION));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_MANUFACTURER));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_MODEL));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_VERSION));
assertEquals(subjectAlternativeNameAttrMap.get(TPM_ID_LABEL_OID), TEST_HOSTNAME);
}
/**
* Test that subject alt name can be built with an EC but no PC.
* @throws IOException an IO error occurs
* @throws URISyntaxException unrecognized URI for EC Path
*/
@Test
public void buildAttributesEndorsementNoPlatform() throws IOException, URISyntaxException {
Path endorsementCredentialPath = Paths.get(getClass().getResource(
NUC1_EC).toURI());
EndorsementCredential endorsementCredential = new EndorsementCredential(
endorsementCredentialPath);
Extension subjectAlternativeName =
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
endorsementCredential, null, TEST_HOSTNAME);
Map<String, String> subjectAlternativeNameAttrMap = getSubjectAlternativeNameAttributes(
subjectAlternativeName);
assertEquals(subjectAlternativeNameAttrMap.get(TPM_MANUFACTURER),
endorsementCredential.getManufacturer());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_MODEL),
endorsementCredential.getModel());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_VERSION),
endorsementCredential.getVersion());
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_MANUFACTURER));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_MODEL));
assertNull(subjectAlternativeNameAttrMap.get(PLATFORM_VERSION));
assertEquals(subjectAlternativeNameAttrMap.get(TPM_ID_LABEL_OID),
TEST_HOSTNAME);
}
/**
* Test that subject alt name can be built with an PC but no EC.
* @throws IOException an IO error occurs
* @throws URISyntaxException unrecognized URI for PC Path
*/
@Test
public void buildAttributesPlatformNoEndorsement() throws IOException, URISyntaxException {
Path platformCredentialPath = Paths.get(getClass().getResource(
INTEL_PC).toURI());
PlatformCredential platformCredential = new PlatformCredential(
platformCredentialPath);
List<PlatformCredential> platformCredentialList = new ArrayList<>();
platformCredentialList.add(platformCredential);
Extension subjectAlternativeName =
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
null, platformCredentialList, TEST_HOSTNAME);
Map<String, String> subjectAlternativeNameAttrMap = getSubjectAlternativeNameAttributes(
subjectAlternativeName);
assertNull(subjectAlternativeNameAttrMap.get(TPM_MANUFACTURER));
assertNull(subjectAlternativeNameAttrMap.get(TPM_MODEL));
assertNull(subjectAlternativeNameAttrMap.get(TPM_VERSION));
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_MANUFACTURER),
platformCredential.getManufacturer());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_MODEL),
platformCredential.getModel());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_VERSION),
platformCredential.getVersion());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_ID_LABEL_OID),
TEST_HOSTNAME);
}
/**
* Test that subject alt name can be built with a PC and an EC.
* @throws IOException an IO error occurs
* @throws URISyntaxException unrecognized URI for EC or PC Path
*/
@Test
public void buildAttributesPlatformAndEndorsement() throws IOException, URISyntaxException {
Path endorsementCredentialPath = Paths.get(getClass().getResource(
NUC1_EC).toURI());
Path platformCredentialPath = Paths.get(getClass().getResource(
INTEL_PC).toURI());
EndorsementCredential endorsementCredential = new EndorsementCredential(
endorsementCredentialPath);
PlatformCredential platformCredential = new PlatformCredential(
platformCredentialPath);
List<PlatformCredential> platformCredentialList = new ArrayList<>();
platformCredentialList.add(platformCredential);
Extension subjectAlternativeName =
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
endorsementCredential, platformCredentialList, TEST_HOSTNAME);
Map<String, String> subjectAlternativeNameAttrMap = getSubjectAlternativeNameAttributes(
subjectAlternativeName);
assertEquals(subjectAlternativeNameAttrMap.get(TPM_MANUFACTURER),
endorsementCredential.getManufacturer());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_MODEL),
endorsementCredential.getModel());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_VERSION),
endorsementCredential.getVersion());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_MANUFACTURER),
platformCredential.getManufacturer());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_MODEL),
platformCredential.getModel());
assertEquals(subjectAlternativeNameAttrMap.get(PLATFORM_VERSION),
platformCredential.getVersion());
assertEquals(subjectAlternativeNameAttrMap.get(TPM_ID_LABEL_OID),
TEST_HOSTNAME);
}
private Map<String, String> getSubjectAlternativeNameAttributes(
Extension subjectAlternativeName) {
Map<String, String> subjectAlternativeNameAttrMap = new HashMap<>();
DLSequence dlSequence = (DLSequence) subjectAlternativeName.getParsedValue();
DERTaggedObject derTaggedObject = (DERTaggedObject) dlSequence.getObjectAt(0);
DERSequence derSequence = (DERSequence) derTaggedObject.getObject();
Enumeration enumeration = derSequence.getObjects();
while (enumeration.hasMoreElements()) {
DERSet set = (DERSet) enumeration.nextElement();
DERSequence innerDerSequence = (DERSequence) set.getObjectAt(0);
subjectAlternativeNameAttrMap.put(innerDerSequence.getObjectAt(0).toString(),
innerDerSequence.getObjectAt(1).toString());
}
return subjectAlternativeNameAttrMap;
}
}

View File

@ -0,0 +1,736 @@
package hirs.attestationca.service;
import hirs.appraiser.SupplyChainAppraiser;
import hirs.data.persist.AppraisalStatus;
import hirs.data.persist.Device;
import hirs.data.persist.DeviceGroup;
import hirs.data.persist.DeviceInfoReport;
import hirs.data.persist.SpringPersistenceTest;
import hirs.data.persist.SupplyChainPolicy;
import hirs.data.persist.SupplyChainValidation;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import hirs.data.persist.certificate.DeviceAssociatedCertificate;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.persist.AppraiserManager;
import hirs.persist.CertificateManager;
import hirs.persist.CrudManager;
import hirs.persist.DBCertificateManager;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBDeviceManager;
import hirs.persist.DeviceGroupManager;
import hirs.persist.DeviceManager;
import hirs.persist.PolicyManager;
import hirs.validation.CredentialValidator;
import hirs.validation.SupplyChainCredentialValidator;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import static hirs.data.persist.AppraisalStatus.Status.FAIL;
import static hirs.data.persist.AppraisalStatus.Status.PASS;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Tests for the {@see SupplyChainValidationServiceImpl}.
*/
public class SupplyChainValidationServiceImplTest extends SpringPersistenceTest {
private static final String NUC1_EC = "/certificates/nuc-1/tpmcert.pem";
private static final String STM_ROOT_CA = "/certificates/stMicroCaCerts/stmtpmekroot.crt";
private static final String GS_ROOT_CA = "/certificates/stMicroCaCerts/gstpmroot.crt";
private static final String INTEL_CA = "/certificates/IntelSigningKey_20April2017.pem";
private static final String NUC_PC = "/certificates/platform_certs_2/Intel_pc.pem";
private static final String STM_TPM_EK_INTERMEDIATE_CA_02 =
"/certificates/STM TPM EK Intermediate CA.CER";
private static final String NUC_EC = "/certificates/nuc_ec.pem";
@Mock
private PolicyManager policyManager;
@Mock
private AppraiserManager appraiserManager;
@Mock
private CertificateManager certificateManager;
@Mock
private CredentialValidator supplyChainCredentialValidator;
@Mock
private CrudManager<SupplyChainValidationSummary> supplyChainValidationSummaryDBManager;
@InjectMocks
private SupplyChainValidationServiceImpl service;
// mocked
private SupplyChainPolicy policy;
private PlatformCredential pc;
private EndorsementCredential ec;
private HashSet<PlatformCredential> pcs;
private Device device;
/**
* Sets up the mocks.
*
@throws IOException won't actually throw, the method is being mocked instead of actually
* called
*/
@BeforeMethod
public void beforeClass() throws IOException {
MockitoAnnotations.initMocks(this);
device = mock(Device.class);
SupplyChainAppraiser appraiser = mock(SupplyChainAppraiser.class);
policy = mock(SupplyChainPolicy.class);
when(appraiserManager.getAppraiser(SupplyChainAppraiser.NAME)).thenReturn(appraiser);
when(policyManager.getDefaultPolicy(appraiser)).thenReturn(policy);
// mock endorsement credential
ec = mock(EndorsementCredential.class);
when(ec.getEncodedPublicKey()).thenReturn(new byte[] {0x0});
when(ec.getIssuerOrganization()).thenReturn("STMicroelectronics NV");
Set<Certificate> resultEcs = new HashSet<>();
resultEcs.add(ec);
// mock platform credential
X509Certificate cert = mock(X509Certificate.class);
pc = mock(PlatformCredential.class);
when(pc.getId()).thenReturn(UUID.randomUUID());
when(pc.getX509Certificate()).thenReturn(cert);
when(pc.getSerialNumber()).thenReturn(BigInteger.ONE);
when(pc.getIssuerOrganization()).thenReturn("STMicroelectronics NV");
when(ec.getSubjectOrganization()).thenReturn("STMicroelectronics NV");
pcs = new HashSet<PlatformCredential>();
pcs.add(pc);
Set<Certificate> resultPcs = new HashSet<>();
resultPcs.add(pc);
// mock credential retrieval
when(certificateManager.get(any(EndorsementCredential.Selector.class)))
.thenReturn(resultEcs);
when(certificateManager.get(any(PlatformCredential.Selector.class)))
.thenReturn(resultPcs);
when(certificateManager.get(any(CertificateAuthorityCredential.Selector.class)))
.thenReturn(Collections.emptySet());
}
/**
* Remove test certificates and close the session factory.
*/
@AfterMethod
public void teardown() {
DBCertificateManager certMan = new DBCertificateManager(sessionFactory);
DBDeviceManager deviceMan = new DBDeviceManager(sessionFactory);
DBDeviceGroupManager groupMan = new DBDeviceGroupManager(sessionFactory);
certMan.deleteAll();
deviceMan.deleteAll();
groupMan.deleteAll();
}
/**
* All validations enabled, all pass.
*/
@Test
public final void testFullSuccessfulValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), PASS);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
// verify the certs were updated with the test device object and saved in the cert man
ArgumentCaptor<DeviceAssociatedCertificate> certificatesCaptor
= ArgumentCaptor.forClass(DeviceAssociatedCertificate.class);
verify(certificateManager, times(3)).update(certificatesCaptor.capture());
List<DeviceAssociatedCertificate> certificateArgs = certificatesCaptor.getAllValues();
for (DeviceAssociatedCertificate certArg : certificateArgs) {
verify(certArg, atLeast(1)).setDevice(device);
}
}
/**
* All validations enabled, fail EC.
*/
@Test
public final void testFailEcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), any(Boolean.class));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), FAIL);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* All validations enabled, fail Pc Cert.
*/
@Test
public final void testFailPcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator)
.validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), FAIL);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* All validations enabled, Pc Attrib. fails.
*/
@Test
public final void testFailPcAttributeValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), FAIL);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* Ec not enabled, all others pass.
*/
@Test
public final void testNoEcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(false);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), PASS);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* Pc cert not enabled, all others pass.
*/
@Test
public final void testNoPcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(false);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), PASS);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* Pc attrib not enabled, all others pass.
*/
@Test
public final void testNoPcAttributeValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(true);
when(policy.isPcAttributeValidationEnabled()).thenReturn(false);
when(policy.isExpiredCertificateValidationEnabled()).thenReturn(true);
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validateEndorsementCredential(eq(ec), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(PASS, "")).when(supplyChainCredentialValidator).
validatePlatformCredential(eq(pc), any(KeyStore.class), eq(true));
doReturn(new AppraisalStatus(FAIL, "")).when(supplyChainCredentialValidator).
validatePlatformCredentialAttributes(eq(pc), any(DeviceInfoReport.class),
any(EndorsementCredential.class));
Assert.assertEquals(service.validateSupplyChain(ec, pcs,
device).getOverallValidationResult(), PASS);
verify(supplyChainValidationSummaryDBManager).save(any(SupplyChainValidationSummary.class));
}
/**
* All enabled, EC is null.
*/
@Test
public final void testNullEcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(true);
Assert.assertEquals(service.validateSupplyChain(null, pcs,
device).getOverallValidationResult(), FAIL);
}
/**
* All enabled, PC is null. Then PC set is empty.
*/
@Test
public final void testNullPcValidation() {
when(policy.isEcValidationEnabled()).thenReturn(false);
when(policy.isPcValidationEnabled()).thenReturn(true);
Assert.assertEquals(service.validateSupplyChain(ec, null,
device).getOverallValidationResult(), FAIL);
final HashSet<PlatformCredential> emptySet = new HashSet<>();
Assert.assertEquals(service.validateSupplyChain(ec, emptySet,
device).getOverallValidationResult(), FAIL);
}
/**
* All enabled, PC is null. Then PC set is empty.
*/
@Test
public final void testNullPcAttributeValidation() {
when(policy.isEcValidationEnabled()).thenReturn(false);
when(policy.isPcValidationEnabled()).thenReturn(false);
when(policy.isPcAttributeValidationEnabled()).thenReturn(true);
Assert.assertEquals(service.validateSupplyChain(ec, null,
device).getOverallValidationResult(), FAIL);
final HashSet<PlatformCredential> emptySet = new HashSet<>();
Assert.assertEquals(service.validateSupplyChain(ec, emptySet,
device).getOverallValidationResult(), FAIL);
}
/**
* Puts an EC, STM CA, and GS CA in the DB, attempts to retrieve the CAs from the EC.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetCaChain() throws URISyntaxException, IOException, KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator
);
CertificateAuthorityCredential globalSignCaCert = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
GS_ROOT_CA).toURI())));
CertificateAuthorityCredential rootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI()))
);
EndorsementCredential endorsementCredential = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC1_EC).toURI())));
realCertMan.save(endorsementCredential);
realCertMan.save(rootCa);
realCertMan.save(globalSignCaCert);
KeyStore ks = mostlyMockedService.getCaChain(endorsementCredential);
String stmCaAlias = rootCa.getId().toString();
String gsCaAlias = globalSignCaCert.getId().toString();
Assert.assertNotNull(ks.getCertificate(stmCaAlias));
Assert.assertNotNull(ks.getCertificate(gsCaAlias));
Assert.assertEquals(ks.size(), 2);
realCertMan.delete(endorsementCredential);
realCertMan.delete(rootCa);
realCertMan.delete(globalSignCaCert);
}
/**
* Puts an EC, and STM CA in the DB, attempts to retrieve the CAs from the EC. The STM CA
* points to a GS CA that is not present.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetNotFullCaChain() throws URISyntaxException, IOException,
KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator
);
CertificateAuthorityCredential rootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI()))
);
EndorsementCredential endorsementCredential = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC1_EC).toURI())));
realCertMan.save(endorsementCredential);
realCertMan.save(rootCa);
KeyStore ks = mostlyMockedService.getCaChain(endorsementCredential);
String stmCaAlias = rootCa.getId().toString();
Assert.assertNotNull(ks.getCertificate(stmCaAlias));
Assert.assertEquals(ks.size(), 1);
realCertMan.delete(endorsementCredential);
realCertMan.delete(rootCa);
}
/**
* Puts an EC in the DB, attempts to retrieve the CA from the EC.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetEmptyCaChain() throws URISyntaxException, IOException,
KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator
);
EndorsementCredential endorsementCredential = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC1_EC).toURI())));
realCertMan.save(endorsementCredential);
KeyStore ks = mostlyMockedService.getCaChain(endorsementCredential);
Assert.assertEquals(ks.size(), 0);
realCertMan.delete(endorsementCredential);
}
/**
* Puts an EC, STM CA, GS CA, and an Intel CA in the DB, attempts to retrieve the CAs
* from the EC.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetCaChainWithExtraCerts() throws URISyntaxException, IOException,
KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator
);
CertificateAuthorityCredential globalSignCaCert = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
GS_ROOT_CA).toURI())));
CertificateAuthorityCredential rootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI()))
);
CertificateAuthorityCredential intelCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
INTEL_CA).toURI()))
);
EndorsementCredential endorsementCredential = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC1_EC).toURI())));
realCertMan.save(endorsementCredential);
realCertMan.save(rootCa);
realCertMan.save(globalSignCaCert);
realCertMan.save(intelCa);
KeyStore ks = mostlyMockedService.getCaChain(endorsementCredential);
String stmCaAlias = rootCa.getId().toString();
String gsCaAlias = globalSignCaCert.getId().toString();
Assert.assertNotNull(ks.getCertificate(stmCaAlias));
Assert.assertNotNull(ks.getCertificate(gsCaAlias));
Assert.assertEquals(ks.size(), 2);
realCertMan.delete(endorsementCredential);
realCertMan.delete(rootCa);
realCertMan.delete(globalSignCaCert);
realCertMan.delete(intelCa);
}
/**
* Puts an Intel PC and Intel CA in the DB, attempts to retrieve the CA from the PC.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetPcCaChain() throws URISyntaxException, IOException, KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator
);
CertificateAuthorityCredential intelCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
INTEL_CA).toURI()))
);
PlatformCredential platformCredential = new PlatformCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC_PC).toURI())));
realCertMan.save(platformCredential);
realCertMan.save(intelCa);
KeyStore ks = mostlyMockedService.getCaChain(platformCredential);
String intelCaAlias = intelCa.getId().toString();
Assert.assertNotNull(ks.getCertificate(intelCaAlias));
Assert.assertEquals(ks.size(), 1);
realCertMan.delete(platformCredential);
realCertMan.delete(intelCa);
}
/**
* Puts an Intel PC, STM CA, and GS CA in the DB, attempts to retrieve the CAs from the PC. None
* should match.
* @throws URISyntaxException failed to parse certificate file location.
* @throws IOException couldn't create certificates from file.
* @throws KeyStoreException was unable to retrieve keystore.
*/
@Test
public final void testGetPcCaChainNoMatches() throws URISyntaxException, IOException,
KeyStoreException {
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
// the main service in this class only uses mocked managers, we need a real DB certificate
// manager for this test, so we make a second service.
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
supplyChainValidationSummaryDBManager,
supplyChainCredentialValidator
);
CertificateAuthorityCredential globalSignCaCert = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
GS_ROOT_CA).toURI())));
CertificateAuthorityCredential rootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI()))
);
PlatformCredential platformCredential = new PlatformCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
NUC_PC).toURI())));
realCertMan.save(platformCredential);
realCertMan.save(rootCa);
realCertMan.save(globalSignCaCert);
KeyStore ks = mostlyMockedService.getCaChain(platformCredential);
Assert.assertEquals(ks.size(), 0);
realCertMan.delete(platformCredential);
realCertMan.delete(rootCa);
realCertMan.delete(globalSignCaCert);
}
/**
* Puts an STM intermediate CA, STM 'root' CA, and GlobalSign root CA into the in-memory
* database, and then runs supply chain validation on a given endorsement credential.
*
* @throws URISyntaxException if building the path to a certificate resource fails
* @throws IOException if there is a problem deserializing a certificate
*/
@Test
public void testVerifyEcAgainstCaChain() throws URISyntaxException, IOException {
when(policy.isEcValidationEnabled()).thenReturn(true);
when(policy.isPcValidationEnabled()).thenReturn(false);
when(policy.isPcAttributeValidationEnabled()).thenReturn(false);
CertificateManager realCertMan = new DBCertificateManager(sessionFactory);
Device storedDevice = getStoredTestDevice();
SupplyChainValidationServiceImpl mostlyMockedService = new SupplyChainValidationServiceImpl(
policyManager,
appraiserManager,
realCertMan,
supplyChainValidationSummaryDBManager,
new SupplyChainCredentialValidator()
);
CertificateAuthorityCredential stmEkRootCa = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_ROOT_CA).toURI())));
CertificateAuthorityCredential stmTpmEkIntermediateCA = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
STM_TPM_EK_INTERMEDIATE_CA_02).toURI())));
CertificateAuthorityCredential globalSignTpmRoot = new CertificateAuthorityCredential(
Files.readAllBytes(Paths.get(getClass().getResource(
GS_ROOT_CA).toURI()))
);
realCertMan.save(stmTpmEkIntermediateCA);
realCertMan.save(globalSignTpmRoot);
realCertMan.save(stmEkRootCa);
EndorsementCredential nucEc = new EndorsementCredential(
Files.readAllBytes(Paths.get(getClass().getResource(NUC_EC).toURI()))
);
realCertMan.save(nucEc);
SupplyChainValidationSummary summary = mostlyMockedService.validateSupplyChain(
nucEc, Collections.emptySet(), storedDevice
);
Assert.assertEquals(summary.getOverallValidationResult(), PASS);
for (SupplyChainValidation validation : summary.getValidations()) {
Assert.assertEquals(
validation.getValidationType(),
SupplyChainValidation.ValidationType.ENDORSEMENT_CREDENTIAL
);
}
// verify the EC was updated with the test device object and saved in the cert man
EndorsementCredential updatedStoredEc =
EndorsementCredential.select(realCertMan).bySerialNumber(nucEc.getSerialNumber())
.getCertificate();
Assert.assertEquals(updatedStoredEc.getDevice().getId(), storedDevice.getId());
realCertMan.delete(stmTpmEkIntermediateCA);
realCertMan.delete(globalSignTpmRoot);
realCertMan.delete(stmEkRootCa);
realCertMan.delete(nucEc);
}
private Device getStoredTestDevice() {
DeviceManager deviceManager = new DBDeviceManager(sessionFactory);
DeviceGroupManager deviceGroupManager = new DBDeviceGroupManager(sessionFactory);
DeviceGroup testGroup = new DeviceGroup("group1");
Device testDevice = new Device("SCVSI-test");
testDevice.setDeviceGroup(deviceGroupManager.saveDeviceGroup(testGroup));
return deviceManager.saveDevice(testDevice);
}
}

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIEJVDCpzANBgkqhkiG9w0BAQUFADCBhzELMAkGA1UEBhMCVVMxCzAJBgNV
BAgTAkNBMRQwEgYDVQQHEwtTYW50YSBDbGFyYTEaMBgGA1UEChMRSW50ZWwgQ29ycG9yYXRpb24x
ITAfBgNVBAsTGFRyYW5zcGFyZW50IFN1cHBseSBDaGFpbjEWMBQGA1UEAxMNd3d3LmludGVsLmNv
bTAeFw0xNzA0MTkwMDAyMTBaFw0zNzEwMzEwMDAyMTBaMIGHMQswCQYDVQQGEwJVUzELMAkGA1UE
CBMCQ0ExFDASBgNVBAcTC1NhbnRhIENsYXJhMRowGAYDVQQKExFJbnRlbCBDb3Jwb3JhdGlvbjEh
MB8GA1UECxMYVHJhbnNwYXJlbnQgU3VwcGx5IENoYWluMRYwFAYDVQQDEw13d3cuaW50ZWwuY29t
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo7Cu8Y3uWKoTQW/RnmNJG5h3PlYdvE2B
v0c2+WUz0AprrVMpUvbJaNoMx47ev9CYvoxuJU0t9JjMm2i1u6Ol6VlK5yAWqr/qySvkryKTUuSx
7sOtkayF4LqD16LlTIAmUhC0KabbrEitpBa1BtKsy8yhGjbm+bYs0D19jjqHH9rOT3I8j04tc5a4
r86dPuCZX8RJTV8tZd51ULFhl+70rIwPY/Ecbl5lzx786v9xW3XnC4/XplvMJXw2wkUo8G98qyeD
Odvha4Y49NswlqyUHnAlk+n4bIF7cvSDi+vb4YRaV2g5u2K3290ePUcNyzGTYgdpBnvOQlna/IZ0
NifXQQIDAQABoyEwHzAdBgNVHQ4EFgQUIQWBMO10djQycITwJ5XfZw6L13MwDQYJKoZIhvcNAQEF
BQADggEBADgcp+GI5S6HSVhQD7wyqKIBCLUk5nC0FzE0W1vBuLrwCagXHXwMC09uDXOXtfNOoQVZ
duS3oaju5i40lSbFmp7V2hewQo6vlum/M05Lg/O7vndKxnKss/PeT5jMyjJ4t00HQ3KEm2aZL1BR
AJZjbH+hZ08bh5pY5uP7husjjcur0xqhFw9Afq4SUbXtA47QBaUuJTtBwTf2r/Nesq4a6zRAtqF/
gmGD8VOSahBrtuIS30GRgcpsk6kKO4fLCvCZwyJ2/Mn+ySMB3+G6XqgDekkykCnnvMVUr+nzasug
d/4I62L3eZ2j5Xpa3O1kFZ+zIMrbLRh7Bu8OdjFdvodwmRU=
-----END CERTIFICATE-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEXjCCA0agAwIBAgIUS5gujeW5kYvYdMJZlIUT6s3F0cwwDQYJKoZIhvcNAQEF
BQAwVTELMAkGA1UEBhMCQ0gxHjAcBgNVBAoTFVNUTWljcm9lbGVjdHJvbmljcyBO
VjEmMCQGA1UEAxMdU1RNIFRQTSBFSyBJbnRlcm1lZGlhdGUgQ0EgMDIwHhcNMTQw
MjIyMDAwMDAwWhcNMjQwMjIyMDAwMDAwWjAAMIIBNzAiBgkqhkiG9w0BAQcwFaIT
MBEGCSqGSIb3DQEBCQQEVENQQQOCAQ8AMIIBCgKCAQEAsdTxu5pRjEOgA0tCNYgn
NmAqLzIxBTBft4pMBGdEk922dvBLvQySN13YnvVF6FnYCc0Y+5hSAZiRCcXpr/M3
6wx5YkePCPss06KQMujy3X9jwxTU0cDbKTjKCmFpQqCqiGIk2f7mss8yIABlwT3R
cBBbcDpGn2wYi5s9UhUfCOQ6D7qEPKJEi5IQC7/oyu5zT5FMUANdsebxrYpALcKK
8/mp5Rwj+xmaAg/+OC9jIeFGLYYu/hQr/1BPYSVicfuIFdc/0VzyJO5KMRozvV3I
2dbzQwqUD4xUxPR+f7VC+3p641Mb7WobIZH7wJm2k0M8HWeErytA66WtAoueU89O
iQIDAQABo4IBZDCCAWAwHwYDVR0jBBgwFoAUVx+Aa0fM55v6NZR87Yi40QBa4J4w
QgYDVR0gBDswOTA3BgRVHSAAMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cuc3Qu
Y29tL1RQTS9yZXBvc2l0b3J5LzBVBgNVHREBAf8ESzBJpEcwRTEWMBQGBWeBBQIB
DAtpZDo1MzU0NEQyMDEXMBUGBWeBBQICDAxTVDMzWlAyNFBWU1AxEjAQBgVngQUC
AwwHaWQ6MEQwQzB/BgNVHQkEeDB2MBYGBWeBBQIQMQ0wCwwDMS4yAgECAgF0MCAG
BWeBBQISMRcwFQIBAAEB/6ADCgEBoQMKAQCiAwoBADA6BgNVBTQxMzAkMCIGCSqG
SIb3DQEBBzAVohMwEQYJKoZIhvcNAQEJBARUQ1BBMAswCQYFKw4DAhoFADAMBgNV
HRMBAf8EAjAAMBMGA1UdJQEB/wQJMAcGBWeBBQgBMA0GCSqGSIb3DQEBBQUAA4IB
AQAb50G/d9D18ahy6RScXObaazgrNZHcF0otH9W1uJzXgSQPjFFYbHAh2+EGI8uD
90Hj9XgZYmcGv0pUHcFw7msNamr3c/Or8+pLPnu5OZtr4jCEZ7/Z75v0Z825Ov8R
N+JIxB9RT0Yd3KAPQsp4d45NHWOPBQPgBi/pW/eJqPO2MJD0uraRqAlNrUD3ppc7
xxsmOoOhyUFcs14KyrgIWNazx+4EElAKU3PthU70cszFAQM2hw/EYBfRwQ5rVZd7
V2x9hMC4POgACE6gVIDV/mHoZe6AfGQKveblJEX9gOccI28vnT14d0CwhN/SvgZF
JigA9V7w26ecFRWXpm79utMU
-----END CERTIFICATE-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEXjCCA0agAwIBAgIUBwCBhWf/NXkWkNLUBJRd9WsObccwDQYJKoZIhvcNAQEF
BQAwVTELMAkGA1UEBhMCQ0gxHjAcBgNVBAoTFVNUTWljcm9lbGVjdHJvbmljcyBO
VjEmMCQGA1UEAxMdU1RNIFRQTSBFSyBJbnRlcm1lZGlhdGUgQ0EgMDIwHhcNMTQw
MjIzMDAwMDAwWhcNMjQwMjIzMDAwMDAwWjAAMIIBNzAiBgkqhkiG9w0BAQcwFaIT
MBEGCSqGSIb3DQEBCQQEVENQQQOCAQ8AMIIBCgKCAQEAvcrvqYUTomoUC5zk5Jhd
myVzoEe94eXX1YFHyElCCpLM4/86ZbADKTHeGwygR4AWClb0Jmmloj+aIRUY3pZD
2GVxDnmD9CBS+60doM1cN0+D01hhg7J/dnaigAbFxPZauSyV9XfTqq1MQlxWpUEf
J4IALc+MhVd0kqSzzqSDxoneu83w1Ssvmah73wqWpansqQRYr1D7ABbkvouO56iu
4z6UditUSbrk3FrZBs+e73tzy9OAzQBg617kU+BKhHCRuRIPYk3tPXHq53Y7Jwvf
CkiEVWAU+MEMZJc/RRIOnWdSdDMxHZVnaxywrC8KUKZ1G3id/GVJfeivPxZVRBdh
sQIDAQABo4IBZDCCAWAwHwYDVR0jBBgwFoAUVx+Aa0fM55v6NZR87Yi40QBa4J4w
QgYDVR0gBDswOTA3BgRVHSAAMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cuc3Qu
Y29tL1RQTS9yZXBvc2l0b3J5LzBVBgNVHREBAf8ESzBJpEcwRTEWMBQGBWeBBQIB
DAtpZDo1MzU0NEQyMDEXMBUGBWeBBQICDAxTVDMzWlAyNFBWU1AxEjAQBgVngQUC
AwwHaWQ6MEQwQzB/BgNVHQkEeDB2MBYGBWeBBQIQMQ0wCwwDMS4yAgECAgF0MCAG
BWeBBQISMRcwFQIBAAEB/6ADCgEBoQMKAQCiAwoBADA6BgNVBTQxMzAkMCIGCSqG
SIb3DQEBBzAVohMwEQYJKoZIhvcNAQEJBARUQ1BBMAswCQYFKw4DAhoFADAMBgNV
HRMBAf8EAjAAMBMGA1UdJQEB/wQJMAcGBWeBBQgBMA0GCSqGSIb3DQEBBQUAA4IB
AQAAbZng7i2L22p05GpbURYk6o7bYH3LZ+nusEGvi0tRkpqr9Qc8vMp8fgYQMaZV
8QiDa5JfYD3vzOjQBRvUdqz8UrzemsuErk4w3yzsBh2lIY54jcXWmJFVk4HVp2wV
xL5EysIII9Fkt2gfcoPSGyIDX4p83Vou5nhNOQPowahMuS6BUfcBKzMM7pK40GUj
N+cijK61zPkvAQArEkAnVNuTxvLS41WW3x1kTtkLUPuTh7SynNAYwoVfl19uNPOs
UTxDrFA7But7Vo0xoj+zSBQqzk0Gp3Pldw6mOUc3uI1UBmVtQGRy7cgsbLJ3bu/6
fLuuAG5/ywVpo4MiG+PkFYXh
-----END CERTIFICATE-----

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,93 @@
apply plugin: 'checkstyle'
apply plugin: 'findbugs'
apply plugin: 'java'
apply plugin: 'jacoco'
apply plugin: 'pmd'
apply plugin: 'war'
sourceCompatibility = 1.8
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
providedCompile libs.servlet_api
compile(project(':HIRS_Utils')) {
exclude module: "javassist"
}
compile(project(':HIRS_AttestationCA'))
compile libs.commons_io
compile libs.commons_lang
compile libs.commons_upload
compile libs.gson
compile libs.guava // for com.google.common
compile libs.hibernate
compile libs.http
compile libs.jstl
compile libs.log4j2
compile libs.log4j2_web
compile libs.servlet_api
compile libs.spring_webmvc
compile 'org.springframework:spring-context-support:4.2.1.RELEASE'
compile 'org.hibernate:hibernate-validator:5.3.4.Final'
compileOnly libs.checkstyle
compileOnly libs.findbugs
runtime fileTree(dir: 'build/plugins', include: ['*.jar'])
testCompile 'org.hamcrest:hamcrest-all:1.3'
// override the servlet API for testing. Required for Spring Integration tests
testCompile 'javax.servlet:javax.servlet-api:3.1.0'
testCompile libs.hsqldb
testCompile libs.spring_test
testCompile libs.testng
testCompile libs.mockito
testCompile libs.testng
testCompile 'org.skyscreamer:jsonassert:1.2.3' // for .andExpect(content().json(json))
testRuntime 'com.jayway.jsonpath:json-path:2.1.0'
}
test {
useTestNG()
}
copyVersion.dependsOn compileJava
war.dependsOn copyVersion
war.dependsOn addPlugins
war {
from(buildDir) {
include 'VERSION'
into 'WEB-INF/classes'
}
archiveName = 'HIRS_AttestationCAPortal.war'
}
ext.configDir = new File(projectDir, 'config')
ext.checkstyleConfigDir = "$configDir/checkstyle"
checkstyle {
toolVersion = '5.7'
configFile = checkstyleConfigFile
configProperties.put('basedir', checkstyleConfigDir)
ignoreFailures = false
showViolations = true
}
ext.findbugsConfigDir = "$configDir/findbugs"
findbugs {
toolVersion = '3.0.0'
ignoreFailures = false
effort = 'max'
excludeFilter = new File(findbugsConfigDir, 'suppressions.xml')
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress checks="MagicNumber" files=".*[/\\]src[/\\]test[/\\]+" />
</suppressions>

View File

@ -0,0 +1,3 @@
<FindBugsFilter>
</FindBugsFilter>

Binary file not shown.

View File

@ -0,0 +1,172 @@
package hirs.attestationca.portal.datatables;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
/**
* Java representation of a jQuery DataTables Column.
*/
public class Column {
/**
* Default constructor.
*/
public Column() {
}
/**
* Constructor.
* @param data the data
* @param name the name
* @param searchable true if searchable
* @param orderable true if orderable
* @param search the Search structure.
*/
public Column(final String data, final String name, final boolean searchable,
final boolean orderable, final Search search) {
this.data = data;
this.name = name;
this.searchable = searchable;
this.orderable = orderable;
this.search = search;
}
/**
* Column's data source.
*
* @see http://datatables.net/reference/option/columns.data
*/
@NotBlank
private String data;
/**
* Column's name.
*
* @see http://datatables.net/reference/option/columns.name
*/
private String name;
/**
* Flag to indicate if this column is searchable (true) or not (false).
*
* @see http://datatables.net/reference/option/columns.searchable
*/
@NotNull
private boolean searchable;
/**
* Flag to indicate if this column is orderable (true) or not (false).
*
* @see http://datatables.net/reference/option/columns.orderable
*/
@NotNull
private boolean orderable;
/**
* Search value to apply to this specific column.
*/
@NotNull
private Search search;
/**
*
* @return the data
*/
public String getData() {
return data;
}
/**
* Sets the data.
* @param data the data
*/
public void setData(final String data) {
this.data = data;
}
/**
*
* @return the name
*/
public String getName() {
return name;
}
/**
* Sets the name.
* @param name the name
*/
public void setName(final String name) {
this.name = name;
}
/**
* Gets the searchable flag.
* @return true if searchable, false otherwise
*/
public boolean isSearchable() {
return searchable;
}
/**
* Sets the searchable flag.
* @param searchable true if searchable, false otherwise
*/
public void setSearchable(final boolean searchable) {
this.searchable = searchable;
}
/**
*
* @return true if orderable, false otherwise
*/
public boolean isOrderable() {
return orderable;
}
/**
* Sets the orderable flag.
* @param orderable true if orderable, false otherwise
*/
public void setOrderable(final boolean orderable) {
this.orderable = orderable;
}
/**
*
* @return the search
*/
public Search getSearch() {
return search;
}
/**
* Sets the search.
* @param search the search
*/
public void setSearch(final Search search) {
this.search = search;
}
/**
* Set the search value to apply to this column.
*
* @param searchValue if any, the search value to apply
*/
public void setSearchValue(final String searchValue) {
this.search.setValue(searchValue);
}
@Override
public String toString() {
return "Column{"
+ "data='" + data + '\''
+ ", name='" + name + '\''
+ ", searchable=" + searchable
+ ", orderable=" + orderable
+ ", search=" + search
+ '}';
}
}

View File

@ -0,0 +1,295 @@
package hirs.attestationca.portal.datatables;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.util.CollectionUtils;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents a data table input in a jQuery DataTable.
*/
public class DataTableInput {
private static final int DEFAULT_LENGTH = 10;
/**
* Default Constructor.
*/
public DataTableInput() {
}
/**
* Constructor.
* @param draw the draw counter
* @param start the paging start indicator
* @param length the number of records in current draw
* @param search the search parameter
* @param order the orderings
* @param columns the columns of the input
*/
public DataTableInput(final Integer draw, final Integer start, final Integer length,
final Search search, final List<Order> order,
final List<Column> columns) {
this.draw = draw;
this.start = start;
this.length = length;
this.search = search;
this.order.addAll(order);
this.columns.addAll(columns);
}
/**
* Draw counter. This is used by DataTables to ensure that the Ajax returns from server-side
* processing requests are drawn in sequence by DataTables (Ajax requests are asynchronous and
* thus can return out of sequence). This is used as part of the draw return parameter (see
* below).
*/
@NotNull
@Min(0)
private int draw = 1;
/**
* Paging first record indicator. This is the start point in the current data set
* (0 index based - i.e. 0 is the first record).
*/
@NotNull
@Min(0)
private int start = 0;
/**
* Number of records that the table can display in the current draw. It is expected that the
* number of records returned will be equal to this number,
* unless the server has fewer records to return. Note that this can be -1 to indicate that
* all records should be returned (although that
* negates any benefits of server-side processing!)
*/
@NotNull
@Min(-1)
private int length = DEFAULT_LENGTH;
/**
* Global search parameter.
*/
@NotNull
private Search search = new Search();
/**
* Order parameter.
*/
@NotEmpty
private List<Order> order = new ArrayList<>();
/**
* Per-column search parameter.
*/
@NotEmpty
private List<Column> columns = new ArrayList<>();
/**
*
* @return the draw counter
*/
public int getDraw() {
return draw;
}
/**
* Sets the draw counter.
* @param draw the draw counter
*/
public void setDraw(final int draw) {
this.draw = draw;
}
/**
* Gets the start indicator.
* @return the start indicator
*/
public int getStart() {
return start;
}
/**
* Gets the start indicator.
* @param start the start indicator
*/
public void setStart(final int start) {
this.start = start;
}
/**
*
* @return the table length for the current draw
*/
public int getLength() {
return length;
}
/**
* Sets the table length for the current draw.
* @param length the table length for the current draw
*/
public void setLength(final int length) {
this.length = length;
}
/**
*
* @return the Search
*/
public Search getSearch() {
return search;
}
/**
* Sets the search.
* @param search the search
*/
public void setSearch(final Search search) {
this.search = search;
}
/**
*
* @return the orders
*/
public List<Order> getOrder() {
return order;
}
/**
* Sets the orders.
* @param order the orders
*/
public void setOrder(final List<Order> order) {
this.order.clear();
this.order.addAll(order);
}
/**
* Gets the table columns.
* @return the columns
*/
public List<Column> getColumns() {
return columns;
}
/**
* Sets the table columns.
* @param columns the columns
*/
public void setColumns(final List<Column> columns) {
this.columns.clear();
this.columns.addAll(columns);
}
/**
*
* @return a {@link Map} of {@link Column} indexed by name
*/
public Map<String, Column> getColumnsAsMap() {
Map<String, Column> map = new HashMap<String, Column>();
for (Column column : columns) {
map.put(column.getData(), column);
}
return map;
}
/**
* Find a column by its name.
*
* @param columnName the name of the column
* @return the given Column, or <code>null</code> if not found
*/
public Column getColumn(final String columnName) {
if (columnName == null) {
return null;
}
for (Column column : columns) {
if (columnName.equals(column.getData())) {
return column;
}
}
return null;
}
/**
* Add a new column.
*
* @param columnName the name of the column
* @param searchable whether the column is searchable or not
* @param orderable whether the column is orderable or not
* @param searchValue if any, the search value to apply
*/
public void addColumn(final String columnName, final boolean searchable,
final boolean orderable, final String searchValue) {
this.columns.add(new Column(columnName, "", searchable, orderable,
new Search(searchValue, false)));
}
/**
* Add an order on the given column.
*
* @param columnName the name of the column
* @param ascending whether the sorting is ascending or descending
*/
public void addOrder(final String columnName, final boolean ascending) {
if (columnName == null) {
return;
}
for (int i = 0; i < columns.size(); i++) {
if (!columnName.equals(columns.get(i).getData())) {
continue;
}
order.add(new Order(i, ascending));
}
}
/**
* Gets the order column name, given the order ordinal value.
* @return the order column name
*/
public String getOrderColumnName() {
// attempt to get the column property based on the order index.
String orderColumnName = "id";
List<Order> orders = getOrder();
if (!CollectionUtils.isEmpty(orders)) {
int orderColumnIndex = orders.get(0).getColumn();
final Column column = getColumns().get(orderColumnIndex);
// use the column's name as the order field for hibernate if set,
// otherwise, use the columns' data field
if (StringUtils.isNotEmpty(column.getName())) {
orderColumnName = column.getName();
} else {
orderColumnName = column.getData();
}
}
return orderColumnName;
}
/**
* Generates a string for this object.
* @return the string
*/
@Override
public String toString() {
return "DataTableInput{"
+ "draw=" + draw
+ ", start=" + start
+ ", length=" + length
+ ", search=" + search
+ ", order=" + order
+ ", columns=" + columns
+ '}';
}
}

View File

@ -0,0 +1,120 @@
package hirs.attestationca.portal.datatables;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import hirs.FilteredRecordsList;
/**
* A Wrapper for Data Table JSON responses. Allows Spring to serialize a data object with additional
* meta data required by data tables.
*
* @param <T> the type of object that is being wrapped.
*/
public final class DataTableResponse<T> {
private List<T> data = new LinkedList<T>();
private int draw;
private long recordsTotal;
private long recordsFiltered;
/**
* Default constructor.
*/
public DataTableResponse() {
}
/**
* Builds a data table response using a FilteredRecordList.
* @param recordList the filtered recordd list
* @param inputQuery the data table input (used for draw)
*/
public DataTableResponse(final FilteredRecordsList<T> recordList,
final DataTableInput inputQuery) {
this(recordList, inputQuery.getDraw(),
recordList.getRecordsTotal(), recordList.getRecordsFiltered());
}
/**
* Constructs a data table response using the specified data with the data table specific
* information.
*
* @param data that is to be displayed by data table
* @param draw the originating draw request ID (usually from a web request)
* @param recordsTotal total number of records inside the data
* @param recordsFiltered number of records excluded from the request
*/
public DataTableResponse(final List<T> data, final int draw, final long recordsTotal,
final long recordsFiltered) {
setData(data);
this.draw = draw;
this.recordsTotal = recordsTotal;
this.recordsFiltered = recordsFiltered;
}
/**
* Gets the data table data.
* @return the data
*/
public List<T> getData() {
return Collections.unmodifiableList(data);
}
/**
* Sets the data table data.
* @param data the data
*/
public void setData(final List<T> data) {
this.data.clear();
this.data.addAll(data);
}
/**
* Gets the table draw index of the table.
* @return the draw index
*/
public int getDraw() {
return draw;
}
/**
* Sets the table draw index of the table.
* @param draw the draw index
*/
public void setDraw(final int draw) {
this.draw = draw;
}
/**
* Gets the total records.
* @return the total records count
*/
public long getRecordsTotal() {
return recordsTotal;
}
/**
* Sets the total record count.
* @param recordsTotal the total records count
*/
public void setRecordsTotal(final long recordsTotal) {
this.recordsTotal = recordsTotal;
}
/**
* Gets the total filtered record count.
* @return the total filtered record count
*/
public long getRecordsFiltered() {
return recordsFiltered;
}
/**
* Sets the total filtered record count.
* @param recordsFiltered the total filtered record count
*/
public void setRecordsFiltered(final long recordsFiltered) {
this.recordsFiltered = recordsFiltered;
}
}

View File

@ -0,0 +1,45 @@
package hirs.attestationca.portal.datatables;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Map;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.view.AbstractUrlBasedView;
/**
* Serializes the DataTableResponse from the view as JSON and writes it to the HTTP response.
*
*/
public class DataTableView extends AbstractUrlBasedView {
private static final Gson GSON = new GsonBuilder().create();
private static final String MODEL_FIELD;
static {
final String name = DataTableResponse.class.getSimpleName();
MODEL_FIELD = name.substring(0, 1).toLowerCase() + name.substring(1);
}
/**
* Serializes the DataTableResponse from the view as JSON and writes it to the HTTP response.
*
* @param model combined output Map (never {@code null}), with dynamic values taking precedence
* over static attributes
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if rendering failed
*/
@Override
protected void renderMergedOutputModel(
final Map<String, Object> model,
final HttpServletRequest request,
final HttpServletResponse response) throws Exception {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
DataTableResponse dataTable = (DataTableResponse) model.get(MODEL_FIELD);
ServletOutputStream out = response.getOutputStream();
String json = GSON.toJson(dataTable);
out.print(json);
}
}

View File

@ -0,0 +1,111 @@
package hirs.attestationca.portal.datatables;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
/**
* Represents a column ordering with regards to a jQuery DataTable.
*/
public class Order {
/**
* Constructor.
*/
public Order() {
}
/**
* Constructor.
* @param column the column index
* @param dir the order direction
*/
public Order(final int column, final String dir) {
this.column = column;
this.dir = dir;
}
/**
* Constructor.
* @param column the column index
* @param isAscending true if ascending order
*/
public Order(final int column, final boolean isAscending) {
this.column = column;
if (isAscending) {
this.dir = "asc";
} else {
this.dir = "desc";
}
}
/**
* Column to which ordering should be applied. This is an index reference
* to the columns array of information that is also submitted to the server.
*/
@NotNull
@Min(0)
private int column;
/**
* Ordering direction for this column. It will be asc or desc to indicate ascending ordering or
* descending ordering, respectively.
*/
@NotNull
@Pattern(regexp = "(desc|asc)")
private String dir;
/**
* Gets the column index.
* @return the column index
*/
public int getColumn() {
return column;
}
/**
* Sets the column index.
* @param column the column index
*/
public void setColumn(final int column) {
this.column = column;
}
/**
* Gets the direction order.
* @return the direction order
*/
public String getDir() {
return dir;
}
/**
* Sets the direction order.
* @param dir the direction order
*/
public void setDir(final String dir) {
this.dir = dir;
}
/**
*
* @return true if ascending order, false otherwise.
*/
public boolean isAscending() {
if (dir.equalsIgnoreCase("asc")) {
return true;
}
return false;
}
@Override
public String toString() {
return "Order{"
+ "column=" + column
+ ", dir='" + dir + '\''
+ '}';
}
}

View File

@ -0,0 +1,78 @@
package hirs.attestationca.portal.datatables;
import org.hibernate.Criteria;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import hirs.FilteredRecordsList;
import hirs.persist.CriteriaModifier;
import hirs.persist.OrderedListQuerier;
/**
* A class to adapt the Javascript DataTable java class abstractions to the DBManager's getting
* of ordered lists.
* @param <T> The type of object to query
*/
public final class OrderedListQueryDataTableAdapter<T> {
private OrderedListQueryDataTableAdapter() {
// do not construct
}
/**
* Gets the ordered list of records using a default, no-op criteria modifier.
* @param clazz the type of objects to query for
* @param dbManager the db manager to execute the actual query
* @param dataTableInput the JS DataTable query abstraction
* @param orderColumnName the name of the column (java object field name) to query on
* @param <T> the parameter type
* @return the filtered record list
*/
public static <T> FilteredRecordsList<T> getOrderedList(final Class<? extends T> clazz,
final OrderedListQuerier<T> dbManager,
final DataTableInput dataTableInput,
final String orderColumnName) {
CriteriaModifier defaultModifier = new CriteriaModifier() {
@Override
public void modify(final Criteria criteria) {
// Do nothing
}
};
return getOrderedList(clazz, dbManager, dataTableInput, orderColumnName, defaultModifier);
}
/**
* Gets the ordered list of records.
* @param clazz the type of objects to query for
* @param dbManager the db manager to execute the actual query
* @param dataTableInput the JS DataTable query abstraction
* @param orderColumnName the name of the column (java object field name) to query on
* @param criteriaModifier the criteria modifier
* @param <T> the parameter type
* @return the filtered record list
*/
public static <T> FilteredRecordsList<T> getOrderedList(final Class<? extends T> clazz,
final OrderedListQuerier<T> dbManager, final DataTableInput dataTableInput,
final String orderColumnName,
final CriteriaModifier criteriaModifier) {
Map<String, Boolean> searchableColumnMap = new HashMap<>();
for (Column column : dataTableInput.getColumns()) {
searchableColumnMap.put(column.getData(), column.isSearchable());
}
List<Order> orders = dataTableInput.getOrder();
boolean isAscending = true;
if (!CollectionUtils.isEmpty(orders)) {
isAscending = orders.get(0).isAscending();
}
return dbManager.getOrderedList(clazz, orderColumnName, isAscending,
dataTableInput.getStart(), dataTableInput.getLength(),
dataTableInput.getSearch().getValue(),
searchableColumnMap, criteriaModifier);
}
}

View File

@ -0,0 +1,89 @@
package hirs.attestationca.portal.datatables;
import javax.validation.constraints.NotNull;
/**
* Represents a jQuery DataTables search parameter.
*/
public class Search {
/**
* Default Constructor.
*/
public Search() {
}
/**
* Constructor for a non-regex search.
* @param value the search value
*/
public Search(final String value) {
this(value, false);
}
/**
* Constructor.
* @param value the search value
* @param regex the search regex
*/
public Search(final String value, final boolean regex) {
this.value = value;
this.regex = regex;
}
/**
* Global search value. To be applied to all columns which have searchable as true.
*/
@NotNull
private String value = "";
/**
* true if the global filter should be treated as a regular expression for advanced searching,
* false otherwise. Note that normally server-side processing scripts will not perform regular
* expression searching for performance reasons on large data sets,
* but it is technically possible and at the discretion of your script.
*/
@NotNull
private boolean regex;
/**
*
* @return the global search value, applied to all columns.
*/
public String getValue() {
return value;
}
/**
* Sets the global search value.
* @param value the global search value
*/
public void setValue(final String value) {
this.value = value;
}
/**
*
* @return true if search should be treated as a regex, false otherwise
*/
public boolean isRegex() {
return regex;
}
/**
* Sets the regex flag.
* @param regex true if the search should be treated as a regex, false otherwise
*/
public void setRegex(final boolean regex) {
this.regex = regex;
}
@Override
public String toString() {
return "Search{"
+ "value='" + value + '\''
+ ", regex=" + regex
+ '}';
}
}

View File

@ -0,0 +1,9 @@
/**
* This packages provides classes to serve jQuery dataTable requests.
*
* DataTableInput, Column, Order, and Search from:
* https://github.com/darrachequesne/spring-data-jpa-datatables
*
* The rest of the package was unnecessary and introduced dependency conflicts.
*/
package hirs.attestationca.portal.datatables;

View File

@ -0,0 +1,155 @@
package hirs.attestationca.portal.model;
import hirs.data.persist.SupplyChainPolicy;
/**
* PolicyPage model object to demonstrate data exchange between policy.jsp page
* form form and controller.
*/
public class PolicyPageModel {
// Variables to communicate policy settings to page
private boolean enableEcValidation;
private boolean enablePcCertificateValidation;
private boolean enablePcCertificateAttributeValidation;
// Variables to get policy settings from page
private String pcValidate;
private String pcAttributeValidate;
private String ecValidate;
/**
* Constructor. Sets fields from policy.
*
* @param policy The supply chain policy
*/
public PolicyPageModel(final SupplyChainPolicy policy) {
this.enableEcValidation = policy.isEcValidationEnabled();
this.enablePcCertificateValidation = policy.isPcValidationEnabled();
this.enablePcCertificateAttributeValidation = policy.isPcAttributeValidationEnabled();
}
/**
* Default constructor required by Spring.
*/
public PolicyPageModel() {
}
/**
* Gets the EC Validation state.
*
* @return the validation state.
*/
public boolean getEnableEcValidation() {
return enableEcValidation;
}
/**
* Gets the Platform Certificate Validation state.
*
* @return the validation state.
*/
public boolean getEnablePcCertificateValidation() {
return enablePcCertificateValidation;
}
/**
* Gets the Platform Certificate AttributeValidation state.
*
* @return the validation state.
*/
public boolean getEnablePcCertificateAttributeValidation() {
return enablePcCertificateAttributeValidation;
}
/**
* Gets the EC Validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getEcValidate() {
return ecValidate;
}
/**
* Gets the Platform Certificate Validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getPcValidate() {
return pcValidate;
}
/**
* Gets the platform certificate attribute validation value.
*
* @return the model string representation of this field (checked or unchecked)
*/
public String getPcAttributeValidate() {
return pcAttributeValidate;
}
/**
* Sets the EC Validation state.
*
* @param enableEcValidation true if performing validation, false otherwise
*/
public void setEnableEcValidation(final boolean enableEcValidation) {
this.enableEcValidation = enableEcValidation;
}
/**
* Sets the Platform Certificate Validation state.
*
* @param enablePcCertificateValidation true if performing validation, false otherwise
*/
public void setEnablePcCertificateValidation(final boolean enablePcCertificateValidation) {
this.enablePcCertificateValidation = enablePcCertificateValidation;
}
/**
* Sets the Platform Certificate Attribute Validation state.
*
* @param enablePcCertificateAttributeValidation true if performing validation, false otherwise
*/
public void setEnablePcCertificateAttributeValidation(
final boolean enablePcCertificateAttributeValidation) {
this.enablePcCertificateAttributeValidation = enablePcCertificateAttributeValidation;
}
/**
* Sets the Platform Certificate Validation state.
*
* @param pcValidate "checked" if enabling validation, false otherwise
*/
public void setPcValidate(final String pcValidate) {
this.pcValidate = pcValidate;
}
/**
* Sets the EC Validation state.
*
* @param ecValidate "checked" if enabling validation, false otherwise
*/
public void setEcValidate(final String ecValidate) {
this.ecValidate = ecValidate;
}
/**
* Sets the PC Attribute Validation state.
*
* @param pcAttributeValidate "checked" if enabling validation, false otherwise
*/
public void setPcAttributeValidate(final String pcAttributeValidate) {
this.pcAttributeValidate = pcAttributeValidate;
}
@Override
public String toString() {
return "PolicyPageModel{"
+ "enableEcValidation=" + enableEcValidation
+ ", enablePcCertificateValidation=" + enablePcCertificateValidation
+ ", enablePcCertificateAttributeValidation="
+ enablePcCertificateAttributeValidation + '}';
}
}

View File

@ -0,0 +1,4 @@
/**
* This packages provides data model classes for the HIRS Attestation CA portal.
*/
package hirs.attestationca.portal.model;

View File

@ -0,0 +1,4 @@
/**
* Root Package for HIRS Attestation CA Portal.
*/
package hirs.attestationca.portal;

View File

@ -0,0 +1,80 @@
package hirs.attestationca.portal.page;
import hirs.attestationca.portal.datatables.DataTableView;
import hirs.attestationca.portal.persistence.PersistenceConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import java.nio.charset.StandardCharsets;
/**
* Specifies the location to scan for page controllers, view resolver for JSON data, and view
* resolver to map view names to jsp files.
*/
@Configuration
@EnableWebMvc
@ComponentScan("hirs.attestationca.portal.page.controllers")
@Import({ PersistenceConfiguration.class })
public class CommonPageConfiguration {
/**
* @return bean to resolve injected annotation.Value
* property expressions for beans.
*/
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
/**
* Makes all URLs that end in "dataTable" use DataTableView to serialize DataTableResponse.
*
* @return ViewResolver that uses DataTableView.
*/
@Bean
public ViewResolver dataTableViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setViewClass(DataTableView.class);
resolver.setViewNames("*dataTable");
resolver.setOrder(0);
return resolver;
}
/**
* Maps view names to the appropriate jsp file.
*
* Only seems to apply to GET requests.
*
* @return a ViewResolver bean containing the mapping.
*/
@Bean
public ViewResolver pageViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
/**
* Creates a Spring Resolver for Multi-part form uploads. This is required
* for spring controllers to be able to process Spring MultiPartFiles
*
* @return bean to handle multipart form requests
*/
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setDefaultEncoding(StandardCharsets.UTF_8.name());
return resolver;
}
}

View File

@ -0,0 +1,209 @@
package hirs.attestationca.portal.page;
import hirs.utils.VersionHelper;
/**
* Contains attributes required to display a portal page and its menu link.
*/
public enum Page {
/**
* Site landing page.
*/
INDEX("HIRS Attestation CA", "Version: " + VersionHelper.getVersion(),
null, false, false, null, null),
/**
* Page to import and manage trust chains.
*/
TRUST_CHAIN("Trust Chain Management", "ic_store",
null, "certificate-request/"),
/**
* Page to display and manage endorsement key credentials.
*/
ENDORSEMENT_KEY_CREDENTIALS("Endorsement Key Credentials", "ic_vpn_key",
"first", "certificate-request/"),
/**
* Page to display and manage platform credentials.
*/
PLATFORM_CREDENTIALS("Platform Credentials", "ic_important_devices",
null, "certificate-request/"),
/**
* Page to display issued certificates.
*/
ISSUED_CERTIFICATES("Issued Attestation Certificates", "ic_library_books",
null, "certificate-request/"),
/**
* Page to display certificate validation reports.
*/
VALIDATION_REPORTS("Validation Reports", "ic_assignment", "first"),
/**
* Non-menu page to display certificate. Reachable from all certificate pages.
*/
CERTIFICATE_DETAILS("Certificate Details", "", null, true, false, null, null),
/**
* Page to display registered devices.
*/
DEVICES("Devices", "ic_devices", "first"),
/**
* Page that manages Attestation CA Policy.
*/
POLICY("Policy", "ic_subtitles"),
/**
* Help page.
*/
HELP("Help", "ic_live_help");
private final String title;
private final String subtitle;
private final String icon;
private final boolean hasMenu;
private final String menuLinkClass;
private final boolean inMenu;
private final String prefixPath;
private final String viewName;
/**
* Constructor for Page.
*
* @param title title of the page
* @param subtitle subtitle of the page
* @param icon icon for the page
* @param hasMenu the page has its own menu
* @param inMenu the page appears in a menu
* @param menuLinkClass the category to which this page belongs
* @param prefixPath prefix path that appears in the URL for this page
*/
Page(final String title,
final String subtitle,
final String icon,
final boolean hasMenu,
final boolean inMenu,
final String menuLinkClass,
final String prefixPath) {
this.title = title;
this.subtitle = subtitle;
this.icon = icon;
this.hasMenu = hasMenu;
this.menuLinkClass = menuLinkClass;
this.inMenu = inMenu;
this.prefixPath = prefixPath;
viewName = this.name().toLowerCase().replaceAll("_", "-");
}
/**
* Constructor for Page.
*
* @param title title of the page
* @param icon icon for the page
* @param menuLinkClass the category to which this page belongs
* @param prefixPath prefix path that appears in the URL for this page
*/
Page(final String title,
final String icon,
final String menuLinkClass,
final String prefixPath) {
this(title, null, icon, true, true, menuLinkClass, prefixPath);
}
/**
* Constructor for Page.
*
* @param title title of the page
* @param icon icon for the page
* @param menuLinkClass the category to which this page belongs
*/
Page(final String title,
final String icon,
final String menuLinkClass) {
this(title, null, icon, true, true, menuLinkClass, null);
}
/**
* Constructor for Page.
*
* @param title title of the page
* @param icon icon for the page
*/
Page(final String title,
final String icon) {
this(title, null, icon, true, true, null, null);
}
/**
* Returns the title of the page.
*
* @return the title of the page.
*/
public String getTitle() {
return title;
}
/**
* Returns the subtitle of the page.
*
* @return the subtitle of the page.
*/
public String getSubtitle() {
return subtitle;
}
/**
* Returns the base filename of the icon for page. E.g. "ic_my_icon", which will be appended
* with appropriate size string (_24dp/_48dp) and file extension (.png) when used.
*
* @return the base filename of the icon for page.
*/
public String getIcon() {
return icon;
}
/**
* Returns true if the page should be displayed in the navigation menu.
*
* @return true if the page should be displayed in the navigation menu.
*/
public boolean getInMenu() {
return inMenu;
}
/**
* Returns the css class to add to the menu link to display it appropriately. E.g. "first" if
* the link is the first in a group to separate it visually from the previous group.
*
* @return he class to add to the menu link to display it appropriately.
*/
public String getMenuLinkClass() {
return menuLinkClass;
}
/**
* Returns true if the page should display the navigation menu.
*
* @return true if the page should display the navigation menu.
*/
public boolean getHasMenu() {
return hasMenu;
}
/**
* Return the page's view name.
*
* @return the page's view name
*/
public String getViewName() {
return viewName;
}
/**
* Return the page's view name.
*
* @return the page's view name
*/
public String getPrefixPath() {
return prefixPath;
}
}

View File

@ -0,0 +1,14 @@
package hirs.attestationca.portal.page;
import hirs.attestationca.configuration.AttestationCertificateAuthorityConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* Main Spring configuration class for the ACA Portal. Uses the Common page configuration,
* as well as the ACA configuration for accessing the ACA certificate.
*/
@Import({ CommonPageConfiguration.class, AttestationCertificateAuthorityConfiguration.class })
public class PageConfiguration extends WebMvcConfigurerAdapter {
}

View File

@ -0,0 +1,172 @@
package hirs.attestationca.portal.page;
import hirs.utils.BannerConfiguration;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import org.apache.http.client.utils.URIBuilder;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
/**
* Abstract class to provide common functionality for page Controllers.
*
* @param <P> PageParams class used by the subclass.
*/
public abstract class PageController<P extends PageParams> {
/**
* Reserved attribute used by page.tag to identify a page's general
* information.
*/
public static final String PAGE_ATTRIBUTE = "page";
/**
* Reserved attribute used by page.tag to identify the page collection used
* for navigation.
*/
public static final String PAGES_ATTRIBUTE = "pages";
/**
* Reserved attribute used by page.tag to identify the banner information.
*/
public static final String BANNER_ATTRIBUTE = "banner";
/**
* Reserved attribute used by page.tag to identify the messages the page
* should display.
*/
public static final String MESSAGES_ATTRIBUTE = "messages";
private final Page page;
/**
* Constructor requiring the Page's display and routing specification.
*
* @param page The page specification for this controller.
*/
public PageController(final Page page) {
this.page = page;
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the path for the view and data model for the page.
*/
@RequestMapping
public abstract ModelAndView initPage(@ModelAttribute final P params, final Model model);
/**
* Creates a generic ModelAndView containing this page's configuration and
* the list of other pages for navigational purposes.
*
* @return A generic ModelAndView containing basic information for the page.
*/
protected final ModelAndView getBaseModelAndView() {
return getBaseModelAndView(page);
}
/**
* Creates a generic ModelAndView containing the specify page
* configuration and the list of other pages for navigational
* purposes.
*
* @param newPage new page to get the model and view
* @return A generic ModelAndView containing basic information for the page.
*/
protected final ModelAndView getBaseModelAndView(final Page newPage) {
ModelMap modelMap = new ExtendedModelMap();
// add page information
modelMap.addAttribute(PAGE_ATTRIBUTE, newPage);
// add other pages for navigation
modelMap.addAttribute(PAGES_ATTRIBUTE, Page.values());
// add banner information
try {
BannerConfiguration banner = new BannerConfiguration();
modelMap.addAttribute(BANNER_ATTRIBUTE, banner);
} catch (IOException ex) {
modelMap.addAttribute(BANNER_ATTRIBUTE, null);
}
return new ModelAndView(newPage.getViewName(), modelMap);
}
/**
* Redirects back to this controller's page with the specified data.
*
* @param params The url parameters to pass to the page.
* @param model The model data to pass to the page.
* @param attr The request's RedirectAttributes to hold the model data.
* @return RedirectView back to the page with the specified parameters.
* @throws URISyntaxException if malformed URI
*/
protected final RedirectView redirectToSelf(
final P params,
final Map<String, ?> model,
final RedirectAttributes attr) throws URISyntaxException {
return redirectTo(page, params, model, attr);
}
/**
* Redirects controller's page with the specified data.
*
* @param newPage new page to get the model and view
* @param params The url parameters to pass to the page.
* @param model The model data to pass to the page.
* @param attr The request's RedirectAttributes to hold the model data.
* @return RedirectView back to the page with the specified parameters.
* @throws URISyntaxException if malformed URI
*/
protected final RedirectView redirectTo(
final Page newPage,
final P params,
final Map<String, ?> model,
final RedirectAttributes attr) throws URISyntaxException {
// create uri with specified parameters
URIBuilder uri = new URIBuilder("../" + newPage.getViewName());
if (params != null) {
for (Entry<String, ?> e : params.asMap().entrySet()) {
Object v = Optional.ofNullable(e.getValue()).orElse("");
uri.addParameter(e.getKey(), v.toString());
}
}
// create view
RedirectView redirect = new RedirectView(uri.toString());
// do not put model attributes in the url
redirect.setExposeModelAttributes(false);
// add model data to forward to redirected page
if (model != null) {
for (Entry<String, ?> e : model.entrySet()) {
attr.addFlashAttribute(e.getKey(), e.getValue());
}
}
return redirect;
}
}

View File

@ -0,0 +1,70 @@
package hirs.attestationca.portal.page;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Encapsulates error, success, and informational messages to display on a page.
*/
public class PageMessages {
private final List<String> error = new ArrayList<>();
private final List<String> success = new ArrayList<>();
private final List<String> info = new ArrayList<>();
/**
* Returns the list of error messages.
*
* @return the list of error messages.
*/
public List<String> getError() {
return Collections.unmodifiableList(error);
}
/**
* Adds an error message.
*
* @param error the error message to add
*/
public void addError(final String error) {
this.error.add(error);
}
/**
* Returns the list of success messages.
*
* @return the list of success messages.
*/
public List<String> getSuccess() {
return Collections.unmodifiableList(success);
}
/**
* Adds a success message.
*
* @param success the success message to add
*/
public void addSuccess(final String success) {
this.success.add(success);
}
/**
* Returns the list of informational messages.
*
* @return the list of informational messages.
*/
public List<String> getInfo() {
return Collections.unmodifiableList(info);
}
/**
* Adds an informational message.
*
* @param info the informational message to add
*/
public void addInfo(final String info) {
this.info.add(info);
}
}

View File

@ -0,0 +1,17 @@
package hirs.attestationca.portal.page;
import java.util.LinkedHashMap;
/**
* Interface for a page's url parameters.
*/
public interface PageParams {
/**
* Allows PageController to iterate over the url parameters.
*
* @return map containing the object's url parameters.
*/
LinkedHashMap<String, ?> asMap();
}

View File

@ -0,0 +1,122 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.CertificateDetailsPageParams;
import hirs.attestationca.portal.util.CertificateStringMapBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.UUID;
import static hirs.attestationca.portal.page.Page.CERTIFICATE_DETAILS;
import hirs.persist.CertificateManager;
import java.io.IOException;
/**
* Controller for the Certificate Details page.
*/
@Controller
@RequestMapping("/certificate-details")
public class CertificateDetailsPageController extends PageController<CertificateDetailsPageParams> {
/**
* Model attribute name used by initPage for the initial data passed to the page.
*/
static final String INITIAL_DATA = "initialData";
private final CertificateManager certificateManager;
private static final Logger LOGGER =
LogManager.getLogger(CertificateDetailsPageController.class);
/**
* Constructor providing the Page's display and routing specification.
* @param certificateManager the certificate manager
*/
@Autowired
public CertificateDetailsPageController(final CertificateManager certificateManager) {
super(CERTIFICATE_DETAILS);
this.certificateManager = certificateManager;
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final CertificateDetailsPageParams params, final Model model) {
// get the basic information to render the page
ModelAndView mav = getBaseModelAndView();
PageMessages messages = new PageMessages();
// Map with the certificate information
HashMap<String, Object> data = new HashMap<>();
// Check if parameters were set
if (params.getId() == null) {
String typeError = "ID was not provided";
messages.addError(typeError);
LOGGER.error(typeError);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else if (params.getType() == null) {
String typeError = "Type was not provided";
messages.addError(typeError);
LOGGER.error(typeError);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else {
try {
String type = params.getType().toLowerCase();
UUID uuid = UUID.fromString(params.getId());
switch(type) {
case "certificateauthority":
data.putAll(CertificateStringMapBuilder.getCertificateAuthorityInformation(
uuid, certificateManager));
break;
case "endorsement":
data.putAll(CertificateStringMapBuilder.getEndorsementInformation(uuid,
certificateManager));
break;
case "platform":
data.putAll(CertificateStringMapBuilder.getPlatformInformation(uuid,
certificateManager));
break;
case "issued":
data.putAll(CertificateStringMapBuilder.getIssuedInformation(uuid,
certificateManager));
break;
default:
String typeError = "Invalid certificate type: " + params.getType();
messages.addError(typeError);
LOGGER.error(typeError);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
break;
}
} catch (IllegalArgumentException | IOException ex) {
String uuidError = "Failed to parse ID from: " + params.getId();
messages.addError(uuidError);
LOGGER.error(uuidError, ex);
}
if (data.isEmpty()) {
String notFoundMessage = "Unable to find certificate with ID: " + params.getId();
messages.addError(notFoundMessage);
LOGGER.warn(notFoundMessage);
mav.addObject(MESSAGES_ATTRIBUTE, messages);
} else {
mav.addObject(INITIAL_DATA, data);
}
}
// return the model and view
return mav;
}
}

View File

@ -0,0 +1,675 @@
package hirs.attestationca.portal.page.controllers;
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;
import hirs.attestationca.portal.page.params.NoPageParams;
import hirs.attestationca.portal.util.CertificateStringMapBuilder;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import hirs.FilteredRecordsList;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.IssuedAttestationCertificate;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.persist.CertificateManager;
import hirs.persist.CriteriaModifier;
import hirs.persist.CrudManager;
import hirs.persist.DBManagerException;
import hirs.persist.OrderedListQuerier;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
/**
* Controller for the Device page.
*/
@Controller
@RequestMapping("/certificate-request")
public class CertificateRequestPageController extends PageController<NoPageParams> {
private final CertificateManager certificateManager;
private final OrderedListQuerier<Certificate> dataTableQuerier;
private CertificateAuthorityCredential certificateAuthorityCredential;
private static final Logger LOGGER = getLogger(CertificateRequestPageController.class);
private static final String TRUSTCHAIN = "trust-chain";
private static final String PLATFORMCREDENTIAL = "platform-credentials";
private static final String ENDORSEMENTCREDENTIAL = "endorsement-key-credentials";
private static final String ISSUEDCERTIFICATES = "issued-certificates";
/**
* Model attribute name used by initPage for the aca cert info.
*/
static final String ACA_CERT_DATA = "acaCertData";
/**
* Constructor providing the Page's display and routing specification.
*
* @param certificateManager the certificate manager
* @param crudManager the CRUD manager for certificates
* @param acaCertificate the ACA's X509 certificate
*/
@Autowired
public CertificateRequestPageController(
final CertificateManager certificateManager,
final CrudManager<Certificate> crudManager,
final X509Certificate acaCertificate) {
super(Page.TRUST_CHAIN);
this.certificateManager = certificateManager;
this.dataTableQuerier = crudManager;
try {
certificateAuthorityCredential
= new CertificateAuthorityCredential(acaCertificate.getEncoded());
} catch (IOException e) {
LOGGER.error("Failed to read ACA certificate", e);
} catch (CertificateEncodingException e) {
LOGGER.error("Error getting encoded ACA certificate", e);
}
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView();
}
/**
* Returns the path for the view and the data model for the page.
*
* @param certificateType String containing the certificate type
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from
* redirect.
* @return the path for the view and data model for the page.
*/
@RequestMapping("/{certificateType}")
public ModelAndView initPage(@PathVariable("certificateType") final String certificateType,
final NoPageParams params, final Model model) {
ModelAndView mav = null;
HashMap<String, String> data = new HashMap<>();
// add page information
switch (certificateType) {
case PLATFORMCREDENTIAL:
mav = getBaseModelAndView(Page.PLATFORM_CREDENTIALS);
break;
case ENDORSEMENTCREDENTIAL:
mav = getBaseModelAndView(Page.ENDORSEMENT_KEY_CREDENTIALS);
break;
case ISSUEDCERTIFICATES:
mav = getBaseModelAndView(Page.ISSUED_CERTIFICATES);
break;
case TRUSTCHAIN:
mav = getBaseModelAndView(Page.TRUST_CHAIN);
// Map with the ACA certificate information
data.putAll(CertificateStringMapBuilder.getCertificateAuthorityInformation(
certificateAuthorityCredential, this.certificateManager));
mav.addObject(ACA_CERT_DATA, data);
break;
default:
// send to an error page
break;
}
return mav;
}
/**
* Queries for the list of Certificates and returns a data table response
* with the records.
*
* @param certificateType String containing the certificate type
* @param input the DataTables search/query parameters
* @return the data table
*/
@ResponseBody
@RequestMapping(value = "/{certificateType}/list",
produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET)
@SuppressWarnings("unchecked")
public DataTableResponse<? extends Certificate> getTableData(
@PathVariable("certificateType") final String certificateType,
final DataTableInput input) {
LOGGER.debug("Handling list request: " + input);
// attempt to get the column property based on the order index.
String orderColumnName = input.getOrderColumnName();
LOGGER.debug("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 Criteria criteria) {
criteria.add(Restrictions.isNull("archivedTime"));
// add a device alias if this query includes the device table
// for getting the device (e.g. device name).
// use left join, since device may be null. Query will return all
// Certs of this type, whether it has a Device or not (device field may be null)
if (hasDeviceTableToJoin(certificateType)) {
criteria.createAlias("device", "device", JoinType.LEFT_OUTER_JOIN);
}
}
};
FilteredRecordsList records
= OrderedListQueryDataTableAdapter.getOrderedList(
getCertificateClass(certificateType), dataTableQuerier,
input, orderColumnName, criteriaModifier);
// special parsing for platform credential
// Add the EndorsementCredential for each PlatformCredential based on the
// serial number. (pc.HolderSerialNumber = ec.SerialNumber)
if (certificateType.equals(PLATFORMCREDENTIAL)) {
EndorsementCredential associatedEC;
if (!records.isEmpty()) {
// loop all the platform certificates
for (int i = 0; i < records.size(); i++) {
PlatformCredential pc = (PlatformCredential) records.get(i);
// find the EC using the PC's "holder serial number"
associatedEC = EndorsementCredential
.select(certificateManager)
.bySerialNumber(pc.getHolderSerialNumber())
.getCertificate();
if (associatedEC != null) {
LOGGER.debug("EC ID for holder s/n " + pc
.getHolderSerialNumber() + " = " + associatedEC.getId());
}
pc.setEndorsementCredential(associatedEC);
}
}
}
LOGGER.debug("Returning list of size: " + records.size());
return new DataTableResponse<>(records, input);
}
/**
* Archives (soft delete) the credential.
*
* @param certificateType String containing the certificate type
* @param id the UUID of the cert to delete
* @param attr RedirectAttributes used to forward data back to the original
* page.
* @return redirect to this page
* @throws URISyntaxException if malformed URI
*/
@RequestMapping(value = "/{certificateType}/delete", method = RequestMethod.POST)
public RedirectView delete(
@PathVariable("certificateType") final String certificateType,
@RequestParam final String id,
final RedirectAttributes attr) throws URISyntaxException {
LOGGER.info("Handling request to delete " + id);
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
try {
UUID uuid = UUID.fromString(id);
Certificate certificate = getCertificateById(certificateType, uuid, certificateManager);
if (null == certificate) {
// Use the term "record" here to avoid user confusion b/t cert and cred
String notFoundMessage = "Unable to locate record with ID: " + uuid;
messages.addError(notFoundMessage);
LOGGER.warn(notFoundMessage);
} else {
certificate.archive();
certificateManager.update(certificate);
String deleteCompletedMessage = "Certificate successfully deleted";
messages.addInfo(deleteCompletedMessage);
LOGGER.info(deleteCompletedMessage);
}
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: " + id;
messages.addError(uuidError);
LOGGER.error(uuidError, ex);
} catch (DBManagerException ex) {
String dbError = "Failed to archive cert: " + id;
messages.addError(dbError);
LOGGER.error(dbError, ex);
}
model.put(MESSAGES_ATTRIBUTE, messages);
return redirectTo(getCertificatePage(certificateType), new NoPageParams(), model, attr);
}
/**
* Handles request to download the cert by writing it to the response stream
* for download.
*
* @param certificateType String containing the certificate type
* @param id the UUID of the cert to download
* @param response the response object (needed to update the header with the
* file name)
* @throws java.io.IOException when writing to response output stream
*/
@RequestMapping(value = "/{certificateType}/download", method = RequestMethod.GET)
public void download(
@PathVariable("certificateType") final String certificateType,
@RequestParam final String id,
final HttpServletResponse response)
throws IOException {
LOGGER.info("Handling request to download " + id);
try {
UUID uuid = UUID.fromString(id);
Certificate certificate = getCertificateById(certificateType, uuid, certificateManager);
if (null == certificate) {
// Use the term "record" here to avoid user confusion b/t cert and cred
String notFoundMessage = "Unable to locate record with ID: " + uuid;
LOGGER.warn(notFoundMessage);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
} else {
StringBuilder fileName = new StringBuilder("filename=\"");
fileName.append(getCertificateClass(certificateType).getSimpleName());
fileName.append("_");
fileName.append(certificate.getSerialNumber());
fileName.append(".cer\"");
// Set filename for download.
response.setHeader("Content-Disposition", "attachment;" + fileName);
response.setContentType("application/octet-stream");
// write cert to output stream
response.getOutputStream().write(certificate.getRawBytes());
}
} catch (IllegalArgumentException ex) {
String uuidError = "Failed to parse ID from: " + id;
LOGGER.error(uuidError, ex);
// send a 404 error when invalid certificate
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
/**
* Handles request to download the ACA cert by writing it to the response
* stream for download.
*
* @param response the response object (needed to update the header with the
* file name)
*
* @throws java.io.IOException when writing to response output stream
*/
@ResponseBody
@RequestMapping(value = "/trust-chain/download-aca-cert", method = RequestMethod.GET)
public void downloadAcaCertificate(final HttpServletResponse response)
throws IOException {
// Set filename for download.
response.setHeader("Content-Disposition", "attachment; filename=\"hirs-aca-cert.cer\"");
response.setContentType("application/octet-stream");
// write cert to output stream
response.getOutputStream().write(certificateAuthorityCredential.getRawBytes());
}
/**
* Upload and processes a credential.
*
* @param certificateType String containing the certificate type
* @param files the files to process
* @param attr the redirection attributes
* @return the redirection view
* @throws URISyntaxException if malformed URI
*/
@RequestMapping(value = "/{certificateType}/upload", method = RequestMethod.POST)
protected RedirectView upload(
@PathVariable("certificateType") final String certificateType,
@RequestParam("file") final MultipartFile[] files,
final RedirectAttributes attr) throws URISyntaxException {
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
for (MultipartFile file :files) {
//Parse certificate
Certificate certificate = parseCertificate(certificateType, file, messages);
//Store only if it was parsed
if (certificate != null) {
storeCertificate(
certificateType,
file.getOriginalFilename(),
messages, certificate,
certificateManager);
}
}
//Add messages to the model
model.put(MESSAGES_ATTRIBUTE, messages);
return redirectTo(getCertificatePage(certificateType), new NoPageParams(), model, attr);
}
/**
* Get the page based on the certificate type.
*
* @param certificateType String containing the certificate type
* @return the page for the certificate type.
*/
private static Page getCertificatePage(final String certificateType) {
// get page information (default to TRUST_CHAIN)
switch (certificateType) {
case PLATFORMCREDENTIAL:
return Page.PLATFORM_CREDENTIALS;
case ENDORSEMENTCREDENTIAL:
return Page.ENDORSEMENT_KEY_CREDENTIALS;
case ISSUEDCERTIFICATES:
return Page.ISSUED_CERTIFICATES;
case TRUSTCHAIN:
default:
return Page.TRUST_CHAIN;
}
}
/**
* Gets the concrete certificate class type to query for.
*
* @param certificateType String containing the certificate type
* @return the certificate class type
*/
private static Class<? extends Certificate> getCertificateClass(final String certificateType) {
switch (certificateType) {
case PLATFORMCREDENTIAL:
return PlatformCredential.class;
case ENDORSEMENTCREDENTIAL:
return EndorsementCredential.class;
case ISSUEDCERTIFICATES:
return IssuedAttestationCertificate.class;
case TRUSTCHAIN:
return CertificateAuthorityCredential.class;
default:
throw new IllegalArgumentException(
String.format("Unknown certificate type: %s", certificateType));
}
}
/**
* Get flag indicating if a device-name join/alias is required for
* displaying the table data. This will be true if displaying a cert that is
* associated with a device.
*
* @param certificateType String containing the certificate type
* @return true if the list criteria modifier requires aliasing the device
* table, false otherwise.
*/
private boolean hasDeviceTableToJoin(final String certificateType) {
boolean hasDevice = true;
// Trust_Chain Credential do not contain the device table to join.
if (certificateType.equals(TRUSTCHAIN)) {
hasDevice = false;
}
return hasDevice;
}
/**
* Gets the certificate by ID.
*
* @param certificateType String containing the certificate type
* @param uuid the ID of the cert
* @param certificateManager the certificate manager to query
* @return the certificate or null if none is found
*/
private Certificate getCertificateById(
final String certificateType,
final UUID uuid,
final CertificateManager certificateManager) {
switch (certificateType) {
case PLATFORMCREDENTIAL:
return PlatformCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
case ENDORSEMENTCREDENTIAL:
return EndorsementCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
case ISSUEDCERTIFICATES:
return IssuedAttestationCertificate
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
case TRUSTCHAIN:
return CertificateAuthorityCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
default:
return null;
}
}
/**
* Gets the certificate by the hash code of its bytes. Looks for both
* archived and unarchived certificates.
*
* @param certificateType String containing the certificate type
* @param certificateHash the hash of the certificate's bytes
* @param certificateManager the certificate manager to query
* @return the certificate or null if none is found
*/
private Certificate getCertificateByHash(
final String certificateType,
final int certificateHash,
final CertificateManager certificateManager) {
switch (certificateType) {
case PLATFORMCREDENTIAL:
return PlatformCredential
.select(certificateManager)
.includeArchived()
.byHashCode(certificateHash)
.getCertificate();
case ENDORSEMENTCREDENTIAL:
return EndorsementCredential
.select(certificateManager)
.includeArchived()
.byHashCode(certificateHash)
.getCertificate();
case TRUSTCHAIN:
return CertificateAuthorityCredential
.select(certificateManager)
.includeArchived()
.byHashCode(certificateHash)
.getCertificate();
default:
return null;
}
}
/**
* Parses an uploaded file into a certificate and populates the given model
* with error messages if parsing fails.
*
* @param certificateType String containing the certificate type
* @param file the file being uploaded from the portal
* @param model the map of page elements to populate with error messages
* upon failure
* @param messages contains any messages that will be display on the page
* @return the parsed certificate or null if parsing failed.
*/
private Certificate parseCertificate(
final String certificateType,
final MultipartFile file,
final PageMessages messages) {
LOGGER.info("Received File of Size: " + file.getSize());
byte[] fileBytes;
String fileName = file.getOriginalFilename();
// build the certificate from the uploaded bytes
try {
fileBytes = file.getBytes();
} catch (IOException e) {
final String failMessage = "Failed to read uploaded file ("
+ fileName + "): ";
LOGGER.error(failMessage, e);
messages.addError(failMessage + e.getMessage());
return null;
}
try {
switch (certificateType) {
case PLATFORMCREDENTIAL:
return new PlatformCredential(fileBytes);
case ENDORSEMENTCREDENTIAL:
return new EndorsementCredential(fileBytes);
case TRUSTCHAIN:
return new CertificateAuthorityCredential(fileBytes);
default:
final String failMessage = "Failed to parse uploaded file ("
+ fileName + "). Invalid certificate type: "
+ certificateType;
LOGGER.error(failMessage);
messages.addError(failMessage);
return null;
}
} catch (IOException e) {
final String failMessage = "Failed to parse uploaded file ("
+ fileName + "): ";
LOGGER.error(failMessage, e);
messages.addError(failMessage + e.getMessage());
return null;
} catch (IllegalArgumentException e) {
final String failMessage = "Certificate format not recognized("
+ fileName + "): ";
LOGGER.error(failMessage, e);
messages.addError(failMessage + e.getMessage());
return null;
}
}
/**
* Store the given certificate in the database.
*
* @param certificateType String containing the certificate type
* @param fileName contain the name of the file of the certificate to
* be stored
* @param messages contains any messages that will be display on the page
* @param certificate the certificate to store
* @param certificateManager the DB manager to use
* @return the messages for the page
*/
private void storeCertificate(
final String certificateType,
final String fileName,
final PageMessages messages,
final Certificate certificate,
final CertificateManager certificateManager) {
Certificate existingCertificate;
// look for an identical certificate in the database
try {
existingCertificate = getCertificateByHash(
certificateType,
certificate.getCertificateHash(),
certificateManager);
} catch (DBManagerException e) {
final String failMessage = "Querying for existing certificate failed ("
+ fileName + "): ";
messages.addError(failMessage + e.getMessage());
LOGGER.error(failMessage, e);
return;
}
try {
// save the new certificate if no match is found
if (existingCertificate == null) {
certificateManager.save(certificate);
final String successMsg = "New certificate successfully uploaded ("
+ fileName + ")";
messages.addSuccess(successMsg);
LOGGER.info(successMsg);
return;
}
} catch (DBManagerException e) {
final String failMessage = "Storing new certificate failed ("
+ fileName + "): ";
messages.addError(failMessage + e.getMessage());
LOGGER.error(failMessage, e);
return;
}
try {
// if an identical certificate is archived, update the existing certificate to
// unarchive it and change the creation date
if (existingCertificate.isArchived()) {
existingCertificate.restore();
existingCertificate.resetCreateTime();
certificateManager.update(existingCertificate);
final String successMsg = "Pre-existing certificate found and unarchived ("
+ fileName + ")";
messages.addSuccess(successMsg);
LOGGER.info(successMsg);
return;
}
} catch (DBManagerException e) {
final String failMessage = "Found an identical pre-existing certificate in the "
+ "archive, but failed to unarchive it (" + fileName + "): ";
messages.addError(failMessage + e.getMessage());
LOGGER.error(failMessage, e);
return;
}
// if an identical certificate is already unarchived, do nothing and show a fail message
final String failMessage
= "Storing certificate failed: an identical certificate already exists ("
+ fileName + ")";
messages.addError(failMessage);
LOGGER.error(failMessage);
}
}

View File

@ -0,0 +1,173 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import static hirs.attestationca.portal.page.Page.DEVICES;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hibernate.criterion.Restrictions;
import hirs.FilteredRecordsList;
import hirs.data.persist.Device;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.DeviceAssociatedCertificate;
import hirs.persist.DBManager;
import hirs.persist.DeviceManager;
/**
* Controller for the Device page.
*/
@Controller
@RequestMapping("/devices")
public class DevicesPageController extends PageController<NoPageParams> {
private final DeviceManager deviceManager;
private final DBManager<Certificate> certificateDBManager;
private static final Logger LOGGER = getLogger(DevicesPageController.class);
/**
* Constructor providing the Page's display and routing specification.
* @param deviceManager the device manager
* @param certificateDBManager the certificate DB manager
*/
@Autowired
public DevicesPageController(
final DeviceManager deviceManager,
final DBManager<Certificate> certificateDBManager) {
super(DEVICES);
this.deviceManager = deviceManager;
this.certificateDBManager = certificateDBManager;
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView();
}
/**
* Returns the list of devices using the datatable input for paging, ordering, and
* filtering.
* @param input the data tables input
* @return the data tables response, including the result set and paging information
*/
@ResponseBody
@RequestMapping(value = "list", produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET)
public DataTableResponse<HashMap<String, Object>> getTableData(
final DataTableInput input) {
LOGGER.debug("Handling request for device list");
String orderColumnName = input.getOrderColumnName();
// get all the devices
FilteredRecordsList<Device> deviceList =
OrderedListQueryDataTableAdapter.getOrderedList(Device.class,
deviceManager, input, orderColumnName);
FilteredRecordsList<HashMap<String, Object>> record
= retrieveDevicesAndAssociatedCertificates(deviceList);
return new DataTableResponse<>(record, input);
}
/**
* Returns the list of devices combined with the certificates.
* @param deviceList list containing the devices
* @return a record list after the device and certificate was mapped together.
*/
private FilteredRecordsList<HashMap<String, Object>> retrieveDevicesAndAssociatedCertificates(
final FilteredRecordsList<Device> deviceList) {
FilteredRecordsList<HashMap<String, Object>> records = new FilteredRecordsList<>();
// hashmap containing the device-certificate relationship
HashMap<String, Object> deviceCertMap = new HashMap<>();
Device device;
Certificate certificate;
// parse if there is a Device
if (!deviceList.isEmpty()) {
// get a list of Certificates that contains the device IDs from the list
List<Certificate> certificateList = certificateDBManager.getList(
Certificate.class,
Restrictions.in("device.id", getDevicesIds(deviceList).toArray()));
// loop all the devices
for (int i = 0; i < deviceList.size(); i++) {
// hashmap containing the list of certificates based on the certificate type
HashMap<String, List<Object>> certificatePropertyMap = new HashMap<>();
device = deviceList.get(i);
deviceCertMap.put("device", device);
// loop all the certificates and combined the ones that match the ID
for (int j = 0; j < certificateList.size(); j++) {
certificate = certificateList.get(j);
// set the certificate if it's the same ID
if (device.getId().equals(
((DeviceAssociatedCertificate) certificate).getDevice().getId())) {
String certificateId = certificate.getClass().getSimpleName();
// create a new list for the certificate type if does not exist
// else add it to the current certificate type list
List<Object> certificateListFromMap
= certificatePropertyMap.get(certificateId);
if (certificateListFromMap != null) {
certificateListFromMap.add(certificate);
} else {
certificatePropertyMap.put(certificateId,
new ArrayList<>(Collections.singletonList(certificate)));
}
}
}
// add the device-certificate map to the record
deviceCertMap.putAll(certificatePropertyMap);
records.add(new HashMap<>(deviceCertMap));
deviceCertMap.clear();
}
}
// set pagination values
records.setRecordsTotal(deviceList.getRecordsTotal());
records.setRecordsFiltered(deviceList.getRecordsFiltered());
return records;
}
/**
* Returns the list of devices IDs.
* @param deviceList list containing the devices
* @return a list of the devices IDs
*/
private List<UUID> getDevicesIds(final FilteredRecordsList<Device> deviceList) {
List<UUID> deviceIds = new ArrayList<UUID>();
// loop all the devices
for (int i = 0; i < deviceList.size(); i++) {
deviceIds.add(deviceList.get(i).getId());
}
return deviceIds;
}
}

View File

@ -0,0 +1,61 @@
package hirs.attestationca.portal.page.controllers;
import static hirs.attestationca.portal.page.Page.HELP;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import java.io.File;
import java.io.IOException;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* Controller for the Help page.
*/
@Controller
@RequestMapping("/help")
public class HelpController extends PageController<NoPageParams> {
@Autowired
private ApplicationContext applicationContext;
private static final String PATH = "/docs";
private static final Logger LOGGER = getLogger(HelpController.class);
/**
* Constructor providing the Page's display and routing specification.
*/
public HelpController() {
super(HELP);
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
ModelAndView mav = getBaseModelAndView();
try {
File[] documents = new File(
applicationContext.getResource(PATH).getFile().getPath()
).listFiles();
mav.addObject("docs", documents);
} catch (IOException ex) {
LOGGER.error("Could not get files from resource.");
}
return mav;
}
}

View File

@ -0,0 +1,38 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.page.PageController;
import static hirs.attestationca.portal.page.Page.INDEX;
import hirs.attestationca.portal.page.params.NoPageParams;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* Controller for the Index page.
*/
@Controller
@RequestMapping("/index")
public class IndexPageController extends PageController<NoPageParams> {
/**
* Constructor providing the Page's display and routing specification.
*/
public IndexPageController() {
super(INDEX);
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView();
}
}

View File

@ -0,0 +1,338 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.model.PolicyPageModel;
import static hirs.attestationca.portal.page.Page.POLICY;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.PageMessages;
import hirs.attestationca.portal.page.params.NoPageParams;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.apache.logging.log4j.Logger;
import hirs.appraiser.Appraiser;
import hirs.appraiser.SupplyChainAppraiser;
import hirs.data.persist.SupplyChainPolicy;
import hirs.persist.AppraiserManager;
import hirs.persist.PolicyManager;
import hirs.persist.PolicyManagerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.view.RedirectView;
/**
* Controller for the Policy page.
*/
@Controller
@RequestMapping("/policy")
public class PolicyPageController extends PageController<NoPageParams> {
private static final Logger LOGGER = getLogger(PolicyPageController.class);
/**
* Represents a web request indicating to enable a setting (based on radio buttons from a
* web form).
*/
private static final String ENABLED_PARAMETER_VALUE = "checked";
private PolicyManager policyManager;
private AppraiserManager appraiserManager;
/**
* Model attribute name used by initPage for the initial data passed to the page.
*/
public static final String INITIAL_DATA = "initialData";
/**
* Flash attribute name used by initPage and post for the data forwarded during the redirect
* from the POST operation back to the page.
*/
public static final String RESULT_DATA = "resultData";
/**
* Constructor.
* @param policyManager the policy manager
* @param appraiserManager the appraiser manager
*/
@Autowired
public PolicyPageController(final PolicyManager policyManager,
final AppraiserManager appraiserManager) {
super(POLICY);
this.policyManager = policyManager;
this.appraiserManager = appraiserManager;
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
// get the basic information to render the page
ModelAndView mav = getBaseModelAndView();
SupplyChainPolicy policy = getDefaultPolicy();
PolicyPageModel pageModel = new PolicyPageModel(policy);
mav.addObject(INITIAL_DATA, pageModel);
LOGGER.debug(pageModel);
return mav;
}
/**
* Updates the Platform Cert Validation policy setting and redirects back
* to the original page.
*
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-pc-validation", method = RequestMethod.POST)
public RedirectView updatePcVal(@ModelAttribute final PolicyPageModel ppModel,
final RedirectAttributes attr) throws URISyntaxException {
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
String successMessage;
boolean pcValidationOptionEnabled =
ppModel.getPcValidate().equalsIgnoreCase(ENABLED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
// If PC policy setting change results in invalid policy, inform user
if (!isPolicyValid(policy.isEcValidationEnabled(), pcValidationOptionEnabled,
policy.isPcAttributeValidationEnabled())) {
handleUserError(model, messages,
"Unable to change Platform Validation setting,"
+ " invalid policy configuration.");
return redirectToSelf(new NoPageParams(), model, attr);
}
// set the policy option and create display message
if (pcValidationOptionEnabled) {
policy.setPcValidationEnabled(true);
successMessage = "Platform certificate validation enabled";
} else {
policy.setPcValidationEnabled(false);
successMessage = "Platform certificate validation disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
// Log and return any error messages to the user
handlePolicyManagerUpdateError(model, messages, e,
"Error changing ACA platform validation Policy",
"Error updating policy. \n" + e.getMessage());
}
return redirectToSelf(new NoPageParams(), model, attr);
}
/**
* Updates the Platform Cert Attribute Validation policy setting and redirects back
* to the original page.
*
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-pc-attribute-validation", method = RequestMethod.POST)
public RedirectView updatePcAttributeVal(@ModelAttribute final PolicyPageModel ppModel,
final RedirectAttributes attr)
throws URISyntaxException {
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
String successMessage;
boolean pcAttributeValidationOptionEnabled = ppModel.getPcAttributeValidate()
.equalsIgnoreCase(ENABLED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
// If PC Attribute Validation is enabled without PC Validation, disallow change
if (!isPolicyValid(policy.isEcValidationEnabled(),
policy.isPcValidationEnabled(), pcAttributeValidationOptionEnabled)) {
handleUserError(model, messages,
"To enable Platform Attribute Validation, Platform Credential Validation"
+ " must also be enabled.");
return redirectToSelf(new NoPageParams(), model, attr);
}
// set the policy option and create display message
if (pcAttributeValidationOptionEnabled) {
policy.setPcAttributeValidationEnabled(true);
successMessage = "Platform certificate attribute validation enabled";
} else {
policy.setPcAttributeValidationEnabled(false);
successMessage = "Platform certificate attribute validation disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
// Log and return any error messages to the user
handlePolicyManagerUpdateError(model, messages, e,
"Error changing ACA platform certificate attribute validation policy",
"Error updating policy. \n" + e.getMessage());
}
return redirectToSelf(new NoPageParams(), model, attr);
}
/**
* Updates the Endorsement Credential Validation policy setting and redirects back
* to the original page.
*
* @param ppModel The data posted by the form mapped into an object.
* @param attr RedirectAttributes used to forward data back to the original page.
* @return View containing the url and parameters
* @throws URISyntaxException if malformed URI
*/
@RequestMapping(value = "update-ec-validation", method = RequestMethod.POST)
public RedirectView updateEcVal(@ModelAttribute final PolicyPageModel ppModel,
final RedirectAttributes attr) throws URISyntaxException {
// set the data received to be populated back into the form
Map<String, Object> model = new HashMap<>();
PageMessages messages = new PageMessages();
String successMessage;
boolean ecValidationOptionEnabled =
ppModel.getEcValidate().equalsIgnoreCase(ENABLED_PARAMETER_VALUE);
try {
SupplyChainPolicy policy = getDefaultPolicyAndSetInModel(ppModel, model);
//If PC Validation is enabled without EC Validation, disallow change
if (!isPolicyValid(ecValidationOptionEnabled, policy.isPcValidationEnabled(),
policy.isPcAttributeValidationEnabled())) {
handleUserError(model, messages,
"To disable Endorsement Credential Validation, Platform Validation"
+ " must also be disabled.");
return redirectToSelf(new NoPageParams(), model, attr);
}
// set the policy option and create success message
if (ecValidationOptionEnabled) {
policy.setEcValidationEnabled(true);
successMessage = "Endorsement credential validation enabled";
} else {
policy.setEcValidationEnabled(false);
successMessage = "Endorsement credential validation disabled";
}
savePolicyAndApplySuccessMessage(ppModel, model, messages, successMessage, policy);
} catch (PolicyManagerException e) {
handlePolicyManagerUpdateError(model, messages, e,
"Error changing ACA endorsement validation policy",
"Error updating policy. \n" + e.getMessage());
}
// return the redirect
return redirectToSelf(new NoPageParams(), model, attr);
}
private void handlePolicyManagerUpdateError(final Map<String, Object> model,
final PageMessages messages,
final PolicyManagerException e,
final String message, final String error) {
LOGGER.error(message, e);
messages.addError(error);
model.put(MESSAGES_ATTRIBUTE, messages);
}
private void handleUserError(final Map<String, Object> model,
final PageMessages messages,
final String errorMessage) {
messages.addError(errorMessage);
model.put(MESSAGES_ATTRIBUTE, messages);
}
/**
* Takes in policy setting states and determines if policy configuration is valid or not.
* PC Attribute Validation must have PC Validation Enabled
* PC Validation must have EC Validation enabled
*
* @param isEcEnable EC Validation Policy State
* @param isPcEnable PC Validation Policy State
* @param isPcAttEnable PC Attribute Validation Policy State
* @return True if policy combination is valid
*/
private static boolean isPolicyValid(final boolean isEcEnable, final boolean isPcEnable,
final boolean isPcAttEnable) {
if (isPcAttEnable && !isPcEnable) {
return false;
} else {
return !isPcEnable || isEcEnable;
}
}
/**
* Helper function to get a fresh load of the default policy from the DB.
*
* @return The default Supply Chain Policy
*/
private SupplyChainPolicy getDefaultPolicy() {
final Appraiser supplyChainAppraiser = appraiserManager.getAppraiser(
SupplyChainAppraiser.NAME);
return (SupplyChainPolicy) policyManager.getDefaultPolicy(
supplyChainAppraiser);
}
/**
* Gets the default policy and applies the current values in to the page model.
* @param ppModel the page model
* @param model the map of string messages to be displayed on the view
* @return The default Supply Chain Policy
*/
private SupplyChainPolicy getDefaultPolicyAndSetInModel(
final PolicyPageModel ppModel, final Map<String, Object> model) {
// load the current default policy from the DB
SupplyChainPolicy policy = getDefaultPolicy();
// set the data received to be populated back into the form
model.put(RESULT_DATA, ppModel);
return policy;
}
private void savePolicyAndApplySuccessMessage(final PolicyPageModel ppModel,
final Map<String, Object> model,
final PageMessages messages,
final String successMessage,
final SupplyChainPolicy policy) {
// save the policy to the DB
policyManager.updatePolicy(policy);
// Log and set the success message
messages.addSuccess(successMessage);
LOGGER.debug("ACA Policy set to: " + ppModel.toString());
model.put(MESSAGES_ATTRIBUTE, messages);
}
}

View File

@ -0,0 +1,95 @@
package hirs.attestationca.portal.page.controllers;
import hirs.attestationca.portal.datatables.DataTableInput;
import hirs.attestationca.portal.datatables.DataTableResponse;
import hirs.attestationca.portal.datatables.OrderedListQueryDataTableAdapter;
import hirs.attestationca.portal.page.PageController;
import hirs.attestationca.portal.page.params.NoPageParams;
import org.apache.logging.log4j.Logger;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hibernate.Criteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import static hirs.attestationca.portal.page.Page.VALIDATION_REPORTS;
import hirs.FilteredRecordsList;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.persist.CriteriaModifier;
import hirs.persist.CrudManager;
/**
* Controller for the Validation Reports page.
*/
@Controller
@RequestMapping("/validation-reports")
public class ValidationReportsPageController extends PageController<NoPageParams> {
private final CrudManager<SupplyChainValidationSummary> supplyChainValidatorSummaryManager;
private static final Logger LOGGER = getLogger(ValidationReportsPageController.class);
/**
* Constructor providing the Page's display and routing specification.
* @param supplyChainValidatorSummaryManager the manager
*/
@Autowired
public ValidationReportsPageController(
final CrudManager<SupplyChainValidationSummary> supplyChainValidatorSummaryManager) {
super(VALIDATION_REPORTS);
this.supplyChainValidatorSummaryManager = supplyChainValidatorSummaryManager;
}
/**
* Returns the path for the view and the data model for the page.
*
* @param params The object to map url parameters into.
* @param model The data model for the request. Can contain data from redirect.
* @return the path for the view and data model for the page.
*/
@Override
@RequestMapping
public ModelAndView initPage(final NoPageParams params, final Model model) {
return getBaseModelAndView();
}
/**
* Gets the list of validation summaries per the data table input query.
* @param input the data table query.
* @return the data table response containing the supply chain summary records
*/
@ResponseBody
@RequestMapping(value = "list", produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET)
public DataTableResponse<SupplyChainValidationSummary> getTableData(
final DataTableInput input) {
LOGGER.debug("Handling request for summary list: " + input);
// attempt to get the column property based on the order index.
String orderColumnName = input.getOrderColumnName();
LOGGER.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 modifier = new CriteriaModifier() {
@Override
public void modify(final Criteria criteria) {
criteria.createAlias("device", "device");
}
};
FilteredRecordsList<SupplyChainValidationSummary> records =
OrderedListQueryDataTableAdapter.getOrderedList(SupplyChainValidationSummary.class,
supplyChainValidatorSummaryManager, input, orderColumnName, modifier);
return new DataTableResponse<>(records, input);
}
}

View File

@ -0,0 +1,4 @@
/**
* This package contains PageController implementations.
*/
package hirs.attestationca.portal.page.controllers;

View File

@ -0,0 +1,4 @@
/**
* This package provides interfaces and superclasses for PageController and associated objects.
*/
package hirs.attestationca.portal.page;

View File

@ -0,0 +1,99 @@
package hirs.attestationca.portal.page.params;
import hirs.attestationca.portal.page.PageParams;
import java.util.LinkedHashMap;
/**
* URL parameters object for the CertificateDetails page and controller.
*/
public class CertificateDetailsPageParams implements PageParams {
private String id;
private String type;
/**
* Constructor to set all Certificate Details URL parameters.
*
* @param id the String parameter to set
* @param type the Integer parameter to set
*/
public CertificateDetailsPageParams(final String id, final String type) {
this.id = id;
this.type = type;
}
/**
* Constructor to set ID Certificate Details URL parameters.
*
* @param id the String parameter to set
*/
public CertificateDetailsPageParams(final String id) {
this.id = id;
}
/**
* Default constructor for Spring.
*/
public CertificateDetailsPageParams() {
id = null;
type = null;
}
/**
* Returns the String id parameter.
*
* @return the String id parameter.
*/
public String getId() {
return id;
}
/**
* Sets the String id parameter.
*
* @param id the String id parameter.
*/
public void setId(final String id) {
this.id = id;
}
/**
* Returns the String type parameter.
*
* @return the String type parameter.
*/
public String getType() {
return type;
}
/**
* Sets the String type parameter.
*
* @param type the String type parameter.
*/
public void setType(final String type) {
this.type = type;
}
/**
* Allows PageController to iterate over the url parameters.
*
* @return map containing the object's URL parameters.
*/
@Override
public LinkedHashMap<String, ?> asMap() {
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("id", id);
map.put("type", type);
return map;
}
@Override
public String toString() {
return "CertificateDetailsPageParams{"
+ "id:' " + id + "',"
+ "type: " + type
+ "}";
}
}

View File

@ -0,0 +1,21 @@
package hirs.attestationca.portal.page.params;
import hirs.attestationca.portal.page.PageParams;
import java.util.LinkedHashMap;
/**
* Minimal implementation of PageParams for pages that do not have url parameters.
*/
public class NoPageParams implements PageParams {
/**
* Returns empty map so when iteration is required, nothing happens.
*
* @return empty map.
*/
@Override
public LinkedHashMap<String, ?> asMap() {
return new LinkedHashMap<>();
}
}

View File

@ -0,0 +1,4 @@
/**
* This package contains url parameter classes for portal pages.
*/
package hirs.attestationca.portal.page.params;

View File

@ -0,0 +1,92 @@
package hirs.attestationca.portal.persistence;
import hirs.data.persist.SupplyChainValidationSummary;
import hirs.persist.AppraiserManager;
import hirs.persist.CrudManager;
import hirs.persist.DBAppraiserManager;
import hirs.persist.DBCertificateManager;
import hirs.persist.DBDeviceGroupManager;
import hirs.persist.DBDeviceManager;
import hirs.persist.DBManager;
import hirs.persist.DBPolicyManager;
import hirs.persist.DeviceGroupManager;
import hirs.persist.DeviceManager;
import hirs.persist.HibernateConfiguration;
import hirs.persist.PolicyManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
/**
* Spring configuration class for persistence beans used by the Attestation CA Portal.
*/
@Configuration
@Import({ HibernateConfiguration.class })
public class PersistenceConfiguration {
@Autowired
private LocalSessionFactoryBean sessionFactory;
/**
* Creates a {@link PolicyManager} ready to use.
*
* @return {@link PolicyManager}
*/
@Bean
public PolicyManager policyManager() {
return new DBPolicyManager(sessionFactory.getObject());
}
/**
* Creates a {@link DeviceManager} ready to use.
*
* @return {@link DeviceManager}
*/
@Bean
public DeviceManager deviceManager() {
return new DBDeviceManager(sessionFactory.getObject());
}
/**
* Creates a {@link DBCertificateManager} ready to use.
*
* @return {@link DBCertificateManager}
*/
@Bean
public DBCertificateManager certificateManager() {
return new DBCertificateManager(sessionFactory.getObject());
}
/**
* Creates a {@link AppraiserManager} ready to use.
*
* @return {@link AppraiserManager}
*/
@Bean
public AppraiserManager appraiserManager() {
return new DBAppraiserManager(sessionFactory.getObject());
}
/**
* Creates a {@link DeviceGroupManager} ready to use.
*
* @return {@link DeviceGroupManager}
*/
@Bean
public DeviceGroupManager deviceGroupManager() {
return new DBDeviceGroupManager(sessionFactory.getObject());
}
/**
* Creates a {@link DBManager} for SupplyChainValidationSummary persistence, ready for use.
*
* @return {@link DBManager}
*/
@Bean
public CrudManager<SupplyChainValidationSummary> supplyChainValidationSummaryManager() {
return new DBManager<>(SupplyChainValidationSummary.class, sessionFactory.getObject());
}
}

View File

@ -0,0 +1,4 @@
/**
* Persistence related classes for Attestation CA Portal.
*/
package hirs.attestationca.portal.persistence;

View File

@ -0,0 +1,353 @@
package hirs.attestationca.portal.util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.UUID;
import hirs.data.persist.certificate.Certificate;
import hirs.data.persist.certificate.CertificateAuthorityCredential;
import hirs.data.persist.certificate.EndorsementCredential;
import hirs.data.persist.certificate.IssuedAttestationCertificate;
import hirs.data.persist.certificate.PlatformCredential;
import hirs.data.persist.certificate.attributes.PlatformConfiguration;
import hirs.persist.CertificateManager;
/**
* Utility class for mapping certificate information in to string maps. These are used to display
* information on a web page, as X509 cert classes do not serialize to JSON
*/
public final class CertificateStringMapBuilder {
private static final Logger LOGGER =
LogManager.getLogger(CertificateStringMapBuilder.class);
private CertificateStringMapBuilder() {
}
/**
* Returns the general information.
*
* @param certificate certificate to get the general information.
* @param certificateManager the certificate manager for retrieving certs.
* @return a hash map with the general certificate information.
*/
public static HashMap<String, String> getGeneralCertificateInfo(
final Certificate certificate, final CertificateManager certificateManager) {
HashMap<String, String> data = new HashMap<>();
if (certificate != null) {
data.put("issuer", certificate.getIssuer());
//Serial number in hex value
data.put("serialNumber", Long.toHexString(certificate.getSerialNumber().longValue()));
data.put("beginValidity", certificate.getBeginValidity().toString());
data.put("endValidity", certificate.getEndValidity().toString());
data.put("signature", Arrays.toString(certificate.getSignature()));
if (certificate.getSubject() != null) {
data.put("subject", certificate.getSubject());
data.put("isSelfSigned",
String.valueOf(certificate.getIssuer().equals(certificate.getSubject())));
} else {
data.put("isSelfSigned", "false");
}
if (certificate.getEncodedPublicKey() != null) {
data.put("encodedPublicKey",
Arrays.toString(certificate.getEncodedPublicKey()));
}
//Get issuer ID if not self signed
if (data.get("isSelfSigned").equals("false")) {
//Get the missing certificate chain for not self sign
Certificate missingCert = containsAllChain(certificate, certificateManager);
if (missingCert != null) {
data.put("missingChainIssuer", missingCert.getIssuer());
}
//Find all certificates that could be the issuer certificate based on subject name
for (Certificate issuerCert:CertificateAuthorityCredential
.select(certificateManager)
.bySubject(certificate.getIssuer())
.getCertificates()) {
try {
//Find the certificate that actually signed this cert
if (certificate.isIssuer(issuerCert)) {
data.put("issuerID", issuerCert.getId().toString());
break;
}
} catch (IOException e) {
LOGGER.error(e);
}
}
}
}
return data;
}
/**
* Recursive function that check if all the certificate chain is present.
*
* @param certificate certificate to get the issuer
* @param certificateManager the certificate manager for retrieving certs.
* @return a boolean indicating if it has the full chain or not.
*/
public static Certificate containsAllChain(
final Certificate certificate,
final CertificateManager certificateManager) {
Set<CertificateAuthorityCredential> issuerCertificates;
//Check if there is a subject organization
if (certificate.getIssuerOrganization() == null
|| certificate.getIssuerOrganization().isEmpty()) {
//Get certificates by subject
issuerCertificates = CertificateAuthorityCredential.select(certificateManager)
.bySubject(certificate.getIssuer())
.getCertificates();
} else {
//Get certificates by subject organization
issuerCertificates = CertificateAuthorityCredential.select(certificateManager)
.bySubjectOrganization(certificate.getIssuerOrganization())
.getCertificates();
}
for (Certificate issuerCert: issuerCertificates) {
try {
// Find the certificate that actually signed this cert
if (certificate.isIssuer(issuerCert)) {
//Check if it's root certificate
if (issuerCert.getIssuer().equals(issuerCert.getSubject())) {
return null;
}
return containsAllChain(issuerCert, certificateManager);
}
} catch (IOException e) {
LOGGER.error(e);
return certificate;
}
}
return certificate;
}
/**
* Returns the Certificate Authority information.
*
* @param uuid ID for the certificate.
* @param certificateManager the certificate manager for retrieving certs.
* @return a hash map with the endorsement certificate information.
*/
public static HashMap<String, String> getCertificateAuthorityInformation(final UUID uuid,
final CertificateManager certificateManager) {
CertificateAuthorityCredential certificate =
CertificateAuthorityCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
String notFoundMessage = "Unable to find Certificate Authority "
+ "Credential with ID: " + uuid;
return getCertificateAuthorityInfoHelper(certificateManager, certificate, notFoundMessage);
}
/**
* Returns the Trust Chain credential information.
*
* @param certificate the certificate
* @param certificateManager the certificate manager for retrieving certs.
* @return a hash map with the endorsement certificate information.
*/
public static HashMap<String, String> getCertificateAuthorityInformation(
final CertificateAuthorityCredential certificate,
final CertificateManager certificateManager) {
return getCertificateAuthorityInfoHelper(certificateManager, certificate,
"No cert provided for mapping");
}
private static HashMap<String, String> getCertificateAuthorityInfoHelper(
final CertificateManager certificateManager,
final CertificateAuthorityCredential certificate, final String notFoundMessage) {
HashMap<String, String> data = new HashMap<>();
if (certificate != null) {
data.putAll(getGeneralCertificateInfo(certificate, certificateManager));
data.put("subjectKeyIdentifier",
Arrays.toString(certificate.getSubjectKeyIdentifier()));
} else {
LOGGER.error(notFoundMessage);
}
return data;
}
/**
* Returns the endorsement credential information.
*
* @param uuid ID for the certificate.
* @param certificateManager the certificate manager for retrieving certs.
* @return a hash map with the endorsement certificate information.
*/
public static HashMap<String, String> getEndorsementInformation(final UUID uuid,
final CertificateManager certificateManager) {
HashMap<String, String> data = new HashMap<>();
EndorsementCredential certificate = EndorsementCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
if (certificate != null) {
data.putAll(getGeneralCertificateInfo(certificate, certificateManager));
// Set extra fields
data.put("credentialType", certificate.getCredentialType());
data.put("manufacturer", certificate.getManufacturer());
data.put("model", certificate.getModel());
data.put("version", certificate.getVersion());
data.put("policyReference", certificate.getPolicyReference());
data.put("revocationLocator", certificate.getRevocationLocator());
// Add hashmap with TPM information if available
if (certificate.getTpmSpecification() != null) {
data.putAll(
convertStringToHash(certificate.getTpmSpecification().toString()));
}
if (certificate.getTpmSecurityAssertions() != null) {
data.putAll(
convertStringToHash(certificate.getTpmSecurityAssertions().toString()));
}
} else {
String notFoundMessage = "Unable to find Endorsement Credential "
+ "with ID: " + uuid;
LOGGER.error(notFoundMessage);
}
return data;
}
/**
* Returns the Platform credential information.
*
* @param uuid ID for the certificate.
* @param certificateManager the certificate manager for retrieving certs.
* @return a hash map with the endorsement certificate information.
* @throws IOException when parsing the certificate
* @throws IllegalArgumentException invalid argument on parsing the certificate
*/
public static HashMap<String, Object> getPlatformInformation(final UUID uuid,
final CertificateManager certificateManager)
throws IllegalArgumentException, IOException {
HashMap<String, Object> data = new HashMap<>();
PlatformCredential certificate = PlatformCredential
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
if (certificate != null) {
data.putAll(getGeneralCertificateInfo(certificate, certificateManager));
data.put("credentialType", certificate.getCredentialType());
data.put("manufacturer", certificate.getManufacturer());
data.put("model", certificate.getModel());
data.put("version", certificate.getVersion());
data.put("platformSerial", certificate.getPlatformSerial());
data.put("chassisSerialNumber", certificate.getChassisSerialNumber());
data.put("platformClass", certificate.getPlatformClass());
data.put("majorVersion",
Integer.toString(certificate.getMajorVersion()));
data.put("minorVersion",
Integer.toString(certificate.getMinorVersion()));
data.put("revisionLevel",
Integer.toString(certificate.getRevisionLevel()));
//x509 credential version
data.put("x509Version", certificate.getX509CredentialVersion());
//CPSuri
data.put("CPSuri", certificate.getCPSuri());
//Get platform Configuration values and set map with it
PlatformConfiguration platformConfiguration = certificate.getPlatformConfiguration();
if (platformConfiguration != null) {
//Component Identifier
data.put("componentsIdentifier", platformConfiguration.getComponentIdentifier());
//Platform Properties
data.put("platformProperties", platformConfiguration.getPlatformProperties());
//Platform Properties URI
data.put("platformPropertiesURI", platformConfiguration.getPlatformPropertiesUri());
}
//TBB Security Assertion
data.put("tbbSecurityAssertion", certificate.getTBBSecurityAssertion());
} else {
String notFoundMessage = "Unable to find Platform Credential "
+ "with ID: " + uuid;
LOGGER.error(notFoundMessage);
}
return data;
}
/**
* Returns a HasHMap of a string.
* Ex: input "TPMSpecification{family='abc',level=0, revision=0}"
* output map[TPMSpecificationFamily] = 'abc'
* map[TPMSpecificationLevel] = 0
* map[TPMSpecificationRevision] = 0
*
* @param str HashMap string to be converted.
* @return a hash map with key-value pairs from the string
*/
private static HashMap<String, String> convertStringToHash(final String str) {
HashMap<String, String> map = new HashMap<>();
String name = str.substring(0, str.indexOf('{')).trim();
String data = str.trim().substring(str.trim().indexOf('{') + 1,
str.trim().length() - 1);
// Separate key and value and parse the key
for (String pair: data.split(",")) {
String[] keyValue = pair.split("=");
// Remove white space and change firt charater in the key to uppsercase
keyValue[0] = Character.toUpperCase(
keyValue[0].trim().charAt(0)) + keyValue[0].trim().substring(1);
map.put(name + keyValue[0], keyValue[1].trim());
}
return map;
}
/**
* Returns the Issued Attestation Certificate information.
*
* @param uuid ID for the certificate.
* @param certificateManager the certificate manager for retrieving certs.
* @return a hash map with the endorsement certificate information.
*/
public static HashMap<String, String> getIssuedInformation(final UUID uuid,
final CertificateManager certificateManager) {
HashMap<String, String> data = new HashMap<>();
IssuedAttestationCertificate certificate = IssuedAttestationCertificate
.select(certificateManager)
.byEntityId(uuid)
.getCertificate();
if (certificate != null) {
data.putAll(getGeneralCertificateInfo(certificate, certificateManager));
// add endorsement credential ID if not null
if (certificate.getEndorsementCredential() != null) {
data.put("endorsementID",
certificate.getEndorsementCredential().getId().toString());
}
// add platform credential IDs if not empty
if (!certificate.getPlatformCredentials().isEmpty()) {
StringBuilder buf = new StringBuilder();
for (PlatformCredential pc: certificate.getPlatformCredentials()) {
buf.append(pc.getId().toString());
buf.append(',');
}
// remove last comma character
buf.deleteCharAt(buf.lastIndexOf(","));
data.put("platformID", buf.toString());
}
} else {
String notFoundMessage = "Unable to find Issued Attestation Certificate "
+ "with ID: " + uuid;
LOGGER.error(notFoundMessage);
}
return data;
}
}

View File

@ -0,0 +1,4 @@
/**
* Utilities classes for the Attestation CA Portal.
*/
package hirs.attestationca.portal.util;

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%C.%M] %-5p : %m%n"/>
</Console>
<RollingFile name="FILE" fileName="logs/HIRS_AttestationCA_Portal.log"
filePattern="logs/HIRS_AttestationCA_Portal.log-%d{yyyy-MM-dd}-%i.log" >
<PatternLayout>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%C.%M] %-5p : %m%n</pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level = "WARN">
<AppenderRef ref="STDOUT" level="WARN" />
<AppenderRef ref="FILE"/>
</Root>
<Logger name="org.hibernate" level="WARN" />
</Loggers>
<!-- prevents an out-of-memory exception caused by the debug logging of very large inserts -->
<category name="org.hibernate.event.def.AbstractFlushingEventListener">
<priority value="INFO"/>
</category>
</Configuration>

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Created-By: 1.7.0_55 (Oracle Corporation)

View File

@ -0,0 +1,617 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="fn" uri = "http://java.sun.com/jsp/jstl/functions" %>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%@taglib prefix="fn" uri = "http://java.sun.com/jsp/jstl/functions"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="style">
<link type="text/css" rel="stylesheet" href="${common}/certificate_details.css"/>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">
<c:choose>
<c:when test="${param.type=='certificateauthority'}">
Certificate Authority
<a href="${portal}/certificate-request/trust-chain/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
</a>
</c:when>
<c:when test="${param.type=='endorsement'}">
Endorsement Certificate
<a href="${portal}/certificate-request/endorsement-key-credentials/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
</a>
</c:when>
<c:when test="${param.type=='platform'}">
Platform Certificate
<a href="${portal}/certificate-request/platform-credentials/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
</a>
</c:when>
<c:when test="${param.type=='issued'}">
Issued Attestation Certificates
<a href="${portal}/certificate-request/issued-certificates/download?id=${param.id}">
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
</a>
</c:when>
<c:otherwise>
Unknown Certificate
</c:otherwise>
</c:choose>
</jsp:attribute>
<jsp:body>
<div id="certificate-details-page" class="container-fluid">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Issuer</span></div>
<div id="issuer" class="col col-md-8">
<!-- Display the issuer, and provide a link to the issuer details if provided -->
<c:choose>
<c:when test="${not empty initialData.issuerID}">
<a href="${portal}/certificate-details?id=${initialData.issuerID}&type=certificateauthority">
${initialData.issuer}
</a>
</c:when>
<c:otherwise>
${initialData.issuer}
</c:otherwise>
</c:choose>
<c:if test="${param.type!='issued'}">
<span class="chainIcon">
<!-- Icon with link for missing certificate for the chain -->
<c:choose>
<c:when test="${initialData.isSelfSigned == 'true'}">
<img src="${icons}/ic_all_inclusive_black_24dp.png"
title="Self sign certificate.">
</c:when>
<c:when test="${empty initialData.missingChainIssuer}">
<img src="${icons}/ic_checkbox_marked_circle_black_green_24dp.png"
title="All certificates in the chain were found.">
</c:when>
<c:otherwise>
<img src="${icons}/ic_error_red_24dp.png"
title="Missing ${initialData.missingChainIssuer} from the chain.">
</c:otherwise>
</c:choose>
</span>
</c:if>
</div>
</div>
<c:if test="${not empty initialData.subject}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Subject</span></div>
<div id="subject" class="col col-md-8">${initialData.subject}</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Serial Number</span></div>
<div id="serialNumber" class="col col-md-8"></div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Validity</span></div>
<div id="validity" class="col col-md-8">
<div>Not Before:&nbsp;<span>${initialData.beginValidity}</span></div>
<div>Not After:&nbsp;<span>${initialData.endValidity}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Signature</span></div>
<div id="signature" class="col col-md-8"></div>
</div>
<c:if test="${not empty initialData.encodedPublicKey}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Public Key</span></div>
<div id="encodedPublicKey" class="col col-md-8"></div>
</div>
</c:if>
<!-- Add the different fields based on the certificate type -->
<c:choose>
<c:when test="${param.type=='certificateauthority'}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Subject Key Identifier</span></div>
<div id="subjectKeyIdentifier" class="col col-md-8"></div>
</div>
</c:when>
<c:when test="${param.type=='endorsement'}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Credential Type</span></div>
<div id="credentialType" class="col col-md-8">${initialData.credentialType}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Manufacturer</span></div>
<div id="manufacturer" class="col col-md-8">${initialData.manufacturer}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Model</span></div>
<div id="model" class="col col-md-8">${initialData.model}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Version</span></div>
<div id="version" class="col col-md-8">${initialData.version}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Policy Reference</span></div>
<div id="policyReference" class="col col-md-8">${initialData.policyReference}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Revocation Locator</span></div>
<div id="revocationLocator" class="col col-md-8">${initialData.revocationLocator}</div>
</div>
<!-- Need to test this -->
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TPM Specification</span></div>
<div id="tpmSpecification" class="col col-md-8">
<div>Family:&nbsp;<span>${initialData.TPMSpecificationFamily}</span></div>
<div>Level:&nbsp;<span>${initialData.TPMSpecificationLevel}</span></div>
<div>Revision:&nbsp;<span>${initialData.TPMSpecificationRevision}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TPM Security Assertion</span></div>
<div id="tpmSecurityAssertion" class="col col-md-8">
<div>Version:&nbsp;<span>${initialData.TPMSecurityAssertionsVersion}</span></div>
<div>Field Upgradeable:&nbsp;<span>${initialData.TPMSecurityAssertionsFieldUpgradeable}</span></div>
<div>ek Generation Type:&nbsp;<span>${initialData.TPMSecurityAssertionsEkGenType}</span></div>
<div>ek Generation Location:&nbsp;<span>${initialData.TPMSecurityAssertionsEkGenLoc}</span></div>
<div>ek Certificate Generation Location:&nbsp;<span>${initialData.TPMSecurityAssertionsEkCertGenLoc}</span></div>
</div>
</div>
</c:when>
<c:when test="${param.type=='platform'}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">X509 Credential Version</span></div>
<div id="credentialVersion" class="col col-md-8 vertical">${initialData.x509Version} (v${initialData.x509Version + 1})</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Credential Type</span></div>
<div id="credentialType" class="col col-md-8">${initialData.credentialType}</div>
</div>
<c:if test="${not empty initialData.CPSuri}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Certification Practice Statement URI</span></div>
<div id="credentialType" class="col col-md-8 vertical">
<a href="${initialData.CPSuri}"> ${initialData.CPSuri}</a>
</div>
</div>
</c:if>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Manufacturer</span></div>
<div id="manufacturer" class="col col-md-8">${initialData.manufacturer}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Model</span></div>
<div id="model" class="col col-md-8">${initialData.model}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Version</span></div>
<div id="version" class="col col-md-8">${initialData.version}</div>
</div>
<c:choose>
<c:when test="${fn:contains(initialData.credentialType, 'TCG')}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">System Serial Number</span></div>
<div id="platformSerial" class="col col-md-8 vertical">${initialData.platformSerial}</div>
</div>
</c:when>
<c:otherwise>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Platform Serial Number</span></div>
<div id="platformSerial" class="col col-md-8 vertical">${initialData.platformSerial}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Chassis Serial Number</span></div>
<div id="chassisSerialNumber" class="col col-md-8 vertical">${initialData.chassisSerialNumber}</div>
</div>
</c:otherwise>
</c:choose>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TCG Credential Specification Version</span></div>
<div id="majorVersion" class="col col-md-8 vertical">${initialData.majorVersion}.${initialData.minorVersion}.${initialData.revisionLevel}</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Platform Class</span></div>
<div id="platformClass" class="col col-md-8">${initialData.platformClass}</div>
</div>
<!-- TBB Security Assertion-->
<c:if test="${not empty initialData.tbbSecurityAssertion}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TBB Security Assertion</span></div>
<div id="tbbsecurity" class="col col-md-8">
<div class="tbbsecurityLine">
<span class="fieldHeader">Version:</span>
<span class="fieldValue">${initialData.tbbSecurityAssertion.getVersion()}&nbsp;(v${initialData.tbbSecurityAssertion.getVersion().getValue() + 1})</span>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">RTM (Root of Trust of Measurement):</span>
<span class="fieldValue">${fn:toUpperCase(initialData.tbbSecurityAssertion.getRtmType().getValue())}</span>
</div>
<!-- CCINFO -->
<c:if test="${not empty initialData.tbbSecurityAssertion.getCcInfo()}">
<c:set var="ccinfo" value="${initialData.tbbSecurityAssertion.getCcInfo()}" />
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#tbbsecurity" class="collapsed"
href="#ccinfocollapse" aria-expanded="false" aria-controls="ccinfocollapse">
Common Criteria Measures Information
</a>
</h4>
</div>
<div id="ccinfocollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
<div class="panel-body">
<div id="ccinfo" class="row">
<div class="tbbsecurityLine">
<span class="fieldHeader">Version:</span>
<span class="fieldValue">${ccinfo.getVersion()}</span>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">Assurance Level:</span>
<span class="fieldValue">${fn:toUpperCase(ccinfo.getAssurancelevel().getValue())}</span>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">Evaluation Status:</span>
<span class="fieldValue">${fn:toUpperCase(ccinfo.getEvaluationStatus().getValue())}</span>
</div>
<div class="tbbsecurityLine">
<c:choose>
<c:when test="${ccinfo.getPlus()=='TRUE'}">
<span class="label label-success">Plus</span>
</c:when>
<c:otherwise>
<span class="label label-danger">Not Plus</span>
</c:otherwise>
</c:choose>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">Strength of Function:</span>
<span class="fieldValue">${fn:toUpperCase(ccinfo.getStrengthOfFunction().getValue())}</span>
</div>
<c:if test="${not empty ccinfo.getProfileOid()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Profile OID:</span>
<span class="fieldValue">${ccinfo.getProfileOid()}</span>
</div>
</c:if>
<c:if test="${not empty ccinfo.getProfileUri()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Profile URI:</span>
<span class="fieldValue">
<a href="${ccinfo.getProfileUri().getUniformResourceIdentifier()}">
${ccinfo.getProfileUri().getUniformResourceIdentifier()}
</a>
</span>
</div>
<c:if test="${not empty ccinfo.getProfileUri().getHashAlgorithm()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Profile Hash Algorithm:</span>
<span class="fieldValue">${ccinfo.getProfileUri().getHashAlgorithm()}</span>
</div>
</c:if>
<c:if test="${not empty ccinfo.getProfileUri().getHashValue()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Profile Hash Value:</span>
<span class="fieldValue">${ccinfo.getProfileUri().getHashValue()}</span>
</div>
</c:if>
</c:if>
<c:if test="${not empty ccinfo.getTargetOid()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Target OID:</span>
<span class="fieldValue">${ccinfo.getTargetOid()}</span>
</div>
</c:if>
<c:if test="${not empty ccinfo.getTargetUri()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Target URI:</span>
<span class="fieldValue">
<a href="${ccinfo.getTargetUri().getUniformResourceIdentifier()}">
${ccinfo.getTargetUri().getUniformResourceIdentifier()}
</a>
</span>
</div>
<c:if test="${not empty ccinfo.getTargetUri().getHashAlgorithm()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Target Hash Algorithm:</span>
<span class="fieldValue">${ccinfo.getTargetUri().getHashAlgorithm()}</span>
</div>
</c:if>
<c:if test="${not empty ccinfo.getTargetUri().getHashValue()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">Target Hash Value:</span>
<span class="fieldValue">${ccinfo.getTargetUri().getHashValue()}</span>
</div>
</c:if>
</c:if>
</div>
</div>
</div>
</div>
</c:if>
<!-- FIPS Level -->
<c:if test="${not empty initialData.tbbSecurityAssertion.getFipsLevel()}">
<c:set var="fipslevel" value="${initialData.tbbSecurityAssertion.getFipsLevel()}" />
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#tbbsecurity" class="collapsed"
href="#fipscollapse" aria-expanded="false" aria-controls="fipscollapse">
FIPS Level
</a>
</h4>
</div>
<div id="fipscollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
<div class="panel-body">
<div id="fipsLevel" class="row">
<div class="tbbsecurityLine">
<span class="fieldHeader">Version:</span>
<span class="fieldValue">${fipslevel.getVersion()}</span>
</div>
<div class="tbbsecurityLine">
<span class="fieldHeader">Level:</span>
<span class="fieldValue">${fn:toUpperCase(fipslevel.getLevel().getValue())}</span>
</div>
<div class="tbbsecurityLine">
<c:choose>
<c:when test="${fipslevel.getPlus()=='TRUE'}">
<span class="label label-success">Plus</span>
</c:when>
<c:otherwise>
<span class="label label-danger">Not Plus</span>
</c:otherwise>
</c:choose>
</div>
</div>
</div>
</div>
</div>
</c:if>
<!-- ISO9000 isCertified and URI -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#tbbsecurity" class="collapsed"
href="#iso9000collapse" aria-expanded="false" aria-controls="iso9000collapse">
ISO 9000
</a>
</h4>
</div>
<div id="iso9000collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree">
<div class="panel-body">
<div id="iso9000" class="row">
<div class="tbbsecurityLine">
<c:choose>
<c:when test="${initialData.tbbSecurityAssertion.getIso9000Certified()=='TRUE'}">
<span class="label label-success">ISO 9000 Certified</span>
</c:when>
<c:otherwise>
<span class="label label-danger">ISO 9000 Not Certified</span>
</c:otherwise>
</c:choose>
</div>
<c:if test="${not empty initialData.tbbSecurityAssertion.getIso9000Uri()}">
<div class="tbbsecurityLine">
<span class="fieldHeader">URI:</span>
<span class="fieldValue">
<a href="${initialData.tbbSecurityAssertion.getIso9000Uri()}">
${initialData.tbbSecurityAssertion.getIso9000Uri()}
</a>
</span>
</div>
</c:if>
</div>
</div>
</div>
</div>
</div>
</div>
</c:if>
<!-- For PC 2.0 -->
<c:if test="${fn:contains(initialData.credentialType, 'TCG')}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">TCG Platform Configuration</span></div>
<div id="platformConfiguration" class="col col-md-8">
<c:if test="${not empty initialData.componentsIdentifier}">
<!-- Component Identifier -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#platformConfiguration" class="collapsed"
href="#componentIdentifiercollapse" aria-expanded="true" aria-controls="componentIdentifiercollapse">
Components
</a>
</h4>
</div>
<div id="componentIdentifiercollapse" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne" aria-expanded="true">
<div class="panel-body">
<div id="componentIdentifier" class="row">
<c:forEach items="${initialData.componentsIdentifier}" var="component">
<div class="component col col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<span data-toggle="tooltip" data-placement="top" title="Manufacturer">${component.getComponentManufacturer()}</span>&nbsp;-&nbsp;
<span data-toggle="tooltip" data-placement="top" title="Model">${component.getComponentModel()}</span>
</div>
<div class="panel-body">
<c:if test="${not empty fn:trim(component.getComponentSerial())}">
<span class="fieldHeader">Serial Number:</span>
<span class="fieldValue">${component.getComponentSerial()}</span><br/>
</c:if>
<c:if test="${not empty fn:trim(component.getComponentRevision())}">
<span class="fieldHeader">Revision:</span>
<span class="fieldValue">${component.getComponentRevision()}</span><br/>
</c:if>
<c:forEach items="${component.getComponentAddress()}" var="address">
<span class="fieldHeader">${address.getAddressTypeValue()} address:</span>
<span class="fieldValue">${address.getAddressValue()}</span><br/>
</c:forEach>
<c:choose>
<c:when test="${component.getFieldReplaceable()=='TRUE'}">
<span class="label label-success">Replaceable</span><br/>
</c:when>
<c:otherwise>
<span class="label label-danger">Irreplaceable</span><br/>
</c:otherwise>
</c:choose>
</div>
</div>
</div>
</c:forEach>
</div>
</div>
</div>
</div>
</c:if>
<c:if test="${not empty initialData.platformProperties}">
<!-- Platform Properties -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingTwo">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#platformConfiguration" class="collapsed"
href="#platformPropertiescollapse" aria-expanded="false" aria-controls="platformPropertiescollapse">
Platform Properties
</a>
</h4>
</div>
<div id="platformPropertiescollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingTwo">
<div class="panel-body">
<div id="platformProperties" class="row">
<c:forEach items="${initialData.platformProperties}" var="property">
<div class="component col col-md-4">
<div class="panel panel-default">
<div class="panel-body">
<span class="fieldHeader">Name:</span>
<span class="fieldValue">${property.getPropertyName()}</span><br/>
<span class="fieldHeader">Value:</span>
<span class="fieldValue">${property.getPropertyValue()}</span><br/>
</div>
</div>
</div>
</c:forEach>
</div>
</div>
</div>
</div>
</c:if>
<c:if test="${not empty initialData.platformPropertiesURI}">
<!-- Platform Properties URI -->
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" data-parent="#platformConfiguration" class="collapsed"
href="#platformPropertiesURIcollapse" aria-expanded="false" aria-controls="platformPropertiesURIcollapse">
Platform Properties URI
</a>
</h4>
</div>
<div id="platformPropertiesURIcollapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree">
<div class="panel-body">
<div id="platformPropertiesURI" class="row">
<span class="fieldHeader">URI:</span>
<a href="${initialData.platformPropertiesURI.getUniformResourceIdentifier()}">
${initialData.platformPropertiesURI.getUniformResourceIdentifier()}
</a>
<c:if test="${not empty initialData.platformPropertiesURI.getHashAlgorithm()}">
<span class="fieldHeader">Hash Algorithm:</span>
<span>${initialData.platformPropertiesURI.getHashAlgorithm()}</span>
</c:if>
<c:if test="${not empty initialData.platformPropertiesURI.getHashValue()}">
<span class="fieldHeader">Hash Value:</span>
<span>${initialData.platformPropertiesURI.getHashValue()}</span>
</c:if>
</div>
</div>
</div>
</div>
</c:if>
</div><!-- close platformConfiguration -->
</div> <!-- Close row -->
</c:if>
</c:when>
<c:when test="${param.type=='issued'}">
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Endorsement Credential</span></div>
<div id="endorsementID" class="col col-md-8">
<c:if test="${not empty initialData.endorsementID}">
<a href="${portal}/certificate-details?id=${initialData.endorsementID}&type=endorsement">
<img src="${icons}/ic_vpn_key_black_24dp.png">
</a>
</c:if>
</div>
</div>
<div class="row">
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Platform Credentials</span></div>
<div id="platformID" class="col col-md-8">
<c:if test="${not empty initialData.platformID}">
<c:forTokens items = "${initialData.platformID}" delims = "," var = "pcID">
<a href="${portal}/certificate-details?id=${pcID}&type=platform">
<img src="${icons}/ic_important_devices_black_24dp.png">
</a>
</c:forTokens>
</c:if>
</div>
</div>
</c:when>
</c:choose>
</div>
<script>
$(document).ready(function() {
var type = "${param.type}";
var signature = ${initialData.signature};
var serialNumber = '${initialData.serialNumber}';
//Format validity time
$("#validity span").each(function(){
$(this).text(formatDateTime($(this).text()));
});
//Convert byte array to string
$("#signature").html(byteToHexString(signature));
//Convert byte array to string
$("#serialNumber").html(parseSerialNumber(serialNumber));
<c:if test="${not empty initialData.encodedPublicKey}">
//Change publick key byte to hex
var publicKey = ${initialData.encodedPublicKey};
$("#encodedPublicKey").html(byteToHexString(publicKey));
</c:if>
<c:if test="${not empty initialData.subjectKeyIdentifier}">
//Change subject byte to hex only for CACertificate
if(type === "certificateauthority"){
var subjectKeyIdentifier = ${initialData.subjectKeyIdentifier};
$("#subjectKeyIdentifier").html(byteToHexString(subjectKeyIdentifier));
}
</c:if>
//Initiliaze tooltips
$('[data-toggle="tooltip"]').tooltip();
//Vertical alignment on data columns
$('.vertical').each(function(){
$(this).css({
'line-height': $(this).height() + 'px'
});
});
//Change link width
$("#headingOne, #headingTwo, #headingThree").each(function(e) {
var width = $(this).width();
//Get link width
var linkWidth = $(this).find('a').width();
//Change width for the link
$(this).find('a').css({
"padding-right": (width-linkWidth) + "px"
});
});
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,138 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Device Listing</jsp:attribute>
<jsp:body>
<!-- text and icon resource variables -->
<c:set var="passIcon" value="${icons}/ic_checkbox_marked_circle_black_green_24dp.png"/>
<c:set var="failIcon" value="${icons}/ic_error_red_24dp.png"/>
<c:set var="errorIcon" value=".${icons}/ic_error_black_24dp.png"/>
<c:set var="unknownIcon" value="${icons}/ic_questionmark_circle_orange_24dp.png"/>
<c:set var="passText" value="Validation Passed"/>
<c:set var="failText" value="Validation Failed"/>
<c:set var="errorText" value="Validation Error"/>
<c:set var="unknownText" value="Unknown Validation Status"/>
<div class="aca-data-table">
<table id="deviceTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Validation Status</th>
<th rowspan="2">Hostname</th>
<th colspan="3">Credentials</th>
</tr>
<tr>
<th>Issued Attestation</th>
<th>Platform</th>
<th>Endorsement</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = portal + '/devices/list';
var columns = [
{
data: 'supplyChainValidationStatus',
searchable:false,
render: function(data, type, full, meta) {
var html = '';
switch(full.device.supplyChainStatus){
case "PASS":
html= '<img src="${passIcon}" title="${passText}">';
break;
case "FAIL":
html = '<img src="${failIcon}" title="${failText}"/>';
break;
case "ERROR":
html = '<img src="${errorIcon}" title="${errorText}">';
break;
default:
html = '<img src="${unknownIcon}" title="${unknownText}">';
break;
}
return html;
}
},
{
data: 'name',
render: function (data, type, full, meta) {
return full.device.name;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display issued attestation certificate
if(full.IssuedAttestationCertificate === undefined) return '';
var size = full.IssuedAttestationCertificate.length;
var html = '';
for(var i = 0; i < size; i++) {
var id = full.IssuedAttestationCertificate[i].id;
html += certificateDetailsLink('issued', id, false);
}
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display platform credential
if(full.PlatformCredential === undefined) return '';
var size = full.PlatformCredential.length;
var html = '';
for(var i = 0; i < size; i++) {
var id = full.PlatformCredential[i].id;
html += certificateDetailsLink('platform', id, false) + '&nbsp;';
}
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display endorsement credential
if(full.EndorsementCredential === undefined) return '';
var size = full.EndorsementCredential.length;
var html = '';
for(var i = 0; i < size; i++) {
var id = full.EndorsementCredential[i].id;
html += certificateDetailsLink('endorsement', id, false) +'&nbsp;';
}
return html;
}
}
];
//Set data tables
setDataTables("#deviceTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,103 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Endorsement Key Credentials</jsp:attribute>
<jsp:body>
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/certificate-request/endorsement-key-credentials/upload" enctype="multipart/form-data">
Import Endorsement Key Credentials
<my:file-chooser id="ek-editor" label="Import Endorsement Key Credentials">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
</form:form>
</div>
<br/>
<div class="aca-data-table">
<table id="endorsementKeyTable" class="display" width="100%">
<thead>
<tr>
<th>Device</th>
<th>Issuer</th>
<th>Type</th>
<th>Manufacturer</th>
<th>Model</th>
<th>Version</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath +'/list';
var columns = [
{
data: 'device.name',
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.device.name;
}
return '';
}
},
{data: 'issuer'},
{data: 'credentialType'},
{data: 'manufacturer'},
{data: 'model'},
{data: 'version'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.beginValidity);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.endValidity);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
var html = '';
html += certificateDetailsLink('endorsement', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#endorsementKeyTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,23 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="pageHeaderTitle">Help</jsp:attribute>
<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>
</jsp:body>
</my:page>

View File

@ -0,0 +1,68 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="pageHeaderTitle">Welcome to the HIRS Attestation CA</jsp:attribute>
<jsp:body>
<div id="index-page" class="container-fluid">
<div class="row">
<div class="col col-md-6 index-left-side">
<div class="row">
<div class="col col-md-12">
<h1>Configuration</h1>
</div>
</div>
<h3>
<a href="${portal}/policy">
<img src="${icons}/ic_store_black_24dp.png" /> Policy
</a>
</h3>
<h4>Configure Identity CA and Supply Chain validation policies.</h4>
<h3>
<a href="${certificateRequest}/trust-chain">
<img src="${icons}/ic_subtitles_black_24dp.png" /> Trust Chain Management
</a>
</h3>
<h4>Upload, view and manage CA certificates that complete trust chains for hardware credentials.</h4>
<h3>
<a href="${certificateRequest}/platform-credentials">
<img src="${icons}/ic_important_devices_black_24dp.png" /> Platform Credentials
</a>
</h3>
<h4>Upload, view and manage platform credentials.</h4>
<h3>
<a href="${certificateRequest}/endorsement-key-credentials">
<img src="${icons}/ic_vpn_key_black_24dp.png" /> Endorsement Credentials
</a>
</h3>
<h4>Upload, view and manage endorsement credentials.</h4>
</div>
<div class="col col-md-6 index-right-side">
<div class="row">
<div class="col col-md-12">
<h1>Status</h1>
</div>
</div>
<h3>
<a href="${certificateRequest}/issued-certificates">
<img src="${icons}/ic_library_books_black_24dp.png" /> Issued Attestation Certificates
</a>
</h3>
<h4>View Attestation Certificates issued by this CA</h4>
<h3>
<a href="${portal}/validation-reports"><img src="${icons}/ic_assignment_black_24dp.png" /> Validation Reports</a>
</h3>
<h4>View a list of device validations carried out by this CA.</h4>
<h3>
<a href="${portal}/devices"><img src="${icons}/ic_devices_black_24dp.png" /> Devices</a>
</h3>
<h4>View devices covered by this CA for supply chain validation.</h4>
</div>
</div>
</div>
</jsp:body>
</my:page>

View File

@ -0,0 +1,124 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Issued Attestation Certificates</jsp:attribute>
<jsp:body>
<div class="aca-data-table">
<table id="issuedTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Hostname</th>
<th rowspan="2">Issuer</th>
<th rowspan="2">Valid (begin)</th>
<th rowspan="2">Valid (end)</th>
<th colspan="2">Credentials</th>
<th rowspan="2">Options</th>
</tr>
<tr>
<th>Endorsement</th>
<th>Platform</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath + '/list';
var columns = [
{
data: 'device.name',
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.device.name;
}
return '';
}
},
{data: 'issuer'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(data);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(data);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display endorsement credential
var html = '';
if (full.endorsementCredential !== undefined
&& full.endorsementCredential !== null){
var id = full.endorsementCredential.id;
html += certificateDetailsLink('endorsement', id, false) +'&nbsp;';
}
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display platform credential
var html = '';
if (full.platformCredentials !== undefined
&& full.platformCredentials !== null) {
var size = full.platformCredentials.length;
for(var i = 0; i < size; i++) {
var id = full.platformCredentials[i].id;
html += certificateDetailsLink('platform', id, false) +'&nbsp;';
}
}
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// set up link to details page
var html = '';
html += certificateDetailsLink('issued', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#issuedTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,124 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Platform Credentials</jsp:attribute>
<jsp:body>
<!-- text and icon resource variables -->
<c:set var="endorsementIcon" value="${icons}/ic_vpn_key_black_24dp.png"/>
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/certificate-request/platform-credentials/upload" enctype="multipart/form-data">
Import Platform Credentials
<my:file-chooser id="platformCredentialEditor" label="Import Platform Credentials">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
</form:form>
</div>
<br/>
<div class="aca-data-table">
<table id="platformTable" class="display" width="100%">
<thead>
<tr>
<th>Device</th>
<th>Issuer</th>
<th>Type</th>
<th>Manufacturer</th>
<th>Model</th>
<th>Version</th>
<th>Board SN</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Endorsement</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath +'/list';
var columns = [
{
data: 'device.name',
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.device.name;
}
return '';
}
},
{data: 'issuer'},
{data: 'credentialType'},
{data: 'manufacturer'},
{data: 'model'},
{data: 'version'},
{data: 'platformSerial'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.beginValidity);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.endValidity);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function (data, type, full, meta) {
//Display endorsement credential
if(full.endorsementCredential === null) return '';
var html = '';
var id = full.endorsementCredential.id;
html = certificateDetailsLink('endorsement', id, false) +'&nbsp;';
return html;
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
var html = '';
html += certificateDetailsLink('platform', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#platformTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,64 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="pageHeaderTitle">Attestation Identity CA Policy Options</jsp:attribute>
<jsp:body>
<ul>
<%-- Platform validation --%>
<div class="aca-input-box">
<form:form method="POST" modelAttribute="initialData" action="policy/update-pc-validation">
<li>Platform Credential Validation: ${initialData.enablePcCertificateValidation ? 'Enabled' : 'Disabled'}
<my:editor id="pcPolicyEditor" label="Edit Settings">
<div class="radio">
<label><input id="pcTop" type="radio" name="pcValidate" ${initialData.enablePcCertificateValidation ? 'checked' : ''} value="checked"/> Platform Credentials will be validated</label>
</div>
<div class="radio">
<label><input id="pcBot" type="radio" name="pcValidate" ${initialData.enablePcCertificateValidation ? '' : 'checked'} value="unchecked"/> Platform Credentials will not be validated</label>
</div>
</my:editor>
</li>
</form:form>
</div>
<%-- Platform attribute validation --%>
<div class="aca-input-box">
<form:form method="POST" modelAttribute="initialData" action="policy/update-pc-attribute-validation">
<li>Platform Attribute Credential Validation: ${initialData.enablePcCertificateAttributeValidation ? 'Enabled' : 'Disabled'}
<my:editor id="pcAttributePolicyEditor" label="Edit Settings">
<div class="radio">
<label><input id="pcAttrTop" type="radio" name="pcAttributeValidate" ${initialData.enablePcCertificateAttributeValidation ? 'checked' : ''} value="checked"/> Platform Credential Attributes will be validated</label>
</div>
<div class="radio">
<label><input id="pcAttrBot" type="radio" name="pcAttributeValidate" ${initialData.enablePcCertificateAttributeValidation ? '' : 'checked'} value="unchecked"/> Platform Credential Attributes will not be validated</label>
</div>
</my:editor>
</li>
</form:form>
</div>
<%-- Endorsement validation --%>
<div class="aca-input-box">
<form:form method="POST" modelAttribute="initialData" action="policy/update-ec-validation">
<li>Endorsement Credential Validation: ${initialData.enableEcValidation ? 'Enabled' : 'Disabled'}
<my:editor id="ecPolicyEditor" label="Edit Settings ">
<div class="radio">
<label><input id="ecTop" type="radio" name="ecValidate" ${initialData.enableEcValidation ? 'checked' : ''} value="checked"/> Endorsement Credentials will be validated</label>
</div>
<div class="radio">
<label><input id="ecBot" type="radio" name="ecValidate" ${initialData.enableEcValidation ? '' : 'checked'} value="unchecked"/> Endorsement Credentials will not be validated</label>
</div>
</my:editor>
</li>
</form:form>
</div>
</ul>
</jsp:body>
</my:page>

View File

@ -0,0 +1,153 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Trust Chain Management</jsp:attribute>
<jsp:body>
<span class="aca-input-box-header">
HIRS Attestation CA Certificate
</span>
<my:details-viewer id="aca-cert-viewer" label="HIRS Attestation CA Certificate">
<div class="container-fluid">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Issuer</span></div>
<div id="issuer" class="col col-md-8">
<!-- Display the issuer, and provide a link to the issuer details if provided -->
<c:choose>
<c:when test="${not empty acaCertData.issuerID}">
<a href="${portal}/certificate-details?id=${acaCertData.issuerID}&type=certificateauthority">
${acaCertData.issuer}
</a>
</c:when>
<c:otherwise>
${acaCertData.issuer}
</c:otherwise>
</c:choose>
</div>
</div>
<c:if test="${not empty acaCertData.subject}">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Subject</span></div>
<div id="subject" class="col col-md-8">${acaCertData.subject}</div>
</div>
</c:if>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Serial Number</span></div>
<div id="serialNumber" class="col col-md-8">${acaCertData.serialNumber}</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Validity</span></div>
<div id="validity" class="col col-md-8">
<div>Not Before:&nbsp;<span>${acaCertData.beginValidity}</span></div>
<div>Not After:&nbsp;<span>${acaCertData.endValidity}</span></div>
</div>
</div>
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Signature</span></div>
<div id="signature" class="col col-md-8"></div>
</div>
<c:if test="${not empty acaCertData.encodedPublicKey}">
<div class="row">
<div class="col-md-2 col-md-offset-1"><span class="colHeader">Public Key</span></div>
<div id="encodedPublicKey" class="col col-md-8"></div>
</div>
</c:if>
<div class="spacer"></div>
</div>
</my:details-viewer>
<a href="${portal}/certificate-request/trust-chain/download-aca-cert">
<img src="${baseURL}/images/icons/ic_file_download_black_24dp.png" title="Download ACA Certificate">
</a>
<div class="aca-input-box-header">
<form:form method="POST" action="${portal}/certificate-request/trust-chain/upload" enctype="multipart/form-data">
Import Trust Chain CA Certificates
<my:file-chooser id="tc-editor" label="Import Trust Chain Certificates">
<input id="importFile" type="file" name="file" multiple="multiple" />
</my:file-chooser>
</form:form>
</div>
<br/>
<br/>
<div class="aca-data-table">
<table id="trustChainTable" class="display" width="100%">
<thead>
<tr>
<th>Issuer</th>
<th>Subject</th>
<th>Valid (begin)</th>
<th>Valid (end)</th>
<th>Options</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = pagePath +'/list';
var signature = ${acaCertData.signature};
//Format validity time
$("#validity span").each(function(){
$(this).text(formatDateTime($(this).text()));
});
//Convert byte array to string
$("#signature").html(byteToHexString(signature));
<c:if test="${not empty acaCertData.encodedPublicKey}">
//Change publick key byte to hex
var publicKey = ${acaCertData.encodedPublicKey};
$("#encodedPublicKey").html(byteToHexString(publicKey));
</c:if>
var columns = [
{data: 'issuer'},
{data: 'subject'},
{
data: 'beginValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.beginValidity);
}
},
{
data: 'endValidity',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.endValidity);
}
},
{
data: 'id',
orderable: false,
searchable:false,
render: function(data, type, full, meta) {
// Set up a delete icon with link to handleDeleteRequest().
// sets up a hidden input field containing the ID which is
// used as a parameter to the REST POST call to delete
var html = '';
html += certificateDetailsLink('certificateauthority', full.id, true);
html += certificateDownloadLink(full.id, pagePath);
html += certificateDeleteLink(full.id, pagePath);
return html;
}
}
];
//Set data tables
setDataTables("#trustChainTable", url, columns);
});
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,196 @@
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%-- JSP TAGS --%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- CONTENT --%>
<my:page>
<jsp:attribute name="script">
<script type="text/javascript" src="${lib}/jquery.spring-friendly/jquery.spring-friendly.js"></script>
</jsp:attribute>
<jsp:attribute name="pageHeaderTitle">Validation Reports</jsp:attribute>
<jsp:body>
<!-- text and icon resource variables -->
<c:set var="passIcon" value="${icons}/ic_checkbox_marked_circle_black_green_24dp.png"/>
<c:set var="failIcon" value="${icons}/ic_error_red_24dp.png"/>
<c:set var="errorIcon" value="${icons}/ic_error_black_24dp.png"/>
<c:set var="unknownIcon" value="${icons}/ic_questionmark_circle_orange_24dp.png"/>
<c:set var="passText" value="Validation Passed"/>
<c:set var="failText" value="Validation Failed"/>
<c:set var="errorText" value="Validation Error"/>
<c:set var="unknownText" value="Unknown Validation Status"/>
<div class="aca-data-table">
<table id="reportTable" class="display" width="100%">
<thead>
<tr>
<th rowspan="2">Result</th>
<th rowspan="2">Timestamp</th>
<th rowspan="2">Device</th>
<th colspan="3">Credential Validations</th>
</tr>
<tr>
<th>Endorsement</th>
<th>Platform</th>
<th>Platform Attributes</th>
</tr>
</thead>
</table>
</div>
<script>
$(document).ready(function() {
var url = portal + '/validation-reports/list';
var columns = [
{
data: 'overallValidationResult',
searchable:false,
render: function (data, type, full, meta) {
var html = '';
var unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// create status icon
var result = full.overallValidationResult;
if (result) {
switch (result) {
case "PASS":
html += '<img src="${passIcon}" title="${passText}"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="${failText}"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="${errorText}"/>';
break;
default:
html += unknownStatus;
break;
}
} else {
html += unknownStatus;
}
return html;
}
},
{
// Note: DB column is create_time, while the
// JSON property / java property is createTime. Need to sort
// on the field createTime, but the column's
// date source is create_time.
data: 'create_time',
name: 'createTime',
searchable:false,
render: function (data, type, full, meta) {
return formatDateTime(full.createTime);
}
},
{
// TODO render a link to a device details page,
// passing the device.id
data: 'device.name'
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "ENDORSEMENT_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "PLATFORM_CREDENTIAL")
}
},
{
data: 'id',
searchable: false,
orderable: false,
render: function (data, type, full, meta) {
return getValidationDisplayHtml(full, "PLATFORM_CREDENTIAL_ATTRIBUTES")
}
},
];
//Set data tables
var dataTable = setDataTables("#reportTable", url, columns);
dataTable.order([1, 'desc']).draw(); //order by createTime
});
/**
* Gets HTML to display (icon tag) for the specified validation type.
* If a validation for the requested type is not found, an empty
* string is returned (and no icon will be displayed).
*/
function getValidationDisplayHtml(full, validation_type) {
var html = '';
// loop through all the validations, looking for the one matching
// the validation_type.
for (var i = 0; i < full.validations.length; i++) {
var curValidation = full.validations[i];
var curResult = curValidation.result;
var curMessage = curValidation.message;
if (curValidation.validationType === validation_type) {
var unknownStatus = '<img class="icon" src="${unknownIcon}" title="${unknownText}"/>';
// display appropriate icon based on result
if (curResult) {
// if this validation is associated with a certificate,
// link to the details page
if (curValidation.certificatesUsed.length > 0) {
var certType = '';
switch (validation_type) {
case "PLATFORM_CREDENTIAL":
case "PLATFORM_CREDENTIAL_ATTRIBUTES":
certType = "platform";
break;
case "ENDORSEMENT_CREDENTIAL":
certType = "endorsement";
break;
}
html += '<a href="${portal}/certificate-details?id='
+ curValidation.certificatesUsed[0].id
+ '&type=' + certType + '">'
}
switch (curResult) {
case "PASS":
html += '<img src="${passIcon}" title="' + curMessage + '"/>';
break;
case "FAIL":
html += '<img src="${failIcon}" title="' + curMessage + '"/>';
break;
case "ERROR":
html += '<img src="${errorIcon}" title="' + curMessage + '"/>';
break;
default:
html += unknownStatus;
break;
}
// add closing tag for href tag if needed.
if (curValidation.certificatesUsed.length > 0) {
html += '</a>';
}
} else {
html += unknownStatus;
}
}
}
return html;
}
</script>
</jsp:body>
</my:page>

View File

@ -0,0 +1,88 @@
<%-- Set variables for the banner if it have one. --%>
<%@tag description="page navigation components" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="my" tagdir="/WEB-INF/tags"%>
<%-- Default values --%>
<c:set var="topBanner" />
<c:set var="bottomBanner" />
<c:set var="bottomBannerInfo" />
<c:if test="${banner.hasBanner}">
<link type="text/css" rel="stylesheet" href="${common}/banner.css"/>
<script>
$(document).ready(function(){
var headerHeight = $('#header').height();
var topBannerHeight = $(".topBanner").height();
//Add margins
$('body').css({'padding-top': headerHeight + topBannerHeight });
$('#header').css({'margin-top': topBannerHeight });
// wait 50ms before setting the new height
window.setTimeout( setHeights, 50);
function setHeights(){
//Set bannerColor
var bannerColor = "${banner.bannerColor}";
$('.bannerColor').css({'background-color': bannerColor});
//get basic heights
var mainHeight = $('.main, .main-without-navigation').height();
var contentHeight = $('.content').height();
var spacerHeight = $('.spacer').height() + $('.extra-spacer').height();
var bottomBannerHeight = $(".bottomBanner").height() + $(".bottomBannerInfo").height();
var pageHeaderHeight = $('.page-header').height() +
parseInt($('.page-header').css('margin-top').replace('px', '')) +
parseInt($('.page-header').css('margin-bottom').replace('px', '')) +
parseInt($('.page-header').css('padding-top').replace('px', '')) +
parseInt($('.page-header').css('padding-bottom').replace('px', '')) +
parseInt($('.page-header').css('border-bottom-width').replace('px', '')) + 1;
//Current total
var totalHeight = pageHeaderHeight + contentHeight + spacerHeight + bottomBannerHeight;
//Check if there is still space
if(mainHeight > totalHeight) {
$('.content').height(mainHeight - totalHeight + contentHeight);
}
}
});
</script>
<c:set var="bannerTemplate">
<c:if test="${not empty banner.bannerDynamic}">
<div class="classBanner dynamic">${banner.bannerDynamic}</div>
</c:if>
<div class="classBanner bannerColor">
${banner.bannerString}
</div>
</c:set>
<c:set var="topBanner" scope="session">
<div class="topBanner">${bannerTemplate}</div>
</c:set>
<c:set var="bottomBanner" scope="session">
<div class="bottomBanner">${bannerTemplate}</div>
</c:set>
<c:set var="bottomBannerInfo" scope="session">
<div class="bottomBannerInfo">
<div class="row">
<div class="col-md-3 col-sm-4 col-xs-5 alignLeft">
<c:forEach var="data" items="${banner.leftContent}">
<span>${data}</span>
</c:forEach>
</div>
<div class="col-md-3 col-sm-4 col-xs-5 col-md-offset-6 col-sm-offset-4 col-xs-offset-2 alignRight">
<c:forEach var="data" items="${banner.rightContent}">
<span>${data}</span>
</c:forEach>
</div>
</div>
</div>
</c:set>
</c:if>

View File

@ -0,0 +1,14 @@
<%@tag description="Generic details viewer icon that opens modal-dialogue" pageEncoding="UTF-8"%>
<%@attribute name="id"%>
<%@attribute name="label"%>
<%@attribute name="customButton" fragment="true" required="false"%>
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>
<a href="#${id}" data-toggle="modal" title="${label}">
<img src="${icons}/ic_assignment_black_24dp.png"/>
</a>
<my:modal-dialogue id="${id}" label="${label}" customButton="${customButton}">
<jsp:doBody/>
</my:modal-dialogue>

View File

@ -0,0 +1,14 @@
<%@tag description="editor icon that opens modal dialog with form" pageEncoding="UTF-8"%>
<%@attribute name="id"%>
<%@attribute name="label"%>
<%@attribute name="customButtons" fragment="true" required="false"%>
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>
<a href="#${id}" data-toggle="modal" title="${label}">
<img src="${icons}/ic_mode_edit_blue_18dp.png"/>
</a>
<my:modal id="${id}" label="${label}" customButtons="${customButtons}">
<jsp:doBody/>
</my:modal>

View File

@ -0,0 +1,14 @@
<%@tag description="file chooser icon that opens modal dialog with form" pageEncoding="UTF-8"%>
<%@attribute name="id"%>
<%@attribute name="label"%>
<%@attribute name="customButtons" fragment="true" required="false"%>
<%@ taglib prefix="my" tagdir="/WEB-INF/tags" %>
<a href="#${id}" data-toggle="modal" title="${label}">
<img src="${icons}/ic_library_add_black_24dp.png"/>
</a>
<my:modal id="${id}" label="${label}" customButtons="${customButtons}">
<jsp:doBody/>
</my:modal>

View File

@ -0,0 +1,24 @@
<%@tag description="basic modal dialog" pageEncoding="UTF-8"%>
<%@attribute name="id"%>
<%@attribute name="label"%>
<%@attribute name="customButton" fragment="true" required="false"%>
<div id="${id}" class="modal fade" role="dialog" style="top:5%">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 id="modal-title">${label}</h1>
</div>
<div class="modal-body">
<jsp:doBody/>
</div>
<div class="modal-footer">
<div class="modal-custom-button">
<jsp:invoke fragment="customButton"/>
</div>
<button class="btn btn-secondary" data-dismiss="modal">Done</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
<%@tag description="modal dialog with form" pageEncoding="UTF-8"%>
<%@attribute name="id"%>
<%@attribute name="label"%>
<%@attribute name="customButtons" fragment="true" required="false"%>
<div id="${id}" class="modal fade" role="dialog" style="top:30%">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 id="modal-title">${label}</h1>
</div>
<div class="modal-body">
<jsp:doBody/>
</div>
<div class="modal-footer">
<div class="modal-custom-buttons">
<jsp:invoke fragment="customButtons"/>
</div>
<button class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<input class="btn btn-primary" type="submit" value="Save">
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,37 @@
<%@tag description="page messages" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:if test="${not empty messages}">
<div id="page-messages-container">
<ul id="page-messages" class="noPaddingOrMargin">
<c:forEach var="error" items="${messages.error}">
<li id="page-errorMessage" class="page-message">
<span class="page-messageIcon">
<img src="${icons}/ic_priority_high_white_24dp.png"/>
</span>
${error}
</li>
</c:forEach>
<c:forEach var="success" items="${messages.success}">
<li id="page-successMessage" class="page-message">
<span class="page-messageIcon">
<img src="${icons}/ic_done_white_24dp.png"/>
</span>
${success}
</li>
</c:forEach>
<c:forEach var="info" items="${messages.info}">
<li id="page-infoMessage" class="page-message">
<span class="page-messageIcon">
<img src="${icons}/ic_priority_high_white_24dp.png"/>
</span>
${info}
</li>
</c:forEach>
</ul>
</div>
</c:if>

Some files were not shown because too many files have changed in this diff Show More