mirror of
https://github.com/nsacyber/HIRS.git
synced 2024-12-18 12:46:30 +00:00
Initial release
This commit is contained in:
commit
d7e44b8310
124
.gitignore
vendored
Normal file
124
.gitignore
vendored
Normal 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
31
.gitlab-ci.yml
Normal 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
6
CONTRIBUTING.md
Normal 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
9
DISCLAIMER.md
Normal 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.
|
83
HIRS_AttestationCA/build.gradle
Normal file
83
HIRS_AttestationCA/build.gradle
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
HIRS_AttestationCA/config/checkstyle/suppressions.xml
Normal file
14
HIRS_AttestationCA/config/checkstyle/suppressions.xml
Normal 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>
|
16
HIRS_AttestationCA/config/genJavaProtoBuf.sh
Executable file
16
HIRS_AttestationCA/config/genJavaProtoBuf.sh
Executable 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
|
File diff suppressed because it is too large
Load Diff
@ -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.");
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Includes the Spring Framework application context configuration classes.
|
||||
*/
|
||||
package hirs.attestationca.configuration;
|
@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Base package that includes common exceptions, interfaces and base implementations for and related
|
||||
* to the ACA.
|
||||
*/
|
||||
package hirs.attestationca;
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* RESTful implementations of the {@link hirs.attestationca.AttestationCertificateAuthority}.
|
||||
*/
|
||||
package hirs.attestationca.rest;
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
12
HIRS_AttestationCA/src/main/resources/applicationContext.xml
Normal file
12
HIRS_AttestationCA/src/main/resources/applicationContext.xml
Normal 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>
|
37
HIRS_AttestationCA/src/main/resources/defaults.properties
Normal file
37
HIRS_AttestationCA/src/main/resources/defaults.properties
Normal 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}
|
24
HIRS_AttestationCA/src/main/resources/log4j2.xml
Normal file
24
HIRS_AttestationCA/src/main/resources/log4j2.xml
Normal 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>
|
8
HIRS_AttestationCA/src/main/resources/setup.properties
Normal file
8
HIRS_AttestationCA/src/main/resources/setup.properties
Normal 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
|
43
HIRS_AttestationCA/src/main/webapp/WEB-INF/web.xml
Normal file
43
HIRS_AttestationCA/src/main/webapp/WEB-INF/web.xml
Normal 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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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-----
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
26
HIRS_AttestationCA/src/test/resources/certificates/nuc-1/tpmcert.pem
Executable file
26
HIRS_AttestationCA/src/test/resources/certificates/nuc-1/tpmcert.pem
Executable 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-----
|
26
HIRS_AttestationCA/src/test/resources/certificates/nuc_ec.pem
Executable file
26
HIRS_AttestationCA/src/test/resources/certificates/nuc_ec.pem
Executable 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.
BIN
HIRS_AttestationCA/src/test/resources/tpm2/ak.name
Normal file
BIN
HIRS_AttestationCA/src/test/resources/tpm2/ak.name
Normal file
Binary file not shown.
BIN
HIRS_AttestationCA/src/test/resources/tpm2/ak.pub
Normal file
BIN
HIRS_AttestationCA/src/test/resources/tpm2/ak.pub
Normal file
Binary file not shown.
BIN
HIRS_AttestationCA/src/test/resources/tpm2/ek.pub
Normal file
BIN
HIRS_AttestationCA/src/test/resources/tpm2/ek.pub
Normal file
Binary file not shown.
93
HIRS_AttestationCAPortal/build.gradle
Normal file
93
HIRS_AttestationCAPortal/build.gradle
Normal 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')
|
||||
}
|
@ -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>
|
@ -0,0 +1,3 @@
|
||||
<FindBugsFilter>
|
||||
|
||||
</FindBugsFilter>
|
BIN
HIRS_AttestationCAPortal/libs/rendersnake-1.8.jar
Normal file
BIN
HIRS_AttestationCAPortal/libs/rendersnake-1.8.jar
Normal file
Binary file not shown.
@ -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
|
||||
+ '}';
|
||||
}
|
||||
}
|
@ -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
|
||||
+ '}';
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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 + '\''
|
||||
+ '}';
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
+ '}';
|
||||
}
|
||||
}
|
@ -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;
|
@ -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 + '}';
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This packages provides data model classes for the HIRS Attestation CA portal.
|
||||
*/
|
||||
package hirs.attestationca.portal.model;
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Root Package for HIRS Attestation CA Portal.
|
||||
*/
|
||||
package hirs.attestationca.portal;
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This package contains PageController implementations.
|
||||
*/
|
||||
package hirs.attestationca.portal.page.controllers;
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This package provides interfaces and superclasses for PageController and associated objects.
|
||||
*/
|
||||
package hirs.attestationca.portal.page;
|
@ -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
|
||||
+ "}";
|
||||
}
|
||||
|
||||
}
|
@ -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<>();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This package contains url parameter classes for portal pages.
|
||||
*/
|
||||
package hirs.attestationca.portal.page.params;
|
@ -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());
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Persistence related classes for Attestation CA Portal.
|
||||
*/
|
||||
package hirs.attestationca.portal.persistence;
|
@ -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;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Utilities classes for the Attestation CA Portal.
|
||||
*/
|
||||
package hirs.attestationca.portal.util;
|
29
HIRS_AttestationCAPortal/src/main/resources/log4j2.xml
Normal file
29
HIRS_AttestationCAPortal/src/main/resources/log4j2.xml
Normal 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>
|
@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Created-By: 1.7.0_55 (Oracle Corporation)
|
||||
|
@ -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: <span>${initialData.beginValidity}</span></div>
|
||||
<div>Not After: <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: <span>${initialData.TPMSpecificationFamily}</span></div>
|
||||
<div>Level: <span>${initialData.TPMSpecificationLevel}</span></div>
|
||||
<div>Revision: <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: <span>${initialData.TPMSecurityAssertionsVersion}</span></div>
|
||||
<div>Field Upgradeable: <span>${initialData.TPMSecurityAssertionsFieldUpgradeable}</span></div>
|
||||
<div>ek Generation Type: <span>${initialData.TPMSecurityAssertionsEkGenType}</span></div>
|
||||
<div>ek Generation Location: <span>${initialData.TPMSecurityAssertionsEkGenLoc}</span></div>
|
||||
<div>ek Certificate Generation Location: <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()} (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> -
|
||||
<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>
|
138
HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/devices.jsp
Normal file
138
HIRS_AttestationCAPortal/src/main/webapp/WEB-INF/jsp/devices.jsp
Normal 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) + ' ';
|
||||
}
|
||||
|
||||
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) +' ';
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
//Set data tables
|
||||
setDataTables("#deviceTable", url, columns);
|
||||
});
|
||||
</script>
|
||||
</jsp:body>
|
||||
|
||||
</my:page>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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) +' ';
|
||||
}
|
||||
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) +' ';
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
@ -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) +' ';
|
||||
|
||||
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>
|
@ -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>
|
@ -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: <span>${acaCertData.beginValidity}</span></div>
|
||||
<div>Not After: <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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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
Loading…
Reference in New Issue
Block a user