/**
 *  ---------
 * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
 * |#       #|
 * |#       #|  Copyright (c) 1999-2010 CardContact Software & System Consulting
 * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
 *  ---------
 *
 *  This file is part of OpenSCDP.
 *
 *  OpenSCDP is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  OpenSCDP 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 OpenSCDP; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * @fileoverview Simple CVC-CA
 */


// Imports
var CVC = require('scsh/eac/CVC').CVC;
var CVCertificateStore = require('scsh/eac/CVCertificateStore').CVCertificateStore;
var PublicKeyReference = require('scsh/eac/PublicKeyReference').PublicKeyReference;
var EAC2CVRequestGenerator = require('scsh/eac/EAC2CVRequestGenerator').EAC2CVRequestGenerator;
var EAC2CVCertificateGenerator = require('scsh/eac/EAC2CVCertificateGenerator').EAC2CVCertificateGenerator;



/**
 * Creates a new CVC-CA instance
 *
 * @class Class supporting a certification authority that can issue CVC certificates
 * for the EAC protocol.
 *
 * @constructor
 * @param {Crypto} crypto the crypto provider to use
 * @param {CVCertificateStore} certstore the certificate store to use
 * @param {String} path the path of holderIDs (eg. "/UTCVCA/UTDVCA/UTTERM")
 */
function CVCCA(crypto, certstore, holderId, parentId, path) {
	GPSystem.log(GPSystem.DEBUG, module.id, "new(" + crypto + "," + certstore + "," + holderId + "," + parentId + "," + path + ")");

	this.crypto = crypto;
	this.certstore = certstore;

	if (typeof(path) == "undefined") {	// ToDo: Remove after migration
		this.holderId = holderId;
		this.parentId = parentId;

		if (this.isRootCA()) {		// CVCA
			this.path = "/" + holderId;
		} else {					// DVCA
			this.path = "/" + parentId + "/" + holderId;
		}
	} else {
		this.path = path;
		var pe = path.substr(1).split("/");
		var l = pe.length;
		assert(l >= 1);
		this.holderId = pe[l - 1];
		if (l > 1) {
			this.parentId = pe[l - 2];
		} else {
			this.parentId = this.holderId;
		}
	}
	this.keyspec = new Key();
	this.keyspec.setComponent(Key.ECC_CURVE_OID, new ByteString("brainpoolP256r1", OID));
	this.taAlgorithmIdentifier = new ByteString("id-TA-ECDSA-SHA-256", OID);
	this.countryseq = null;
}

exports.CVCCA = CVCCA;



/**
 * Set factory generating EACCryptoProvider for private key operations (create, use, delete)
 *
 * @param {EACCryptoProvider} eaccp the EAC Crypto Provider for this instance
 * @param {String} eaccpid the instance id
 */
CVCCA.prototype.setEACCryptoProviderFactory = function(eaccpf, eaccpid) {
	this.eaccpf = eaccpf;
	this.eaccpid = eaccpid;
}



/**
 * Return a suitable crypto instance
 *
 */
CVCCA.prototype.getCrypto = function() {
	GPSystem.log(GPSystem.DEBUG, module.id, "getCrypto()");

	if (this.crypto) {
		return this.crypto;
	}

	if (this.eaccpf) {
		var eaccp = this.eaccpf.getEACCryptoProvider(this.eaccpid, true);
		return eaccp.getCrypto();
	}

	return this.certstore.getCrypto()
}



/**
 * Returns true if this is a root CA
 *
 * @returns true if this is a root CA
 * @type boolean
 */
CVCCA.prototype.isRootCA = function() {
	GPSystem.log(GPSystem.DEBUG, module.id, "isRootCA()");

	return this.holderId == this.parentId;
}



/**
 * Returns true if this CA has a certificate.
 *
 * @returns true if this CA is operational
 * @type boolean
 */
CVCCA.prototype.hasCertificate = function() {
	GPSystem.log(GPSystem.DEBUG, module.id, "hasCertificate()");

	var currentchr = this.certstore.getCurrentCHR(this.path);
	return currentchr != null;
}



/**
 * Returns true if this CA is operational.
 *
 * @returns true if this CA is operational
 * @type boolean
 */
CVCCA.prototype.isOperational = function() {
	GPSystem.log(GPSystem.DEBUG, module.id, "isOperational()");

	var currentchr = this.certstore.getCurrentCHR(this.path);
	if (currentchr == null) {
		return false;
	}
	var cvc = this.certstore.getCertificate(this.path, currentchr);
	return !cvc.isExpired();
}



/**
 * Sets the key specification for generating requests
 *
 * @param {Key} keyparam a key object containing key parameters (e.g. EC Curve)
 * @param {ByteString} algorithm the terminal authentication algorithm object identifier
 */
CVCCA.prototype.setKeySpec = function(keyparam, algorithm) {
	GPSystem.log(GPSystem.DEBUG, module.id, "setKeySpec(" + keyparam + "," + algorithm + ")");

	this.keyspec = keyparam;
	this.taAlgorithmIdentifier = algorithm;
}



/**
 * Set flags that controls the removal of the previous key if the certificate for the new key is imported
 *
 * @param {boolean} removePreviousKey true to remove, false to keep
 */
CVCCA.prototype.setRemovePreviousKey = function(removePreviousKey) {
	GPSystem.log(GPSystem.DEBUG, module.id, "setRemovePreviousKey(" + removePreviousKey + ")");

	this.removePreviousKey = removePreviousKey;
}



/**
 * Set country code to be included in sequence number of public key reference
 *
 * @param {String} countryseq the two character country code
 */
CVCCA.prototype.setCountryCodeForSequence = function(countryseq) {
	GPSystem.log(GPSystem.DEBUG, module.id, "setCountryCodeForSequence(" + countryseq + ")");

	this.countryseq = countryseq;
}



/**
 * Return private key handle, either from EACCryptoProvider or Certificate Store
 *
 */
CVCCA.prototype.getPrivateKey = function(path, chr) {
	GPSystem.log(GPSystem.DEBUG, module.id, "getPrivateKey(" + path + "," + chr + ")");

	if (this.eaccpf) {
		var eaccp = this.eaccpf.getEACCryptoProvider(this.eaccpid, true);

		var blob = this.certstore.getSigner(path, chr);
		var prkey = eaccp.getPrivateKey(path, chr, blob);

		if (prkey && !blob) {		// Have a key but no matching signer
			GPSystem.log(GPSystem.DEBUG, module.id, "Signer " + chr + " not found in database, but on crypto device. Creating signer now.");

			// Calculate current signer number
			var seq = chr.getSequenceNo();
			var cnt = parseInt(seq.substr(-5), 10);			// Full 5 digit sequence
			if (isNaN(cnt)) {
				var cnt = parseInt(seq.substr(-3), 10);		// 3 Digit sequence number
			}

			GPSystem.log(GPSystem.DEBUG, module.id, "Setting signer number to " + cnt);
			this.certstore.setSignerNo(path, cnt);
			this.certstore.newSigner(path, chr);
		}
		return prkey;
	} else {
		return this.certstore.getPrivateKey(path, chr);
	}
}



/**
 * Generate a certificate request
 *
 * @param {PublicKeyReference} car the CA at which this request is addressed
 * @param {boolean} forceInitial force an initial request, even if a current certificate is available
 * @param {boolean} signinitial sign with initial key (sequence = 00000)
 * @return the certificate request
 * @type CVC
 */
CVCCA.prototype.generateRequest = function(car, forceinitial, signinitial) {
	GPSystem.log(GPSystem.DEBUG, module.id, "generateRequest(" + car + "," + forceinitial + "," + signinitial + ")");

	if (typeof(this.certstore.sc) != "undefined") {
		return this.generateRequestHSM(car, forceinitial, signinitial);
	}

	if (this.eaccpf) {
		var eaccp = this.eaccpf.getEACCryptoProvider(this.eaccpid, true);
		if (typeof(eaccp.generateRequest) == "function") {
			return this.generateRequestHSMCP(eaccp, car, forceinitial, signinitial);
		}
	}

	// Obtain key parameter

	if (typeof(this.keyspec.getComponent(Key.ECC_P)) != "undefined") {
		var prk = new Key(this.keyspec);
		prk.setType(Key.PRIVATE);
		var keyalg = Crypto.EC;
	} else {
		var prk = new Key();
		prk.setType(Key.PRIVATE);
		var keyalg = Crypto.RSA;
	}
	var puk = new Key(this.keyspec);
	puk.setType(Key.PUBLIC);

	// Determine CHR
	var currentchr = this.certstore.getCurrentCHR(this.path);
	var nextchr = this.certstore.getNextCHR(this.path, this.countryseq);

	// Generate key pair
	if (this.eaccpf) {
		var eaccp = this.eaccpf.getEACCryptoProvider(this.eaccpid, true);
		eaccp.generateKeyPair(this.path, nextchr, keyalg, prk, puk);
	} else {
		this.certstore.generateKeyPair(this.path, nextchr, keyalg, prk, puk);
	}

	// Generate certificate request
	var reqGenerator = new EAC2CVRequestGenerator(this.getCrypto());

	// Set CPI
	reqGenerator.setProfileIdentifier(0x00);

	// Set public key for request
	reqGenerator.setPublicKey(puk);

	// Set oid of algorithm
	reqGenerator.setTAAlgorithmIdentifier(this.taAlgorithmIdentifier);

	// Set CHR for the request
	reqGenerator.setCHR(nextchr);

	if ((typeof(car) != "undefined") && (car != null)) {
		reqGenerator.setCAR(car);
	}

	if ((currentchr != null) && !forceinitial) {
		var previousprk = this.getPrivateKey(this.path, currentchr);
		var previouscvc = this.certstore.getCertificate(this.path, currentchr);
		var req = reqGenerator.generateAuthenticatedCVRequest(prk, previousprk, currentchr, previouscvc.getPublicKeyOID());
	} else {
		// Generate the request
		if (signinitial) {
			var initialchr = new PublicKeyReference(nextchr.getHolder() + "00000");
			var firstprk = this.getPrivateKey(this.path, initialchr);
			var req = reqGenerator.generateAuthenticatedCVRequest(prk, firstprk, initialchr);
		} else {
			var req = reqGenerator.generateCVRequest(prk);
		}
	}

	req = new CVC(req);

	this.certstore.storeRequest(this.path, req);

	return req;
}



/**
 * Generate a certificate request using a SmartCard-HSM based private key
 *
 * @param {PublicKeyReference} car the CA at which this request is addressed
 * @param {boolean} forceInitial force an initial request, even if a current certificate is available
 * @param {boolean} signinitial sign with initial key (sequence = 00000)
 * @return the certificate request
 * @type CVC
 */
CVCCA.prototype.generateRequestHSM = function(car, forceinitial, signinitial) {
	GPSystem.log(GPSystem.DEBUG, module.id, "generateRequestHSM(" + car + "," + forceinitial + "," + signinitial + ")");

	var req = this.certstore.generateRequest(this.path, car, forceinitial, signinitial, this.keyspec, this.taAlgorithmIdentifier, this.countryseq);
	this.certstore.storeRequest(this.path, req);

	return req;
}



/**
 * Generate a certificate request using a SmartCard-HSM based private key via EACCryptoProvider
 *
 * @param {EACCryptoProvider} prov the EACCryptoProvider
 * @param {PublicKeyReference} car the CA at which this request is addressed
 * @param {boolean} forceInitial force an initial request, even if a current certificate is available
 * @param {boolean} signinitial sign with initial key (sequence = 00000)
 * @return the certificate request
 * @type CVC
 */
CVCCA.prototype.generateRequestHSMCP = function(prov, car, forceinitial, signinitial) {
	GPSystem.log(GPSystem.DEBUG, module.id, "generateRequestHSM(" + car + "," + forceinitial + "," + signinitial + ")");

	// Determine CHR
	var currentchr = this.certstore.getCurrentCHR(this.path);
	do	{
		var nextchr = this.certstore.getNextCHR(this.path, this.countryseq);
		var prk = this.getPrivateKey(this.path, nextchr);
		if (!prk) {
			break;
		}
		GPSystem.log(GPSystem.INFO, module.id, "CHR " + nextchr + " already in use, skipping to next CHR");
	} while (true);

	var req = prov.generateRequest(this.path, currentchr, nextchr, car, forceinitial, signinitial, this.keyspec, this.taAlgorithmIdentifier);
	this.certstore.storeRequest(this.path, req);
	this.certstore.newSigner(this.path, nextchr);

	return req;
}



/**
 * Counter-sign a request
 *
 * @param {CVC} req the initial request
 * @return the certificate request
 * @type CVC
 */
CVCCA.prototype.counterSignRequest = function(request) {
	GPSystem.log(GPSystem.DEBUG, module.id, "counterSignRequest('" + request + "')");

	assert(!request.isAuthenticatedRequest());

	var car = this.certstore.getCurrentCHR(this.path);
	assert(car != null);

	var cacvc = this.certstore.getCertificate(this.path, car);
	assert(cacvc != null);

	var signingTAAlgorithmIdentifier = cacvc.getPublicKeyOID();
	var prk = this.getPrivateKey(this.path, car);

	var req = EAC2CVRequestGenerator.signAuthenticatedCVRequest(this.getCrypto(), request.getASN1(), prk, car, signingTAAlgorithmIdentifier);
	return new CVC(req);
}



/**
 * Generate an initial certificate request
 *
 * @param {PublicKeyReference} car the CA at which this request is addressed
 * @return the certificate request
 * @type CVC
 */
CVCCA.prototype.generateInitialRequest = function(car) {
	GPSystem.log(GPSystem.DEBUG, module.id, "generateInitialRequest(" + car + ")");

	return this.generateRequest(car, true, false);
}



/**
 * Generate a signed initial certificate request
 *
 * @param {PublicKeyReference} car the CA at which this request is addressed
 * @return the certificate request
 * @type CVC
 */
CVCCA.prototype.generateSignedInitialRequest = function(car) {
	GPSystem.log(GPSystem.DEBUG, module.id, "generateSignedInitialRequest(" + car + ")");

	return this.generateRequest(car, true, true);
}



/**
 * Generate certificate for certificate request
 *
 * <p>Certificate contents is defined through the policy object:</p>
 * <pre>
 *  	var policy = { certificateValidityDays: 2,
 * 				   chatRoleOID: new ByteString("id-IS", OID),
 * 				   chatRights: new ByteString("E3", HEX),
 * 				   includeDomainParameter: true,
 * 				   extensions: []
 * 				 };
 * </pre>
 *
 * @param {CVC} req the certificate request
 * @param {Object} policy the object with policy settings
 * @returns the certificate
 * @type CVC
 */
CVCCA.prototype.generateCertificate = function(req, policy) {
	GPSystem.log(GPSystem.DEBUG, module.id, "generateCertificate('" + req + "'" + policy + ")");

	var car = this.certstore.getCurrentCHR(this.path);
	var maxExpDate = null;

	if (car == null) {				// No CA certificate found
		if (this.isRootCA()) {
			car = req.getCHR();		// Generate a self-signed root certificate
		} else {
			throw new GPError("CVCCA", GPError.INVALID_DATA, 0, "No current certificate found");
		}
		if (policy.publicKeyOID) {
			var signingTAAlgorithmIdentifier = policy.publicKeyOID;
		} else {
			var signingTAAlgorithmIdentifier = req.getPublicKeyOID();
		}
	} else {
		var cacvc = this.certstore.getCertificate(this.path, car);
		var signingTAAlgorithmIdentifier = cacvc.getPublicKeyOID();
		if (policy.shellModelForExpirationDate) {
			maxExpDate = cacvc.getCXD();
		}
	}

	var generator = new EAC2CVCertificateGenerator(this.getCrypto());
	generator.setCAR(car);
	generator.setCHR(req.getCHR());
	var effDate = new Date();
	if (policy.ced) {
		effDate = policy.ced;
	}
	effDate.setHours(12, 0, 0, 0);
	var expDate = new Date((policy.certificateValidityDays - 1) * (1000 * 60 * 60 * 24) + effDate.getTime());
	expDate.setHours(12, 0, 0, 0);

	if (maxExpDate != null) {
		if (effDate.getTime() > maxExpDate.getTime()) {
			throw new GPError("CVCCA", GPError.INVALID_DATA, 0, "CA certificate is expired");
		}
		// Expiration date of issued certificate must not exceed expiration date of issuing CA
		if (expDate.getTime() > maxExpDate.getTime()) {
			expDate = maxExpDate;
		}
	}

	generator.setEffectiveDate(effDate);
	generator.setExpiryDate(expDate);
	generator.setChatOID(policy.chatRoleOID);
	generator.setChatAuthorizationLevel(policy.chatRights);
	generator.setPublicKey(req.getPublicKey());
	generator.setProfileIdentifier(0x00);
	if (policy.publicKeyOID) {
		generator.setTAAlgorithmIdentifier(policy.publicKeyOID);
	} else {
		generator.setTAAlgorithmIdentifier(req.getPublicKeyOID());
	}

	generator.setIncludeDomainParameters(policy.includeDomainParameter);
	generator.setExtensions(policy.extensions);
	var prk = this.getPrivateKey(this.path, car);
	var cvc = generator.generateCVCertificate(prk, signingTAAlgorithmIdentifier);

	return cvc;
}



/**
 * Store issued certificate
 *
 * @param {CVC} cert a newly issued certificate
 */
CVCCA.prototype.storeCertificate = function(cert) {
	GPSystem.log(GPSystem.DEBUG, module.id, "storeCertificate('" + cert + "')");

	var chrHolder = cert.getCHR().getHolder();
	this.certstore.storeCertificate(this.path + "/" + chrHolder, cert, false);
}



/**
 * Remove previous key
 *
 * @param {PublicKeyReference} previous the previously used CHR
 */
CVCCA.prototype.removePreviouslyUsedKey = function(previous) {
	GPSystem.log(GPSystem.DEBUG, module.id, "removePreviouslyUsedKey('" + previous + "')");

	if (previous.equals(this.certstore.getCurrentCHR(this.path))) {
		GPSystem.log(GPSystem.INFO, module.id, "CHR " + previous + " is still the active key. Not deleting it");
		return;
	}

	if (this.eaccpf) {
		var eaccp = this.eaccpf.getEACCryptoProvider(this.eaccpid, true);
		eaccp.deletePrivateKey(this.path, previous);
	} else {
		this.certstore.deletePrivateKey(this.path, previous);
	}
}



/**
 * Import a certificate into the certificate store and make it the current certificate
 *
 * @param {CVC} cert the certificate
 */
CVCCA.prototype.importCertificate = function(cert) {
	GPSystem.log(GPSystem.DEBUG, module.id, "importCertificate('" + cert + "')");

	var chr = cert.getCHR();
	var prk = this.getPrivateKey(this.path, chr);
	if (prk == null) {
		throw new GPError("CVCCA", GPError.INVALID_DATA, 0, "Invalid certificate, no matching private key");
	}
	var c = this.certstore.getCertificate(this.path, cert.getCHR());
	if (c != null) {
		GPSystem.log(GPSystem.INFO, module.id, "Certificate " + c + " already existing");
	}

	// Save currentchr
	var currentchr = this.certstore.getCurrentCHR(this.path);

	if (this.isRootCA() && !this.isOperational()) {
		this.certstore.storeCertificate(this.path, cert, (c == null));
	} else {
		if (!this.certstore.insertCertificate(this.getCrypto(), cert, this.path)) {
			throw new GPError("CVCCA", GPError.CRYPTO_FAILED, 0, "Could not validate certificate");
		}
	}
	if (this.removePreviousKey && currentchr) {
		this.removePreviouslyUsedKey(currentchr);
	}
}



/**
 * Import a list of certificates into the certificate store
 *
 * @param {CVC[]} certs the list of certificates
 */
CVCCA.prototype.importCertificates = function(certs) {
	GPSystem.log(GPSystem.DEBUG, module.id, "importCertificates('" + certs + "')");

	// Save currentchr
	var currentchr = this.certstore.getCurrentCHR(this.path);

	var list = this.certstore.insertCertificates(this.getCrypto(), certs, true, this.path);

	if (this.removePreviousKey && currentchr) {
		this.removePreviouslyUsedKey(currentchr);
	}

	return list;
}



/**
 * Returns a list of relevant certificates.
 *
 * <p>If the CA is the root CA, then all self-signed and link certificates are returned.</p>
 * <p>If the CA is a DVCA, then all certificates of the associated root and the current
 *    DVCA certificate is returned.</p>
 *
 * @param {PublicKeyReference} fromCAR the optional starting point for the list if not a root CA
 */
CVCCA.prototype.getCertificateList = function(fromCAR) {
	GPSystem.log(GPSystem.DEBUG, module.id, "getCertificateList(" + fromCAR + ")");

	var list;

	if (this.isRootCA()) {
		list = this.certstore.listCertificates(this.path);
	} else {
		var path = this.path;

		while(true) {
			var chr = this.certstore.getCurrentCHR(path);
			if (chr == null) {
				var ofs = path.lastIndexOf("/");
				if (ofs == 0) {
					list = [];
				} else {
					path = path.substr(0, ofs);
					continue;
				}
			} else {
				list = this.certstore.getCertificateChain(path, chr, fromCAR);
			}
			break;
		}
	}

	return list;
}



/**
 * Return certificate issued by this CA
 *
 * @param {PublicKeyReference} chr the certificate holder reference
 * @returns the certificate or null if not found
 * @type CVC
 */
CVCCA.prototype.getIssuedCertificate = function(chr) {
	GPSystem.log(GPSystem.DEBUG, module.id, "getIssuedCertificate(" + chr + ")");

	var path = this.path + "/" + chr.getHolder();

	var cvc = this.certstore.getCertificate(path, chr);
	if (cvc == null) {
		GPSystem.trace("No certificate found for " + chr);
		return null;
	}

	return cvc;
}



/**
 * Return authentic public key with domain parameter for a given CHR subordinate to the CA
 *
 * @param {PublicKeyReference} chr the certificate holder reference
 * @returns the public key or null
 * @type Key
 */
CVCCA.prototype.getAuthenticPublicKey = function(chr) {
	GPSystem.log(GPSystem.DEBUG, module.id, "getAuthenticPublicKey(" + chr + ")");

	var cvc = this.getIssuedCertificate(chr);

	if (cvc == null) {
		return null;
	}

	if (this.isRootCA()) {
		var dp = this.certstore.getDomainParameter(cvc.getCAR());
	} else {
		var dvcacvc = this.certstore.getCertificate(this.path, cvc.getCAR());
		if (dvcacvc == null) {
			GPSystem.trace("No certificate found for " + cvc.getCAR());
			return null;
		}
		var dp = this.certstore.getDomainParameter(dvcacvc.getCAR());
	}

	return(cvc.getPublicKey(dp));
}



CVCCA.test = function(daofcvca, daofdvca, daofterm) {

	var crypto = new Crypto();

	var ss = new CVCertificateStore(daofcvca);
	ss.setContextMarker(0);
	var cvca = new CVCCA(crypto, ss, null, null, "/UTCVCA");

	// Create a new request
	var req = cvca.generateRequest(null, false);
	GPSystem.trace("Request: " + req);
	GPSystem.trace(req.getASN1());

	assert(req.verifyWith(crypto, req.getPublicKey()));

	// Create self-signed or link certificate based on request
	var policy = { certificateValidityDays: 2,
				   chatRoleOID: new ByteString("id-IS", OID),
				   chatRights: new ByteString("E3", HEX),
				   includeDomainParameter: true,
				   extensions: []
				 };
	var cert = cvca.generateCertificate(req, policy);
	GPSystem.trace("Certificate: " + cert);
	GPSystem.trace(cert.getASN1());

	// Import certificate into store, making it the most current certificate
	cvca.importCertificate(cert);

	// Generate additional self-signed root certificate
	// This must be done after the link certificate has been imported
	var policy = { certificateValidityDays: 2,
				   chatRoleOID: new ByteString("id-IS", OID),
				   chatRights: new ByteString("E3", HEX),
				   includeDomainParameter: true,
				   extensions: []
				 };
	var cert = cvca.generateCertificate(req, policy);
	GPSystem.trace("Certificate: " + cert);
	GPSystem.trace(cert.getASN1());

	// Import certificate into store, making it the most current certificate
	cvca.importCertificate(cert);

	var ss = new CVCertificateStore(daofdvca);
	ss.setContextMarker(1);
	var dvca = new CVCCA(crypto, ss, null, null, "/UTCVCA/UTDVCA");

	var certlist = cvca.getCertificateList();
	var list = dvca.importCertificates(certlist);

	if (list.length > 0) {
		GPSystem.trace("Warning: Could not import the following certificates");
		for (var i = 0; i < list.length; i++) {
			GPSystem.trace(list[i]);
		}
	}

	// Create a new request
	var req = dvca.generateRequest(null, false);
	GPSystem.trace("Request: " + req);
	GPSystem.trace(req.getASN1());

	// Sign this request with root CA
	// This must be done after the link certificate has been imported
	var policy = { certificateValidityDays: 2,
				   chatRoleOID: new ByteString("id-IS", OID),
				   chatRights: new ByteString("A3", HEX),
				   includeDomainParameter: false,
				   extensions: []
				 };
	var cert = cvca.generateCertificate(req, policy);
	GPSystem.trace("Certificate: " + cert);
	GPSystem.trace(cert.getASN1());

	cvca.storeCertificate(cert);
	dvca.importCertificate(cert);


	var ss = new CVCertificateStore(daofterm);
	ss.setContextMarker(2);
	var term = new CVCCA(crypto, ss, null, null, "/UTCVCA/UTDVCA/UTTERM");

	term.setRemovePreviousKey(true);

	var certlist = dvca.getCertificateList();
	GPSystem.trace("Certificate list: ");
	GPSystem.trace(certlist);
	var list = term.importCertificates(certlist);

	if (list.length > 0) {
		GPSystem.trace("Warning: Could not import the following certificates");
		for (var i = 0; i < list.length; i++) {
			GPSystem.trace(list[i]);
		}
	}

	// Create a new request
	var req = term.generateRequest(null, false);
	GPSystem.trace("Request: " + req);
	GPSystem.trace(req.getASN1());

	// Sign this request with DVCA
	// This must be done after the link certificate has been imported
	var policy = { certificateValidityDays: 2,
				   chatRoleOID: new ByteString("id-IS", OID),
				   chatRights: new ByteString("23", HEX),
				   includeDomainParameter: false,
				   extensions: []
				 };
	var cert = dvca.generateCertificate(req, policy);
	GPSystem.trace("Certificate: " + cert);
	GPSystem.trace(cert.getASN1());

	dvca.storeCertificate(cert);
	term.importCertificate(cert);
}
