/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2008, 2009 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.ccn.impl.security.crypto.util;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.cert.CRLException;
import java.security.cert.X509CRL;
import java.util.Date;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.x509.X509V2CRLGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
* Helper wrapper around BouncyCastle's CRL support. BouncyCastle's CRL support
* is a bit dodgy; as it relies on the verifier being able to inherit algorithm parameters (e.g. DSA params)
* for the signer key, if any, from the CA certificate.
*/
public class MinimalCRLGenerator {
/**
* Reason codes.
**/
public static final int REASON_UNSPECIFIED = 0;
public static final int REASON_KEY_COMPROMISE = 1;
public static final int REASON_CA_COMPROMISE = 2;
public static final int REASON_AFFILIATION_CHANGED = 4;
public static final int REASON_SUPERSEDED = 5;
public static final int REASON_CESSATION_OF_OPERATION = 6;
public static final int REASON_CERTIFICATE_HOLD = 7;
public static final int REASON_REMOVE_FROM_CRL = 8;
public static final String[] REASONS = {"unspecified", "key_compromise",
"ca_compromise", "extra_space", "affiliation_changed",
"superseded", "cessation_of_operation", "certificate_hold",
"remove_from_crl"};
public static final int REASON_CODES[] = {REASON_UNSPECIFIED, REASON_KEY_COMPROMISE,
REASON_CA_COMPROMISE, REASON_AFFILIATION_CHANGED, REASON_SUPERSEDED,
REASON_CESSATION_OF_OPERATION, REASON_CERTIFICATE_HOLD, REASON_REMOVE_FROM_CRL
};
/**
* One month (avg), in milliseconds.
*/
public static final int DEFAULT_DURATION = (int)(1000 * 60 * 60 * 24 * 365.25/12);
protected static final String DEFAULT_HASH = "SHA1";
protected X509V2CRLGenerator _crlGenerator = new X509V2CRLGenerator();
// Local copies of useful fields
protected Date _thisUpdate = null;
protected Date _nextUpdate = null;
/**
* Constructor for X509CRLGenerator.
* @param issuerName Issuer's name in X.500 format (C=US,...)
* @param thisUpdate date of issuance of this CRL. If null, filled in with "now".
* @param nextUpdate date of expiration of this CRL.
* @param extensions currently pass through a BouncyCastle X509Extensions object to constructor.
* No easy way to map this back into java X509Extensions....
*/
public MinimalCRLGenerator(String issuerName, Date thisUpdate, Date nextUpdate) {
this(new X509Name(issuerName), thisUpdate, nextUpdate);
}
public MinimalCRLGenerator(X509Name issuerName, Date thisUpdate, Date nextUpdate) {
super();
_crlGenerator.setIssuerDN(issuerName);
if (null == thisUpdate) {
thisUpdate = new Date();
}
_thisUpdate = thisUpdate;
_crlGenerator.setThisUpdate(thisUpdate);
_nextUpdate = nextUpdate;
_crlGenerator.setNextUpdate(nextUpdate);
}
/**
* Same as above, only sets thisUpdate to now and nextUpdate to now+duration.
* @param duration length of validity in milliseconds. If <= 0,
* defaults to DEFAULT_DURATION.
**/
public MinimalCRLGenerator(String issuerName, long duration) {
this(new X509Name(issuerName), duration);
}
public MinimalCRLGenerator(X509Name issuerName, long duration) {
super();
_crlGenerator.setIssuerDN(issuerName);
_thisUpdate = new Date();
if (duration <= 0)
duration = DEFAULT_DURATION;
_nextUpdate = new Date(_thisUpdate.getTime() + duration);
_crlGenerator.setThisUpdate(_thisUpdate);
_crlGenerator.setNextUpdate(_nextUpdate);
}
/**
* Add an extension to the CRL.
**/
public void addExtension(String oid, boolean critical, byte [] encodedValue) {
_crlGenerator.addExtension(oid, critical, encodedValue);
}
/**
* Reason codes listed above.
**/
public void addRevokedCertificate(BigInteger serialNumber, Date revocationTime, int reason) {
if (revocationTime == null)
revocationTime = _thisUpdate;
_crlGenerator.addCRLEntry(serialNumber, revocationTime, reason);
}
/**
* Add a certificate to a CRL.
* @param serialNumber
* @param revocationTime
* @param reason must be one of the entries in the REASONS array, or null or "" for
* REASON_UNSPECIFIED.
*/
public void addRevokedCertificate(BigInteger serialNumber, Date revocationTime, String reason)
throws InvalidParameterException {
// maps a null reason into REASON_UNSPECIFIED
int reason_code = reasonToReasonCode(reason);
if (reason_code < 0)
throw new InvalidParameterException("Unknown reason code: " + reason);
addRevokedCertificate(serialNumber, revocationTime, reason_code);
}
public static int reasonToReasonCode(String reason) {
if ((null == reason) || (reason.equals("")))
return REASON_UNSPECIFIED;
for (int i=0; i < REASONS.length; i++) {
if (REASONS[i].equalsIgnoreCase(reason))
return REASON_CODES[i];
}
return -1;
}
public static String reasonCodeToReason(int code) {
for (int i=0; i < REASON_CODES.length; i++) {
if (REASON_CODES[i] == code)
return REASONS[i];
}
return null;
}
/**
* If the digestAlgorithm is null, SHA-1 is used.
* @return the DER-encoded signed CRL.
**/
public X509CRL sign(String hashAlgorithm, PrivateKey signingKey, String provider)
throws InvalidKeyException, SignatureException, NoSuchProviderException, CRLException, IllegalStateException, NoSuchAlgorithmException {
String sigAlgName =
OIDLookup.getSignatureAlgorithm(((null == hashAlgorithm) || (hashAlgorithm.length() == 0)) ?
DEFAULT_HASH : hashAlgorithm,
signingKey.getAlgorithm());
System.out.println("Signature algorithm: " + sigAlgName + " provider: " + provider);
_crlGenerator.setSignatureAlgorithm(sigAlgName);
if (null == provider) {
provider = BouncyCastleProvider.PROVIDER_NAME;
}
return _crlGenerator.generate(signingKey, provider);
}
}