mirror of
https://github.com/nsacyber/HIRS.git
synced 2025-06-09 10:51:51 +00:00
parent
23570f71c3
commit
f192ce5826
@ -47,8 +47,14 @@ public final class CredentialManagementHelper {
|
|||||||
|
|
||||||
LOG.info("Parsing Endorsement Credential of length " + endorsementBytes.length);
|
LOG.info("Parsing Endorsement Credential of length " + endorsementBytes.length);
|
||||||
|
|
||||||
EndorsementCredential endorsementCredential =
|
EndorsementCredential endorsementCredential;
|
||||||
EndorsementCredential.parseWithPossibleHeader(endorsementBytes);
|
try {
|
||||||
|
endorsementCredential = EndorsementCredential
|
||||||
|
.parseWithPossibleHeader(endorsementBytes);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
LOG.error(iae.getMessage());
|
||||||
|
throw iae;
|
||||||
|
}
|
||||||
int certificateHash = endorsementCredential.getCertificateHash();
|
int certificateHash = endorsementCredential.getCertificateHash();
|
||||||
EndorsementCredential existingCredential =
|
EndorsementCredential existingCredential =
|
||||||
EndorsementCredential.select(certificateManager).includeArchived()
|
EndorsementCredential.select(certificateManager).includeArchived()
|
||||||
|
@ -33,6 +33,7 @@ import javax.persistence.Transient;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -65,6 +66,7 @@ public abstract class Certificate extends ArchivableEntity {
|
|||||||
private static final String PEM_FOOTER = "-----END CERTIFICATE-----";
|
private static final String PEM_FOOTER = "-----END CERTIFICATE-----";
|
||||||
private static final String PEM_ATTRIBUTE_HEADER = "-----BEGIN ATTRIBUTE CERTIFICATE-----";
|
private static final String PEM_ATTRIBUTE_HEADER = "-----BEGIN ATTRIBUTE CERTIFICATE-----";
|
||||||
private static final String PEM_ATTRIBUTE_FOOTER = "-----END ATTRIBUTE CERTIFICATE-----";
|
private static final String PEM_ATTRIBUTE_FOOTER = "-----END ATTRIBUTE CERTIFICATE-----";
|
||||||
|
private static final String MALFORMED_CERT_MESSAGE = "Malformed certificate detected.";
|
||||||
private static final int MAX_CERT_LENGTH_BYTES = 2048;
|
private static final int MAX_CERT_LENGTH_BYTES = 2048;
|
||||||
private static final int MAX_NUMERIC_PRECISION = 49; // Can store up to 160 bit values
|
private static final int MAX_NUMERIC_PRECISION = 49; // Can store up to 160 bit values
|
||||||
private static final int MAX_PUB_KEY_MODULUS_HEX_LENGTH = 1024;
|
private static final int MAX_PUB_KEY_MODULUS_HEX_LENGTH = 1024;
|
||||||
@ -265,6 +267,8 @@ public abstract class Certificate extends ArchivableEntity {
|
|||||||
this.certificateBytes = Base64.decode(possiblePem);
|
this.certificateBytes = Base64.decode(possiblePem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.certificateBytes = trimCertificate(this.certificateBytes);
|
||||||
|
|
||||||
// Extract certificate data
|
// Extract certificate data
|
||||||
switch (getCertificateType()) {
|
switch (getCertificateType()) {
|
||||||
case X509_CERTIFICATE:
|
case X509_CERTIFICATE:
|
||||||
@ -328,6 +332,53 @@ public abstract class Certificate extends ArchivableEntity {
|
|||||||
this.certAndTypeHash = Objects.hash(certificateHash, getClass().getSimpleName());
|
this.certAndTypeHash = Objects.hash(certificateHash, getClass().getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("magicnumber")
|
||||||
|
private byte[] trimCertificate(final byte[] certificateBytes) {
|
||||||
|
int certificateStart = 0;
|
||||||
|
int certificateLength = 0;
|
||||||
|
ByteBuffer certificateByteBuffer = ByteBuffer.wrap(certificateBytes);
|
||||||
|
|
||||||
|
StringBuilder malformedCertStringBuilder = new StringBuilder(MALFORMED_CERT_MESSAGE);
|
||||||
|
while (certificateByteBuffer.hasRemaining()) {
|
||||||
|
// Check if there isn't an ASN.1 structure in the provided bytes
|
||||||
|
if (certificateByteBuffer.remaining() <= 2) {
|
||||||
|
throw new IllegalArgumentException(malformedCertStringBuilder
|
||||||
|
.append(" No certificate length field could be found.").toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for first ASN.1 Sequence marked by the two bytes (0x30) and (0x82)
|
||||||
|
// The check advances our position in the ByteBuffer by one byte
|
||||||
|
int currentPosition = certificateByteBuffer.position();
|
||||||
|
if (certificateByteBuffer.get() == (byte) 0x30
|
||||||
|
&& certificateByteBuffer.get(currentPosition + 1) == (byte) 0x82) {
|
||||||
|
// Check if we have anything more in the buffer than an ASN.1 Sequence header
|
||||||
|
if (certificateByteBuffer.remaining() <= 3) {
|
||||||
|
throw new IllegalArgumentException(malformedCertStringBuilder
|
||||||
|
.append(" Certificate is nothing more than ASN.1 Sequence.")
|
||||||
|
.toString());
|
||||||
|
}
|
||||||
|
// Mark the start of the first ASN.1 Sequence / Certificate Body
|
||||||
|
certificateStart = currentPosition;
|
||||||
|
|
||||||
|
// Parse the length as the 2-bytes following the start of the ASN.1 Sequence
|
||||||
|
certificateLength = Short.toUnsignedInt(
|
||||||
|
certificateByteBuffer.getShort(currentPosition + 2));
|
||||||
|
// Add the 4 bytes that comprise the start of the ASN.1 Sequence and the length
|
||||||
|
certificateLength += 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (certificateStart + certificateLength > certificateBytes.length) {
|
||||||
|
throw new IllegalArgumentException(malformedCertStringBuilder
|
||||||
|
.append(" Value of certificate length field extends beyond length")
|
||||||
|
.append(" of provided certificate.").toString());
|
||||||
|
}
|
||||||
|
// Return bytes representing the main certificate body
|
||||||
|
return Arrays.copyOfRange(certificateBytes, certificateStart,
|
||||||
|
certificateStart + certificateLength);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the type of certificate.
|
* @return the type of certificate.
|
||||||
* @throws java.io.IOException if there is a problem extracting information from the certificate
|
* @throws java.io.IOException if there is a problem extracting information from the certificate
|
||||||
|
@ -373,6 +373,8 @@ public class EndorsementCredential extends DeviceAssociatedCertificate {
|
|||||||
revision.getValue());
|
revision.getValue());
|
||||||
LOGGER.debug("Found TPM Spec:" + tpmSpecification.toString());
|
LOGGER.debug("Found TPM Spec:" + tpmSpecification.toString());
|
||||||
} else if (addToMapping && key.equals(TPM_SECURITY_ASSERTIONS)) {
|
} else if (addToMapping && key.equals(TPM_SECURITY_ASSERTIONS)) {
|
||||||
|
// TODO(apldev3): Update this block to properly parse TPM Security Assertions
|
||||||
|
// per the document "TCG EK Credential Profile For TPM Family 2.0; Level 0" (pg. 19)
|
||||||
ASN1Integer ver = (ASN1Integer) seq.getObjectAt(ASN1_VER_INDEX);
|
ASN1Integer ver = (ASN1Integer) seq.getObjectAt(ASN1_VER_INDEX);
|
||||||
ASN1Boolean fieldUpgradeable = (ASN1Boolean) seq.getObjectAt(ASN1_UPGRADEABLE_INDEX);
|
ASN1Boolean fieldUpgradeable = (ASN1Boolean) seq.getObjectAt(ASN1_UPGRADEABLE_INDEX);
|
||||||
tpmSecurityAssertions = new TPMSecurityAssertions(ver.getValue(),
|
tpmSecurityAssertions = new TPMSecurityAssertions(ver.getValue(),
|
||||||
|
@ -7,6 +7,7 @@ import org.testng.annotations.Test;
|
|||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -87,6 +88,9 @@ public class CertificateTest {
|
|||||||
private static final String RDN_COMMA_SEPARATED_ORGANIZATION = "STMicroelectronics NV";
|
private static final String RDN_COMMA_SEPARATED_ORGANIZATION = "STMicroelectronics NV";
|
||||||
private static final String RDN_MULTIVALUE_ORGANIZATION = "Nuvoton Technology Corporation";
|
private static final String RDN_MULTIVALUE_ORGANIZATION = "Nuvoton Technology Corporation";
|
||||||
|
|
||||||
|
private static final String EK_CERT_WITH_PADDED_BYTES =
|
||||||
|
"/certificates/ek_cert_with_padded_bytes.cer";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that a certificate can be constructed from a byte array.
|
* Tests that a certificate can be constructed from a byte array.
|
||||||
@ -268,6 +272,75 @@ public class CertificateTest {
|
|||||||
Assert.assertEquals(platformCert.getEndValidity(), attrCertHolder.getNotAfter());
|
Assert.assertEquals(platformCert.getEndValidity(), attrCertHolder.getNotAfter());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that Certificate correctly trims out additional padding from a given certificate.
|
||||||
|
*
|
||||||
|
* @throws IOException if there is a problem reading the cert file at the given path
|
||||||
|
* @throws URISyntaxException if there is a problem constructing the file's URI
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testCertificateTrim() throws IOException, URISyntaxException {
|
||||||
|
byte[] rawFileBytes = Files.readAllBytes(Paths.get(CertificateTest.class
|
||||||
|
.getResource(EK_CERT_WITH_PADDED_BYTES).toURI()));
|
||||||
|
byte[] expectedCertBytes = Arrays.copyOfRange(rawFileBytes, 0, 908);
|
||||||
|
Certificate ekCert = getTestCertificate(EndorsementCredential.class,
|
||||||
|
EK_CERT_WITH_PADDED_BYTES);
|
||||||
|
Assert.assertEquals(ekCert.getSerialNumber(), new BigInteger("16842032579184247954"));
|
||||||
|
Assert.assertEquals(ekCert.getIssuer(),
|
||||||
|
"CN=Nuvoton TPM Root CA 2010+O=Nuvoton Technology Corporation+C=TW");
|
||||||
|
Assert.assertEquals(ekCert.getSubject(), "");
|
||||||
|
Assert.assertEquals(ekCert.getRawBytes(), expectedCertBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that Certificate correctly throws IllegalArgumentException when no length field is
|
||||||
|
* found in the provided byte array.
|
||||||
|
*
|
||||||
|
* @throws IOException if there is a problem reading the cert file at the given path
|
||||||
|
* @throws URISyntaxException if there is a problem constructing the file's URI
|
||||||
|
*/
|
||||||
|
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||||
|
expectedExceptionsMessageRegExp = ".* No certificate length field could be found\\.")
|
||||||
|
public void testCertificateTrimThrowsWhenNoLengthFieldFound() throws IOException,
|
||||||
|
URISyntaxException {
|
||||||
|
byte[] rawFileBytes = Files.readAllBytes(Paths.get(CertificateTest.class
|
||||||
|
.getResource(EK_CERT_WITH_PADDED_BYTES).toURI()));
|
||||||
|
new EndorsementCredential(Arrays.copyOfRange(rawFileBytes, 0, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that Certificate correctly throws IllegalArgumentException when the byte array only
|
||||||
|
* contains a header for an ASN.1 Sequence.
|
||||||
|
*
|
||||||
|
* @throws IOException if there is a problem reading the cert file at the given path
|
||||||
|
* @throws URISyntaxException if there is a problem constructing the file's URI
|
||||||
|
*/
|
||||||
|
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||||
|
expectedExceptionsMessageRegExp = ".* Certificate is nothing more than ASN.1 Sequence\\.")
|
||||||
|
public void testCertificateTrimThrowsWhenOnlyASN1Sequence() throws IOException,
|
||||||
|
URISyntaxException {
|
||||||
|
byte[] rawFileBytes = Files.readAllBytes(Paths.get(CertificateTest.class
|
||||||
|
.getResource(EK_CERT_WITH_PADDED_BYTES).toURI()));
|
||||||
|
new EndorsementCredential(Arrays.copyOfRange(rawFileBytes, 0, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that Certificate correctly throws IllegalArgumentException when the provided
|
||||||
|
* Certificate has a length that extends beyond the byte array as a whole.
|
||||||
|
*
|
||||||
|
* @throws IOException if there is a problem reading the cert file at the given path
|
||||||
|
* @throws URISyntaxException if there is a problem constructing the file's URI
|
||||||
|
*/
|
||||||
|
@Test(expectedExceptions = IllegalArgumentException.class,
|
||||||
|
expectedExceptionsMessageRegExp = ".* Value of certificate length field extends beyond"
|
||||||
|
+ " length of provided certificate\\.")
|
||||||
|
public void testCertificateTrimThrowsWhenLengthIsTooLarge() throws IOException,
|
||||||
|
URISyntaxException {
|
||||||
|
byte[] rawFileBytes = Files.readAllBytes(Paths.get(CertificateTest.class
|
||||||
|
.getResource(EK_CERT_WITH_PADDED_BYTES).toURI()));
|
||||||
|
new EndorsementCredential(Arrays.copyOfRange(rawFileBytes, 0, 42));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that the equals method on {@link Certificate} works as expected.
|
* Tests that the equals method on {@link Certificate} works as expected.
|
||||||
*
|
*
|
||||||
|
@ -21,6 +21,8 @@ public class EndorsementCredentialTest {
|
|||||||
= "/certificates/nuc-1/tpmcert.pem";
|
= "/certificates/nuc-1/tpmcert.pem";
|
||||||
private static final String TEST_ENDORSEMENT_CREDENTIAL_NUC2
|
private static final String TEST_ENDORSEMENT_CREDENTIAL_NUC2
|
||||||
= "/certificates/nuc-2/tpmcert.pem";
|
= "/certificates/nuc-2/tpmcert.pem";
|
||||||
|
private static final String EK_CERT_WITH_SECURITY_ASSERTIONS =
|
||||||
|
"/certificates/ek_cert_with_security_assertions.cer";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests the successful parsing of an EC using a test cert from STM.
|
* Tests the successful parsing of an EC using a test cert from STM.
|
||||||
@ -182,4 +184,22 @@ public class EndorsementCredentialTest {
|
|||||||
Assert.assertNotEquals(ec2, ec3);
|
Assert.assertNotEquals(ec2, ec3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that EndorsementCredential correctly parses out TPM Security Assertions from a
|
||||||
|
* provided TPM EK Certificate.
|
||||||
|
*
|
||||||
|
* @throws IOException if there is a problem reading the cert file at the given path
|
||||||
|
*/
|
||||||
|
@Test(enabled = false)
|
||||||
|
// TODO(apldev3): Reenable test when update to security assertions is made in
|
||||||
|
// EndorsementCredential
|
||||||
|
public void testTpmSecurityAssertionsParsing() throws IOException {
|
||||||
|
Path fPath = Paths.get(CertificateTest.class
|
||||||
|
.getResource(EK_CERT_WITH_SECURITY_ASSERTIONS).getPath());
|
||||||
|
EndorsementCredential ec = new EndorsementCredential(fPath);
|
||||||
|
|
||||||
|
// TODO(apldev3): Make assertions about TPMSecurityAssertions fields
|
||||||
|
System.out.println(ec);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user