mirror of
https://github.com/nsacyber/HIRS.git
synced 2024-12-20 05:28:22 +00:00
[#25] Make ACA exception handling more descriptive
This commit is contained in:
parent
6847c814af
commit
87be5a396b
@ -3,6 +3,9 @@ package hirs.attestationca;
|
|||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
import hirs.attestationca.exceptions.CertificateProcessingException;
|
||||||
|
import hirs.attestationca.exceptions.IdentityProcessingException;
|
||||||
|
import hirs.attestationca.exceptions.UnexpectedServerException;
|
||||||
import hirs.data.persist.AppraisalStatus;
|
import hirs.data.persist.AppraisalStatus;
|
||||||
import hirs.data.persist.BIOSComponentInfo;
|
import hirs.data.persist.BIOSComponentInfo;
|
||||||
import hirs.data.persist.BaseboardComponentInfo;
|
import hirs.data.persist.BaseboardComponentInfo;
|
||||||
@ -238,7 +241,8 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
if (publicKeyModulus != null) {
|
if (publicKeyModulus != null) {
|
||||||
ekPublicKey = assemblePublicKey(publicKeyModulus.toByteArray());
|
ekPublicKey = assemblePublicKey(publicKeyModulus.toByteArray());
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("TPM 1.2 Provisioning requires RSA EKC");
|
throw new IdentityProcessingException("TPM 1.2 Provisioning requires EK "
|
||||||
|
+ "Credentials to be created with RSA");
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.error("Could not retrieve the public key modulus from the EK cert");
|
LOG.error("Could not retrieve the public key modulus from the EK cert");
|
||||||
@ -277,7 +281,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
|
|
||||||
if (deviceInfoReport == null) {
|
if (deviceInfoReport == null) {
|
||||||
LOG.error("Failed to deserialize Device Info Report");
|
LOG.error("Failed to deserialize Device Info Report");
|
||||||
throw new IllegalArgumentException("Device Info Report failed to deserialize "
|
throw new IdentityProcessingException("Device Info Report failed to deserialize "
|
||||||
+ "from Identity Request");
|
+ "from Identity Request");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,7 +386,8 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
LOG.info("Got identity claim");
|
LOG.info("Got identity claim");
|
||||||
|
|
||||||
if (ArrayUtils.isEmpty(identityClaim)) {
|
if (ArrayUtils.isEmpty(identityClaim)) {
|
||||||
throw new IllegalArgumentException("identityClaim cannot be null or empty");
|
LOG.error("Identity claim empty throwing exception.");
|
||||||
|
throw new IdentityProcessingException("identityClaim cannot be null or empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
// attempt to deserialize Protobuf IdentityClaim
|
// attempt to deserialize Protobuf IdentityClaim
|
||||||
@ -466,7 +471,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
try {
|
try {
|
||||||
request = ProvisionerTpm2.CertificateRequest.parseFrom(certificateRequest);
|
request = ProvisionerTpm2.CertificateRequest.parseFrom(certificateRequest);
|
||||||
} catch (InvalidProtocolBufferException ipbe) {
|
} catch (InvalidProtocolBufferException ipbe) {
|
||||||
throw new IdentityProcessingException(
|
throw new CertificateProcessingException(
|
||||||
"Could not deserialize certificate request", ipbe);
|
"Could not deserialize certificate request", ipbe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,7 +517,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
} else {
|
} else {
|
||||||
LOG.error("Could not process credential request. Invalid nonce provided: "
|
LOG.error("Could not process credential request. Invalid nonce provided: "
|
||||||
+ request.getNonce().toString());
|
+ request.getNonce().toString());
|
||||||
throw new IdentityProcessingException("Invalid nonce given in request");
|
throw new CertificateProcessingException("Invalid nonce given in request");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,7 +529,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
RSAPublicKey parsePublicKey(final byte[] publicArea) {
|
RSAPublicKey parsePublicKey(final byte[] publicArea) {
|
||||||
int pubLen = publicArea.length;
|
int pubLen = publicArea.length;
|
||||||
if (pubLen < RSA_MODULUS_LENGTH) {
|
if (pubLen < RSA_MODULUS_LENGTH) {
|
||||||
throw new IdentityProcessingException(
|
throw new IllegalArgumentException(
|
||||||
"EK or AK public data segment is not long enough");
|
"EK or AK public data segment is not long enough");
|
||||||
}
|
}
|
||||||
// public data ends with 256 byte modulus
|
// public data ends with 256 byte modulus
|
||||||
@ -661,7 +666,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
|
|
||||||
if (deviceInfoReport == null) {
|
if (deviceInfoReport == null) {
|
||||||
LOG.error("Failed to deserialize Device Info Report");
|
LOG.error("Failed to deserialize Device Info Report");
|
||||||
throw new IllegalArgumentException("Device Info Report failed to deserialize "
|
throw new IdentityProcessingException("Device Info Report failed to deserialize "
|
||||||
+ "from Identity Claim");
|
+ "from Identity Claim");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -882,7 +887,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||||
return keyFactory.generatePublic(keySpec);
|
return keyFactory.generatePublic(keySpec);
|
||||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||||
throw new IdentityProcessingException(
|
throw new UnexpectedServerException(
|
||||||
"Encountered unexpected error creating public key: " + e.getMessage(), e);
|
"Encountered unexpected error creating public key: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -948,7 +953,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
} catch (NoSuchAlgorithmException | IllegalBlockSizeException | NoSuchPaddingException
|
} catch (NoSuchAlgorithmException | IllegalBlockSizeException | NoSuchPaddingException
|
||||||
| InvalidKeyException | BadPaddingException
|
| InvalidKeyException | BadPaddingException
|
||||||
| InvalidAlgorithmParameterException e) {
|
| InvalidAlgorithmParameterException e) {
|
||||||
throw new IdentityProcessingException(
|
throw new CertificateProcessingException(
|
||||||
"Encountered error while generating ACA session key: " + e.getMessage(), e);
|
"Encountered error while generating ACA session key: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1004,7 +1009,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
} catch (BadPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException
|
} catch (BadPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException
|
||||||
| InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException
|
| InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException
|
||||||
| CertificateEncodingException e) {
|
| CertificateEncodingException e) {
|
||||||
throw new IdentityProcessingException(
|
throw new CertificateProcessingException(
|
||||||
"Encountered error while generating Identity Response: " + e.getMessage(), e);
|
"Encountered error while generating Identity Response: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1066,7 +1071,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
.setProvider("BC").getCertificate(holder);
|
.setProvider("BC").getCertificate(holder);
|
||||||
return certificate;
|
return certificate;
|
||||||
} catch (IOException | OperatorCreationException | CertificateException e) {
|
} catch (IOException | OperatorCreationException | CertificateException e) {
|
||||||
throw new IdentityProcessingException("Encountered error while generating "
|
throw new CertificateProcessingException("Encountered error while generating "
|
||||||
+ "identity credential: " + e.getMessage(), e);
|
+ "identity credential: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1159,7 +1164,8 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
| InvalidKeyException | InvalidAlgorithmParameterException
|
| InvalidKeyException | InvalidAlgorithmParameterException
|
||||||
| NoSuchPaddingException e) {
|
| NoSuchPaddingException e) {
|
||||||
throw new IdentityProcessingException(
|
throw new IdentityProcessingException(
|
||||||
"Encountered error while making credential: " + e.getMessage(), e);
|
"Encountered error while making the identity claim challenge: "
|
||||||
|
+ e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1418,7 +1424,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
* Helper method to extract a DER encoded ASN.1 certificate from an X509 certificate.
|
* Helper method to extract a DER encoded ASN.1 certificate from an X509 certificate.
|
||||||
*
|
*
|
||||||
* @param certificate the X509 certificate to be converted to DER encoding
|
* @param certificate the X509 certificate to be converted to DER encoding
|
||||||
* @throws {@link IdentityProcessingException} if error occurs during encoding retrieval
|
* @throws {@link UnexpectedServerException} if error occurs during encoding retrieval
|
||||||
* @return the byte array representing the DER encoded certificate
|
* @return the byte array representing the DER encoded certificate
|
||||||
*/
|
*/
|
||||||
private byte[] getDerEncodedCertificate(final X509Certificate certificate) {
|
private byte[] getDerEncodedCertificate(final X509Certificate certificate) {
|
||||||
@ -1426,7 +1432,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
return certificate.getEncoded();
|
return certificate.getEncoded();
|
||||||
} catch (CertificateEncodingException e) {
|
} catch (CertificateEncodingException e) {
|
||||||
LOG.error("Error converting certificate to ASN.1 DER Encoding.", e);
|
LOG.error("Error converting certificate to ASN.1 DER Encoding.", e);
|
||||||
throw new IdentityProcessingException(
|
throw new UnexpectedServerException(
|
||||||
"Encountered error while converting X509 Certificate: "
|
"Encountered error while converting X509 Certificate: "
|
||||||
+ e.getMessage(), e);
|
+ e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@ -1441,7 +1447,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
* @param endorsementCredential the endorsement credential used to generate the AC
|
* @param endorsementCredential the endorsement credential used to generate the AC
|
||||||
* @param platformCredentials the platform credentials used to generate the AC
|
* @param platformCredentials the platform credentials used to generate the AC
|
||||||
* @param device the device to which the attestation certificate is tied
|
* @param device the device to which the attestation certificate is tied
|
||||||
* @throws {@link IdentityProcessingException} if error occurs in persisting the Attestation
|
* @throws {@link CertificateProcessingException} if error occurs in persisting the Attestation
|
||||||
* Certificate
|
* Certificate
|
||||||
*/
|
*/
|
||||||
private void saveAttestationCertificate(final byte[] derEncodedAttestationCertificate,
|
private void saveAttestationCertificate(final byte[] derEncodedAttestationCertificate,
|
||||||
@ -1456,7 +1462,7 @@ public abstract class AbstractAttestationCertificateAuthority
|
|||||||
certificateManager.save(attCert);
|
certificateManager.save(attCert);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.error("Error saving generated Attestation Certificate to database.", e);
|
LOG.error("Error saving generated Attestation Certificate to database.", e);
|
||||||
throw new IdentityProcessingException(
|
throw new CertificateProcessingException(
|
||||||
"Encountered error while storing Attestation Certificate: "
|
"Encountered error while storing Attestation Certificate: "
|
||||||
+ e.getMessage(), e);
|
+ e.getMessage(), e);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package hirs.attestationca;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple POJO that will provide a clean error message to clients making
|
||||||
|
* REST requests to the ACA. It is to be serialized to JSON for the return message.
|
||||||
|
*/
|
||||||
|
public class AcaRestError {
|
||||||
|
|
||||||
|
private String error;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic constructor necessary for Jackson JSON serialization to work properly.
|
||||||
|
*/
|
||||||
|
public AcaRestError() {
|
||||||
|
// Don't remove this constructor as it's required for JSON mapping
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameterized constructor for creating this class normally.
|
||||||
|
*
|
||||||
|
* @param error the error message to store in this object
|
||||||
|
*/
|
||||||
|
public AcaRestError(final String error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple getter to get the error message stored in this object.
|
||||||
|
*
|
||||||
|
* @return the error message
|
||||||
|
*/
|
||||||
|
public String getError() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple setter to get the error message stored in this object.
|
||||||
|
*
|
||||||
|
* @param error the new error message to store in this object
|
||||||
|
*/
|
||||||
|
public void setError(final String error) {
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -54,8 +54,8 @@ import hirs.utils.LogConfigurationUtil;
|
|||||||
@PropertySource(value = "file:/etc/hirs/aca/aca.properties",
|
@PropertySource(value = "file:/etc/hirs/aca/aca.properties",
|
||||||
ignoreResourceNotFound = true)
|
ignoreResourceNotFound = true)
|
||||||
})
|
})
|
||||||
@ComponentScan({ "hirs.attestationca", "hirs.attestationca.service", "hirs.validation",
|
@ComponentScan({ "hirs.attestationca", "hirs.attestationca.service", "hirs.attestationca.rest",
|
||||||
"hirs.data.service" })
|
"hirs.validation", "hirs.data.service" })
|
||||||
@Import(HibernateConfiguration.class)
|
@Import(HibernateConfiguration.class)
|
||||||
@EnableWebMvc
|
@EnableWebMvc
|
||||||
public class AttestationCertificateAuthorityConfiguration extends WebMvcConfigurerAdapter {
|
public class AttestationCertificateAuthorityConfiguration extends WebMvcConfigurerAdapter {
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package hirs.attestationca.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic exception thrown while a {@link hirs.attestationca.AttestationCertificateAuthority}
|
||||||
|
* is processing a newly created Attestation Certificate for a validated identity.
|
||||||
|
*/
|
||||||
|
public class CertificateProcessingException extends RuntimeException {
|
||||||
|
/**
|
||||||
|
* Constructs a generic instance of this exception using the specified reason.
|
||||||
|
*
|
||||||
|
* @param reason for the exception
|
||||||
|
*/
|
||||||
|
public CertificateProcessingException(final String reason) {
|
||||||
|
super(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a instance of this exception with the specified reason and backing root
|
||||||
|
* exception.
|
||||||
|
*
|
||||||
|
* @param reason for this exception
|
||||||
|
* @param rootException causing this exception
|
||||||
|
*/
|
||||||
|
public CertificateProcessingException(final String reason, final Throwable rootException) {
|
||||||
|
super(reason, rootException);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package hirs.attestationca;
|
package hirs.attestationca.exceptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic exception thrown while a {@link AttestationCertificateAuthority} is processing a newly
|
* Generic exception thrown while a {@link hirs.attestationca.AttestationCertificateAuthority}
|
||||||
* submitted Identity.
|
* is processing a newly submitted Identity.
|
||||||
*/
|
*/
|
||||||
public class IdentityProcessingException extends RuntimeException {
|
public class IdentityProcessingException extends RuntimeException {
|
||||||
/**
|
/**
|
@ -0,0 +1,27 @@
|
|||||||
|
package hirs.attestationca.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic exception thrown when a {@link hirs.attestationca.AttestationCertificateAuthority}
|
||||||
|
* encounters an unexpected condition that can't be handled.
|
||||||
|
*/
|
||||||
|
public class UnexpectedServerException extends RuntimeException {
|
||||||
|
/**
|
||||||
|
* Constructs a generic instance of this exception using the specified reason.
|
||||||
|
*
|
||||||
|
* @param reason for the exception
|
||||||
|
*/
|
||||||
|
public UnexpectedServerException(final String reason) {
|
||||||
|
super(reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a instance of this exception with the specified reason and backing root
|
||||||
|
* exception.
|
||||||
|
*
|
||||||
|
* @param reason for this exception
|
||||||
|
* @param rootException causing this exception
|
||||||
|
*/
|
||||||
|
public UnexpectedServerException(final String reason, final Throwable rootException) {
|
||||||
|
super(reason, rootException);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Custom exceptions of the {@link hirs.attestationca.AttestationCertificateAuthority}.
|
||||||
|
*/
|
||||||
|
package hirs.attestationca.exceptions;
|
@ -0,0 +1,72 @@
|
|||||||
|
package hirs.attestationca.rest;
|
||||||
|
|
||||||
|
import hirs.attestationca.AcaRestError;
|
||||||
|
import hirs.attestationca.exceptions.CertificateProcessingException;
|
||||||
|
import hirs.attestationca.exceptions.IdentityProcessingException;
|
||||||
|
import hirs.attestationca.exceptions.UnexpectedServerException;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.context.request.WebRequest;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle processing of exceptions for ACA REST API.
|
||||||
|
*/
|
||||||
|
@ControllerAdvice
|
||||||
|
public class AttestationCertificateAuthorityExceptionHandler
|
||||||
|
extends ResponseEntityExceptionHandler {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger(
|
||||||
|
AttestationCertificateAuthorityExceptionHandler.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to handle errors of the type {@link CertificateProcessingException},
|
||||||
|
* {@link IdentityProcessingException}, and {@link IllegalArgumentException}
|
||||||
|
* that are thrown when performing a RESTful operation.
|
||||||
|
*
|
||||||
|
* @param ex exception that was thrown
|
||||||
|
* @param request the web request that started the RESTful operation
|
||||||
|
* @return the response entity that will form the message returned to the client
|
||||||
|
*/
|
||||||
|
@ExceptionHandler({ CertificateProcessingException.class, IdentityProcessingException.class,
|
||||||
|
IllegalArgumentException.class })
|
||||||
|
public final ResponseEntity<Object> handleExpectedExceptions(final Exception ex,
|
||||||
|
final WebRequest request) {
|
||||||
|
LOGGER.error(String.format("The ACA has encountered an expected exception: %s",
|
||||||
|
ex.getMessage()), ex);
|
||||||
|
return handleGeneralException(ex, HttpStatus.BAD_REQUEST, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to handle errors of the type {@link IllegalStateException} and
|
||||||
|
* {@link UnexpectedServerException} that are thrown when performing a RESTful operation.
|
||||||
|
*
|
||||||
|
* @param ex exception that was thrown
|
||||||
|
* @param request the web request that started the RESTful operation
|
||||||
|
* @return the response entity that will form the message returned to the client
|
||||||
|
*/
|
||||||
|
@ExceptionHandler({ IllegalStateException.class, UnexpectedServerException.class })
|
||||||
|
public final ResponseEntity<Object> handleUnexpectedExceptions(final Exception ex,
|
||||||
|
final WebRequest request) {
|
||||||
|
LOGGER.error(String.format("The ACA has encountered an unexpected exception: %s",
|
||||||
|
ex.getMessage()), ex);
|
||||||
|
return handleGeneralException(ex, HttpStatus.INTERNAL_SERVER_ERROR, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseEntity<Object> handleGeneralException(final Exception ex,
|
||||||
|
final HttpStatus responseStatus,
|
||||||
|
final WebRequest request) {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
|
return handleExceptionInternal(ex, new AcaRestError(ex.getMessage()),
|
||||||
|
headers, responseStatus, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,18 +1,14 @@
|
|||||||
package hirs.attestationca.rest;
|
package hirs.attestationca.rest;
|
||||||
|
|
||||||
import hirs.attestationca.IdentityProcessingException;
|
|
||||||
import hirs.persist.DBManager;
|
import hirs.persist.DBManager;
|
||||||
import hirs.persist.TPM2ProvisionerState;
|
import hirs.persist.TPM2ProvisionerState;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.http.HttpStatus;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
@ -72,8 +68,7 @@ public class RestfulAttestationCertificateAuthority
|
|||||||
@Override
|
@Override
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@RequestMapping(value = "/identity-request/process", method = RequestMethod.POST,
|
@RequestMapping(value = "/identity-request/process", method = RequestMethod.POST,
|
||||||
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
|
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
|
||||||
public byte[] processIdentityRequest(@RequestBody final byte[] request) {
|
public byte[] processIdentityRequest(@RequestBody final byte[] request) {
|
||||||
return super.processIdentityRequest(request);
|
return super.processIdentityRequest(request);
|
||||||
}
|
}
|
||||||
@ -87,8 +82,7 @@ public class RestfulAttestationCertificateAuthority
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
@RequestMapping(value = "/identity-claim-tpm2/process",
|
@RequestMapping(value = "/identity-claim-tpm2/process",
|
||||||
method = RequestMethod.POST,
|
method = RequestMethod.POST,
|
||||||
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
|
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
|
||||||
public byte[] processIdentityClaimTpm2(@RequestBody final byte[] request) {
|
public byte[] processIdentityClaimTpm2(@RequestBody final byte[] request) {
|
||||||
return super.processIdentityClaimTpm2(request);
|
return super.processIdentityClaimTpm2(request);
|
||||||
}
|
}
|
||||||
@ -103,8 +97,7 @@ public class RestfulAttestationCertificateAuthority
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
@RequestMapping(value = "/request-certificate-tpm2",
|
@RequestMapping(value = "/request-certificate-tpm2",
|
||||||
method = RequestMethod.POST,
|
method = RequestMethod.POST,
|
||||||
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE,
|
consumes = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
||||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
|
||||||
public byte[] processCertificateRequest(@RequestBody final byte[] request) {
|
public byte[] processCertificateRequest(@RequestBody final byte[] request) {
|
||||||
return super.processCertificateRequest(request);
|
return super.processCertificateRequest(request);
|
||||||
}
|
}
|
||||||
@ -118,28 +111,9 @@ public class RestfulAttestationCertificateAuthority
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@RequestMapping(value = "/public-key", method = RequestMethod.GET,
|
@RequestMapping(value = "/public-key", method = RequestMethod.GET)
|
||||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
|
|
||||||
public byte[] getPublicKey() {
|
public byte[] getPublicKey() {
|
||||||
return super.getPublicKey();
|
return super.getPublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle processing of exceptions for ACA REST API.
|
|
||||||
* @param e exception thrown during invocation of ACA REST API
|
|
||||||
* @return exception thrown during invocation of ACA REST API
|
|
||||||
*/
|
|
||||||
@ExceptionHandler
|
|
||||||
@ResponseBody
|
|
||||||
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
|
|
||||||
public Exception handleException(final Exception e) {
|
|
||||||
if (e instanceof IdentityProcessingException) {
|
|
||||||
LOG.error("Processing exception while provisioning", e.getMessage(), e);
|
|
||||||
} else {
|
|
||||||
LOG.error(String.format("Encountered unexpected error while processing identity "
|
|
||||||
+ "claim: %s", e.getMessage()), e);
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,7 @@ public class SupplyChainValidationServiceImpl implements SupplyChainValidationSe
|
|||||||
*
|
*
|
||||||
* @param credential the credential whose CA chain should be retrieved
|
* @param credential the credential whose CA chain should be retrieved
|
||||||
* @return A keystore ontaining all relevant CA credentials to the given certificate's
|
* @return A keystore ontaining all relevant CA credentials to the given certificate's
|
||||||
* organization
|
* organization or null if the keystore can't be assembled
|
||||||
*/
|
*/
|
||||||
public KeyStore getCaChain(final Certificate credential) {
|
public KeyStore getCaChain(final Certificate credential) {
|
||||||
KeyStore caKeyStore = null;
|
KeyStore caKeyStore = null;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package hirs.attestationca;
|
package hirs.attestationca;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
import hirs.attestationca.exceptions.IdentityProcessingException;
|
||||||
import hirs.utils.HexUtils;
|
import hirs.utils.HexUtils;
|
||||||
import org.apache.commons.codec.binary.Hex;
|
import org.apache.commons.codec.binary.Hex;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
@ -158,9 +159,9 @@ public class AbstractAttestationCertificateAuthorityTest {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link AbstractAttestationCertificateAuthority#processIdentityClaimTpm2(byte[])}
|
* Tests {@link AbstractAttestationCertificateAuthority#processIdentityClaimTpm2(byte[])}
|
||||||
* where the byte array is null. Expects an illegal argument exception to be thrown.
|
* where the byte array is null. Expects an identity processing exception to be thrown.
|
||||||
*/
|
*/
|
||||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
@Test(expectedExceptions = IdentityProcessingException.class)
|
||||||
public void testProcessIdentityClaimTpm2NullRequest() {
|
public void testProcessIdentityClaimTpm2NullRequest() {
|
||||||
aca.processIdentityClaimTpm2(null);
|
aca.processIdentityClaimTpm2(null);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ class RestfulClientProvisioner {
|
|||||||
static const char * const PROP_FILE_LOC;
|
static const char * const PROP_FILE_LOC;
|
||||||
static const char * const PROP_ACA_FQDN;
|
static const char * const PROP_ACA_FQDN;
|
||||||
static const char * const PROP_ACA_PORT;
|
static const char * const PROP_ACA_PORT;
|
||||||
|
static const char * const ACA_ERROR_FIELDNAME;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,6 +47,27 @@ namespace file_utils {
|
|||||||
int readSize);
|
int readSize);
|
||||||
} // namespace file_utils
|
} // namespace file_utils
|
||||||
|
|
||||||
|
namespace json_utils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class that provides functions to parse information from ACA
|
||||||
|
* output.
|
||||||
|
*/
|
||||||
|
class JSONFieldParser {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Parses the target field of the provided JSON object as a string.
|
||||||
|
*
|
||||||
|
* @param jsonObject the JSON-formatted object
|
||||||
|
* @param jsonFieldName the name of the field to parse from the JSON object
|
||||||
|
* @return the value of the target field in the JSON object
|
||||||
|
*/
|
||||||
|
static std::string parseJsonStringField(const std::string& jsonObject,
|
||||||
|
const std::string& jsonFieldName);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace json_utils
|
||||||
|
|
||||||
namespace string_utils {
|
namespace string_utils {
|
||||||
/**
|
/**
|
||||||
* Converts a binary string to a hex string.
|
* Converts a binary string to a hex string.
|
||||||
@ -165,7 +186,7 @@ class Tpm2ToolsVersionChecker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class that provides functions to parse information from tpm2_tools
|
* Utility class that provides functions to parse information from tpm2_tools
|
||||||
* output.
|
* output.
|
||||||
*/
|
*/
|
||||||
|
@ -19,6 +19,7 @@ using hirs::pb::IdentityClaimResponse;
|
|||||||
using hirs::pb::CertificateRequest;
|
using hirs::pb::CertificateRequest;
|
||||||
using hirs::pb::CertificateResponse;
|
using hirs::pb::CertificateResponse;
|
||||||
using hirs::properties::Properties;
|
using hirs::properties::Properties;
|
||||||
|
using hirs::json_utils::JSONFieldParser;
|
||||||
using hirs::string_utils::binaryToHex;
|
using hirs::string_utils::binaryToHex;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::stringstream;
|
using std::stringstream;
|
||||||
@ -31,6 +32,8 @@ const char * const RestfulClientProvisioner::PROP_ACA_FQDN
|
|||||||
= "ATTESTATION_CA_FQDN";
|
= "ATTESTATION_CA_FQDN";
|
||||||
const char * const RestfulClientProvisioner::PROP_ACA_PORT
|
const char * const RestfulClientProvisioner::PROP_ACA_PORT
|
||||||
= "ATTESTATION_CA_PORT";
|
= "ATTESTATION_CA_PORT";
|
||||||
|
const char * const RestfulClientProvisioner::ACA_ERROR_FIELDNAME
|
||||||
|
= "error";
|
||||||
|
|
||||||
RestfulClientProvisioner::RestfulClientProvisioner() {
|
RestfulClientProvisioner::RestfulClientProvisioner() {
|
||||||
Properties props(PROP_FILE_LOC);
|
Properties props(PROP_FILE_LOC);
|
||||||
@ -66,7 +69,9 @@ string RestfulClientProvisioner::sendIdentityClaim(
|
|||||||
+ "process"},
|
+ "process"},
|
||||||
cpr::Body{identityClaimByteString},
|
cpr::Body{identityClaimByteString},
|
||||||
cpr::Header{{"Content-Type",
|
cpr::Header{{"Content-Type",
|
||||||
"application/octet-stream"}},
|
"application/octet-stream"},
|
||||||
|
{"Accept",
|
||||||
|
"application/octet-stream, application/json"}},
|
||||||
cpr::VerifySsl{false});
|
cpr::VerifySsl{false});
|
||||||
|
|
||||||
// Check ACA response, should be 200 if successful
|
// Check ACA response, should be 200 if successful
|
||||||
@ -91,8 +96,11 @@ string RestfulClientProvisioner::sendIdentityClaim(
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
stringstream errormsg;
|
stringstream errormsg;
|
||||||
errormsg << "Couldn't communicate with ACA server. "
|
errormsg << "Error communicating with ACA server. "
|
||||||
<< "Received response code: " << to_string(r.status_code);
|
<< "Received response code: " << to_string(r.status_code)
|
||||||
|
<< "\n\nError message fom ACA was: "
|
||||||
|
<< JSONFieldParser::parseJsonStringField(r.text,
|
||||||
|
ACA_ERROR_FIELDNAME);
|
||||||
throw HirsRuntimeException(errormsg.str(),
|
throw HirsRuntimeException(errormsg.str(),
|
||||||
"RestfulClientProvisioner::sendIdentityClaim");
|
"RestfulClientProvisioner::sendIdentityClaim");
|
||||||
}
|
}
|
||||||
@ -110,7 +118,9 @@ string RestfulClientProvisioner::sendAttestationCertificateRequest(
|
|||||||
+ "/request-certificate-tpm2"},
|
+ "/request-certificate-tpm2"},
|
||||||
cpr::Body{certificateRequestByteString},
|
cpr::Body{certificateRequestByteString},
|
||||||
cpr::Header{{"Content-Type",
|
cpr::Header{{"Content-Type",
|
||||||
"application/octet-stream"}},
|
"application/octet-stream"},
|
||||||
|
{"Accept",
|
||||||
|
"application/octet-stream, application/json"}},
|
||||||
cpr::VerifySsl{false});
|
cpr::VerifySsl{false});
|
||||||
|
|
||||||
// Check ACA response, should be 200 if successful
|
// Check ACA response, should be 200 if successful
|
||||||
@ -131,9 +141,11 @@ string RestfulClientProvisioner::sendAttestationCertificateRequest(
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
stringstream errormsg;
|
stringstream errormsg;
|
||||||
errormsg << "Couldn't communicate with ACA server. "
|
errormsg << "Error communicating with ACA server. "
|
||||||
<< "Received response code: " << to_string(r.status_code)
|
<< "Received response code: " << to_string(r.status_code)
|
||||||
<< "\n\nWith message body: " << r.text;
|
<< "\n\nError message from ACA was: "
|
||||||
|
<< JSONFieldParser::parseJsonStringField(r.text,
|
||||||
|
ACA_ERROR_FIELDNAME);
|
||||||
throw HirsRuntimeException(errormsg.str(),
|
throw HirsRuntimeException(errormsg.str(),
|
||||||
"RestfulClientProvisioner::sendAttestationCertificateRequest");
|
"RestfulClientProvisioner::sendAttestationCertificateRequest");
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,25 @@ using hirs::exception::HirsRuntimeException;
|
|||||||
|
|
||||||
namespace hirs {
|
namespace hirs {
|
||||||
|
|
||||||
|
namespace json_utils {
|
||||||
|
|
||||||
|
string JSONFieldParser::parseJsonStringField(const std::string &jsonObject,
|
||||||
|
const std::string &jsonFieldName) {
|
||||||
|
stringstream regexPatternStream;
|
||||||
|
regexPatternStream << "(?i)\\\""
|
||||||
|
<< jsonFieldName
|
||||||
|
<< "\\\"\\s*:\\s*\\\"(.*)\\\"";
|
||||||
|
|
||||||
|
string value;
|
||||||
|
if (RE2::PartialMatch(jsonObject, regexPatternStream.str(), &value)) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace json_utils
|
||||||
|
|
||||||
namespace file_utils {
|
namespace file_utils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
using hirs::file_utils::dirExists;
|
using hirs::file_utils::dirExists;
|
||||||
using hirs::file_utils::fileExists;
|
using hirs::file_utils::fileExists;
|
||||||
|
using hirs::json_utils::JSONFieldParser;
|
||||||
using hirs::string_utils::binaryToHex;
|
using hirs::string_utils::binaryToHex;
|
||||||
using hirs::string_utils::contains;
|
using hirs::string_utils::contains;
|
||||||
using hirs::string_utils::longToHex;
|
using hirs::string_utils::longToHex;
|
||||||
@ -58,6 +59,57 @@ class UtilsTest : public :: testing::Test {
|
|||||||
|
|
||||||
const char UtilsTest::kFileName[] = "bitsAndBytes";
|
const char UtilsTest::kFileName[] = "bitsAndBytes";
|
||||||
|
|
||||||
|
TEST_F(UtilsTest, ParseJsonFieldSuccess) {
|
||||||
|
stringstream jsonObject;
|
||||||
|
jsonObject << R"({"error":"identityClaim cannot be null or empty"})";
|
||||||
|
|
||||||
|
string errorMessage = JSONFieldParser::parseJsonStringField(
|
||||||
|
jsonObject.str(), "error");
|
||||||
|
string expectedOutput = "identityClaim cannot be null or empty";
|
||||||
|
ASSERT_EQ(expectedOutput, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UtilsTest, ParseJsonFieldSuccessCaseInsensitive) {
|
||||||
|
stringstream jsonObject;
|
||||||
|
jsonObject << R"({"ERROR":"identityClaim cannot be null or empty"})";
|
||||||
|
|
||||||
|
string errorMessage = JSONFieldParser::parseJsonStringField(
|
||||||
|
jsonObject.str(), "error");
|
||||||
|
string expectedOutput = "identityClaim cannot be null or empty";
|
||||||
|
ASSERT_EQ(expectedOutput, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UtilsTest, ParseJsonFieldSuccessWhiteSpaces) {
|
||||||
|
stringstream jsonObject;
|
||||||
|
jsonObject << R"({"error" : "identityClaim cannot be null or empty"})";
|
||||||
|
|
||||||
|
string errorMessage = JSONFieldParser::parseJsonStringField(
|
||||||
|
jsonObject.str(), "error");
|
||||||
|
string expectedOutput = "identityClaim cannot be null or empty";
|
||||||
|
ASSERT_EQ(expectedOutput, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UtilsTest, ParseJsonFieldSuccessMultiJsonFields) {
|
||||||
|
stringstream jsonObject;
|
||||||
|
jsonObject << R"({"error" : "identityClaim cannot be null or empty",)"
|
||||||
|
<< "\n" << R"("endpoint":"url.com"})";
|
||||||
|
|
||||||
|
string errorMessage = JSONFieldParser::parseJsonStringField(
|
||||||
|
jsonObject.str(), "error");
|
||||||
|
string expectedOutput = "identityClaim cannot be null or empty";
|
||||||
|
ASSERT_EQ(expectedOutput, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(UtilsTest, ParseJsonFieldInvalidJson) {
|
||||||
|
stringstream jsonObject;
|
||||||
|
jsonObject << R"({error:"identityClaim cannot be null or empty"})";
|
||||||
|
|
||||||
|
string errorMessage = JSONFieldParser::parseJsonStringField(
|
||||||
|
jsonObject.str(), "error");
|
||||||
|
string expectedOutput = "";
|
||||||
|
ASSERT_EQ(expectedOutput, errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(UtilsTest, DirectoryExists) {
|
TEST_F(UtilsTest, DirectoryExists) {
|
||||||
mkdir(kFileName, 0755);
|
mkdir(kFileName, 0755);
|
||||||
ASSERT_TRUE(dirExists(kFileName));
|
ASSERT_TRUE(dirExists(kFileName));
|
||||||
|
Loading…
Reference in New Issue
Block a user