// --- BEGIN COPYRIGHT BLOCK ---
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package com.netscape.ca;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.mozilla.jss.CryptoManager;
import org.mozilla.jss.asn1.ASN1Util;
import org.mozilla.jss.asn1.GeneralizedTime;
import org.mozilla.jss.asn1.INTEGER;
import org.mozilla.jss.asn1.InvalidBERException;
import org.mozilla.jss.asn1.OBJECT_IDENTIFIER;
import org.mozilla.jss.asn1.OCTET_STRING;
import org.mozilla.jss.crypto.CryptoStore;
import org.mozilla.jss.crypto.CryptoToken;
import org.mozilla.jss.crypto.KeyPairAlgorithm;
import org.mozilla.jss.crypto.KeyPairGenerator;
import org.mozilla.jss.crypto.NoSuchItemOnTokenException;
import org.mozilla.jss.crypto.SignatureAlgorithm;
import org.mozilla.jss.crypto.TokenException;
import org.mozilla.jss.pkix.cert.Extension;
import org.mozilla.jss.pkix.primitive.Name;
import com.netscape.certsrv.apps.CMS;
import com.netscape.certsrv.authentication.IAuthToken;
import com.netscape.certsrv.authority.ICertAuthority;
import com.netscape.certsrv.base.EBaseException;
import com.netscape.certsrv.base.EPropertyNotFound;
import com.netscape.certsrv.base.IConfigStore;
import com.netscape.certsrv.base.ISubsystem;
import com.netscape.certsrv.base.Nonces;
import com.netscape.certsrv.base.PKIException;
import com.netscape.certsrv.ca.AuthorityID;
import com.netscape.certsrv.ca.CADisabledException;
import com.netscape.certsrv.ca.CAEnabledException;
import com.netscape.certsrv.ca.CAMissingCertException;
import com.netscape.certsrv.ca.CAMissingKeyException;
import com.netscape.certsrv.ca.CANotFoundException;
import com.netscape.certsrv.ca.CANotLeafException;
import com.netscape.certsrv.ca.CATypeException;
import com.netscape.certsrv.ca.ECAException;
import com.netscape.certsrv.ca.ICRLIssuingPoint;
import com.netscape.certsrv.ca.ICertificateAuthority;
import com.netscape.certsrv.ca.IssuerUnavailableException;
import com.netscape.certsrv.cert.CertEnrollmentRequest;
import com.netscape.certsrv.dbs.IDBSubsystem;
import com.netscape.certsrv.dbs.certdb.ICertRecord;
import com.netscape.certsrv.dbs.certdb.ICertificateRepository;
import com.netscape.certsrv.dbs.crldb.ICRLRepository;
import com.netscape.certsrv.dbs.replicadb.IReplicaIDRepository;
import com.netscape.certsrv.ldap.ELdapException;
import com.netscape.certsrv.ldap.ILdapConnFactory;
import com.netscape.certsrv.logging.ILogger;
import com.netscape.certsrv.ocsp.IOCSPService;
import com.netscape.certsrv.policy.IPolicyProcessor;
import com.netscape.certsrv.profile.IEnrollProfile;
import com.netscape.certsrv.profile.IProfile;
import com.netscape.certsrv.profile.IProfileSubsystem;
import com.netscape.certsrv.publish.ICRLPublisher;
import com.netscape.certsrv.publish.IPublisherProcessor;
import com.netscape.certsrv.request.ARequestNotifier;
import com.netscape.certsrv.request.IPolicy;
import com.netscape.certsrv.request.IRequest;
import com.netscape.certsrv.request.IRequestListener;
import com.netscape.certsrv.request.IRequestNotifier;
import com.netscape.certsrv.request.IRequestQueue;
import com.netscape.certsrv.request.IRequestScheduler;
import com.netscape.certsrv.request.IService;
import com.netscape.certsrv.request.RequestStatus;
import com.netscape.certsrv.security.ISigningUnit;
import com.netscape.certsrv.util.IStatsSubsystem;
import com.netscape.cms.servlet.cert.CertEnrollmentRequestFactory;
import com.netscape.cms.servlet.cert.EnrollmentProcessor;
import com.netscape.cms.servlet.processors.CAProcessor;
import com.netscape.cmscore.base.ArgBlock;
import com.netscape.cmscore.dbs.CRLRepository;
import com.netscape.cmscore.dbs.CertRecord;
import com.netscape.cmscore.dbs.CertificateRepository;
import com.netscape.cmscore.dbs.DBSubsystem;
import com.netscape.cmscore.dbs.ReplicaIDRepository;
import com.netscape.cmscore.ldap.PublisherProcessor;
import com.netscape.cmscore.listeners.ListenerPlugin;
import com.netscape.cmscore.request.RequestSubsystem;
import com.netscape.cmscore.security.KeyCertUtil;
import com.netscape.cmscore.util.Debug;
import com.netscape.cmsutil.crypto.CryptoUtil;
import com.netscape.cmsutil.ldap.LDAPPostReadControl;
import com.netscape.cmsutil.ldap.LDAPUtil;
import com.netscape.cmsutil.ocsp.BasicOCSPResponse;
import com.netscape.cmsutil.ocsp.CertID;
import com.netscape.cmsutil.ocsp.CertStatus;
import com.netscape.cmsutil.ocsp.GoodInfo;
import com.netscape.cmsutil.ocsp.KeyHashID;
import com.netscape.cmsutil.ocsp.NameID;
import com.netscape.cmsutil.ocsp.OCSPRequest;
import com.netscape.cmsutil.ocsp.OCSPResponse;
import com.netscape.cmsutil.ocsp.OCSPResponseStatus;
import com.netscape.cmsutil.ocsp.ResponderID;
import com.netscape.cmsutil.ocsp.ResponseBytes;
import com.netscape.cmsutil.ocsp.ResponseData;
import com.netscape.cmsutil.ocsp.RevokedInfo;
import com.netscape.cmsutil.ocsp.SingleResponse;
import com.netscape.cmsutil.ocsp.TBSRequest;
import com.netscape.cmsutil.ocsp.UnknownInfo;
import netscape.ldap.LDAPAttribute;
import netscape.ldap.LDAPAttributeSet;
import netscape.ldap.LDAPConnection;
import netscape.ldap.LDAPConstraints;
import netscape.ldap.LDAPControl;
import netscape.ldap.LDAPEntry;
import netscape.ldap.LDAPException;
import netscape.ldap.LDAPModification;
import netscape.ldap.LDAPModificationSet;
import netscape.ldap.LDAPSearchConstraints;
import netscape.ldap.LDAPSearchResults;
import netscape.ldap.controls.LDAPEntryChangeControl;
import netscape.ldap.controls.LDAPPersistSearchControl;
import netscape.ldap.util.DN;
import netscape.security.pkcs.PKCS10;
import netscape.security.util.DerOutputStream;
import netscape.security.util.DerValue;
import netscape.security.x509.AlgorithmId;
import netscape.security.x509.CertificateChain;
import netscape.security.x509.CertificateIssuerName;
import netscape.security.x509.CertificateSubjectName;
import netscape.security.x509.CertificateVersion;
import netscape.security.x509.X500Name;
import netscape.security.x509.X500Signer;
import netscape.security.x509.X509CRLImpl;
import netscape.security.x509.X509CertImpl;
import netscape.security.x509.X509CertInfo;
import netscape.security.x509.X509ExtensionException;
import netscape.security.x509.X509Key;
/**
* A class represents a Certificate Authority that is
* responsible for certificate specific operations.
*
*
* @author lhsiao
* @version $Revision$, $Date$
*/
public class CertificateAuthority
implements ICertificateAuthority, ICertAuthority, IOCSPService, Runnable {
public static final String OFFICIAL_NAME = "Certificate Manager";
public final static OBJECT_IDENTIFIER OCSP_NONCE = new OBJECT_IDENTIFIER("1.3.6.1.5.5.7.48.1.2");
/* The static conn factory is initialised by the host authority's
* 'init' method, before any lightweight CAs are instantiated
*/
private static ILdapConnFactory dbFactory = null;
private static final Map caMap =
Collections.synchronizedSortedMap(new TreeMap());
protected CertificateAuthority hostCA = null;
protected AuthorityID authorityID = null;
protected AuthorityID authorityParentID = null;
protected String authorityDescription = null;
protected boolean authorityEnabled = true;
private boolean hasKeys = false;
private ECAException signingUnitException = null;
protected ISubsystem mOwner = null;
protected IConfigStore mConfig = null;
protected ILogger mLogger = CMS.getLogger();
protected Hashtable mCRLIssuePoints = new Hashtable();
protected CRLIssuingPoint mMasterCRLIssuePoint = null; // the complete crl.
protected SigningUnit mSigningUnit;
protected SigningUnit mOCSPSigningUnit;
protected SigningUnit mCRLSigningUnit;
protected CertificateIssuerName mIssuerObj = null;
protected CertificateSubjectName mSubjectObj = null;
protected X500Name mName = null;
protected X500Name mCRLName = null;
protected X500Name mOCSPName = null;
protected String mNickname = null; // nickname of CA signing cert.
protected String mOCSPNickname = null; // nickname of OCSP signing cert.
protected long mCertSerialNumberCounter = System.currentTimeMillis();
protected long mRequestID = System.currentTimeMillis();
protected String[] mAllowedSignAlgors = null;
protected CertificateRepository mCertRepot = null;
protected CRLRepository mCRLRepot = null;
protected ReplicaIDRepository mReplicaRepot = null;
protected CertificateChain mCACertChain = null;
protected CertificateChain mOCSPCertChain = null;
protected X509CertImpl mCRLCert = null;
protected org.mozilla.jss.crypto.X509Certificate mCRLX509Cert = null;
protected X509CertImpl mCaCert = null;
protected org.mozilla.jss.crypto.X509Certificate mCaX509Cert = null;
protected X509CertImpl mOCSPCert = null;
protected org.mozilla.jss.crypto.X509Certificate mOCSPX509Cert = null;
protected String[] mCASigningAlgorithms = null;
protected PublisherProcessor mPublisherProcessor = null;
protected IRequestQueue mRequestQueue = null;
protected CAPolicy mPolicy = null;
protected CAService mService = null;
protected IRequestNotifier mNotify = null;
protected IRequestNotifier mPNotify = null;
protected long mNumOCSPRequest = 0;
protected long mTotalTime = 0;
protected long mTotalData = 0;
protected long mSignTime = 0;
protected long mLookupTime = 0;
protected static final int FASTSIGNING_DISABLED = 0;
protected static final int FASTSIGNING_ENABLED = 1;
protected CertificateVersion mDefaultCertVersion;
protected long mDefaultValidity;
protected boolean mEnablePastCATime;
protected boolean mEnableOCSP;
protected int mFastSigning = FASTSIGNING_DISABLED;
protected static final long SECOND = 1000; // 1000 milliseconds
protected static final long MINUTE = 60 * SECOND;
protected static final long HOUR = 60 * MINUTE;
protected static final long DAY = 24 * HOUR;
protected static final long YEAR = DAY * 365;
protected static final String PROP_CERT_REPOS_DN = "CertificateRepositoryDN";
protected static final String PROP_REPOS_DN = "RepositoryDN";
protected static final String PROP_REPLICAID_DN = "dbs.replicadn";
// for the notification listeners
/**
* Package constants
*/
public IRequestListener mCertIssuedListener = null;
public IRequestListener mCertRevokedListener = null;
public IRequestListener mReqInQListener = null;
/* cache responder ID for performance */
private ResponderID mResponderIDByName = null;
private ResponderID mResponderIDByHash = null;
protected Hashtable mListenerPlugins = null;
/**
* Internal constants
*/
protected ICRLPublisher mCRLPublisher = null;
private String mId = null;
private boolean mByName = true;
private boolean mUseNonces = true;
private int mMaxNonces = 100;
/* Variables to manage loading and tracking of lightweight CAs
*
* The initialLoadDone latch causes the host authority's 'init'
* method to block until the monitor thread has finished the
* initial loading of lightweight CAs.
*
* In other words: the "server startup" cannot complete until
* all the lightweight CAs that exist at start time are loaded.
*/
private static boolean stopped = false;
private static boolean foundHostAuthority = false;
private static Integer initialNumAuthorities = null;
private static int numAuthoritiesLoaded = 0;
private static CountDownLatch initialLoadDone = new CountDownLatch(1);
/* Maps and sets of entryUSNs and nsUniqueIds for avoiding race
* conditions and unnecessary reloads related to replication */
private static TreeMap entryUSNs = new TreeMap<>();
private static TreeMap nsUniqueIds = new TreeMap<>();
private static TreeSet deletedNsUniqueIds = new TreeSet<>();
/**
* Constructs a CA subsystem.
*/
public CertificateAuthority() {
hostCA = this;
}
/**
* Construct and initialise a lightweight authority
*/
private CertificateAuthority(
CertificateAuthority hostCA,
X500Name dn,
AuthorityID aid,
AuthorityID parentAID,
String signingKeyNickname,
String authorityDescription,
boolean authorityEnabled
) throws EBaseException {
setId(hostCA.getId());
this.hostCA = hostCA;
// cert and key may not have been replicated to local nssdb
// yet, so set DN based on data from LDAP
this.mName = dn;
this.authorityID = aid;
this.authorityParentID = parentAID;
this.authorityDescription = authorityDescription;
this.authorityEnabled = authorityEnabled;
mNickname = signingKeyNickname;
init(hostCA.mOwner, hostCA.mConfig);
}
public boolean isHostAuthority() {
return hostCA == this;
}
public void ensureReady()
throws ECAException {
if (!authorityEnabled)
throw new CADisabledException("Authority is disabled");
if (!isReady()) {
if (signingUnitException != null)
throw signingUnitException;
else
throw new CAMissingKeyException("Authority does not yet have signing key and cert in local NSSDB");
}
}
public boolean isReady() {
return hasKeys;
}
public boolean getAuthorityEnabled() {
return authorityEnabled;
}
/**
* Retrieves subsystem identifier.
*/
public String getId() {
return mId;
}
public CertificateVersion getDefaultCertVersion() {
return mDefaultCertVersion;
}
public boolean isEnablePastCATime() {
return mEnablePastCATime;
}
/**
* Sets subsystem identifier.
*/
public void setId(String id) throws EBaseException {
mId = id;
}
/**
* updates the Master CRL now
*/
public void updateCRLNow() throws EBaseException {
if (mMasterCRLIssuePoint != null) {
mMasterCRLIssuePoint.updateCRLNow();
}
}
public void publishCRLNow() throws EBaseException {
if (mMasterCRLIssuePoint != null) {
mMasterCRLIssuePoint.publishCRL();
}
}
public ICRLPublisher getCRLPublisher() {
return mCRLPublisher;
}
/**
* @deprecated
*/
public IPolicyProcessor getPolicyProcessor() {
return mPolicy.getPolicyProcessor();
}
public boolean noncesEnabled() {
return mUseNonces;
}
public Map