Skip to content

Commit

Permalink
[#25] Make ACA exception handling more descriptive
Browse files Browse the repository at this point in the history
  • Loading branch information
apldev3 committed Oct 29, 2018
1 parent f192ce5 commit e70ad8f
Show file tree
Hide file tree
Showing 16 changed files with 322 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import com.google.protobuf.ByteString;

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.BIOSComponentInfo;
import hirs.data.persist.BaseboardComponentInfo;
Expand Down Expand Up @@ -238,7 +241,8 @@ public byte[] processIdentityRequest(final byte[] identityRequest) {
if (publicKeyModulus != null) {
ekPublicKey = assemblePublicKey(publicKeyModulus.toByteArray());
} 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) {
LOG.error("Could not retrieve the public key modulus from the EK cert");
Expand Down Expand Up @@ -277,7 +281,7 @@ public byte[] processIdentityRequest(final byte[] identityRequest) {

if (deviceInfoReport == null) {
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");
}

Expand Down Expand Up @@ -382,7 +386,8 @@ public byte[] processIdentityClaimTpm2(final byte[] identityClaim) {
LOG.info("Got identity claim");

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
Expand Down Expand Up @@ -466,7 +471,7 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) {
try {
request = ProvisionerTpm2.CertificateRequest.parseFrom(certificateRequest);
} catch (InvalidProtocolBufferException ipbe) {
throw new IdentityProcessingException(
throw new CertificateProcessingException(
"Could not deserialize certificate request", ipbe);
}

Expand Down Expand Up @@ -512,7 +517,7 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) {
} else {
LOG.error("Could not process credential request. Invalid nonce provided: "
+ request.getNonce().toString());
throw new IdentityProcessingException("Invalid nonce given in request");
throw new CertificateProcessingException("Invalid nonce given in request");
}
}

Expand All @@ -524,7 +529,7 @@ public byte[] processCertificateRequest(final byte[] certificateRequest) {
RSAPublicKey parsePublicKey(final byte[] publicArea) {
int pubLen = publicArea.length;
if (pubLen < RSA_MODULUS_LENGTH) {
throw new IdentityProcessingException(
throw new IllegalArgumentException(
"EK or AK public data segment is not long enough");
}
// public data ends with 256 byte modulus
Expand Down Expand Up @@ -661,7 +666,7 @@ private Device processDeviceInfo(final ProvisionerTpm2.IdentityClaim claim) {

if (deviceInfoReport == null) {
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");
}

Expand Down Expand Up @@ -882,7 +887,7 @@ private PublicKey assemblePublicKey(final BigInteger modulus) {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new IdentityProcessingException(
throw new UnexpectedServerException(
"Encountered unexpected error creating public key: " + e.getMessage(), e);
}
}
Expand Down Expand Up @@ -948,7 +953,7 @@ byte[] generateAsymmetricContents(final IdentityProof proof, final SymmetricKey
} catch (NoSuchAlgorithmException | IllegalBlockSizeException | NoSuchPaddingException
| InvalidKeyException | BadPaddingException
| InvalidAlgorithmParameterException e) {
throw new IdentityProcessingException(
throw new CertificateProcessingException(
"Encountered error while generating ACA session key: " + e.getMessage(), e);
}
}
Expand Down Expand Up @@ -1004,7 +1009,7 @@ SymmetricAttestation generateAttestation(final X509Certificate credential,
} catch (BadPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException
| InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException
| CertificateEncodingException e) {
throw new IdentityProcessingException(
throw new CertificateProcessingException(
"Encountered error while generating Identity Response: " + e.getMessage(), e);
}
}
Expand Down Expand Up @@ -1066,7 +1071,7 @@ X509Certificate generateCredential(final PublicKey publicKey,
.setProvider("BC").getCertificate(holder);
return certificate;
} catch (IOException | OperatorCreationException | CertificateException e) {
throw new IdentityProcessingException("Encountered error while generating "
throw new CertificateProcessingException("Encountered error while generating "
+ "identity credential: " + e.getMessage(), e);
}
}
Expand Down Expand Up @@ -1159,7 +1164,8 @@ protected ByteString tpm20MakeCredential(final RSAPublicKey ek, final RSAPublicK
| InvalidKeyException | InvalidAlgorithmParameterException
| NoSuchPaddingException e) {
throw new IdentityProcessingException(
"Encountered error while making credential: " + e.getMessage(), e);
"Encountered error while making the identity claim challenge: "
+ e.getMessage(), e);
}
}

Expand Down Expand Up @@ -1418,15 +1424,15 @@ private Set<PlatformCredential> parsePcsFromIdentityClaim(
* 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
* @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
*/
private byte[] getDerEncodedCertificate(final X509Certificate certificate) {
try {
return certificate.getEncoded();
} catch (CertificateEncodingException e) {
LOG.error("Error converting certificate to ASN.1 DER Encoding.", e);
throw new IdentityProcessingException(
throw new UnexpectedServerException(
"Encountered error while converting X509 Certificate: "
+ e.getMessage(), e);
}
Expand All @@ -1441,7 +1447,7 @@ private byte[] getDerEncodedCertificate(final X509Certificate certificate) {
* @param endorsementCredential the endorsement credential 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
* @throws {@link IdentityProcessingException} if error occurs in persisting the Attestation
* @throws {@link CertificateProcessingException} if error occurs in persisting the Attestation
* Certificate
*/
private void saveAttestationCertificate(final byte[] derEncodedAttestationCertificate,
Expand All @@ -1456,7 +1462,7 @@ private void saveAttestationCertificate(final byte[] derEncodedAttestationCertif
certificateManager.save(attCert);
} catch (Exception e) {
LOG.error("Error saving generated Attestation Certificate to database.", e);
throw new IdentityProcessingException(
throw new CertificateProcessingException(
"Encountered error while storing Attestation Certificate: "
+ e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@
@PropertySource(value = "file:/etc/hirs/aca/aca.properties",
ignoreResourceNotFound = true)
})
@ComponentScan({ "hirs.attestationca", "hirs.attestationca.service", "hirs.validation",
"hirs.data.service" })
@ComponentScan({ "hirs.attestationca", "hirs.attestationca.service", "hirs.attestationca.rest",
"hirs.validation", "hirs.data.service" })
@Import(HibernateConfiguration.class)
@EnableWebMvc
public class AttestationCertificateAuthorityConfiguration extends WebMvcConfigurerAdapter {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package hirs.attestationca;
package hirs.attestationca.exceptions;

/**
* Generic exception thrown while a {@link AttestationCertificateAuthority} is processing a newly
* submitted Identity.
* Generic exception thrown while a {@link hirs.attestationca.AttestationCertificateAuthority}
* is processing a newly submitted Identity.
*/
public class IdentityProcessingException extends RuntimeException {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Custom exceptions of the {@link hirs.attestationca.AttestationCertificateAuthority}.
*/
package hirs.attestationca.exceptions;
Original file line number Diff line number Diff line change
@@ -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);
}

}
Loading

0 comments on commit e70ad8f

Please sign in to comment.