package org.apereo.cas.adaptors.x509.authentication.revocation;
import org.apereo.cas.util.DateTimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.cert.X509CRLEntry;
import java.time.ZonedDateTime;
/**
* Exception that describes a revoked X.509 certificate.
*
* @author Marvin S. Addison
* @since 3.4.6
*
*/
public class RevokedCertificateException extends GeneralSecurityException {
/** OID for reasonCode CRL extension. */
public static final String CRL_REASON_OID = "2.5.29.21";
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 8827788431199129708L;
/** The LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(RevokedCertificateException.class);
/** CRL revocation reason codes per RFC 3280. */
public enum Reason {
/** The Unspecified. */
Unspecified,
/** The Key compromise. */
KeyCompromise,
/** The CA compromise. */
CACompromise,
/** The Affiliation changed. */
AffiliationChanged,
/** The Superseded. */
Superseded,
/** The Cessation of operation. */
CessationOfOperation,
/** The Certificate hold. */
CertificateHold,
/** The Remove from crl. */
RemoveFromCRL,
/** The Privilege withdrawn. */
PrivilegeWithdrawn,
/** The AA compromise. */
AACompromise;
/**
* Convert code to reason.
*
* @param code the code
* @return the reason
*/
public static Reason fromCode(final int code) {
final Reason[] reasons = Reason.values();
for (int i = 0; i < reasons.length; i++) {
if (i == code) {
return reasons[i];
}
}
throw new IllegalArgumentException("Unknown CRL reason code.");
}
}
/** The revocation date. */
private final ZonedDateTime revocationDate;
/** The serial. */
private final BigInteger serial;
/** The reason. */
private final Reason reason;
/**
* Instantiates a new revoked certificate exception.
*
* @param revoked the revoked
* @param serial the serial
*/
public RevokedCertificateException(final ZonedDateTime revoked, final BigInteger serial) {
this(revoked, serial, null);
}
/**
* Instantiates a new revoked certificate exception.
*
* @param revoked the revoked
* @param serial the serial
* @param reason the reason
*/
public RevokedCertificateException(final ZonedDateTime revoked, final BigInteger serial, final Reason reason) {
this.revocationDate = ZonedDateTime.from(revoked);
this.serial = serial;
this.reason = reason;
}
/**
* Instantiates a new revoked certificate exception.
*
* @param entry the entry
*/
public RevokedCertificateException(final X509CRLEntry entry) {
this(DateTimeUtils.zonedDateTimeOf(entry.getRevocationDate()), entry.getSerialNumber(), getReasonFromX509Entry(entry));
}
/**
* Get reason from the x509 entry.
* @param entry the entry
* @return reason or null
*/
private static Reason getReasonFromX509Entry(final X509CRLEntry entry) {
if (entry.hasExtensions()) {
try {
final int code = Integer.parseInt(
new String(entry.getExtensionValue(CRL_REASON_OID), "ASCII"));
if (code < Reason.values().length) {
return Reason.fromCode(code);
}
} catch (final Exception e) {
LOGGER.trace("An exception occurred when resolving extension value: [{}]", e.getMessage());
}
}
return null;
}
/**
* Gets the revocation date.
*
* @return Returns the revocationDate.
*/
public ZonedDateTime getRevocationDate() {
return this.revocationDate == null ? null : ZonedDateTime.from(this.revocationDate);
}
/**
* Gets the serial.
*
* @return Returns the serial.
*/
public BigInteger getSerial() {
return this.serial;
}
/**
* Gets the reason.
*
* @return Returns the reason.
*/
public Reason getReason() {
return this.reason;
}
@Override
public String getMessage() {
if (this.reason != null) {
return String.format("Certificate %s revoked on %s for reason %s",
this.serial, this.revocationDate, this.reason);
}
return String.format("Certificate %s revoked on %s", this.serial, this.revocationDate);
}
}