mirror of
https://github.com/nsacyber/HIRS.git
synced 2025-02-06 11:10:15 +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 |