mirror of
https://github.com/nsacyber/HIRS.git
synced 2024-12-28 08:48:59 +00:00
Create IDevID certificate page (#727)
Adding IDevID certificate page and parsing support
This commit is contained in:
parent
55629a704e
commit
80ba36cbf2
@ -0,0 +1,25 @@
|
||||
package hirs.attestationca.persist.entity.manager;
|
||||
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.IDevIDCertificate;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Repository
|
||||
public interface IDevIDCertificateRepository extends JpaRepository<IDevIDCertificate, UUID> {
|
||||
|
||||
List<IDevIDCertificate> findByArchiveFlag(boolean archiveFlag);
|
||||
|
||||
Page<IDevIDCertificate> findByArchiveFlag(boolean archiveFlag, Pageable pageable);
|
||||
/*List<IDevIDCertificate> findBySubject(String subject);
|
||||
List<IDevIDCertificate> findBySubjectSorted(String subject);
|
||||
List<IDevIDCertificate> findBySubjectAndArchiveFlag(String subject, boolean archiveFlag);
|
||||
List<IDevIDCertificate> findBySubjectSortedAndArchiveFlag(String subject, boolean archiveFlag);
|
||||
IDevIDCertificate findBySubjectKeyIdentifier(byte[] subjectKeyIdentifier);
|
||||
IDevIDCertificate findBySubjectKeyIdStringAndArchiveFlag(String subjectKeyIdString, boolean archiveFlag);
|
||||
*/
|
||||
}
|
@ -318,9 +318,10 @@ public abstract class Certificate extends ArchivableEntity {
|
||||
switch (getCertificateType()) {
|
||||
case X509_CERTIFICATE:
|
||||
X509Certificate x509Certificate = getX509Certificate();
|
||||
|
||||
this.serialNumber = x509Certificate.getSerialNumber();
|
||||
this.issuer = x509Certificate.getIssuerX500Principal().getName();
|
||||
this.subject = x509Certificate.getSubjectX500Principal().getName();
|
||||
this.issuer = Certificate.getIssuerDNString(x509Certificate);
|
||||
this.subject = Certificate.getSubjectDNString(x509Certificate);
|
||||
this.encodedPublicKey = x509Certificate.getPublicKey().getEncoded();
|
||||
BigInteger publicKeyModulus = getPublicKeyModulus(x509Certificate);
|
||||
|
||||
@ -851,6 +852,46 @@ public abstract class Certificate extends ArchivableEntity {
|
||||
return Files.readAllBytes(certificatePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a formatted subject DN string from a certificate. This allows for extended support of DNs found in
|
||||
* various RFCs.
|
||||
*
|
||||
* @param certificate the certificate holding subject DNs
|
||||
* @return IOException if there is an issue decoding the subject DNs
|
||||
*/
|
||||
public static String getSubjectDNString(final X509Certificate certificate)
|
||||
throws IOException {
|
||||
X509CertificateHolder certificateHolder = null;
|
||||
try {
|
||||
certificateHolder = new X509CertificateHolder(certificate.getEncoded());
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new IOException("Could not encode certificate", e);
|
||||
}
|
||||
|
||||
X500Name x500Name = certificateHolder.getSubject();
|
||||
return x500Name.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a formatted issuer DN string from a certificate. This allows for extended support of DNs found in
|
||||
* various RFCs.
|
||||
*
|
||||
* @param certificate the certificate holding issuer DNs
|
||||
* @return IOException if there is an issue decoding the issuer DNs
|
||||
*/
|
||||
public static String getIssuerDNString(final X509Certificate certificate)
|
||||
throws IOException {
|
||||
X509CertificateHolder certificateHolder = null;
|
||||
try {
|
||||
certificateHolder = new X509CertificateHolder(certificate.getEncoded());
|
||||
} catch (CertificateEncodingException e) {
|
||||
throw new IOException("Could not encode certificate", e);
|
||||
}
|
||||
|
||||
X500Name x500Name = certificateHolder.getIssuer();
|
||||
return x500Name.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve an RSA-based X509 certificate's public key modulus.
|
||||
*
|
||||
|
@ -0,0 +1,333 @@
|
||||
package hirs.attestationca.persist.entity.userdefined.certificate;
|
||||
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Transient;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.bouncycastle.asn1.*;
|
||||
import org.bouncycastle.asn1.x509.CertificatePolicies;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.PolicyInformation;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Log4j2
|
||||
public class IDevIDCertificate extends Certificate {
|
||||
|
||||
// Undefined expiry date, as specified in 802.1AR
|
||||
private static final long UNDEFINED_EXPIRY_DATE = 253402300799L;
|
||||
|
||||
// Supported OIDs
|
||||
@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
|
||||
private static final String SUBJECT_ALTERNATIVE_NAME_EXTENSION = "2.5.29.17";
|
||||
private static final String HARDWARE_MODULE_NAME_OID = "1.3.6.1.5.5.7.8.4";
|
||||
private static final String HWTYPE_TCG_TPM2_OID = "2.23.133.1.2";
|
||||
|
||||
// Other OIDs (policy)
|
||||
private static final String POLICY_QUALIFIER_VERIFIED_TPM_RESIDENCY = "2.23.133.11.1.1";
|
||||
private static final String POLICY_QUALIFIER_VERIFIED_TPM_FIXED = "2.23.133.11.1.2";
|
||||
private static final String POLICY_QUALIFIER_VERIFIED_TPM_RESTRICTED = "2.23.133.11.1.3";
|
||||
|
||||
@Getter
|
||||
@Transient
|
||||
private byte[] subjectAltName;
|
||||
|
||||
/**
|
||||
* Corresponds to the hwType field found in a Hardware Module Name (if present).
|
||||
*/
|
||||
@Getter
|
||||
@Column
|
||||
private String hwType;
|
||||
|
||||
/**
|
||||
* Corresponds to the serial number found in a Hardware Module Name (if present).
|
||||
*/
|
||||
@Getter
|
||||
@Column
|
||||
private byte[] hwSerialNum;
|
||||
|
||||
/**
|
||||
* TPM policy qualifiers (TCG only).
|
||||
*/
|
||||
@Getter
|
||||
@Column
|
||||
private String tpmPolicies;
|
||||
|
||||
/**
|
||||
* Construct a new IDevIDCertificate given its binary contents. The given
|
||||
* certificate should represent a valid X.509 certificate.
|
||||
*
|
||||
* @param certificateBytes the contents of a certificate file
|
||||
* @throws IOException if there is a problem extracting information from the certificate
|
||||
*/
|
||||
public IDevIDCertificate(final byte[] certificateBytes)
|
||||
throws IOException {
|
||||
super(certificateBytes);
|
||||
|
||||
this.parseIDevIDCertificate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new IDevIDCertificate by parsing the file at the given path.
|
||||
* The given certificate should represent a valid X.509 certificate.
|
||||
*
|
||||
* @param certificatePath the path on disk to a certificate
|
||||
* @throws IOException if there is a problem reading the file
|
||||
*/
|
||||
public IDevIDCertificate(final Path certificatePath)
|
||||
throws IOException {
|
||||
super(certificatePath);
|
||||
|
||||
this.parseIDevIDCertificate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor for Hibernate.
|
||||
*/
|
||||
protected IDevIDCertificate() {
|
||||
subjectAltName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains TPM policy qualifiers from the Certificate Policies extension, if present. These policy qualifiers are
|
||||
* specified in the TCG document "TPM 2.0 Keys for Device Identity and Attestation".
|
||||
*
|
||||
* @return A {@link java.util.Map} containing the policy qualifiers obtained.
|
||||
* @throws IOException if policy qualifiers cannot be parsed from extension value
|
||||
*/
|
||||
public Map<String, Boolean> getTPMPolicyQualifiers(byte[] policyBytes) throws IOException {
|
||||
CertificatePolicies certPolicies =
|
||||
CertificatePolicies.getInstance(JcaX509ExtensionUtils.parseExtensionValue(policyBytes));
|
||||
Map<String, Boolean> policyQualifiers = new HashMap<>();
|
||||
boolean verifiedTPMResidency = false;
|
||||
boolean verifiedTPMFixed = false;
|
||||
boolean verifiedTPMRestricted = false;
|
||||
|
||||
if (certPolicies != null) {
|
||||
// Must contain at least one Policy
|
||||
for (PolicyInformation policy : certPolicies.getPolicyInformation()) {
|
||||
// Add the data based on the OIDs
|
||||
switch (policy.getPolicyIdentifier().toString()) {
|
||||
case POLICY_QUALIFIER_VERIFIED_TPM_RESIDENCY:
|
||||
verifiedTPMResidency = true;
|
||||
break;
|
||||
case POLICY_QUALIFIER_VERIFIED_TPM_FIXED:
|
||||
verifiedTPMFixed = true;
|
||||
break;
|
||||
case POLICY_QUALIFIER_VERIFIED_TPM_RESTRICTED:
|
||||
verifiedTPMRestricted = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add to map
|
||||
policyQualifiers.put("verifiedTPMResidency", Boolean.valueOf(verifiedTPMResidency));
|
||||
policyQualifiers.put("verifiedTPMFixed", Boolean.valueOf(verifiedTPMFixed));
|
||||
policyQualifiers.put("verifiedTPMRestricted", Boolean.valueOf(verifiedTPMRestricted));
|
||||
|
||||
return policyQualifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses fields related to IDevID certificates.
|
||||
* @throws IOException if a problem is encountered during parsing
|
||||
*/
|
||||
private void parseIDevIDCertificate() throws IOException {
|
||||
|
||||
this.subjectAltName =
|
||||
getX509Certificate().getExtensionValue(SUBJECT_ALTERNATIVE_NAME_EXTENSION);
|
||||
|
||||
if (this.subjectAltName != null) {
|
||||
|
||||
ASN1InputStream input;
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(subjectAltName);
|
||||
input = new ASN1InputStream(byteArrayInputStream);
|
||||
|
||||
ASN1OctetString obj = (ASN1OctetString) input.readObject();
|
||||
boolean tcgOid = false;
|
||||
|
||||
// Parse the otherName structure. According to the specification "TPM 2.0 Keys for Device Identity and
|
||||
// Attestation", otherName can contain up to two structures: HardwareModuleName and PermanentIdentifier.
|
||||
// Currently, this parser only supports HardwareModuleName (if present).
|
||||
|
||||
if (obj != null) {
|
||||
// Parse Hardware Module Name structure, comprised of a hwType and hwSerialNum, and associated OID
|
||||
// See also RFC 4108
|
||||
ASN1Sequence seq1 = ASN1Sequence.getInstance(obj.getOctets());
|
||||
|
||||
// Iterate over GeneralNames sequence until HardwareModuleName is found
|
||||
Iterator<ASN1Encodable> seqIterator = seq1.iterator();
|
||||
ASN1TaggedObject tagObj1;
|
||||
ASN1Encodable encodable;
|
||||
while (seqIterator.hasNext()) {
|
||||
encodable = seqIterator.next();
|
||||
if (encodable != null) {
|
||||
tagObj1 = ASN1TaggedObject.getInstance(encodable.toASN1Primitive());
|
||||
|
||||
if (tagObj1 != null && tagObj1.hasContextTag(0)) { // Corresponds to otherName
|
||||
seq1 = ASN1Sequence.getInstance(tagObj1, false);
|
||||
|
||||
ASN1ObjectIdentifier obj1 = ASN1ObjectIdentifier.getInstance(seq1.getObjectAt(0));
|
||||
tagObj1 = ASN1TaggedObject.getInstance(seq1.getObjectAt(1));
|
||||
|
||||
if (obj1.toString().equals(HARDWARE_MODULE_NAME_OID)) {
|
||||
|
||||
// HardwareModuleName sequence
|
||||
seq1 = ASN1Sequence.getInstance(tagObj1, false);
|
||||
seq1 = ASN1Sequence.getInstance(seq1.getObjectAt(0));
|
||||
|
||||
obj1 = ASN1ObjectIdentifier.getInstance(seq1.getObjectAt(0));
|
||||
ASN1OctetString obj2;
|
||||
try {
|
||||
obj2 = ASN1OctetString.getInstance(seq1.getObjectAt(1));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Some certs have been found to contain tagged objects for hwSerialNum.
|
||||
// Handle this as a special case.
|
||||
log.warn("Could not parse octet string for hwSerialNum. Attempting to parse tag.");
|
||||
try {
|
||||
tagObj1 = ASN1TaggedObject.getInstance(seq1.getObjectAt(1));
|
||||
obj2 = ASN1OctetString.getInstance(tagObj1, false);
|
||||
}
|
||||
catch (Exception i) { // Invalid object found
|
||||
log.warn("Invalid object found for hwSerialNum.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If an OID corresponding to TPM 2.0 for hwType is supported, according to the
|
||||
// specification "TPM 2.0 Keys for Device Identity and Attestation", the contents of
|
||||
// the hwSerialNum field will be parsed accordingly.
|
||||
hwType = obj1.toString();
|
||||
if (hasTCGOIDs()) {
|
||||
tcgOid = true;
|
||||
}
|
||||
|
||||
// Convert octet string to byte array
|
||||
hwSerialNum = obj2.getOctets();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for certificate policy qualifiers, which should be present for IDevIDs if in compliance with the
|
||||
// TCG specification.
|
||||
// For interoperability reasons, this will only log a warning if a TCG OID is specified above.
|
||||
byte[] policyBytes = getX509Certificate().getExtensionValue(Extension.certificatePolicies.getId());
|
||||
Map<String, Boolean> policyQualifiers = null;
|
||||
|
||||
if (policyBytes != null) {
|
||||
policyQualifiers = this.getTPMPolicyQualifiers(policyBytes);
|
||||
}
|
||||
if (tcgOid) {
|
||||
boolean failCondition;
|
||||
if (policyQualifiers != null) {
|
||||
StringBuilder qualifierSB = new StringBuilder();
|
||||
policyQualifiers.forEach((key, value) -> {
|
||||
if (value) {
|
||||
if (qualifierSB.length() > 0) {
|
||||
qualifierSB.append(" ");
|
||||
}
|
||||
qualifierSB.append(key);
|
||||
}
|
||||
});
|
||||
tpmPolicies = qualifierSB.toString();
|
||||
|
||||
failCondition = !(policyQualifiers.get("verifiedTPMResidency") &&
|
||||
(policyQualifiers.get("verifiedTPMFixed") ||
|
||||
policyQualifiers.get("verifiedTPMRestricted")));
|
||||
} else {
|
||||
failCondition = true;
|
||||
}
|
||||
if (failCondition) {
|
||||
log.warn("TPM policy qualifiers not found, or do not meet logical criteria. Certificate may not " +
|
||||
"be in compliance with TCG specification.");
|
||||
}
|
||||
}
|
||||
|
||||
// Log a warning if notAfter field has an expiry date that is not indefinite
|
||||
if (!this.getEndValidity().toInstant().equals(Instant.ofEpochSecond(UNDEFINED_EXPIRY_DATE))) {
|
||||
log.warn("IDevID does not contain an indefinite expiry date. This may indicate an invalid " +
|
||||
"certificate.");
|
||||
}
|
||||
|
||||
input.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to check whether a given IDevID certificate has TCG OIDs, in order to check compliance with various
|
||||
* fields.
|
||||
*
|
||||
* @return a boolean value
|
||||
*/
|
||||
public boolean hasTCGOIDs() {
|
||||
if (this.getHwType() != null) {
|
||||
return this.getHwType().equals(HWTYPE_TCG_TPM2_OID);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("checkstyle:avoidinlineconditionals")
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IDevIDCertificate that = (IDevIDCertificate) o;
|
||||
|
||||
if (!Objects.equals(getTpmPolicies(), that.getTpmPolicies())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(getHwType(), that.getHwType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.equals(getHwSerialNum(), that.getHwSerialNum());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"checkstyle:magicnumber", "checkstyle:avoidinlineconditionals"})
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + (getTpmPolicies() != null ? getTpmPolicies().hashCode() : 0);
|
||||
result = 31 * result + (getHwType() != null ? getHwType().hashCode() : 0);
|
||||
result = 31 * result + (getHwSerialNum() != null ? Arrays.hashCode(getHwSerialNum()) : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -27,6 +27,11 @@ public enum Page {
|
||||
*/
|
||||
PLATFORM_CREDENTIALS("Platform Certificates", "ic_important_devices",
|
||||
null, "certificate-request/"),
|
||||
/**
|
||||
* Page to display and manage IDevID certificates.
|
||||
*/
|
||||
IDEVID_CERTIFICATES("IDevID Certificates", "ic_important_devices",
|
||||
null, "certificate-request/"),
|
||||
/**
|
||||
* Page to display issued certificates.
|
||||
*/
|
||||
|
@ -3,6 +3,7 @@ package hirs.attestationca.portal.page.controllers;
|
||||
import hirs.attestationca.persist.entity.manager.CACredentialRepository;
|
||||
import hirs.attestationca.persist.entity.manager.CertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
|
||||
import hirs.attestationca.persist.entity.manager.IDevIDCertificateRepository;
|
||||
import hirs.attestationca.portal.page.Page;
|
||||
import hirs.attestationca.portal.page.PageController;
|
||||
import hirs.attestationca.portal.page.PageMessages;
|
||||
@ -34,21 +35,25 @@ public class CertificateDetailsPageController extends PageController<Certificate
|
||||
private final CertificateRepository certificateRepository;
|
||||
private final CACredentialRepository caCredentialRepository;
|
||||
private final ComponentResultRepository componentResultRepository;
|
||||
private final IDevIDCertificateRepository iDevIDCertificateRepository;
|
||||
|
||||
/**
|
||||
* Constructor providing the Page's display and routing specification.
|
||||
* @param certificateRepository the certificate repository
|
||||
* @param componentResultRepository the component result repository
|
||||
* @param caCredentialRepository the ca credential manager
|
||||
* @param iDevIDCertificateRepository the idevid certificate repository
|
||||
*/
|
||||
@Autowired
|
||||
public CertificateDetailsPageController(final CertificateRepository certificateRepository,
|
||||
final ComponentResultRepository componentResultRepository,
|
||||
final CACredentialRepository caCredentialRepository) {
|
||||
final CACredentialRepository caCredentialRepository,
|
||||
final IDevIDCertificateRepository iDevIDCertificateRepository) {
|
||||
super(Page.CERTIFICATE_DETAILS);
|
||||
this.certificateRepository = certificateRepository;
|
||||
this.componentResultRepository = componentResultRepository;
|
||||
this.caCredentialRepository = caCredentialRepository;
|
||||
this.iDevIDCertificateRepository = iDevIDCertificateRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,6 +105,10 @@ public class CertificateDetailsPageController extends PageController<Certificate
|
||||
data.putAll(CertificateStringMapBuilder.getIssuedInformation(uuid,
|
||||
certificateRepository, caCredentialRepository));
|
||||
break;
|
||||
case "idevid":
|
||||
data.putAll(CertificateStringMapBuilder.getIdevidInformation(uuid,
|
||||
certificateRepository, caCredentialRepository));
|
||||
break;
|
||||
default:
|
||||
String typeError = "Invalid certificate type: " + params.getType();
|
||||
messages.addError(typeError);
|
||||
|
@ -8,12 +8,14 @@ import hirs.attestationca.persist.entity.manager.CACredentialRepository;
|
||||
import hirs.attestationca.persist.entity.manager.CertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.ComponentResultRepository;
|
||||
import hirs.attestationca.persist.entity.manager.EndorsementCredentialRepository;
|
||||
import hirs.attestationca.persist.entity.manager.IDevIDCertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.IssuedCertificateRepository;
|
||||
import hirs.attestationca.persist.entity.manager.PlatformCertificateRepository;
|
||||
import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.IDevIDCertificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
|
||||
@ -89,9 +91,11 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
private final EndorsementCredentialRepository endorsementCredentialRepository;
|
||||
private final IssuedCertificateRepository issuedCertificateRepository;
|
||||
private final CACredentialRepository caCredentialRepository;
|
||||
private final IDevIDCertificateRepository iDevIDCertificateRepository;
|
||||
|
||||
private static final String TRUSTCHAIN = "trust-chain";
|
||||
private static final String PLATFORMCREDENTIAL = "platform-credentials";
|
||||
private static final String IDEVIDCERTIFICATE = "idevid-certificates";
|
||||
private static final String ENDORSEMENTCREDENTIAL = "endorsement-key-credentials";
|
||||
private static final String ISSUEDCERTIFICATES = "issued-certificates";
|
||||
|
||||
@ -109,8 +113,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
* @param endorsementCredentialRepository the endorsement credential manager
|
||||
* @param issuedCertificateRepository the issued certificate manager
|
||||
* @param caCredentialRepository the ca credential manager
|
||||
* @param acaCertificate the ACA's X509 certificate
|
||||
*/
|
||||
* @param acaCertificate the ACA's X509 certificate */
|
||||
@Autowired
|
||||
public CertificatePageController(final CertificateRepository certificateRepository,
|
||||
final PlatformCertificateRepository platformCertificateRepository,
|
||||
@ -118,6 +121,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
final EndorsementCredentialRepository endorsementCredentialRepository,
|
||||
final IssuedCertificateRepository issuedCertificateRepository,
|
||||
final CACredentialRepository caCredentialRepository,
|
||||
final IDevIDCertificateRepository iDevIDCertificateRepository,
|
||||
final X509Certificate acaCertificate) {
|
||||
super(Page.TRUST_CHAIN);
|
||||
this.certificateRepository = certificateRepository;
|
||||
@ -126,6 +130,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
this.endorsementCredentialRepository = endorsementCredentialRepository;
|
||||
this.issuedCertificateRepository = issuedCertificateRepository;
|
||||
this.caCredentialRepository = caCredentialRepository;
|
||||
this.iDevIDCertificateRepository = iDevIDCertificateRepository;
|
||||
|
||||
try {
|
||||
certificateAuthorityCredential
|
||||
@ -171,6 +176,9 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
case PLATFORMCREDENTIAL:
|
||||
mav = getBaseModelAndView(Page.PLATFORM_CREDENTIALS);
|
||||
break;
|
||||
case IDEVIDCERTIFICATE:
|
||||
mav = getBaseModelAndView(Page.IDEVID_CERTIFICATES);
|
||||
break;
|
||||
case ENDORSEMENTCREDENTIAL:
|
||||
mav = getBaseModelAndView(Page.ENDORSEMENT_KEY_CREDENTIALS);
|
||||
break;
|
||||
@ -318,6 +326,23 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
log.debug("Returning list of size: " + records.size());
|
||||
return new DataTableResponse<>(records, input);
|
||||
}
|
||||
else if (certificateType.equals(IDEVIDCERTIFICATE)) {
|
||||
FilteredRecordsList<IDevIDCertificate> records = new FilteredRecordsList<IDevIDCertificate>();
|
||||
org.springframework.data.domain.Page<IDevIDCertificate> pagedResult =
|
||||
this.iDevIDCertificateRepository.findByArchiveFlag(false, paging);
|
||||
|
||||
if (pagedResult.hasContent()) {
|
||||
records.addAll(pagedResult.getContent());
|
||||
records.setRecordsTotal(pagedResult.getContent().size());
|
||||
} else {
|
||||
records.setRecordsTotal(input.getLength());
|
||||
}
|
||||
|
||||
records.setRecordsFiltered(iDevIDCertificateRepository.findByArchiveFlag(false).size());
|
||||
|
||||
log.debug("Returning list of size: " + records.size());
|
||||
return new DataTableResponse<>(records, input);
|
||||
}
|
||||
|
||||
return new DataTableResponse<>(new FilteredRecordsList<>(), input);
|
||||
}
|
||||
@ -677,6 +702,7 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
case PLATFORMCREDENTIAL -> Page.PLATFORM_CREDENTIALS;
|
||||
case ENDORSEMENTCREDENTIAL -> Page.ENDORSEMENT_KEY_CREDENTIALS;
|
||||
case ISSUEDCERTIFICATES -> Page.ISSUED_CERTIFICATES;
|
||||
case IDEVIDCERTIFICATE -> Page.IDEVID_CERTIFICATES;
|
||||
default -> Page.TRUST_CHAIN;
|
||||
};
|
||||
}
|
||||
@ -695,6 +721,8 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
return EndorsementCredential.class;
|
||||
case ISSUEDCERTIFICATES:
|
||||
return IssuedAttestationCertificate.class;
|
||||
case IDEVIDCERTIFICATE:
|
||||
return IDevIDCertificate.class;
|
||||
case TRUSTCHAIN:
|
||||
return CertificateAuthorityCredential.class;
|
||||
default:
|
||||
@ -728,6 +756,10 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
return this.certificateRepository
|
||||
.findByCertificateHash(certificateHash,
|
||||
"CertificateAuthorityCredential");
|
||||
case IDEVIDCERTIFICATE:
|
||||
return this.certificateRepository
|
||||
.findByCertificateHash(certificateHash,
|
||||
"IDevIDCertificate");
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -791,6 +823,8 @@ public class CertificatePageController extends PageController<NoPageParams> {
|
||||
return new PlatformCredential(fileBytes);
|
||||
case ENDORSEMENTCREDENTIAL:
|
||||
return new EndorsementCredential(fileBytes);
|
||||
case IDEVIDCERTIFICATE:
|
||||
return new IDevIDCertificate(fileBytes);
|
||||
case TRUSTCHAIN:
|
||||
if (CredentialHelper.isMultiPEM(new String(fileBytes, StandardCharsets.UTF_8))) {
|
||||
try (ByteArrayInputStream certInputStream = new ByteArrayInputStream(fileBytes)) {
|
||||
|
@ -7,6 +7,7 @@ import hirs.attestationca.persist.entity.userdefined.Certificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.CertificateAuthorityCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.ComponentResult;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.EndorsementCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.IDevIDCertificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.IssuedAttestationCertificate;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.PlatformCredential;
|
||||
import hirs.attestationca.persist.entity.userdefined.certificate.attributes.ComponentIdentifier;
|
||||
@ -20,6 +21,7 @@ import org.bouncycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -60,8 +62,8 @@ public final class CertificateStringMapBuilder {
|
||||
data.put("certificateId", certificate.getId().toString());
|
||||
}
|
||||
data.put("authInfoAccess", certificate.getAuthorityInfoAccess());
|
||||
data.put("beginValidity", certificate.getBeginValidity().toString());
|
||||
data.put("endValidity", certificate.getEndValidity().toString());
|
||||
data.put("beginValidity", Long.toString(certificate.getBeginValidity().getTime()));
|
||||
data.put("endValidity", Long.toString(certificate.getEndValidity().getTime()));
|
||||
data.put("signature", Arrays.toString(certificate.getSignature()));
|
||||
data.put("signatureSize", Integer.toString(certificate.getSignature().length
|
||||
* Certificate.MIN_ATTR_CERT_LENGTH));
|
||||
@ -525,4 +527,90 @@ public final class CertificateStringMapBuilder {
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IDevID Certificate information.
|
||||
*
|
||||
* @param uuid ID for the certificate.
|
||||
* @param certificateRepository the certificate manager for retrieving certs.
|
||||
* @return a hash map with the endorsement certificate information.
|
||||
*/
|
||||
public static HashMap<String, String> getIdevidInformation(final UUID uuid,
|
||||
final CertificateRepository certificateRepository,
|
||||
final CACredentialRepository caCredentialRepository) {
|
||||
|
||||
HashMap<String, String> data = new HashMap<>();
|
||||
IDevIDCertificate certificate = (IDevIDCertificate) certificateRepository.getCertificate(uuid);
|
||||
|
||||
if (certificate != null) {
|
||||
data.putAll(getGeneralCertificateInfo(certificate, certificateRepository, caCredentialRepository));
|
||||
|
||||
if (certificate.getHwType() != null) {
|
||||
data.put("hwType", certificate.getHwType());
|
||||
String hwTypeReadable;
|
||||
if (certificate.hasTCGOIDs()) {
|
||||
hwTypeReadable = "TPM-Bound IDevID";
|
||||
}
|
||||
else {
|
||||
hwTypeReadable = "Manufacturer Specific";
|
||||
}
|
||||
data.put("hwTypeReadable", hwTypeReadable);
|
||||
}
|
||||
|
||||
if (certificate.getHwSerialNum() != null) {
|
||||
String hwSerialStr = new String(certificate.getHwSerialNum(), StandardCharsets.US_ASCII);
|
||||
|
||||
// Obtain colon-delimited fields from hwSerialNum field, if present
|
||||
if (certificate.hasTCGOIDs()) {
|
||||
if (hwSerialStr.contains(":")) {
|
||||
String[] hwSerialArray = hwSerialStr.split(":");
|
||||
if (hwSerialArray.length >= 3) {
|
||||
data.put("tcgTpmManufacturer", hwSerialArray[0]);
|
||||
data.put("ekAuthorityKeyIdentifier", hwSerialArray[1]);
|
||||
data.put("ekCertificateSerialNumber", hwSerialArray[2]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Corresponds to digest of EK certificate
|
||||
data.put("ekCertificateDigest", Boolean.valueOf(true).toString());
|
||||
String hwSerialToAdd = Hex.toHexString(certificate.getHwSerialNum());
|
||||
data.put("hwSerialNumHex", Boolean.valueOf(true).toString());
|
||||
data.put("hwSerialNum", hwSerialToAdd);
|
||||
}
|
||||
}
|
||||
else {
|
||||
String hwSerialToAdd = hwSerialStr;
|
||||
|
||||
// Check if hwSerialNum is a printable ASCII string; default to hex otherwise
|
||||
if (hwSerialStr.chars().allMatch(c -> c > 0x20 && c <= 0x7F)) {
|
||||
data.put("hwSerialNum", hwSerialStr);
|
||||
} else {
|
||||
hwSerialToAdd = Hex.toHexString(certificate.getHwSerialNum());
|
||||
data.put("hwSerialNumHex", Boolean.valueOf(true).toString());
|
||||
}
|
||||
data.put("hwSerialNum", hwSerialToAdd);
|
||||
}
|
||||
}
|
||||
|
||||
if (certificate.getKeyUsage() != null) {
|
||||
data.put("keyUsage", certificate.getKeyUsage());
|
||||
}
|
||||
|
||||
if (certificate.getExtendedKeyUsage() != null
|
||||
&& !certificate.getExtendedKeyUsage().isEmpty()) {
|
||||
data.put("extendedKeyUsage", certificate.getExtendedKeyUsage());
|
||||
}
|
||||
|
||||
if (certificate.getTpmPolicies() != null) {
|
||||
data.put("tpmPolicies", certificate.getTpmPolicies());
|
||||
}
|
||||
|
||||
data.put("x509Version", Integer.toString(certificate
|
||||
.getX509CredentialVersion()));
|
||||
} else {
|
||||
String notFoundMessage = "Unable to find IDevIDCertificate with ID: " + uuid;
|
||||
log.error(notFoundMessage);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,12 @@
|
||||
<img src="${icons}/ic_file_download_black_24dp.png" title="Download Certificate">
|
||||
</a>
|
||||
</c:when>
|
||||
<c:when test="${param.type=='idevid'}">
|
||||
IDevID Certificate
|
||||
<a href="${portal}/certificate-request/idevid-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>
|
||||
@ -105,10 +111,48 @@
|
||||
</c:if>
|
||||
<c:if test="${not empty initialData.serialNumber}">
|
||||
<div class="row">
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Serial Number</span></div>
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Certificate Serial Number</span></div>
|
||||
<div id="serialNumber" class="col col-md-8 vertical"></div>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="${not empty initialData.hwSerialNum}">
|
||||
<div class="row">
|
||||
<c:choose>
|
||||
<c:when test="${not empty initialData.ekCertificateDigest}">
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader help-text" title="Defined in RFC 4108, and contained in the Subject Alternate Name. This certificate is also TCG-compliant for this field, indicated by the use of a TCG OID for Hardware Type.">Hardware Module Name</span></div>
|
||||
<div class="col col-md-8">
|
||||
<div><span class="help-text" title="Contains an OID that, in conjunction with the Hardware Serial Number, identifies the device's hardware module. This certificate is using a TCG OID, indicating the use of a Trusted Platform Module. OID value: ${initialData.hwType}">Hardware Type</span>: <span id="hwTypeReadable">${initialData.hwTypeReadable}</span></div>
|
||||
<div><span class="help-text" title="TCG-compliant: the device's TPM does not contain an EK certificate, and the Hardware Serial Number represents a digest of the EK Certificate public key. See TCG specification titled "TPM 2.0 Keys for Device Identity and Attestation".">Hardware Serial Number</span>:</div>
|
||||
<ul>
|
||||
<li>EK Certificate Public Key: <span id="hwSerialNum">${initialData.hwSerialNum}</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader help-text" title="Defined in RFC 4108, and contained in the Subject Alternate Name. This certificate is not using a TCG OID for Hardware Type, so these fields may have manufacturer-specific context.">Hardware Module Name</span></div>
|
||||
<div class="col col-md-8">
|
||||
<div><span class="help-text" title="Contains an OID that, in conjunction with the Hardware Serial Number, identifies the device's hardware module. This certificate is using a non-TCG OID, possibly indicating manufacturer-specific context. OID value: ${initialData.hwType}">Hardware Type</span>: <span id="hwTypeReadable">${initialData.hwTypeReadable}</span></div>
|
||||
<div><span class="help-text" title="Used for identifying the device's hardware module. This field may have manufacturer-specific context.">Hardware Serial Number</span>: <span id="hwSerialNum">${initialData.hwSerialNum}</span></div>
|
||||
</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="${not empty initialData.tcgTpmManufacturer}">
|
||||
<div class="row">
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader help-text" title="Defined in RFC 4108, and contained in the Subject Alternate Name. This certificate is also TCG-compliant for this field, indicated by the use of a TCG OID for Hardware Type.">Hardware Module Name</span></div>
|
||||
<div class="col col-md-8">
|
||||
<div><span class="help-text" title="Contains an OID that, in conjunction with the Hardware Serial Number, identifies the device's hardware module. This certificate is using a TCG OID, indicating the use of a Trusted Platform Module. OID value: ${initialData.hwType}">Hardware Type</span>: <span id="hwTypeReadable">${initialData.hwTypeReadable}</span></div>
|
||||
<div><span class="help-text" title="TCG-compliant: the device's TPM contains an EK certificate, and the below fields are parsed accordingly from the Hardware Serial Number. See TCG specification titled "TPM 2.0 Keys for Device Identity and Attestation".">Hardware Serial Number</span>:
|
||||
<ul>
|
||||
<li>TCG TPM Manufacturer Code: <span id="tcgTpmManufacturer">${initialData.tcgTpmManufacturer}</span></li>
|
||||
<li>EK Authority Key Identifier: <span id="ekAuthorityKeyIdentifier">${initialData.ekAuthorityKeyIdentifier}</span></li>
|
||||
<li>EK CertificateSerialNumber: <span id="ekCertificateSerialNumber">${initialData.ekCertificateSerialNumber}</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</c:if>
|
||||
<c:if test="${not empty initialData.beginValidity}">
|
||||
<div class="row">
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Validity</span></div>
|
||||
@ -196,10 +240,14 @@
|
||||
<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>
|
||||
<c:choose>
|
||||
<c:when test="${not empty initialData.credentialType}">
|
||||
<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 vertical">${initialData.credentialType}</div>
|
||||
</div>
|
||||
</c:when>
|
||||
</c:choose>
|
||||
<!-- Add the different fields based on the certificate type -->
|
||||
<c:choose>
|
||||
<c:when test="${param.type=='certificateauthority'}">
|
||||
@ -878,6 +926,35 @@
|
||||
</div>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:when test="${param.type=='idevid'}">
|
||||
<c:choose>
|
||||
<c:when test="${not empty initialData.tpmPolicies}">
|
||||
<div class="row">
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader help-text" title="TPM verification policies, as defined in the TCG specification "TPM 2.0 Keys for Device Identity and Attestation".">TPM Policies</span></div>
|
||||
<div id="tpmPolicies" class="col col-md-8 vertical">${initialData.tpmPolicies}</div>
|
||||
</div>
|
||||
</c:when>
|
||||
</c:choose>
|
||||
<div class="row">
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Key Usage</span></div>
|
||||
<c:choose>
|
||||
<c:when test="${not empty initialData.keyUsage}">
|
||||
<div id="keyUsage" class="col col-md-8 vertical">${initialData.keyUsage}</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div id="keyUsage" class="col col-md-8 vertical">Not Specified</div>
|
||||
</c:otherwise>
|
||||
</c:choose>
|
||||
</div>
|
||||
<c:choose>
|
||||
<c:when test="${not empty initialData.extendedKeyUsage}">
|
||||
<div class="row">
|
||||
<div class="col-md-1 col-md-offset-1"><span class="colHeader">Extended Key Usage</span></div>
|
||||
<div id="extendedKeyUsage" class="col col-md-8 vertical">${initialData.extendedKeyUsage}</div>
|
||||
</div>
|
||||
</c:when>
|
||||
</c:choose>
|
||||
</c:when>
|
||||
</c:choose>
|
||||
</div>
|
||||
<script>
|
||||
@ -890,7 +967,8 @@
|
||||
|
||||
//Format validity time
|
||||
$("#validity span").each(function () {
|
||||
$(this).text(formatDateTime($(this).text()));
|
||||
var dateText = $(this).text();
|
||||
return $(this).text(formatCertificateDate(dateText));
|
||||
});
|
||||
|
||||
//Convert byte array to string
|
||||
@ -935,6 +1013,19 @@
|
||||
}
|
||||
</c:if>
|
||||
|
||||
<c:if test="${not empty initialData.hwSerialNumHex}">
|
||||
var hwSerialNum = '${initialData.hwSerialNum}';
|
||||
$("#hwSerialNum").html(parseHexString(hwSerialNum));
|
||||
</c:if>
|
||||
|
||||
<c:if test="${not empty initialData.tcgTpmManufacturer}">
|
||||
var ekAKI = '${initialData.ekAuthorityKeyIdentifier};'
|
||||
var ekCSN = '${initialData.ekCertificateSerialNumber};'
|
||||
|
||||
$("#ekAuthorityKeyIdentifier").html(parseHexString(ekAKI));
|
||||
$("#ekCertificateSerialNumber").html(parseHexString(ekCSN));
|
||||
</c:if>
|
||||
|
||||
//Initilize tooltips
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
|
@ -0,0 +1,85 @@
|
||||
<%@page contentType="text/html" pageEncoding="UTF-8"%>
|
||||
|
||||
<%-- JSP TAGS --%>
|
||||
<%@taglib prefix="c" uri="jakarta.tags.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">IDevID Certificates</jsp:attribute>
|
||||
|
||||
<jsp:body>
|
||||
<div class="aca-input-box-header">
|
||||
<form:form method="POST" action="${portal}/certificate-request/idevid-certificates/upload" enctype="multipart/form-data">
|
||||
Import IDevID Certificates
|
||||
<my:file-chooser id="idevid-editor" label="Import IDevID Certificates">
|
||||
<input id="importFile" type="file" name="file" multiple="multiple" />
|
||||
</my:file-chooser>
|
||||
<a href="${portal}/certificate-request/idevid-certificates/bulk">
|
||||
<img src="${icons}/ic_file_download_black_24dp.png" title="Download All IDevID Certificates">
|
||||
</a>
|
||||
</form:form>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="aca-data-table">
|
||||
<table id="idevidCertificateTable" 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 columns = [
|
||||
{data: 'issuer'},
|
||||
{data: 'subject'},
|
||||
{
|
||||
data: 'beginValidity',
|
||||
searchable:false,
|
||||
render: function (data, type, full, meta) {
|
||||
return formatCertificateDate(full.beginValidity);
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'endValidity',
|
||||
searchable:false,
|
||||
render: function (data, type, full, meta) {
|
||||
return formatCertificateDate(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('idevid', full.id, true);
|
||||
html += certificateDownloadLink(full.id, pagePath);
|
||||
html += certificateDeleteLink(full.id, pagePath);
|
||||
return html;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
//Set data tables
|
||||
setDataTables("#idevidCertificateTable", url, columns);
|
||||
});
|
||||
</script>
|
||||
</jsp:body>
|
||||
</my:page>
|
@ -41,6 +41,12 @@
|
||||
</a>
|
||||
</h3>
|
||||
<h4>Upload, view and manage endorsement credentials.</h4>
|
||||
<h3>
|
||||
<a href="${certificateRequest}/idevid-certificates">
|
||||
<img src="${icons}/ic_important_devices_black_24dp.png" /> IDevID Certificates
|
||||
</a>
|
||||
</h3>
|
||||
<h4>Upload, view and manage IDevID certificates.</h4>
|
||||
<h3>
|
||||
<a href="${portal}/reference-manifests">
|
||||
<img src="${icons}/ic_important_devices_black_24dp.png" /> Reference Integrity Manifests
|
||||
|
@ -64,3 +64,10 @@
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
text-decoration: underline dashed;
|
||||
text-underline-position:under;
|
||||
text-decoration-thickness: 1px;
|
||||
cursor: help;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
function byteToHexString(arr){
|
||||
var str = "";
|
||||
$.each(arr, function(index, value){
|
||||
str += ('0' + (value & 0xFF).toString(16)).slice(-2) + ": ";
|
||||
str += ('0' + (value & 0xFF).toString(16)).slice(-2) + ":";
|
||||
});
|
||||
return (str.substring(0, str.length - 2)).toUpperCase();
|
||||
}
|
||||
@ -14,7 +14,7 @@ function parseHexString(hexString) {
|
||||
if(str.length === 2) {
|
||||
return str;
|
||||
}
|
||||
return str.match(/.{2}/g).join(': ');
|
||||
return str.match(/.{2}/g).join(':');
|
||||
}
|
||||
|
||||
//Parse the HEX string value to display as byte hex string
|
||||
@ -28,7 +28,7 @@ function parseSerialNumber(hexString){
|
||||
return str;
|
||||
}
|
||||
//Parse and return
|
||||
return newString = hexString.match(/.{2}/g).join(':');
|
||||
return newString = str.match(/.{2}/g).join(':');
|
||||
|
||||
}
|
||||
|
||||
@ -111,6 +111,10 @@ function certificateDetailsLink(type, id, sameType){
|
||||
icon += "/ic_vpn_key_black_24dp.png";
|
||||
title = "View Endorsement Certificate Details";
|
||||
break;
|
||||
case "idevid":
|
||||
icon += "/ic_vpn_key_black_24dp.png";
|
||||
title = "View IDevID Certificate Details";
|
||||
break;
|
||||
}
|
||||
}
|
||||
var html = '<a href=' + href + '>'
|
||||
@ -197,3 +201,18 @@ function rimDownloadLink(id, pagePath){
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a given date to a UTC string, or returns an indefinite icon
|
||||
* @param date to format
|
||||
*/
|
||||
function formatCertificateDate(dateText) {
|
||||
var date = +dateText; // Convert to numeric
|
||||
|
||||
if (date == 253402300799000)
|
||||
{
|
||||
return 'Indefinite';
|
||||
}
|
||||
|
||||
return new Date(date).toUTCString();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user