Merge pull request #327 from nsacyber/issue-308

[#308] Certificate RDN update
This commit is contained in:
Cyrus 2021-01-05 10:07:04 -05:00 committed by GitHub
commit 20f94b94ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 318 additions and 242 deletions

View File

@ -67,11 +67,11 @@ import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@ -411,11 +411,8 @@ public abstract class AbstractAttestationCertificateAuthority
// and later tpm20MakeCredential function
RSAPublicKey ekPub = parsePublicKey(claim.getEkPublicArea().toByteArray());
AppraisalStatus.Status validationResult = AppraisalStatus.Status.FAIL;
try {
validationResult = doSupplyChainValidation(claim, ekPub);
} catch (Exception ex) {
LOG.error(ex);
}
validationResult = doSupplyChainValidation(claim, ekPub);
if (validationResult == AppraisalStatus.Status.PASS) {
RSAPublicKey akPub = parsePublicKey(claim.getAkPublicArea().toByteArray());
@ -555,19 +552,15 @@ public abstract class AbstractAttestationCertificateAuthority
if (request.getQuote() != null && !request.getQuote().isEmpty()) {
parseTPMQuote(request.getQuote().toStringUtf8());
TPMInfo savedInfo = device.getDeviceInfo().getTPMInfo();
TPMInfo tpmInfo = null;
try {
tpmInfo = new TPMInfo(savedInfo.getTPMMake(),
TPMInfo tpmInfo = new TPMInfo(savedInfo.getTPMMake(),
savedInfo.getTPMVersionMajor(),
savedInfo.getTPMVersionMinor(),
savedInfo.getTPMVersionRevMajor(),
savedInfo.getTPMVersionRevMinor(),
savedInfo.getPcrValues(),
this.tpmQuoteHash.getBytes("UTF-8"),
this.tpmQuoteSignature.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
LOG.error(e);
}
this.tpmQuoteHash.getBytes(StandardCharsets.UTF_8),
this.tpmQuoteSignature.getBytes(StandardCharsets.UTF_8));
DeviceInfoReport dvReport = new DeviceInfoReport(
device.getDeviceInfo().getNetworkInfo(),
device.getDeviceInfo().getOSInfo(),
@ -859,18 +852,14 @@ public abstract class AbstractAttestationCertificateAuthority
// Get TPM info, currently unimplemented
TPMInfo tpm;
try {
tpm = new TPMInfo(DeviceInfoReport.NOT_SPECIFIED,
(short) 0,
(short) 0,
(short) 0,
(short) 0,
this.pcrValues.getBytes("UTF-8"),
this.tpmQuoteHash.getBytes("UTF-8"),
this.tpmQuoteSignature.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
tpm = new TPMInfo();
}
tpm = new TPMInfo(DeviceInfoReport.NOT_SPECIFIED,
(short) 0,
(short) 0,
(short) 0,
(short) 0,
this.pcrValues.getBytes(StandardCharsets.UTF_8),
this.tpmQuoteHash.getBytes(StandardCharsets.UTF_8),
this.tpmQuoteSignature.getBytes(StandardCharsets.UTF_8));
// Create final report
DeviceInfoReport dvReport = new DeviceInfoReport(nw, os, fw, hw, tpm,
@ -1272,7 +1261,13 @@ public abstract class AbstractAttestationCertificateAuthority
IssuedCertificateAttributeHelper.buildSubjectAlternativeNameFromCerts(
endorsementCredential, platformCredentials, deviceName);
Extension authKeyIdentifier = IssuedCertificateAttributeHelper
.buildAuthorityKeyIdentifier(endorsementCredential);
builder.addExtension(subjectAlternativeName);
if (authKeyIdentifier != null) {
builder.addExtension(authKeyIdentifier);
}
// identify cert as an AIK with this extension
if (IssuedCertificateAttributeHelper.EXTENDED_KEY_USAGE_EXTENSION != null) {
builder.addExtension(IssuedCertificateAttributeHelper.EXTENDED_KEY_USAGE_EXTENSION);

View File

@ -4,12 +4,14 @@ 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.ASN1OctetString;
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.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
@ -19,7 +21,6 @@ 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;
@ -66,6 +67,31 @@ public final class IssuedCertificateAttributeHelper {
// do not construct publicly
}
/**
* This method builds the AKI extension that will be stored in the generated
* Attestation Issued Certificate.
* @param endorsementCredential EK object to pull AKI from.
* @return the AKI extension.
* @throws IOException on bad get instance for AKI.
*/
public static Extension buildAuthorityKeyIdentifier(
final EndorsementCredential endorsementCredential) throws IOException {
if (endorsementCredential == null || endorsementCredential.getX509Certificate() == null) {
return null;
}
byte[] extValue = endorsementCredential.getX509Certificate()
.getExtensionValue(Extension.authorityKeyIdentifier.getId());
if (extValue == null) {
return null;
}
byte[] authExtension = ASN1OctetString.getInstance(extValue).getOctets();
AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance(authExtension);
return new Extension(Extension.authorityKeyIdentifier, true, aki.getEncoded());
}
/**
* Builds the subject alternative name based on the supplied certificates.
* @param endorsementCredential the endorsement credential
@ -88,10 +114,8 @@ public final class IssuedCertificateAttributeHelper {
// 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);
}
for (PlatformCredential platformCredential : platformCredentials) {
populatePlatformCredentialAttributes(platformCredential, nameBuilder);
}
// add the OID for the TCG-required TPM ID label
@ -112,7 +136,7 @@ public final class IssuedCertificateAttributeHelper {
private static void populatePlatformCredentialAttributes(
final PlatformCredential platformCredential,
final X500NameBuilder nameBuilder) throws IOException {
if (null == platformCredential) {
if (platformCredential == null) {
return;
}
@ -134,7 +158,7 @@ public final class IssuedCertificateAttributeHelper {
private static void populateEndorsementCredentialAttributes(
final EndorsementCredential endorsementCredential, final X500NameBuilder nameBuilder) {
if (null == endorsementCredential) {
if (endorsementCredential == null) {
return;
}

View File

@ -16,10 +16,12 @@ import hirs.data.persist.PCRPolicy;
import hirs.data.persist.ArchivableEntity;
import hirs.tpm.eventlog.TCGEventLog;
import hirs.tpm.eventlog.TpmPcrEvent;
import hirs.utils.BouncyCastleUtils;
import hirs.utils.ReferenceManifestValidator;
import hirs.validation.SupplyChainCredentialValidator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
@ -51,7 +53,6 @@ import hirs.data.persist.ReferenceManifest;
import hirs.persist.AppraiserManager;
import hirs.persist.CertificateManager;
import hirs.persist.ReferenceManifestManager;
import hirs.persist.CertificateSelector;
import hirs.persist.CrudManager;
import hirs.persist.DBManagerException;
import hirs.persist.PersistenceConfiguration;
@ -803,27 +804,49 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
* certs with an identical subject and issuer org)
*
* @param credential the credential whose CA chain should be retrieved
* @param previouslyQueriedOrganizations a list of organizations to refrain
* @param previouslyQueriedSubjects a list of organizations to refrain
* from querying
* @return a Set containing all relevant CA credentials to the given
* certificate's organization
*/
private Set<CertificateAuthorityCredential> getCaChainRec(
final Certificate credential,
final Set<String> previouslyQueriedOrganizations
) {
CertificateSelector<CertificateAuthorityCredential> caSelector
= CertificateAuthorityCredential.select(certificateManager)
.bySubjectOrganization(credential.getIssuerOrganization());
Set<CertificateAuthorityCredential> certAuthsWithMatchingOrg = caSelector.getCertificates();
final Set<String> previouslyQueriedSubjects) {
CertificateAuthorityCredential skiCA = null;
Set<CertificateAuthorityCredential> certAuthsWithMatchingIssuer = new HashSet<>();
if (credential.getAuthKeyId() != null
&& !credential.getAuthKeyId().isEmpty()) {
byte[] bytes = Hex.decode(credential.getAuthKeyId());
skiCA = CertificateAuthorityCredential
.select(certificateManager)
.bySubjectKeyIdentifier(bytes).getCertificate();
}
Set<String> queriedOrganizations = new HashSet<>(previouslyQueriedOrganizations);
queriedOrganizations.add(credential.getIssuerOrganization());
if (skiCA == null) {
if (credential.getIssuerSorted() == null
|| credential.getIssuerSorted().isEmpty()) {
certAuthsWithMatchingIssuer = CertificateAuthorityCredential
.select(certificateManager)
.bySubject(credential.getIssuer())
.getCertificates();
} else {
//Get certificates by subject organization
certAuthsWithMatchingIssuer = CertificateAuthorityCredential
.select(certificateManager)
.bySubjectSorted(credential.getIssuerSorted())
.getCertificates();
}
} else {
certAuthsWithMatchingIssuer.add(skiCA);
}
Set<String> queriedOrganizations = new HashSet<>(previouslyQueriedSubjects);
queriedOrganizations.add(credential.getIssuer());
HashSet<CertificateAuthorityCredential> caCreds = new HashSet<>();
for (CertificateAuthorityCredential cred : certAuthsWithMatchingOrg) {
for (CertificateAuthorityCredential cred : certAuthsWithMatchingIssuer) {
caCreds.add(cred);
if (!queriedOrganizations.contains(cred.getIssuerOrganization())) {
if (!BouncyCastleUtils.x500NameCompare(cred.getIssuer(),
cred.getSubject())) {
caCreds.addAll(getCaChainRec(cred, queriedOrganizations));
}
}

View File

@ -119,7 +119,7 @@ public class SupplyChainValidationServiceImplTest extends SpringPersistenceTest
// mock endorsement credential
ec = mock(EndorsementCredential.class);
when(ec.getEncodedPublicKey()).thenReturn(new byte[] {0x0});
when(ec.getIssuerOrganization()).thenReturn("STMicroelectronics NV");
when(ec.getIssuerSorted()).thenReturn("STMicroelectronics NV");
Set<Certificate> resultEcs = new HashSet<>();
resultEcs.add(ec);
@ -131,8 +131,8 @@ public class SupplyChainValidationServiceImplTest extends SpringPersistenceTest
when(pc.getX509Certificate()).thenReturn(cert);
when(pc.getSerialNumber()).thenReturn(BigInteger.ONE);
when(pc.getPlatformSerial()).thenReturn(String.valueOf(Integer.MIN_VALUE));
when(pc.getIssuerOrganization()).thenReturn("STMicroelectronics NV");
when(ec.getSubjectOrganization()).thenReturn("STMicroelectronics NV");
when(pc.getIssuerSorted()).thenReturn("STMicroelectronics NV");
when(ec.getSubjectSorted()).thenReturn("STMicroelectronics NV");
pcs = new HashSet<PlatformCredential>();
pcs.add(pc);
@ -142,8 +142,8 @@ public class SupplyChainValidationServiceImplTest extends SpringPersistenceTest
when(delta.getId()).thenReturn(UUID.randomUUID());
when(delta.getX509Certificate()).thenReturn(deltaCert);
//when(delta.getSerialNumber()).thenReturn(BigInteger.ONE);
when(delta.getIssuerOrganization()).thenReturn("STMicroelectronics NV");
when(delta.getSubjectOrganization()).thenReturn("STMicroelectronics NV");
when(delta.getIssuerSorted()).thenReturn("STMicroelectronics NV");
when(delta.getSubjectSorted()).thenReturn("STMicroelectronics NV");
Set<Certificate> resultPcs = new HashSet<>();
resultPcs.add(pc);

View File

@ -7,6 +7,7 @@ import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.List;
import java.util.Comparator;
@ -21,6 +22,8 @@ import hirs.data.persist.certificate.attributes.ComponentIdentifier;
import hirs.data.persist.certificate.attributes.PlatformConfiguration;
import hirs.persist.CertificateManager;
import hirs.utils.BouncyCastleUtils;
import org.bouncycastle.util.encoders.Hex;
import java.util.Collections;
/**
@ -134,20 +137,35 @@ public final class CertificateStringMapBuilder {
public static Certificate containsAllChain(
final Certificate certificate,
final CertificateManager certificateManager) {
Set<CertificateAuthorityCredential> issuerCertificates;
Set<CertificateAuthorityCredential> issuerCertificates = new HashSet<>();
CertificateAuthorityCredential skiCA = null;
//Check if there is a subject organization
if (certificate.getIssuerOrganization() == null
|| certificate.getIssuerOrganization().isEmpty()) {
//Get certificates by subject
issuerCertificates = CertificateAuthorityCredential.select(certificateManager)
.bySubject(certificate.getIssuer())
.getCertificates();
if (certificate.getAuthKeyId() != null
&& !certificate.getAuthKeyId().isEmpty()) {
byte[] bytes = Hex.decode(certificate.getAuthKeyId());
skiCA = CertificateAuthorityCredential
.select(certificateManager)
.bySubjectKeyIdentifier(bytes).getCertificate();
} else {
//Get certificates by subject organization
issuerCertificates = CertificateAuthorityCredential.select(certificateManager)
.bySubjectOrganization(certificate.getIssuerOrganization())
.getCertificates();
LOGGER.info(String.format("Certificate (%s) for %s has no authority key identifier.",
certificate.getClass().toString(), certificate.getSubject()));
}
if (skiCA == null) {
if (certificate.getIssuerSorted() == null
|| certificate.getIssuerSorted().isEmpty()) {
//Get certificates by subject
issuerCertificates = CertificateAuthorityCredential.select(certificateManager)
.bySubject(certificate.getIssuer())
.getCertificates();
} else {
//Get certificates by subject organization
issuerCertificates = CertificateAuthorityCredential.select(certificateManager)
.bySubjectSorted(certificate.getIssuerSorted())
.getCertificates();
}
} else {
issuerCertificates.add(skiCA);
}
for (Certificate issuerCert : issuerCertificates) {

View File

@ -130,7 +130,7 @@ int provision() {
RestfulClientProvisioner provisioner;
string nonceBlob = provisioner.sendIdentityClaim(identityClaim);
if (nonceBlob == "") {
cout << "----> Provisioning failed.";
cout << "----> Provisioning failed." << endl;
cout << "Please refer to the Attestation CA for details." << endl;
return 0;
}

View File

@ -32,6 +32,7 @@ import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
@ -362,7 +363,7 @@ public class DeviceInfoCollector extends AbstractCollector {
if (debianRelease.exists()) {
try {
reader = new BufferedReader(new InputStreamReader(
new FileInputStream(debianRelease), "UTF-8"));
new FileInputStream(debianRelease), StandardCharsets.UTF_8));
while ((line = reader.readLine()) != null) {
String[] ubuntuTokens = line.split("=");
if (ubuntuTokens.length == 2) {
@ -394,7 +395,7 @@ public class DeviceInfoCollector extends AbstractCollector {
} else if (redhatRelease.exists()) {
try {
reader = new BufferedReader(new InputStreamReader(
new FileInputStream(redhatRelease), "UTF-8"));
new FileInputStream(redhatRelease), StandardCharsets.UTF_8));
while ((line = reader.readLine()) != null) {
String[] redhatTokens = line.split("release");
if (redhatTokens.length == 2) {
@ -543,7 +544,7 @@ public class DeviceInfoCollector extends AbstractCollector {
Process quoteProcess = processBuilder.start();
quoteReader
= new BufferedReader(new InputStreamReader(
quoteProcess.getInputStream(), "utf-8"));
quoteProcess.getInputStream(), StandardCharsets.UTF_8));
} catch (IOException e) {
LOGGER.info("IOException occurred while attempting to read "
+ "tpm_version command, assume the TPM is not present and "

View File

@ -12,7 +12,7 @@ import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.logging.log4j.Logger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
@ -100,8 +100,7 @@ public final class PCRPolicy extends Policy {
LOGGER.info("Validating quote from associated device.");
boolean validated = false;
short localityAtRelease = 0;
Charset charset = Charset.forName("UTF-8");
String quoteString = new String(tpmQuote, charset);
String quoteString = new String(tpmQuote, StandardCharsets.UTF_8);
TPMMeasurementRecord[] measurements = new TPMMeasurementRecord[baselinePcrs.length];
try {

View File

@ -12,9 +12,6 @@ import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.bouncycastle.asn1.x509.AttributeCertificateInfo;
import org.bouncycastle.asn1.x509.AttCertIssuer;
@ -62,8 +59,10 @@ import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.DistributionPoint;
@ -163,6 +162,12 @@ public abstract class Certificate extends ArchivableEntity {
public static final String ISSUER_FIELD = "issuer";
@Column(nullable = false)
private final String issuer;
/**
* Holds the name of the 'issuerSorted' field.
*/
public static final String ISSUER_SORTED_FIELD = "issuerSorted";
@Column
private final String issuerSorted;
/**
* Holds the name of the 'subject' field.
@ -170,20 +175,12 @@ public abstract class Certificate extends ArchivableEntity {
public static final String SUBJECT_FIELD = "subject";
@Column(nullable = true)
private final String subject;
/**
* Holds the name of the 'issuerOrganization' field.
* Holds the name of the 'subjectSorted' field.
*/
public static final String ISSUER_ORGANIZATION_FIELD = "issuerOrganization";
public static final String SUBJECT_SORTED_FIELD = "subjectSorted";
@Column
private String issuerOrganization = null;
/**
* Holds the name of the 'subjectOrganization' field.
*/
public static final String SUBJECT_ORGANIZATION_FIELD = "subjectOrganization";
@Column
private String subjectOrganization = null;
private final String subjectSorted;
/**
* Holds the name of the 'encodedPublicKey' field.
@ -255,12 +252,15 @@ public abstract class Certificate extends ArchivableEntity {
private String keyUsage;
private String extendedKeyUsage;
private byte[] policyConstraints;
/**
* Holds the name of the 'authorityKeyIdentifier' field.
*/
public static final String AUTHORITY_KEY_ID_FIELD = "authorityKeyIdentifier";
private String authorityKeyIdentifier;
private String authorityInfoAccess;
private String crlPoints;
private int publicKeySize;
/**
* Default constructor necessary for Hibernate.
*/
@ -269,6 +269,8 @@ public abstract class Certificate extends ArchivableEntity {
this.serialNumber = BigInteger.ZERO;
this.issuer = null;
this.subject = null;
this.issuerSorted = null;
this.subjectSorted = null;
this.encodedPublicKey = null;
this.publicKeyModulusHexValue = null;
@ -359,8 +361,8 @@ public abstract class Certificate extends ArchivableEntity {
this.beginValidity = x509Certificate.getNotBefore();
this.endValidity = x509Certificate.getNotAfter();
this.holderSerialNumber = BigInteger.ZERO;
this.issuerOrganization = getOrganization(this.issuer);
this.subjectOrganization = getOrganization(this.subject);
this.issuerSorted = parseSortDNs(this.issuer);
this.subjectSorted = parseSortDNs(this.subject);
this.policyConstraints = x509Certificate
.getExtensionValue(POLICY_CONSTRAINTS);
authKeyIdentifier = AuthorityKeyIdentifier
@ -395,7 +397,7 @@ public abstract class Certificate extends ArchivableEntity {
// Set null values (Attribute certificates do not have this values)
this.subject = null;
this.subjectOrganization = null;
this.subjectSorted = null;
this.encodedPublicKey = null;
this.publicKeyModulusHexValue = null;
this.publicKeySize = 0;
@ -434,7 +436,7 @@ public abstract class Certificate extends ArchivableEntity {
this.signature = attCert.getSignatureValue().getBytes();
this.issuer = getAttributeCertificateIssuerNames(
attCertInfo.getIssuer())[0].toString();
this.issuerOrganization = getOrganization(this.issuer);
this.issuerSorted = parseSortDNs(this.issuer);
// Parse notBefore and notAfter dates
this.beginValidity = recoverDate(attCertInfo
@ -536,39 +538,6 @@ public abstract class Certificate extends ArchivableEntity {
return CertificateType.INVALID_CERTIFICATE;
}
/**
* Extracts the organization field out of a distinguished name. Returns null if
* no organization field exists.
* @param distinguishedName distinguished name to extract the organization from
* @return the value of the organization field
*/
protected static String getOrganization(final String distinguishedName) {
String organization = null;
// Return null for empty strings
if (distinguishedName.isEmpty()) {
return null;
}
// Parse string to X500Name
X500Name name = new X500Name(distinguishedName);
if (name.getRDNs(RFC4519Style.o).length > 0) {
RDN rdn = name.getRDNs(RFC4519Style.o)[0];
// For multivalue check the RDNs Attributes
if (rdn.isMultiValued()) {
for (AttributeTypeAndValue att: rdn.getTypesAndValues()) {
if (RFC4519Style.o.equals(att.getType())) {
organization = att.getValue().toString();
}
}
} else {
organization = rdn.getFirst().getValue().toString();
}
}
return organization;
}
private boolean isPEM(final String possiblePEM) {
return possiblePEM.contains(PEM_HEADER) || possiblePEM.contains(PEM_ATTRIBUTE_HEADER);
}
@ -785,41 +754,39 @@ public abstract class Certificate extends ArchivableEntity {
* @throws IOException if there is an issue deserializing either certificate
*/
public boolean isIssuer(final Certificate issuer) throws IOException {
CertificateType cType = issuer.getCertificateType();
if (cType != CertificateType.X509_CERTIFICATE) {
throw new IllegalArgumentException("issuer cert must be X509Certificate");
}
boolean isIssuer = false;
X509Certificate issuerX509 = issuer.getX509Certificate();
// Validate if it's the issuer
switch (getCertificateType()) {
case X509_CERTIFICATE:
X509Certificate certX509 = getX509Certificate();
try {
certX509.verify(issuerX509.getPublicKey());
isIssuer = true;
} catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException
| NoSuchProviderException | SignatureException e) {
LOGGER.error(e);
}
break;
case ATTRIBUTE_CERTIFICATE:
AttributeCertificate attCert = getAttributeCertificate();
String algorith = "SHA256withRSA";
try {
Signature sig = Signature.getInstance(algorith);
sig.initVerify(issuerX509.getPublicKey());
sig.update(attCert.getAcinfo().getEncoded());
isIssuer = sig.verify(attCert.getSignatureValue().getBytes());
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
LOGGER.error(e);
}
break;
default:
break;
// only run if of the correct type, otherwise false
if (issuer.getCertificateType() == CertificateType.X509_CERTIFICATE) {
X509Certificate issuerX509 = issuer.getX509Certificate();
// Validate if it's the issuer
switch (getCertificateType()) {
case X509_CERTIFICATE:
X509Certificate certX509 = getX509Certificate();
try {
certX509.verify(issuerX509.getPublicKey());
isIssuer = true;
} catch (CertificateException | NoSuchAlgorithmException | InvalidKeyException
| NoSuchProviderException | SignatureException e) {
LOGGER.error(e);
}
break;
case ATTRIBUTE_CERTIFICATE:
AttributeCertificate attCert = getAttributeCertificate();
String algorithm = "SHA256withRSA";
try {
Signature sig = Signature.getInstance(algorithm);
sig.initVerify(issuerX509.getPublicKey());
sig.update(attCert.getAcinfo().getEncoded());
isIssuer = sig.verify(attCert.getSignatureValue().getBytes());
} catch (NoSuchAlgorithmException
| InvalidKeyException
| SignatureException e) {
LOGGER.error(e);
}
break;
default:
break;
}
}
return isIssuer;
@ -1055,17 +1022,17 @@ public abstract class Certificate extends ArchivableEntity {
}
/**
* @return this certificate's associated issuer organization
* @return this certificate's associated issuer sorted
*/
public String getIssuerOrganization() {
return issuerOrganization;
public String getIssuerSorted() {
return issuerSorted;
}
/**
* @return this certificate's associated subject organization
* @return this certificate's associated subject sorted
*/
public String getSubjectOrganization() {
return subjectOrganization;
public String getSubjectSorted() {
return subjectSorted;
}
/**
@ -1187,6 +1154,36 @@ public abstract class Certificate extends ArchivableEntity {
}
}
/**
* This method is to take the DNs from certificates and sort them in an order
* that will be used to lookup issuer certificates. This will not be stored in
* the certificate, just the DB for lookup.
* @param distinguishedName the original DN string.
* @return a modified string of sorted DNs
*/
public static String parseSortDNs(final String distinguishedName) {
StringBuilder sb = new StringBuilder();
String dnsString;
if (distinguishedName == null || distinguishedName.isEmpty()) {
sb.append("BLANK");
} else {
dnsString = distinguishedName.trim();
dnsString = dnsString.toLowerCase();
List<String> dnValArray = Arrays.asList(dnsString.split(","));
Collections.sort(dnValArray);
ListIterator<String> dnListIter = dnValArray.listIterator();
while (dnListIter.hasNext()) {
sb.append(dnListIter.next());
if (dnListIter.hasNext()) {
sb.append(",");
}
}
}
return sb.toString();
}
/**
* Retrieve the X509 Name array from the issuer in an Attribute Certificate.
*

View File

@ -2,6 +2,7 @@ package hirs.data.persist.certificate;
import hirs.persist.CertificateManager;
import hirs.persist.CertificateSelector;
import org.apache.commons.codec.binary.Hex;
import javax.persistence.Column;
import javax.persistence.Entity;
@ -15,7 +16,8 @@ import java.util.Arrays;
*/
@Entity
public class CertificateAuthorityCredential extends Certificate {
@SuppressWarnings("PMD.AvoidUsingHardCodedIP") // this is not an IP address; PMD thinks it is
@SuppressWarnings("PMD.AvoidUsingHardCodedIP")
private static final String SUBJECT_KEY_IDENTIFIER_EXTENSION = "2.5.29.14";
/**
@ -26,9 +28,12 @@ public class CertificateAuthorityCredential extends Certificate {
@Column
private final byte[] subjectKeyIdentifier;
/*
@Column
private String subjectKeyIdString;
/**
* this field is part of the TCG CA specification, but has not yet been found in
* manufacturer-provided CAs, and is therefore not currently parsed
* manufacturer-provided CAs, and is therefore not currently parsed.
*/
@Column
private String credentialType = "TCPA Trusted Platform Module Endorsement";
@ -82,6 +87,9 @@ public class CertificateAuthorityCredential extends Certificate {
super(certificateBytes);
this.subjectKeyIdentifier =
getX509Certificate().getExtensionValue(SUBJECT_KEY_IDENTIFIER_EXTENSION);
if (this.subjectKeyIdentifier != null) {
this.subjectKeyIdString = Hex.encodeHexString(this.subjectKeyIdentifier);
}
}
/**
@ -119,12 +127,20 @@ public class CertificateAuthorityCredential extends Certificate {
* @return this certificate's subject key identifier.
*/
public byte[] getSubjectKeyIdentifier() {
if (null != subjectKeyIdentifier) {
if (subjectKeyIdentifier != null) {
return subjectKeyIdentifier.clone();
}
return null;
}
/**
* Getter for the string rep of the ID.
* @return a string
*/
public String getSubjectKeyIdString() {
return this.subjectKeyIdString;
}
@Override
@SuppressWarnings("checkstyle:avoidinlineconditionals")
public boolean equals(final Object o) {

View File

@ -175,7 +175,8 @@ public abstract class CertificateSelector<T extends Certificate> {
public CertificateSelector<T> byIssuer(final String issuer) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(issuer),
"issuer cannot be null or empty."
String.format("%s: issuer cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.ISSUER_FIELD, issuer);
@ -192,7 +193,8 @@ public abstract class CertificateSelector<T extends Certificate> {
public CertificateSelector<T> bySubject(final String subject) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(subject),
"subject cannot be null or empty."
String.format("%s: subject cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.SUBJECT_FIELD, subject);
@ -200,36 +202,38 @@ public abstract class CertificateSelector<T extends Certificate> {
}
/**
* Specify an issuer organization string that certificates must have to be considered
* Specify the sorted issuer string that certificates must have to be considered
* as matching.
*
* @param organization certificate issuer organization string to query, not empty or null
* @param issuerSorted certificate issuer organization string to query, not empty or null
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> byIssuerOrganization(final String organization) {
public CertificateSelector<T> byIssuerSorted(final String issuerSorted) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(organization),
"organization cannot be null or empty."
StringUtils.isNotEmpty(issuerSorted),
String.format("%s: issuerSorted cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.ISSUER_ORGANIZATION_FIELD, organization);
setFieldValue(Certificate.ISSUER_SORTED_FIELD, issuerSorted);
return this;
}
/**
* Specify a subject organization string that certificates must have to be considered
* Specify the sorted subject string that certificates must have to be considered
* as matching.
*
* @param organization certificate subject organization string to query, not empty or null
* @param subjectSorted certificate subject organization string to query, not empty or null
* @return this instance (for chaining further calls)
*/
public CertificateSelector<T> bySubjectOrganization(final String organization) {
public CertificateSelector<T> bySubjectSorted(final String subjectSorted) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(organization),
"organization cannot be null or empty."
StringUtils.isNotEmpty(subjectSorted),
String.format("%s: subjectSorted cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.SUBJECT_ORGANIZATION_FIELD, organization);
setFieldValue(Certificate.SUBJECT_SORTED_FIELD, subjectSorted);
return this;
}
@ -243,7 +247,8 @@ public abstract class CertificateSelector<T extends Certificate> {
public CertificateSelector<T> byEncodedPublicKey(final byte[] encodedPublicKey) {
Preconditions.checkArgument(
ArrayUtils.isNotEmpty(encodedPublicKey),
"publicKey cannot be null or empty."
String.format("%s: publicKey cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(
@ -254,6 +259,23 @@ public abstract class CertificateSelector<T extends Certificate> {
return this;
}
/**
* Specify the authority key identifier to find certificate(s).
* @param authorityKeyIdentifier the string of the AKI associated with the certificate.
* @return this instance
*/
public CertificateSelector<T> byAuthorityKeyIdentifier(final String authorityKeyIdentifier) {
Preconditions.checkArgument(
StringUtils.isNotEmpty(authorityKeyIdentifier),
String.format("%s: authorityKeyIdentifier cannot be null or empty.",
this.certificateClass.toString())
);
setFieldValue(Certificate.AUTHORITY_KEY_ID_FIELD, authorityKeyIdentifier);
return this;
}
/**
* Specify a public key modulus that certificates must have to be considered
* as matching.
@ -264,7 +286,8 @@ public abstract class CertificateSelector<T extends Certificate> {
public CertificateSelector<T> byPublicKeyModulus(final BigInteger publicKeyModulus) {
Preconditions.checkArgument(
publicKeyModulus != null,
"Public key modulus cannot be null"
String.format("%s: Public key modulus cannot be null",
this.certificateClass.toString())
);
setFieldValue(
@ -428,8 +451,7 @@ public abstract class CertificateSelector<T extends Certificate> {
// construct and execute query
private Set<T> execute() {
Set<T> results = certificateManager.get(this);
return results;
return certificateManager.get(this);
}
/**

View File

@ -2,8 +2,8 @@ package hirs.tpm.eventlog;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
@ -350,9 +350,8 @@ public final class TCGEventLog {
*
* @param log The Event Log
* @return true if EfiSpecIDEvent is found and indicates that the format is crypto agile
* @throws UnsupportedEncodingException if parsing error occurs.
*/
private boolean isLogCrytoAgile(final byte[] log) throws UnsupportedEncodingException {
private boolean isLogCrytoAgile(final byte[] log) {
byte[] eType = new byte[UefiConstants.SIZE_4];
System.arraycopy(log, UefiConstants.SIZE_4, eType, 0, UefiConstants.SIZE_4);
byte[] eventType = HexUtils.leReverseByte(eType);
@ -361,8 +360,10 @@ public final class TCGEventLog {
return false;
} // Event Type should be EV_NO_ACTION
byte[] signature = new byte[SIG_SIZE];
System.arraycopy(log, SIG_OFFSET, signature, 0, SIG_SIZE); // should be "Spec ID Event03"
String sig = new String(signature, "UTF-8").substring(0, SIG_SIZE - 1); // remove null char
// should be "Spec ID Event03"
System.arraycopy(log, SIG_OFFSET, signature, 0, SIG_SIZE);
// remove null char
String sig = new String(signature, StandardCharsets.UTF_8).substring(0, SIG_SIZE - 1);
return sig.equals("Spec ID Event03");
}

View File

@ -1,6 +1,7 @@
package hirs.tpm.eventlog.events;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import hirs.tpm.eventlog.uefi.UefiConstants;
import hirs.utils.HexUtils;
@ -38,7 +39,7 @@ public class EvCompactHash {
if (event.length == UefiConstants.SIZE_4) { // older PFP defines as 4 byte ESI pointer.
eventInfo = " ESI = " + HexUtils.byteArrayToHexString(event);
} else { // otherwise assume the event content is a string
eventInfo = " " + new String(event, "UTF-8");
eventInfo = " " + new String(event, StandardCharsets.UTF_8);
}
return eventInfo;
}

View File

@ -1,6 +1,6 @@
package hirs.tpm.eventlog.events;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import hirs.tpm.eventlog.TcgTpmtHa;
@ -67,13 +67,13 @@ public class EvEfiSpecIdEvent {
/**
* EvEfiSpecIdEvent Constructor.
* @param efiSpecId byte array holding the spec ID Event.
* @throws UnsupportedEncodingException if input fails to parse.
*/
public EvEfiSpecIdEvent(final byte[] efiSpecId) throws UnsupportedEncodingException {
public EvEfiSpecIdEvent(final byte[] efiSpecId) {
byte[] signatureBytes = new byte[UefiConstants.SIZE_16];
System.arraycopy(efiSpecId, 0, signatureBytes, 0, UefiConstants.SIZE_16);
signature = HexUtils.byteArrayToHexString(signatureBytes);
signature = new String(signatureBytes, "UTF-8").substring(0, UefiConstants.SIZE_15);
signature = new String(signatureBytes, StandardCharsets.UTF_8)
.substring(0, UefiConstants.SIZE_15);
byte[] platformClassBytes = new byte[UefiConstants.SIZE_4];
System.arraycopy(efiSpecId, UefiConstants.OFFSET_16, platformClassBytes, 0,
@ -167,12 +167,12 @@ public class EvEfiSpecIdEvent {
*/
public String toString() {
String specInfo = "";
if (signature == "Spec ID Event#") {
if (signature.equals("Spec ID Event#")) {
specInfo += "Platform Profile Specification version = " + vMaj + "." + vMin
+ " using errata version" + errata;
} else {
specInfo = "EV_NO_ACTION event named " + signature
+ " ecncountered but support for processing it has not been added to this application";
+ " encountered but support for processing it has not been added to this application";
}
return specInfo;
}

View File

@ -1,6 +1,7 @@
package hirs.tpm.eventlog.events;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import hirs.tpm.eventlog.uefi.UefiConstants;
import hirs.tpm.eventlog.uefi.UefiGuid;
@ -25,10 +26,9 @@ public class EvSCrtmVersion {
/**
* Checks if event data is null and if not it converts to a String.
* @param data byte array holding the vent content.
* @throws UnsupportedEncodingException if parsing issues exist.
* @return String representation of the version.
*/
public String sCrtmVersion(final byte[] data) throws UnsupportedEncodingException {
public String sCrtmVersion(final byte[] data) {
UefiGuid guid = null;
if (data == null) {
description = "invalid content event data";
@ -42,7 +42,7 @@ public class EvSCrtmVersion {
} else if (data.length < UefiConstants.SIZE_4) {
description = HexUtils.byteArrayToHexString(data);
} else if (EvPostCode.isAscii(data)) {
description = new String(data, "UTF-8");
description = new String(data, StandardCharsets.UTF_8);
} else {
description = "Unknown Version format";
}

View File

@ -1,6 +1,7 @@
package hirs.tpm.eventlog.uefi;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import hirs.utils.HexUtils;
@ -276,10 +277,8 @@ private String hardDriveSubType(final byte[] path, final int offset) {
* @param path
* @param offset
* @return file path info.
* @throws UnsupportedEncodingException
*/
private String filePathSubType(final byte[] path, final int offset)
throws UnsupportedEncodingException {
private String filePathSubType(final byte[] path, final int offset) {
subType = "File Path = ";
byte[] lengthBytes = new byte[UefiConstants.SIZE_2];
System.arraycopy(path, 2 + offset, lengthBytes, 0, UefiConstants.SIZE_2);
@ -287,7 +286,7 @@ private String filePathSubType(final byte[] path, final int offset)
byte[] filePath = new byte[subTypeLength];
System.arraycopy(path, UefiConstants.OFFSET_4 + offset, filePath, 0, subTypeLength);
byte[] fileName = convertChar16tobyteArray(filePath);
subType += new String(fileName, "UTF-8");
subType += new String(fileName, StandardCharsets.UTF_8);
return subType;
}

View File

@ -1,6 +1,6 @@
package hirs.tpm.eventlog.uefi;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import hirs.utils.HexUtils;
/**
@ -39,9 +39,8 @@ public class UefiPartition {
/**
* Processes a UEFI defined partition entry.
* @param table byte array holding the partition table.
* @throws UnsupportedEncodingException if parsing of the data fails.
*/
public UefiPartition(final byte[] table) throws UnsupportedEncodingException {
public UefiPartition(final byte[] table) {
byte[] partitionGuidBytes = new byte[UefiConstants.SIZE_16];
System.arraycopy(table, 0, partitionGuidBytes, 0, UefiConstants.SIZE_16);
partitionTypeGUID = new UefiGuid(partitionGuidBytes);
@ -56,7 +55,7 @@ public class UefiPartition {
System.arraycopy(table, UefiConstants.PART_NAME_LENGTH, partitionNameBytes,
0, UefiConstants.UEFI_PT_LENGTH);
byte[] pName = convertChar16tobyteArray(partitionNameBytes);
partitionName = new String(pName, "UTF-8").trim();
partitionName = new String(pName, StandardCharsets.UTF_8).trim();
}
/**

View File

@ -3,6 +3,7 @@ package hirs.tpm.eventlog.uefi;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
@ -24,7 +25,7 @@ public class UefiVariable {
/** UEFI defined variable identifier GUID. */
private UefiGuid uefiGuid = null;
/** List of Signature lists. */
private ArrayList<UefiSignatureList> certSuperList = new ArrayList<UefiSignatureList>();
private ArrayList<UefiSignatureList> certSuperList = new ArrayList<>();
/** Name of the UEFI variable. */
private String varName = "";
/** UEFI defined Boot Variable. */
@ -70,15 +71,15 @@ public UefiVariable(final byte[] variableData)
uefiVaribelData = new byte[variableLength];
System.arraycopy(variableData, UefiConstants.OFFSET_32
+ nlength * UefiConstants.SIZE_2, uefiVaribelData, 0, variableLength);
varName = new String(name, "UTF-8");
varName = new String(name, StandardCharsets.UTF_8);
String tmpName = varName;
if (varName.contains("Boot00")) {
tmpName = "Boot00";
}
switch (tmpName) {
case "PK": processSigList(uefiVaribelData); break;
case "KEK": processSigList(uefiVaribelData); break;
case "db": processSigList(uefiVaribelData); break;
case "PK":
case "KEK":
case "db":
case "dbx": processSigList(uefiVaribelData); break;
case "Boot00": bootv = new UefiBootVariable(uefiVaribelData); break;
case "BootOrder": booto = new UefiBootOrder(uefiVaribelData); break;
@ -147,14 +148,15 @@ public String toString() {
tmpName = varName;
}
switch (tmpName) {
case "Shim": efiVariable.append(printCert(uefiVaribelData, 0)); break;
case "Shim":
case "MokList": efiVariable.append(printCert(uefiVaribelData, 0)); break;
case "Boot00": efiVariable.append(bootv.toString()); break;
case "BootOrder": efiVariable.append(booto.toString()); break;
case "SecureBoot": efiVariable.append(sb.toString()); break;
default:
if (!tmpName.isEmpty()) {
efiVariable.append("Data not provided for UEFI variable named " + tmpName + " ");
efiVariable.append(String.format("Data not provided for UEFI variable named %s ",
tmpName));
} else {
efiVariable.append("Data not provided ");
}

View File

@ -5,6 +5,7 @@ import org.apache.commons.exec.DefaultExecuteResultHandler;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
/**
* An implementation of ExecResult that facilitates working with
@ -84,7 +85,7 @@ public class AsynchronousExecResult implements ExecResult {
checkFinished();
if (stdOut != null) {
return ((ByteArrayOutputStream) stdOut).toString("UTF-8");
return ((ByteArrayOutputStream) stdOut).toString(StandardCharsets.UTF_8.toString());
} else {
return null;
}

View File

@ -481,28 +481,6 @@ public class CertificateTest {
);
}
/**
* Tests that Certificate's getOrganization method can properly parse an organization
* from a comma separated RDN.
* @throws IOException unable to parse organization
*/
@Test
public void testGetSingleOrganization() throws IOException {
String parsedOrg = Certificate.getOrganization(RDN_COMMA_SEPARATED);
Assert.assertEquals(RDN_COMMA_SEPARATED_ORGANIZATION, parsedOrg);
}
/**
* Tests that Certificate's getOrganization method can properly parse an organization
* from a multivalue RDN.
* @throws IOException unable to parse organization
*/
@Test
public void testGetMultiRdnOrganization() throws IOException {
String parsedOrg = Certificate.getOrganization(RDN_MULTIVALUE);
Assert.assertEquals(RDN_MULTIVALUE_ORGANIZATION, parsedOrg);
}
/**
* Construct a CertificateAuthorityCredential from the given parameters.
*

View File

@ -100,7 +100,7 @@ public class CertificateSelectorTest extends SpringPersistenceTest {
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNullByIssuerOrganization() {
CertificateAuthorityCredential.select(certMan).byIssuerOrganization(null);
CertificateAuthorityCredential.select(certMan).byIssuerSorted(null);
}
/**
@ -108,6 +108,6 @@ public class CertificateSelectorTest extends SpringPersistenceTest {
*/
@Test(expectedExceptions = IllegalArgumentException.class)
public void testNullBySubjectOrganization() {
CertificateAuthorityCredential.select(certMan).bySubjectOrganization(null);
CertificateAuthorityCredential.select(certMan).bySubjectSorted(null);
}
}

View File

@ -344,7 +344,7 @@ public class DBCertificateManagerTest extends SpringPersistenceTest {
Set<CertificateAuthorityCredential> retrievedCerts =
CertificateAuthorityCredential.select(certMan)
.bySubjectOrganization(stmEkCert.getIssuerOrganization())
.bySubjectSorted(stmEkCert.getIssuerSorted())
.getCertificates();
Assert.assertEquals(
@ -355,7 +355,7 @@ public class DBCertificateManagerTest extends SpringPersistenceTest {
Set<CertificateAuthorityCredential> secondRetrievedCerts =
CertificateAuthorityCredential.select(certMan)
.bySubjectOrganization(stmRootCaCert.getIssuerOrganization())
.bySubjectSorted(stmRootCaCert.getIssuerSorted())
.getCertificates();
Assert.assertEquals(
@ -380,7 +380,7 @@ public class DBCertificateManagerTest extends SpringPersistenceTest {
Set<CertificateAuthorityCredential> retrievedCerts =
CertificateAuthorityCredential.select(certMan)
.byIssuerOrganization(stmRootCaCert.getIssuerOrganization())
.byIssuerSorted(stmRootCaCert.getIssuerSorted())
.getCertificates();
Assert.assertEquals(
@ -706,7 +706,7 @@ public class DBCertificateManagerTest extends SpringPersistenceTest {
/**
* Tests that a {@link CertificateSelector} can be used to retrieve certificates in various
* forms, including {@link Certificate}, {@link X509Certificate}, and {@link KeyStore}.
* forms, including {@link Certificate}.
*
* @throws IOException if there is a problem creating the certificate
* @throws KeyStoreException if there is a problem constructing the resultant KeyStore