org.glite.security.delegation.impl.GliteDelegation.java Source code

Java tutorial

Introduction

Here is the source code for org.glite.security.delegation.impl.GliteDelegation.java

Source

/*
 * Copyright (c) Members of the EGEE Collaboration. 2004. See
 * http://www.eu-egee.org/partners/ for details on the copyright holders.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.glite.security.delegation.impl;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.StringBufferInputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Calendar;

import org.apache.log4j.Logger;
import org.bouncycastle.jce.PKCS10CertificationRequest;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PEMWriter;
import org.glite.security.SecurityContext;
import org.glite.security.delegation.GrDPConstants;
import org.glite.security.delegation.GrDPX509Util;
import org.glite.security.delegation.GrDProxyDlgeeOptions;
import org.glite.security.delegation.DelegationException;
import org.glite.security.delegation.NewProxyReq;
import org.glite.security.delegation.storage.GrDPStorage;
import org.glite.security.delegation.storage.GrDPStorageCacheElement;
import org.glite.security.delegation.storage.GrDPStorageElement;
import org.glite.security.delegation.storage.GrDPStorageException;
import org.glite.security.delegation.storage.GrDPStorageFactory;
import org.glite.security.util.CertUtil;
import org.glite.security.util.DN;
import org.glite.security.util.DNHandler;
import org.glite.security.util.FileCertReader;
import org.glite.security.util.PrivateKeyReader;
import org.glite.security.util.axis.InitSecurityContext;

/**
 * Implementation of the logic of the Glite Delegation Interface on the server side.
 * 
 * @author Ricardo Rocha <Ricardo.Rocha@cern.ch>
 * @author Akos Frohner <Akos.Frohner@cern.ch>
 * @author Joni Hahkala <Joni.Hahkala@cern.ch>
 * 
 */
public class GliteDelegation {

    /** Local logger object. */
    private static Logger logger = Logger.getLogger(GliteDelegation.class);

    /** The default key size to be used. Can be overwritten by setting the
    * dlgeeKeySize property in the dlgee.properties file if the value there is bigger. */
    private int DEFAULT_KEY_SIZE = 1024;

    /** Set at instantiation time. Remains false if a bad configuration set was found. */
    private boolean m_bad_config = false;

    /** Local object interfacing the storage area. */
    private GrDPStorage m_storage = null;

    /** Key size being used. */
    private int m_keySize;

    /** the cert reader, make static to avoid initializing it for each read. */
    private static FileCertReader s_reader = null;

    /** The static class initializer that loads single FileCertReader that is shared by all instances to save resources. */
    {
        try {
            s_reader = new FileCertReader();
        } catch (CertificateException e3) {
            logger.error("Failed to initialize certificate reader: " + e3.getMessage());
            throw new RuntimeException("Failed to initialize certificate reader: " + e3.getMessage());

        }
    }

    /**
     * Loads the DLGEE properties from the default config file and calls the appropriate constructor.
     * 
     * @throws IOException Failed to load the DLGEE config file
     * @see #GliteDelegation(GrDProxyDlgeeOptions)
     */
    public GliteDelegation() throws IOException {
        this(new GrDProxyDlgeeOptions(GrDPX509Util.getDlgeePropertyFile()));
    }

    /**
     * Class constructor.
     * 
     * Creates a new storage handler instance (implementation depending on configuration) to be used later.
     * 
     * Sets the value of the key size as defined in the configuration.
     * 
     * @param dlgeeOpt the options object for configuring the delegation receiver.
     */
    public GliteDelegation(GrDProxyDlgeeOptions dlgeeOpt) {

        //        this.m_dlgeeOpt = dlgeeOpt;
        if (logger.isDebugEnabled()) {
            logger.debug("Using DLGEE properties: " + "DN: " + dlgeeOpt.getDlgeeDN()
                    + ". Pass: <hidden>. proxyFile: " + dlgeeOpt.getDlgeeProxyFile() + ". "
                    + "delegationStorageFactory: " + dlgeeOpt.getDlgeeStorageFactory());
        }
        // Get a GrDStorage instance
        try {
            GrDPStorageFactory stgFactory = GrDPX509Util.getGrDPStorageFactory(dlgeeOpt.getDlgeeStorageFactory());

            m_storage = stgFactory.createGrDPStorage(dlgeeOpt);
        } catch (Exception e) {
            logger.error("Failed to get a GrDPStorage instance. Delegation is not active.", e);
            m_bad_config = true;
            return;
        }

        // Set the size of the key, if not defined or smaller than the default, use default.
        m_keySize = dlgeeOpt.getDlgeeKeySize();
        if (m_keySize == -1 || m_keySize < DEFAULT_KEY_SIZE) {
            m_keySize = DEFAULT_KEY_SIZE;
        }
    }

    /**
     * Generates a new proxy certificate proxy request based on the client DN and voms attributes in SecurityContext.
     * Also checks if the request with given (or generated if not given) id already exists.
     * 
     * @param inDelegationID The delegation id used.
     * @return The generated Proxy request in PEM encoding.
     * @throws DelegationException Thrown in case of failures.
     */
    public String getProxyReq(String inDelegationID) throws DelegationException {
        logger.debug("Processing getProxyReq.");
        String delegationID = inDelegationID;

        GrDPStorageElement elem = null;

        // Init Security Context
        InitSecurityContext.init();

        // Get security context
        SecurityContext sc = SecurityContext.getCurrentContext();
        if (sc == null) {
            logger.debug("Failed to get SecurityContext.");
            throw new DelegationException("Failed to get client security information.");
        }

        // Check if a bad configuration was detected on launch (and fail if
        // true)
        if (m_bad_config) {
            logger.error("Service is misconfigured. Stopping execution.");
            throw new DelegationException("Service is misconfigured.");
        }

        // Get client DN
        DN clientDN = sc.getClientDN();
        if (clientDN == null) {
            logger.error("Failed to get client DN.");
            throw new DelegationException("Failed to get client DN.");
        }
        logger.debug(
                "Got proxy delegation request from client '" + clientDN.getRFCDN() + "', getting VOMS attributes.");

        // Get the VOMS attributes
        String[] vomsAttributes = GrDPX509Util.getVOMSAttributes(sc);

        logger.debug("Got VOMS attributes.");

        // Generate a delegation id from the client DN and VOMS attributes
        if (delegationID == null || delegationID.length() == 0) {
            delegationID = GrDPX509Util.genDlgID(clientDN.getRFCDN(), vomsAttributes);
        }

        logger.debug("Delegation id is: " + delegationID);

        // Search for an existing entry in storage for this delegation ID (null
        // if non existing)
        try {
            elem = m_storage.findGrDPStorageElement(delegationID, clientDN.getRFCDN());
        } catch (GrDPStorageException e) {
            logger.error("Failure on storage interaction.", e);
            throw new DelegationException("Internal failure.");
        }

        // Throw error in case there was already a credential with the given id
        if (elem != null) {
            String vomsAttrsStr = GrDPX509Util.toStringVOMSAttrs(vomsAttributes);
            logger.debug("Delegation ID '" + delegationID + "' already exists" + " for client (DN='"
                    + clientDN.getRFCDN() + "; VOMS ATTRS='" + vomsAttrsStr + "'). Call renewProxyReq.");
            throw new DelegationException(
                    "Delegation ID '" + delegationID + "' already exists" + " for client (DN='"
                            + clientDN.getRFCDN() + "; VOMS ATTRS='" + vomsAttrsStr + "'). Call renewProxyReq.");
        }

        // Create and store the new certificate request
        return createAndStoreCertificateRequest(sc.getClientCert(), delegationID, clientDN, vomsAttributes);
    }

    /**
     * Generates a new proxy request object based on the DN and voms attributes in the security context.
     * Also checks if the request with given (or generated if not given) id already exists.
     * 
     * @param inDelegationID the delegation id to use, will be generated if not given.
     * @return The newProxyReq object.
     * @throws DelegationException thrown in case of failure.
     */
    public NewProxyReq getNewProxyReq(String inDelegationID) throws DelegationException {
        logger.debug("Processing getNewProxyReq.");

        String delegationID = inDelegationID;

        GrDPStorageElement elem = null;

        // Init Security Context
        InitSecurityContext.init();

        // Get security context
        SecurityContext sc = SecurityContext.getCurrentContext();
        if (sc == null) {
            logger.debug("Failed to get SecurityContext.");
            throw new DelegationException("Failed to get client security information.");
        }

        // Check if a bad configuration was detected on launch (and fail if
        // true)
        if (m_bad_config) {
            logger.error("Service is misconfigured. Stopping execution.");
            throw new DelegationException("Service is misconfigured.");
        }

        // Get client DN
        DN clientDN = sc.getClientDN();
        if (clientDN == null) {
            logger.error("Failed to get client DN.");
            throw new DelegationException("Failed to get client DN.");
        }
        logger.debug("Got proxy delegation request from client '" + clientDN + "'");

        // Get the VOMS attributes
        String[] vomsAttributes = GrDPX509Util.getVOMSAttributes(sc);

        // Generate a delegation id from the client DN and VOMS attributes
        if (delegationID == null || delegationID.length() == 0) {
            delegationID = GrDPX509Util.genDlgID(clientDN.getRFCDN(), vomsAttributes);
        }

        // Search for an existing entry in storage for this delegation ID (null
        // if non existing)
        try {
            elem = m_storage.findGrDPStorageElement(delegationID, clientDN.getRFCDN());
        } catch (GrDPStorageException e) {
            logger.error("Failure on storage interaction.", e);
            throw new DelegationException("Internal failure.");
        }

        // Throw error in case there was already a credential with the given id
        if (elem != null) {
            String vomsAttrsStr = GrDPX509Util.toStringVOMSAttrs(vomsAttributes);
            String errorMsg = "Delegation ID '" + delegationID + "' already exists" + " for client (DN='" + clientDN
                    + "; VOMS ATTRS='" + vomsAttrsStr + "'). Call renewProxyReq.";

            logger.debug(errorMsg);
            throw new DelegationException(errorMsg);
        }

        // Create and store the new certificate request
        String certRequest = createAndStoreCertificateRequest(sc.getClientCert(), delegationID, clientDN,
                vomsAttributes);

        // Create and return the proxy request object
        NewProxyReq newProxyReq = new NewProxyReq();
        newProxyReq.setDelegationID(delegationID);
        newProxyReq.setProxyRequest(certRequest);

        return newProxyReq;
    }

    /**
     * Generates a new delegation request for the existing delegation with the given (or generated) delegation. 
     * 
     * @param inDelegationID The delegation id to use, will be genarated if not given.
     * @return The delegation request in PEM format.
     * @throws DelegationException Thrown in case of failure.
     */
    public String renewProxyReq(String inDelegationID) throws DelegationException {
        logger.debug("Processing renewProxyReq.");

        String delegationID = inDelegationID;

        GrDPStorageElement elem = null;

        // Init Security Context
        InitSecurityContext.init();

        // Get security context
        SecurityContext sc = SecurityContext.getCurrentContext();
        if (sc == null) {
            logger.debug("Failed to get SecurityContext.");
            throw new DelegationException("Failed to get client security information.");
        }

        // Check if a bad configuration was detected on launch (and fail if
        // true)
        if (m_bad_config) {
            logger.error("Service is misconfigured. Stopping execution.");
            throw new DelegationException("Service is misconfigured.");
        }

        // Get client DN
        DN clientDN = sc.getClientDN();
        if (clientDN == null) {
            logger.error("Failed to get client DN.");
            throw new DelegationException("Failed to get client DN.");
        }
        logger.debug("Got proxy delegation request from client '" + clientDN + "'");

        // Get the VOMS attributes
        String[] vomsAttributes = GrDPX509Util.getVOMSAttributes(sc);

        // Generate a delegation id from the client DN and VOMS attributes
        if (delegationID == null || delegationID.length() == 0) {
            delegationID = GrDPX509Util.genDlgID(clientDN.getRFCDN(), vomsAttributes);
        }

        // Search for an existing entry in storage for this delegation ID (null
        // if non existing)
        try {
            elem = m_storage.findGrDPStorageElement(delegationID, clientDN.getRFCDN());
        } catch (GrDPStorageException e) {
            logger.error("Failure on storage interaction.", e);
            throw new DelegationException("Internal failure.");
        }

        // Check that the DLG ID had a corresponding delegated credential
        if (elem == null) {
            logger.debug("Failed to renew credential as there was no delegation with ID '" + delegationID
                    + "' for client '" + clientDN + "'");
        }

        // Create and store the new certificate request
        return createAndStoreCertificateRequest(sc.getClientCert(), delegationID, clientDN, vomsAttributes);
    }

    /**
     * @param inDelegationID
     * @param proxy
     * @throws DelegationException
     */
    public void putProxy(String inDelegationID, String proxy) throws DelegationException {
        logger.info("Processing putProxy.");

        String delegationID = inDelegationID;

        // Init Security Context
        InitSecurityContext.init();

        // Get security context
        SecurityContext sc = SecurityContext.getCurrentContext();
        if (sc == null) {
            logger.debug("Failed to get SecurityContext.");
            throw new DelegationException("Failed to get client security information.");
        }

        // Check if a bad configuration was detected on launch (and fail if
        // true)
        if (m_bad_config) {
            logger.error("Service is misconfigured. Stopping execution.");
            throw new DelegationException("Service is misconfigured.");
        }

        // Check for a null proxy
        if (proxy == null) {
            logger.error("Failed to putProxy as proxy was null.");
            throw new DelegationException("No proxy was given.");
        }

        // Load given proxy
        X509Certificate[] proxyCertChain;
        try {
            proxyCertChain = s_reader.readCertChain(new BufferedInputStream(new StringBufferInputStream(proxy)))
                    .toArray(new X509Certificate[] {});
        } catch (IOException e2) {
            logger.error("Failed to load proxy certificate chain: " + e2.getMessage());
            throw new DelegationException("Failed to load proxy certificate chain: " + e2.getMessage());
        }
        if (proxyCertChain == null || proxyCertChain.length == 0) {
            logger.error("Failed to load proxy certificate chain - chain was null or size 0.");
            throw new DelegationException("Failed to load proxy certificate chain.");
        }
        logger.debug("Given proxy certificate loaded successfully.");

        // check if the chain is within it's validity period.
        for (int i = 0; i < proxyCertChain.length; i++) {
            // Check if the proxy is currently valid
            try {
                proxyCertChain[i].checkValidity();
            } catch (CertificateExpiredException e) {
                throw new DelegationException(
                        "Failed proxy validation - it expired on: " + proxyCertChain[0].getNotAfter());
            } catch (CertificateNotYetValidException e) {
                throw new DelegationException(
                        "Failed proxy validation - it will be valid from: " + proxyCertChain[0].getNotBefore());
            }
        }

        // Get the given proxy information
        String proxySubjectDN = DNHandler.getSubject(proxyCertChain[0]).getRFCDN();
        String proxyIssuerDN = DNHandler.getIssuer(proxyCertChain[0]).getRFCDN();
        if (logger.isDebugEnabled()) {
            logger.debug("Proxy Subject DN: " + proxySubjectDN);
            logger.debug("Proxy Issuer DN: " + proxyIssuerDN);
            logger.debug("Proxy Public key:" + proxyCertChain[0].getPublicKey());
            logger.debug("chain length is: " + proxyCertChain.length);
            logger.debug("last cert is:" + proxyCertChain[proxyCertChain.length - 1]);

            for (int n = 0; n < proxyCertChain.length; n++) {
                logger.debug("cert [" + n + "] is from " + DNHandler.getSubject(proxyCertChain[n]).getRFCDN());
            }
        }

        if (proxySubjectDN == null || proxyIssuerDN == null) {
            logger.error("Failed to get DN (subject or issuer) out of proxy. It came null");
            throw new DelegationException("Failed to get DN (subject or issuer) out of proxy.");
        }
        String clientDN = null;

        // Get client information from security context
        try {
            clientDN = CertUtil.getUserDN(sc.getClientCertChain()).getRFCDN();
        } catch (IOException e) {
            throw new DelegationException("No user certificate found in the proxy chain: " + e.getMessage());
        }
        if (clientDN == null) {
            logger.error("Failed to get client DN. It came null");
            throw new DelegationException("Failed to get client DN.");
        }
        logger.debug("Client DN: " + clientDN);

        // Get the client VOMS attributes (for the DLG ID)
        String[] clientVOMSAttributes = GrDPX509Util.getVOMSAttributes(sc);

        // Get a delegation ID for the given proxy (or take the specified one if
        // given)
        // TODO: Should the dlg id here be generated from the client or the proxy info?
        // Also, should the client and proxy VOMS attributes be checked for a match?
        if (delegationID == null || delegationID.length() == 0) {
            delegationID = GrDPX509Util.genDlgID(clientDN, clientVOMSAttributes);
        }
        logger.debug("Delegation ID is '" + delegationID + "'");

        // Check that the client is the issuer of the given proxy
        // TODO: more strict check
        if (!proxyIssuerDN.endsWith(clientDN)) {
            String message = "Client '" + clientDN + "' is not issuer of proxy '" + proxyIssuerDN + "'.";
            logger.error(message);
            throw new DelegationException(message);
        }

        String cacheID = delegationID;
        try {
            cacheID = delegationID + '+' + GrDPX509Util.generateSessionID(proxyCertChain[0].getPublicKey());
        } catch (GeneralSecurityException e) {
            logger.error("Error while generating the session ID." + e);
            throw new DelegationException("Failed to generate the session ID.");
        }
        logger.debug("Cache ID (delegation ID + session ID): " + cacheID);

        // Get the cache entry for this delegation ID
        GrDPStorageCacheElement cacheElem = null;
        try {
            cacheElem = m_storage.findGrDPStorageCacheElement(cacheID, clientDN);
        } catch (GrDPStorageException e) {
            logger.error("Failed to get certificate request information from storage.", e);
            throw new DelegationException("Internal failure.");
        }

        // Check if the delegation request existed
        if (cacheElem == null) {
            logger.info("Could not find cache ID '" + cacheID + "' for DN '" + clientDN + "' in cache.");
            throw new DelegationException("Could not find a proper delegation request");
        }
        logger.debug("Got from cache element for cache ID '" + cacheID + "' and DN '" + clientDN + "'");

        // the public key of the cached certificate request has to 
        // match the public key of the proxy certificate, otherwise
        // this is an answer to a different request
        PEMReader pemReader = new PEMReader(new StringReader(cacheElem.getCertificateRequest()));
        PKCS10CertificationRequest req;
        try {
            req = (PKCS10CertificationRequest) pemReader.readObject();
        } catch (IOException e1) {
            logger.error("Could not load the original certificate request from cache.");
            throw new DelegationException(
                    "Could not load the original certificate request from cache: " + e1.getMessage());
        }
        if (req == null) {
            logger.error("Could not load the original certificate request from cache.");
            throw new DelegationException("Could not load the original certificate request from cache.");
        }
        try {
            if (!req.getPublicKey().equals(proxyCertChain[0].getPublicKey())) {
                logger.error("The proxy and the original request's public key do not match.");
                logger.error("Proxy public key: " + proxyCertChain[0].getPublicKey());
                logger.error("Request public key: " + req.getPublicKey());
                throw new DelegationException("The proxy and the original request's public key do not match.");
            }
        } catch (GeneralSecurityException ge) {
            logger.error("Error while decoding the certificate request: " + ge);
            throw new DelegationException("Error while decoding the certificate request.");
        }

        // Add the private key to the proxy certificate chain and check it was ok
        String completeProxy = getProxyWithPrivateKey(proxyCertChain, cacheElem.getPrivateKey());
        if (completeProxy == null) {
            logger.error("Failed to add private key to the proxy certificate chain.");
            throw new DelegationException("Could not properly process given proxy.");
        }

        // Save the proxy in proxy storage (copying the rest from the info taken
        // from the cache)
        try {
            GrDPStorageElement elem = m_storage.findGrDPStorageElement(delegationID, clientDN);
            if (elem != null) {
                elem.setCertificate(completeProxy);
                elem.setTerminationTime(proxyCertChain[0].getNotAfter());
                m_storage.updateGrDPStorageElement(elem);
            } else {
                elem = new GrDPStorageElement();
                elem.setDelegationID(delegationID);
                elem.setDN(clientDN);
                elem.setVomsAttributes(clientVOMSAttributes);
                elem.setCertificate(completeProxy);
                elem.setTerminationTime(proxyCertChain[0].getNotAfter());
                m_storage.insertGrDPStorageElement(elem);
            }
        } catch (GrDPStorageException e) {
            logger.error("Failed to put certificate request in storage.", e);
            throw new DelegationException("Internal failure: " + e.getMessage());
        }
        logger.debug("Delegation finished successfully.");

        // Remove the credential from storage cache
        try {
            m_storage.deleteGrDPStorageCacheElement(cacheID, clientDN);
        } catch (GrDPStorageException e) {
            logger.warn("Failed to remove credential from storage cache.");
        }

    }

    public void destroy(String inDelegationID) throws DelegationException {
        logger.debug("Processing destroy.");

        String delegationID = inDelegationID;

        GrDPStorageElement elem = null;

        // Check if a bad configuration was detected on launch (and fail if
        // true)
        if (m_bad_config) {
            logger.error("Service is misconfigured. Stopping execution.");
            throw new DelegationException("Service is misconfigured.");
        }

        // Init Security Context
        InitSecurityContext.init();

        // Get security context
        SecurityContext sc = SecurityContext.getCurrentContext();
        if (sc == null) {
            logger.debug("Failed to get SecurityContext.");
            throw new DelegationException("Failed to get client security information.");
        }

        String clientDN = null;

        // Get client information
        try {
            clientDN = CertUtil.getUserDN(sc.getClientCertChain()).getRFCDN();
        } catch (IOException e) {
            throw new DelegationException("No user certificate found in the proxy chain: " + e.getMessage());
        }
        if (clientDN == null) {
            logger.error("Failed to get client DN. It came null");
            throw new DelegationException("Failed to get client DN.");
        }
        logger.debug("Got destroy request for delegation id '" + delegationID + "' from client '" + clientDN + "'");

        // Get the client's VOMS attributes
        String[] vomsAttributes = GrDPX509Util.getVOMSAttributes(sc);

        // Generate a delegation id from the client DN and VOMS attributes
        if (delegationID == null || delegationID.length() == 0) {
            delegationID = GrDPX509Util.genDlgID(clientDN, vomsAttributes);
        }

        // Search for an existing entry in storage for this delegation ID (null
        // if non existing)
        try {
            elem = m_storage.findGrDPStorageElement(delegationID, clientDN);
        } catch (GrDPStorageException e) {
            logger.error("Failure on storage interaction. Exception: ", e);
            throw new DelegationException("Internal failure.");
        }

        // Throw exception if non-existing
        if (elem == null) {
            logger.debug("Failed to find delegation ID '" + delegationID + "' for client '" + clientDN
                    + "' in storage.");
            throw new DelegationException("Failed to find delegation ID '" + delegationID + "' in storage.");
        }

        // Remove the credential from storage
        try {
            m_storage.deleteGrDPStorageElement(delegationID, clientDN);
        } catch (GrDPStorageException e) {
            logger.error("Inconsistency needs manual intervention. Delegation ID '" + delegationID + " of client '"
                    + clientDN + "' was found, " + "but could not be removed from storage.");
            throw new DelegationException("Failed to destroy delegated credential.");
        }

        logger.debug("Delegated credential destroyed.");
    }

    public Calendar getTerminationTime(String inDelegationID) throws DelegationException {
        logger.debug("Processing getTerminationTime.");

        String delegationID = inDelegationID;

        GrDPStorageElement elem = null;

        // Check if a bad configuration was detected on launch (and fail if
        // true)
        if (m_bad_config) {
            logger.error("Service is misconfigured. Stopping execution.");
            throw new DelegationException("Service is misconfigured.");
        }

        // Init Security Context
        InitSecurityContext.init();

        // Get security context
        SecurityContext sc = SecurityContext.getCurrentContext();
        if (sc == null) {
            logger.debug("Failed to get SecurityContext.");
            throw new DelegationException("Failed to get client security information.");
        }

        String clientDN = null;

        // Get client information
        try {
            clientDN = CertUtil.getUserDN(sc.getClientCertChain()).getRFCDN();
        } catch (IOException e) {
            throw new DelegationException("No user certificate found in the proxy chain: " + e.getMessage());
        }
        if (clientDN == null) {
            logger.error("Failed to get client DN. It came null");
            throw new DelegationException("Failed to get client DN.");
        }
        logger.debug("Got getTerminationTime request for delegation id '" + delegationID + "' from client '"
                + clientDN + "'");

        // Get the client's VOMS attributes
        String[] vomsAttributes = GrDPX509Util.getVOMSAttributes(sc);

        // Generate a delegation id from the client DN and VOMS attributes
        if (delegationID == null || delegationID.length() == 0) {
            delegationID = GrDPX509Util.genDlgID(clientDN, vomsAttributes);
        }

        // Search for an existing entry in storage for this delegation ID (null
        // if non existing)
        try {
            elem = m_storage.findGrDPStorageElement(delegationID, clientDN);
        } catch (GrDPStorageException e) {
            logger.error("Failure on storage interaction. Exception: ", e);
            throw new DelegationException("Internal failure.");
        }

        // Throw exception if non-existing
        if (elem == null) {
            logger.debug("Failed to find delegation ID '" + delegationID + "' for client '" + clientDN
                    + "' in storage.");
            throw new DelegationException("Failed to find delegation ID '" + delegationID + "' in storage.");
        }

        // Build a calendar object with the proper time
        Calendar cal = Calendar.getInstance();
        cal.setTime(elem.getTerminationTime());

        return cal;
    }

    /**
     * Creates a new certificate request and stores it in the storage cache
     * area.
     * 
     * @param dlgID The delegation ID of the new delegation
     * @param clientDN The DN of the owner of the delegated credential
     * @param vomsAttributes The list of VOMS attributes in the delegated
     *        credential
     * @return The certificate request for the new delegated credential
     * @throws DelegationException Failed to create or store the new
     *         credential request
     */
    private String createAndStoreCertificateRequest(X509Certificate parentCert, String dlgID, DN clientDN,
            String[] vomsAttributes) throws DelegationException {

        // Get a random KeyPair
        KeyPair keyPair = GrDPX509Util.getKeyPair(m_keySize);

        String privateKey = PrivateKeyReader.getPEM(keyPair.getPrivate());
        logger.debug("KeyPair generation was successfull.");
        logger.debug("Public key is: " + keyPair.getPublic());

        // Generate the certificate request
        String certRequest = null;
        try {
            certRequest = GrDPX509Util.createCertificateRequest(parentCert,
                    GrDPConstants.DEFAULT_SIGNATURE_ALGORITHM, keyPair);
        } catch (GeneralSecurityException e) {
            logger.error("Error while generating the certificate request." + e);
            throw new DelegationException("Failed to generate a certificate request.");
        }
        logger.debug("Certificate request generation was successfull.");

        String cacheID = null;
        try {
            cacheID = dlgID + '+' + GrDPX509Util.generateSessionID(keyPair.getPublic());
        } catch (GeneralSecurityException e) {
            logger.error("Error while generating the session ID." + e);
            throw new DelegationException("Failed to generate the session ID.");
        }
        logger.debug("Cache ID (delegation ID + session ID): " + cacheID);

        try {
            // TODO: remove search from cache, as the public key is used as random ID, each transaction is individual
            // and search always fails, no update of request is possible and would give rise to race conditions.

            // Store the certificate request in cache
            GrDPStorageCacheElement cacheElem = m_storage.findGrDPStorageCacheElement(cacheID, clientDN.getRFCDN());
            if (cacheElem != null) {
                cacheElem.setCertificateRequest(certRequest);
                cacheElem.setPrivateKey(privateKey);
                cacheElem.setVomsAttributes(vomsAttributes);
                m_storage.updateGrDPStorageCacheElement(cacheElem);
            } else {
                cacheElem = new GrDPStorageCacheElement();
                cacheElem.setDelegationID(cacheID);
                cacheElem.setDN(clientDN.getRFCDN());
                cacheElem.setVomsAttributes(vomsAttributes);
                cacheElem.setCertificateRequest(certRequest);
                cacheElem.setPrivateKey(privateKey);
                m_storage.insertGrDPStorageCacheElement(cacheElem);
            }
        } catch (GrDPStorageException e) {
            logger.error("Failed to put certificate request in storage.", e);
            throw new DelegationException("Internal failure.");
        }
        logger.debug("New certificate request successfully stored in cache.");

        return certRequest;
    }

    /**
     * Adds the given private key to the proxy certificate chain.
     *
     * The process is done the globus way - private key added right after
     * the first certificate in the chain.
     * 
     * @param proxyChain The proxy chain to which to add the private key
     * @param privateKey The encoded private key to be added to the proxy certificate chain
     * @return An encoded proxy certificate with the given private key added
     */
    private String getProxyWithPrivateKey(X509Certificate[] proxyChain, String privateKey) {

        // Don't use the CertUtil routines as single writer is faster.
        StringWriter writer = new StringWriter();
        PEMWriter pemWriter = new PEMWriter(writer);

        try {
            pemWriter.writeObject(proxyChain[0]);
            // make sure the writers are in sync.
            pemWriter.flush();
            // write the private key string.
            writer.write(privateKey);
            // make sure the writers are still in sync.
            writer.flush();

            // add rest of the certs.
            for (int i = 1; i < proxyChain.length; i++) {
                pemWriter.writeObject(proxyChain[i]);
            }
            pemWriter.flush();
        } catch (IOException e) {
            logger.error("Failed to encode certificate in proxy chain: " + e.getMessage());
            return null;
        }
        return writer.toString();
    }
}