org.cesecore.certificates.crl.RevokedCertInfo Maven / Gradle / Ivy
/*************************************************************************
* *
* CESeCore: CE Security Core *
* *
* This software is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or any later version. *
* *
* See terms of license at gnu.org. *
* *
*************************************************************************/
package org.cesecore.certificates.crl;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.cesecore.util.CompressedCollection;
/**
* Holds information about a revoked certificate. The information kept here is the
* information that goes into a CRLEntry.
*
* @version $Id: RevokedCertInfo.java 28792 2018-04-27 16:03:01Z samuellb $
*
**/
public class RevokedCertInfo implements Serializable {
/** Version number for serialization */
private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(RevokedCertInfo.class);
/** Constants defining different revocation reasons. */
public static final int NOT_REVOKED = RevocationReasons.NOT_REVOKED.getDatabaseValue();
public static final int REVOCATION_REASON_UNSPECIFIED = RevocationReasons.UNSPECIFIED.getDatabaseValue();
public static final int REVOCATION_REASON_KEYCOMPROMISE = RevocationReasons.KEYCOMPROMISE.getDatabaseValue();
public static final int REVOCATION_REASON_CACOMPROMISE = RevocationReasons.CACOMPROMISE.getDatabaseValue();
public static final int REVOCATION_REASON_AFFILIATIONCHANGED = RevocationReasons.AFFILIATIONCHANGED.getDatabaseValue();
public static final int REVOCATION_REASON_SUPERSEDED = RevocationReasons.SUPERSEDED.getDatabaseValue();
public static final int REVOCATION_REASON_CESSATIONOFOPERATION = RevocationReasons.CESSATIONOFOPERATION.getDatabaseValue();
public static final int REVOCATION_REASON_CERTIFICATEHOLD = RevocationReasons.CERTIFICATEHOLD.getDatabaseValue();
// Value 7 is not used, see RFC5280
public static final int REVOCATION_REASON_REMOVEFROMCRL = RevocationReasons.REMOVEFROMCRL.getDatabaseValue();
public static final int REVOCATION_REASON_PRIVILEGESWITHDRAWN = RevocationReasons.PRIVILEGESWITHDRAWN.getDatabaseValue();
public static final int REVOCATION_REASON_AACOMPROMISE = RevocationReasons.AACOMPROMISE.getDatabaseValue();
/** BigInteger (serialNumber) in byte format, BigInteger.toByteArray() */
private byte[] userCertificate;
private long revocationDate;
private long expireDate;
private int reason;
/** Fingerprint in byte format, String.getBytes() */
private byte[] fingerprint;
/**
* A default constructor is needed to instantiate
* RevokedCertInfo objects using <jsp:useBean> by Tomcat 5.
*/
public RevokedCertInfo() {
fingerprint = null;
userCertificate = null;
revocationDate = 0;
expireDate = 0;
reason = REVOCATION_REASON_UNSPECIFIED;
}
/**
* Constructor filling in the whole object.
*
* @param reason {@link RevokedCertInfo#REVOCATION_REASON_UNSPECIFIED}
*
**/
public RevokedCertInfo(final byte[] fingerprint, final byte[] sernoBigIntegerArray, final long revdate, final int reason, final long expdate) {
this.fingerprint = fingerprint;
this.userCertificate = sernoBigIntegerArray;
this.revocationDate = revdate;
this.reason = reason;
this.expireDate = expdate;
}
/**
* Certificate fingerprint
**/
public String getCertificateFingerprint() {
return fingerprint == null ? null : new String(fingerprint);
}
/**
* Certificate fingerprint
**/
public void setCertificateFingerprint(final String fp) {
this.fingerprint = fp == null ? null : fp.getBytes();
}
/**
* Certificate serial number
**/
public BigInteger getUserCertificate() {
return userCertificate == null ? null : new BigInteger(userCertificate);
}
/**
* Certificate serial number
**/
public void setUserCertificate(final BigInteger serno) {
this.userCertificate = serno==null ? null : serno.toByteArray();
}
/**
* @return true is there is a revocationDate set (getRevocationDate() != null), false otherwise
*/
public boolean isRevocationDateSet() {
return revocationDate != 0;
}
/**
* Date when the certificate was revoked.
**/
public Date getRevocationDate() {
return revocationDate == 0 ? null : new Date(revocationDate);
}
/**
* Date when the certificate was revoked.
**/
public void setRevocationDate(final Date date) {
this.revocationDate = date == null ? 0 : date.getTime();
}
/**
* Date when the certificate expires.
**/
public Date getExpireDate() {
return expireDate == 0 ? null : new Date(expireDate);
}
/**
* Date when the certificate expires.
**/
public void setExpireDate(final Date date) {
this.expireDate = date==null ? 0 : date.getTime();
}
/**
* The reason the certificate was revoked.
*
* ReasonFlags ::= BIT STRING {
* unspecified(0),
* keyCompromise(1),
* cACompromise(2),
* affiliationChanged(3),
* superseded(4),
* cessationOfOperation(5),
* certficateHold(6)
* removeFromCRL(8)
* privilegeWithdrawn(9)
* aACompromise(10)
* }
*
* @see #REVOCATION_REASON_UNSPECIFIED
**/
public int getReason() {
return this.reason;
}
/**
* The reason the certificate was revoked.
* @param reason {@link RevokedCertInfo#REVOCATION_REASON_UNSPECIFIED}
**/
public void setReason(final int reason) {
this.reason = reason;
}
@Override
public String toString() {
return this.userCertificate == null ? "null" : new BigInteger(userCertificate).toString();
}
/**
* A quick way to tell if the certificate has been revoked.
* @return true if the certificate has been revoked, otherwise false.
*/
public boolean isRevoked() {
return isRevoked(reason);
}
/**
* Returns true if the certificate is permanently revoked (i.e. revoked and not just "on hold")
*/
public boolean isPermanentlyRevoked() {
return isPermanentlyRevoked(reason);
}
public static boolean isRevoked(int revocationReason) {
return revocationReason != NOT_REVOKED && revocationReason != REVOCATION_REASON_REMOVEFROMCRL;
}
public static boolean isPermanentlyRevoked(int revocationReason) {
return isRevoked(revocationReason) && revocationReason != REVOCATION_REASON_CERTIFICATEHOLD;
}
/**
* This method returns the revocation reason as a text string that is understandable.
* TODO: The strings in the enum should be easier for users to change, used from "publicweb/retrieve/check_status_result.jsp"
*
* @return A string describing the reason for revocation.
*/
public String getHumanReadableReason() {
RevocationReasons revocationReason = RevocationReasons.getFromDatabaseValue(reason);
if (revocationReason != null) {
return revocationReason.getHumanReadable();
} else {
return "unknown";
}
}
/**
* Merges two collections of RevokedCertInfo. Note that the parameters are slightly different. Duplicates are removed according to these rules:
*
* - Older permanent status changes win over newer ones
*
- Permanent status changes always win over temporary ones ("on hold" / "re-activate")
*
- More recent temporary status changes win over older ones
*
*
* @param a First collection of RevokedCertInfo. May not contain duplicates for the same serial number.
* @param b Second collection of RevokedCertInfo. May contain duplicates
* @param lastBaseCrlDate Entries in unrevoked state will only be included if they are more recent than this date. (<= 0 means never include them)
* @return CompressionCollection of certificates. May simply be a reference to a
if b
is empty, or a new merged CompressedCollection with any duplicates removed.
*/
public static Collection mergeByDateAndStatus(final Collection a, final Collection b, final long lastBaseCrlDate) {
// We can optimize this case, but not the reverse, since b can contain duplicates that should be filtered.
if (b.isEmpty()) {
return a;
}
// Merge revocation information
final Map permRevoked = new HashMap<>();
final Map tempRevoked = new HashMap<>();
for (final RevokedCertInfo revoked : a) {
final BigInteger serial = revoked.getUserCertificate();
if (revoked.isPermanentlyRevoked()) {
permRevoked.put(serial, revoked);
} else {
tempRevoked.put(serial, revoked);
}
}
for (final RevokedCertInfo revoked : b) {
final BigInteger serial = revoked.getUserCertificate();
final Date revdate = revoked.getRevocationDate();
final RevokedCertInfo permDate = permRevoked.get(serial);
if (permDate != null) {
// Older permanent status changes win over newer ones
if (permDate.getRevocationDate().after(revdate) && revoked.isPermanentlyRevoked()) {
permRevoked.put(serial, revoked);
tempRevoked.remove(serial);
}
continue;
}
if (revoked.isPermanentlyRevoked()) {
// Permanently revoked wins over temporary revoked/re-activated
permRevoked.put(serial, revoked);
tempRevoked.remove(serial);
continue;
}
final RevokedCertInfo tempDate = tempRevoked.get(serial);
// More recent temporary status changes win over older ones
if (tempDate == null || tempDate.getRevocationDate().before(revdate)) {
tempRevoked.put(serial, revoked);
}
}
final CompressedCollection mergedRevokedData = new CompressedCollection<>();
mergedRevokedData.addAll(permRevoked.values()); // Permanently revoked entries are always added
for (final RevokedCertInfo revoked : tempRevoked.values()) {
if (!revoked.isRevoked() && (lastBaseCrlDate <= 0 || revoked.getRevocationDate().getTime() <= lastBaseCrlDate)) {
continue; // REMOVEFROMCRL entries are not added in Base CRLs (lastBaseCrlDate=0) or if already removed from the latest Base CRL
}
mergedRevokedData.add(revoked);
}
mergedRevokedData.closeForWrite();
if (log.isDebugEnabled()) {
log.debug("mergeByDateAndStatus: Merged to " + mergedRevokedData.size() + " entries");
}
return mergedRevokedData;
}
}