org.globus.gsi.gssapi.GlobusGSSContextImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.globus.gsi.gssapi.GlobusGSSContextImpl.java

Source

/*
 * Copyright 1999-2010 University of Chicago
 *
 * 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.globus.gsi.gssapi;

import org.globus.gsi.util.CertificateUtil;
import org.globus.gsi.util.ProxyCertificateUtil;

import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import org.ietf.jgss.MessageProp;
import org.ietf.jgss.ChannelBinding;

import org.gridforum.jgss.ExtendedGSSContext;
import org.gridforum.jgss.ExtendedGSSCredential;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Date;
import java.util.Calendar;
import java.util.Map;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.KeyPair;
import java.security.GeneralSecurityException;
import java.security.interfaces.RSAPublicKey;
import java.security.interfaces.RSAPrivateKey;

import org.globus.gsi.ProviderLoader;
import org.globus.gsi.stores.ResourceSigningPolicyStore;

import java.security.cert.CertStore;
import java.security.cert.CertificateFactory;
import java.security.KeyStore;

import org.globus.gsi.GSIConstants;
import org.globus.gsi.TrustedCertificates;
import org.globus.gsi.X509Credential;
import org.globus.gsi.util.CertificateLoadUtil;
import org.globus.gsi.bc.BouncyCastleUtil;
import org.globus.gsi.bc.BouncyCastleCertProcessingFactory;
import org.globus.util.I18n;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import org.globus.gsi.jsse.SSLConfigurator;

import org.bouncycastle.jce.provider.X509CertificateObject;

/*
import COM.claymoresystems.ptls.SSLConn;
import COM.claymoresystems.ptls.SSLRecord;
import COM.claymoresystems.ptls.SSLDebug;
import COM.claymoresystems.ptls.SSLCipherSuite;
import COM.claymoresystems.ptls.SSLCipherState;
import COM.claymoresystems.ptls.SSLHandshake;
import COM.claymoresystems.sslg.SSLPolicyInt;
import COM.claymoresystems.sslg.CertVerifyPolicyInt;
import COM.claymoresystems.cert.X509Cert;
import COM.claymoresystems.util.Util;
*/

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.globus.gsi.stores.Stores;

/**
 * Implementation of SSL/GSI mechanism for Java GSS-API. The implementation
 * is based on JSSE (for SSL API) and the
 * <a href="http://www.bouncycastle.org/">BouncyCastle library</a>
 * (for certificate processing API).
 * <BR>
 * The implementation is not designed to be thread-safe.
 */
public class GlobusGSSContextImpl implements ExtendedGSSContext {

    private static Log logger = LogFactory.getLog(GlobusGSSContextImpl.class.getName());

    private static I18n i18n = I18n.getI18n("org.globus.gsi.gssapi.errors",
            GlobusGSSContextImpl.class.getClassLoader());

    static {
        new ProviderLoader();
    }

    /*DEL
        private static Log sslLog =
    LogFactory.getLog(SSLDebug.class.getName());
    */

    /**
     * KeyPair generation with cache of keypairs if configured
        
     */

    private KeyPairCache keyPairCache = KeyPairCache.getKeyPairCache();

    /** Used to distinguish between a token created by
     * <code>wrap</code> with {@link GSSConstants#GSI_BIG
     * GSSConstants.GSI_BIG}
     * QoP and a regular token created by <code>wrap</code>. */
    public static final int GSI_WRAP = 26;
    /** SSL3_RT_GSSAPI_OPENSSL */

    private static final int GSI_SEQUENCE_SIZE = 8;

    private static final int GSI_MESSAGE_DIGEST_PADDING = 12;

    private static final String[] ENABLED_PROTOCOLS = { "TLSv1", "SSLv3" };
    // TODO: Delete this once GRAM server is fixed and we no longer
    //       would be talking to old GRAM servers.
    private static final String[] GRAM_PROTOCOLS = { "SSLv3" };

    /*DEL
        private static final short [] NO_ENCRYPTION = {SSLPolicyInt.TLS_RSA_WITH_NULL_MD5};
    */
    private static final String[] NO_ENCRYPTION = { "SSL_RSA_WITH_NULL_SHA", "SSL_RSA_WITH_NULL_MD5" };

    // TODO: Delete these once GRAM server is fixed and we no longer
    //       would be talking to old GRAM servers.
    private static final String[] GRAM_ENCRYPTION_CIPHER_SUITES = { "SSL_RSA_WITH_3DES_EDE_CBC_SHA" };
    private static final String[] GRAM_NO_ENCRYPTION_CIPHER_SUITES = { "SSL_RSA_WITH_NULL_SHA" };

    private static final byte[] DELEGATION_TOKEN = new byte[] { GSIConstants.DELEGATION_CHAR };

    private static final int UNDEFINED = 0, INITIATE = 1, ACCEPT = 2;

    /** Handshake state */
    protected int state = HANDSHAKE;

    /* handshake states */
    private static final int HANDSHAKE = 0, CLIENT_START_DEL = 2, CLIENT_END_DEL = 3, SERVER_START_DEL = 4,
            SERVER_END_DEL = 5;

    /** Delegation state */
    protected int delegationState = DELEGATION_START;

    /* delegation states */
    private static final int DELEGATION_START = 0, DELEGATION_SIGN_CERT = 1, DELEGATION_COMPLETE_CRED = 2;

    /** Credential delegated using delegation API */
    protected ExtendedGSSCredential delegatedCred;

    /** Delegation finished indicator */
    protected boolean delegationFinished = false;

    // gss context state variables
    protected boolean credentialDelegation = false;
    protected boolean anonymity = false;
    protected boolean encryption = true;
    protected boolean established = false;

    /** The name of the context initiator */
    protected GSSName sourceName = null;

    /** The name of the context acceptor */
    protected GSSName targetName = null;

    /** Context role */
    protected int role = UNDEFINED;

    /** Credential delegated during context establishment */
    protected ExtendedGSSCredential delegCred;

    // these can be set via setOption
    /*DEL
        protected Integer delegationType = GSIConstants.DELEGATION_TYPE_LIMITED;
    */
    protected GSIConstants.DelegationType delegationType = GSIConstants.DelegationType.LIMITED;
    protected Integer gssMode = GSIConstants.MODE_GSI;
    protected Boolean checkContextExpiration = Boolean.FALSE;
    protected Boolean rejectLimitedProxy = Boolean.FALSE;
    protected Boolean requireClientAuth = Boolean.TRUE;
    protected Boolean acceptNoClientCerts = Boolean.FALSE;
    protected Boolean requireAuthzWithDelegation = Boolean.TRUE;
    protected Boolean forceSSLv3AndConstrainCipherSuitesForGram = Boolean.FALSE;

    // *** implementation-specific variables ***

    /** Credential of this context. Might be anonymous */
    protected GlobusGSSCredentialImpl ctxCred;

    /** Expected target name. Used for authorization in initiator */
    protected GSSName expectedTargetName = null;

    /** Context expiration date. */
    protected Date goodUntil = null;

    protected SSLConfigurator sslConfigurator = null;
    protected SSLContext sslContext = null;
    protected SSLEngine sslEngine = null;

    /*DEL
        protected SSLConn conn;
    */
    protected boolean conn = false;
    /*DEL
        protected PureTLSContext context;
        protected SSLPolicyInt policy;
        protected TokenInputStream in;
        protected ByteArrayOutputStream out;
    */
    private byte[] savedInBytes = null;
    private ByteBuffer outByteBuff = null;
    protected BouncyCastleCertProcessingFactory certFactory;

    /** Used during delegation */
    protected KeyPair keyPair;

    protected TrustedCertificates tc;

    protected Map proxyPolicyHandlers;

    /** Limited peer credentials */
    protected Boolean peerLimited = null;

    private String[] bannedCiphers = new String[0];

    /**
     * @param target expected target name. Can be null.
     * @param cred credential. Cannot be null. Might be anonymous.
     */
    public GlobusGSSContextImpl(GSSName target, GlobusGSSCredentialImpl cred) throws GSSException {

        if (cred == null) {
            throw new GSSException(GSSException.NO_CRED);
        }

        this.expectedTargetName = target;
        this.ctxCred = cred;

        /*DEL
                this.context = new PureTLSContext();
        */
        try {

            this.sslConfigurator = new SSLConfigurator();

            // Need to set this so we are able to communicate properly with
            // GT4.0.8 servers that use only SSLv3 (no TLSv1). Thanks to
            // Jon Siwek for pointing this and the following link out:
            // http://java.sun.com/j2se/1.4.2/relnotes.html#security
            if (System.getProperty("com.sun.net.ssl.rsaPreMasterSecretFix") == null)
                System.setProperty("com.sun.net.ssl.rsaPreMasterSecretFix", "true");

            // WARNING WARNING:
            // The new jglobus2-based srm-client is not compatible with old bestman2
            // servers UNLESS we change this setting.
            //
            // The protection we are turning off helps against the BEAST attack.
            // When enabled, it will insert empty TLS application records into the
            // stream.  However, the old server will deadlock on the extra records.
            //
            // To our knowledge, the BEAST attack is not applicable to this client as
            // we don't have any concurrent insecure connections.  Regardless, we ought
            // to remove this as soon as we can drop support for the old servers.
            //
            // -BB.  Sept 24, 2012.
            //
            System.setProperty("jsse.enableCBCProtection", "false");

        } catch (Exception e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }

        /*DEL
                CertVerifyPolicyInt certPolicy = PureTLSUtil.getDefaultCertVerifyPolicy();
            
                this.policy = new SSLPolicyInt();
                this.policy.negotiateTLS(false);
                this.policy.waitOnClose(false);
                this.policy.setCertVerifyPolicy(certPolicy);
                this.context.setPolicy(policy);
            
           // TODO
                setSSLDebugging();
        */
    }

    /*DEL
        private void setSSLDebugging() {
    if (sslLog.isTraceEnabled()) {
        SSLDebug.setDebug( 0xffff );
    } else if (sslLog.isDebugEnabled()) {
        SSLDebug.setDebug( SSLDebug.DEBUG_CERT );
    }
        }
    */

    /*
     * If the result indicates that we have outstanding tasks to do,
     * go ahead and run them in this thread.
     */
    private void runDelegatedTasks(SSLEngine engine) throws Exception {

        Runnable runnable;
        while ((runnable = engine.getDelegatedTask()) != null) {
            logger.debug("\trunning delegated task...");
            runnable.run();
        }
        SSLEngineResult.HandshakeStatus hsStatus = engine.getHandshakeStatus();
        if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
            throw new Exception("handshake shouldn't need additional tasks");
        }
        logger.debug("\tnew HandshakeStatus: " + hsStatus);
    }

    private X509Certificate bcConvert(X509Certificate cert) throws GSSException {
        if (!(cert instanceof X509CertificateObject)) {
            ByteArrayInputStream inputStream = null;
            try {
                inputStream = new ByteArrayInputStream(cert.getEncoded());
                return CertificateLoadUtil.loadCertificate(inputStream);
            } catch (Exception e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (Exception e) {
                        logger.warn("Unable to close streamreader.");
                    }
                }
            }
        } else {
            return cert;
        }
    }

    /**
     * This function drives the accepting side of the context establishment
     * process. It is expected to be called in tandem with the
     * {@link #initSecContext(byte[], int, int) initSecContext} function.
     * <BR>
     * The behavior of context establishment process can be modified by
     * {@link GSSConstants#GSS_MODE GSSConstants.GSS_MODE}
     * and {@link GSSConstants#REJECT_LIMITED_PROXY
     * GSSConstants.REJECT_LIMITED_PROXY} context options. If the
     * {@link GSSConstants#GSS_MODE GSSConstants.GSS_MODE}
     * option is set to
     * {@link GSIConstants#MODE_SSL GSIConstants.MODE_SSL}
     * the context establishment process will be compatible with regular SSL
     * (no credential delegation support). If the option is set to
     * {@link GSIConstants#MODE_GSI GSIConstants.MODE_GSI}
     * credential delegation during context establishment process will be accepted.
     * If the {@link GSSConstants#REJECT_LIMITED_PROXY
     * GSSConstants.REJECT_LIMITED_PROXY} option is enabled, a peer
     * presenting limited proxy credential will be automatically
     * rejected and the context establishment process will be aborted.
     *
     * @return a byte[] containing the token to be sent to the peer.
     *         null indicates that no token is generated (needs more data)
     */
    public byte[] acceptSecContext(byte[] inBuff, int off, int len) throws GSSException {
        logger.debug("enter acceptSecContext");

        if (!this.conn) {
            this.role = ACCEPT;

            logger.debug("enter initializing in acceptSecContext");

            if (this.ctxCred.getName().isAnonymous()) {
                throw new GlobusGSSException(GSSException.DEFECTIVE_CREDENTIAL, GlobusGSSException.UNKNOWN,
                        "acceptCtx00");
            }

            if (this.ctxCred.getUsage() != GSSCredential.ACCEPT_ONLY
                    && this.ctxCred.getUsage() != GSSCredential.INITIATE_AND_ACCEPT) {
                throw new GlobusGSSException(GSSException.DEFECTIVE_CREDENTIAL, GlobusGSSException.UNKNOWN,
                        "badCredUsage");
            }

            setCredential();

            try {
                init(this.role);
            } catch (SSLException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            }

            this.conn = true;
            logger.debug("done initializing in acceptSecContext");
        }

        /*DEL
                this.out.reset();
                this.in.putToken(inBuff, off, len);
        */
        this.outByteBuff.clear();
        ByteBuffer inByteBuff;
        if (savedInBytes != null) {
            if (len > 0) {
                byte[] allInBytes = new byte[savedInBytes.length + len];
                logger.debug("ALLOCATED for allInBytes " + savedInBytes.length + " + " + len + " bytes\n");
                System.arraycopy(savedInBytes, 0, allInBytes, 0, savedInBytes.length);
                System.arraycopy(inBuff, off, allInBytes, savedInBytes.length, len);
                inByteBuff = ByteBuffer.wrap(allInBytes, 0, allInBytes.length);
            } else {
                inByteBuff = ByteBuffer.wrap(savedInBytes, 0, savedInBytes.length);
            }
            savedInBytes = null;
        } else {
            inByteBuff = ByteBuffer.wrap(inBuff, off, len);
        }

        switch (state) {

        case HANDSHAKE:

            try {
                logger.debug("STATUS BEFORE: " + this.sslEngine.getHandshakeStatus().toString());
                SSLEngineResult.HandshakeStatus handshake_status = sslEngine.getHandshakeStatus();

                if (handshake_status == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
                    // return null;
                    throw new Exception("GSSAPI in HANDSHAKE state but " + "SSLEngine in NOT_HANDSHAKING state!");
                } else {
                    outByteBuff = this.sslProcessHandshake(inByteBuff, outByteBuff);
                }

                logger.debug("STATUS AFTER: " + this.sslEngine.getHandshakeStatus().toString());

                outByteBuff.flip();

                /*DEL
                                if (this.conn.getHandshake().finishedP()) {
                */
                if (this.sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
                    // the wrap/unwrap above has resulted in handshaking
                    // being complete on our end.

                    logger.debug("acceptSecContext handshake finished");
                    handshakeFinished();

                    // acceptor
                    for (X509Certificate cert : this.ctxCred.getCertificateChain()) {
                        setGoodUntil(cert.getNotAfter());
                    }
                    this.targetName = this.ctxCred.getName();

                    // initiator - peer
                    /*DEL
                                        Vector chain = this.conn.getCertificateChain();
                    */
                    Certificate[] chain;
                    try {
                        chain = this.sslEngine.getSession().getPeerCertificates();
                    } catch (SSLPeerUnverifiedException e) {
                        chain = null;
                    }
                    if (chain == null || chain.length == 0) {
                        this.sourceName = new GlobusGSSName();
                        this.anonymity = true;
                    } else {
                        /*DEL
                                                X509Cert crt = (X509Cert)chain.elementAt(chain.size()-1);
                                                setGoodUntil(crt.getValidityNotAfter());
                            
                                                String identity = verifyChain(chain);
                        */
                        for (X509Certificate cert : (X509Certificate[]) chain) {
                            setGoodUntil(cert.getNotAfter());
                        }

                        String identity = BouncyCastleUtil.getIdentity(
                                bcConvert(BouncyCastleUtil.getIdentityCertificate((X509Certificate[]) chain)));
                        this.sourceName = new GlobusGSSName(CertificateUtil.toGlobusID(identity, false));
                        this.peerLimited = Boolean.valueOf(ProxyCertificateUtil
                                .isLimitedProxy(BouncyCastleUtil.getCertificateType((X509Certificate) chain[0])));

                        logger.debug("Peer Identity is: " + identity + " Target name is: " + this.targetName
                                + " Limited Proxy: " + this.peerLimited.toString());

                        this.anonymity = false;
                    }

                    if (this.gssMode == GSIConstants.MODE_GSI) {
                        this.state = SERVER_START_DEL;
                    } else {
                        setDone();
                    }
                }
            } catch (IOException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            } catch (Exception e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            }

            break;

        case SERVER_START_DEL:

            try {
                if (inByteBuff.remaining() <= 0) {
                    return null;
                }

                /*DEL
                                int delChar = this.conn.getInStream().read();
                */
                outByteBuff = sslDataUnwrap(inByteBuff, outByteBuff);
                outByteBuff.flip();
                byte[] delChar = new byte[outByteBuff.remaining()];
                outByteBuff.get(delChar, 0, delChar.length);
                /*DEL
                                if (delChar != GSIConstants.DELEGATION_CHAR) {
                */
                if (!Arrays.equals(delChar, DELEGATION_TOKEN)) {
                    setDone();
                    break;
                }

                /*DEL
                                Vector chain = this.conn.getCertificateChain();
                */
                Certificate[] chain;
                try {
                    chain = this.sslEngine.getSession().getPeerCertificates();
                } catch (SSLPeerUnverifiedException e) {
                    chain = null;
                }
                if (chain == null || chain.length == 0) {
                    throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.DELEGATION_ERROR,
                            "noClientCert");
                }

                X509Certificate tmpCert = (X509Certificate) chain[0];
                /*DEL
                PureTLSUtil.convertCert((X509Cert)chain.lastElement());
                */
                byte[] req = generateCertRequest(tmpCert);
                /*DEL
                                this.conn.getOutStream().write(req, 0, req.length);
                */
                inByteBuff = ByteBuffer.wrap(req, 0, req.length);
                outByteBuff.clear();
                outByteBuff = sslDataWrap(inByteBuff, outByteBuff);
                outByteBuff.flip();

            } catch (GeneralSecurityException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            }

            this.state = SERVER_END_DEL;
            break;

        case SERVER_END_DEL:

            try {
                if (inByteBuff.remaining() <= 0) {
                    return null;
                }

                /*DEL
                                X509Certificate certificate = CertUtil.loadCertificate(this.conn.getInStream());
                */
                outByteBuff = sslDataUnwrap(inByteBuff, outByteBuff);
                outByteBuff.flip();
                if (!outByteBuff.hasRemaining())
                    break;
                byte[] buf = new byte[outByteBuff.remaining()];
                outByteBuff.get(buf, 0, buf.length);
                ByteArrayInputStream inStream = new ByteArrayInputStream(buf, 0, buf.length);
                CertificateFactory cf = null;
                X509Certificate certificate = null;
                try {
                    cf = CertificateFactory.getInstance("X.509");
                    certificate = (X509Certificate) cf.generateCertificate(inStream);
                } finally {
                    inStream.close();
                }

                if (logger.isTraceEnabled()) {
                    logger.trace("Received delegated cert: " + certificate.toString());
                }

                verifyDelegatedCert(certificate);

                /*DEL
                                Vector chain = this.conn.getCertificateChain();
                */
                Certificate[] chain = this.sslEngine.getSession().getPeerCertificates();
                int chainLen = chain.length;
                X509Certificate[] newChain = new X509Certificate[chainLen + 1];
                newChain[0] = bcConvert((X509Certificate) certificate);
                for (int i = 0; i < chainLen; i++) {
                    /*DEL
                                        newChain[i+1] = PureTLSUtil.convertCert((X509Cert)chain.elementAt(chainLen - 1 - i));
                    */
                    newChain[i + 1] = bcConvert((X509Certificate) chain[i]);
                }

                X509Credential proxy = new X509Credential(this.keyPair.getPrivate(), newChain);

                this.delegCred = new GlobusGSSCredentialImpl(proxy, GSSCredential.INITIATE_AND_ACCEPT);

            } catch (GeneralSecurityException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            } catch (IOException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            }
            setDone();
            break;

        default:
            throw new GSSException(GSSException.FAILURE);
        }

        if (inByteBuff.hasRemaining()) {
            // Likely BUFFER_UNDERFLOW; save the
            // inByteBuff bytes here like in the unwrap() case
            logger.debug("Not all data processed; Original: " + len + " Remaining: " + inByteBuff.remaining()
                    + " Handshaking status: " + sslEngine.getHandshakeStatus());
            logger.debug("SAVING unprocessed " + inByteBuff.remaining() + "BYTES\n");
            savedInBytes = new byte[inByteBuff.remaining()];
            inByteBuff.get(savedInBytes, 0, savedInBytes.length);
        }

        logger.debug("exit acceptSecContext");
        /*DEL
                return (this.out.size() > 0) ? this.out.toByteArray() : null;
        */
        if (this.outByteBuff.hasRemaining()) {
            // TODO can we avoid this copy if the ByteBuffer is array based
            // and we return that array, each time allocating a new array
            // for outByteBuff?
            byte[] out = new byte[this.outByteBuff.remaining()];
            this.outByteBuff.get(out, 0, out.length);
            return out;
        } else
            return null;
    }

    // Meant for non-handshake processing
    private ByteBuffer sslDataWrap(ByteBuffer inBBuff, ByteBuffer outBBuff) throws GSSException {
        try {

            if (sslEngine.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
                throw new Exception("SSLEngine handshaking needed! " + "HandshakeStatus: "
                        + sslEngine.getHandshakeStatus().toString());
            }
            int iter = 0;
            do {
                logger.debug("PROCESSING DATA (WRAP) " + ++iter + ": " + inBBuff.remaining());
                SSLEngineResult result = sslEngine.wrap(inBBuff, outBBuff);
                if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
                    runDelegatedTasks(sslEngine);
                    continue;
                }
                if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
                    // just increase it to the size needed.
                    int pktSize = sslEngine.getSession().getPacketBufferSize();
                    ByteBuffer b = ByteBuffer.allocate(pktSize + outBBuff.position());
                    outBBuff.flip();
                    b.put(outBBuff);
                    outBBuff = b;
                    continue;
                } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
                    throw new GlobusGSSException(GSSException.FAILURE, new Exception("Unexpected BUFFER_UNDERFLOW;"
                            + " Handshaking status: " + sslEngine.getHandshakeStatus()));
                }
                if (result.getStatus() != SSLEngineResult.Status.OK) {
                    throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.TOKEN_FAIL,
                            result.getStatus().toString());
                }
            } while (inBBuff.hasRemaining());

            return outBBuff;
        } catch (Exception e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }
    }

    // Not all in inBBuff might be consumed by this method!!!
    private ByteBuffer sslDataUnwrap(ByteBuffer inBBuff, ByteBuffer outBBuff) throws GSSException {
        try {
            int iter = 0;
            if (sslEngine.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
                throw new Exception("SSLEngine handshaking needed! " + "HandshakeStatus: "
                        + sslEngine.getHandshakeStatus().toString());
            }
            do {
                logger.debug("PROCESSING DATA (UNWRAP) " + ++iter + ": " + inBBuff.remaining());
                SSLEngineResult result = sslEngine.unwrap(inBBuff, outBBuff);
                if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
                    runDelegatedTasks(sslEngine);
                    continue;
                }
                if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
                    // increase it to the size needed.
                    int appSize = sslEngine.getSession().getApplicationBufferSize();
                    ByteBuffer b = ByteBuffer.allocate(appSize + outBBuff.position());
                    outBBuff.flip();
                    b.put(outBBuff);
                    outBBuff = b;
                    continue;
                } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
                    // More data needed from peer
                    break;
                } else if (result.getStatus() == SSLEngineResult.Status.CLOSED) {
                    throw new ClosedGSSException();
                }

                if (result.getStatus() != SSLEngineResult.Status.OK) {
                    throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.TOKEN_FAIL,
                            result.getStatus().toString());
                }
            } while (inBBuff.hasRemaining());
            return outBBuff;
        } catch (IllegalArgumentException e) {
            throw new GlobusGSSException(GSSException.DEFECTIVE_TOKEN, e);
        } catch (SSLException e) {
            if (e.toString().endsWith("bad record MAC"))
                throw new GlobusGSSException(GSSException.BAD_MIC, e);
            else if (e.toString().endsWith("ciphertext sanity check failed"))
                throw new GlobusGSSException(GSSException.DEFECTIVE_TOKEN, e);
            else
                throw new GlobusGSSException(GSSException.FAILURE, e);
        } catch (GSSException e) {
            throw e;
        } catch (Exception e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }
    }

    private ByteBuffer sslProcessHandshake(ByteBuffer inBBuff, ByteBuffer outBBuff) throws GSSException {
        // Loopon until we need more from peer or we are done with handshaking.
        try {
            done: do {
                while (sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
                    SSLEngineResult result = sslEngine.wrap(inBBuff, outBBuff);
                    if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
                        runDelegatedTasks(sslEngine);
                        continue;
                    }
                    if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
                        // increase it to the size needed.
                        int pktSize = sslEngine.getSession().getPacketBufferSize();
                        ByteBuffer b = ByteBuffer.allocate(pktSize + outBBuff.position());
                        outBBuff.flip();
                        b.put(outBBuff);
                        outBBuff = b;
                        continue;
                    } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
                        throw new GlobusGSSException(GSSException.FAILURE,
                                new Exception("Unexpected BUFFER_UNDERFLOW;" + " Handshaking status: "
                                        + sslEngine.getHandshakeStatus()));
                    }
                    if (result.getStatus() != SSLEngineResult.Status.OK) {
                        throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.TOKEN_FAIL,
                                result.getStatus().toString());
                    }
                }

                int iter = 0;
                while (sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
                    logger.debug("PROCESSING " + ++iter + ": " + inBBuff.remaining());
                    SSLEngineResult result = sslEngine.unwrap(inBBuff, outBBuff);
                    if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
                        runDelegatedTasks(sslEngine);
                        continue;
                    }
                    if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
                        // increase it to the size needed.
                        int appSize = sslEngine.getSession().getApplicationBufferSize();
                        ByteBuffer b = ByteBuffer.allocate(appSize + outBBuff.position());
                        outBBuff.flip();
                        b.put(outBBuff);
                        outBBuff = b;
                        continue;
                    } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
                        // More data needed from peer
                        // break out of outer loop
                        break done;
                    }
                    if (result.getStatus() != SSLEngineResult.Status.OK) {
                        throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.TOKEN_FAIL,
                                result.getStatus().toString());
                    }
                }
            } while (sslEngine.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING);

            return outBBuff;
        } catch (Exception e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }
    }

    /**
     * This function drives the initiating side of the context establishment
     * process. It is expected to be called in tandem with the
     * {@link #acceptSecContext(byte[], int, int) acceptSecContext} function.
     * <BR>
     * The behavior of context establishment process can be modified by
     * {@link GSSConstants#GSS_MODE GSSConstants.GSS_MODE},
     * {@link GSSConstants#DELEGATION_TYPE GSSConstants.DELEGATION_TYPE}, and
     * {@link GSSConstants#REJECT_LIMITED_PROXY GSSConstants.REJECT_LIMITED_PROXY}
     * context options. If the {@link GSSConstants#GSS_MODE GSSConstants.GSS_MODE}
     * option is set to {@link GSIConstants#MODE_SSL GSIConstants.MODE_SSL}
     * the context establishment process will be compatible with regular SSL
     * (no credential delegation support). If the option is set to
     * {@link GSIConstants#MODE_GSI GSIConstants.GSS_MODE_GSI}
     * credential delegation during context establishment process will performed.
     * The delegation type to be performed can be set using the
     * {@link GSSConstants#DELEGATION_TYPE GSSConstants.DELEGATION_TYPE}
     * context option. If the {@link GSSConstants#REJECT_LIMITED_PROXY
     * GSSConstants.REJECT_LIMITED_PROXY} option is enabled,
     * a peer presenting limited proxy credential will be automatically
     * rejected and the context establishment process will be aborted.
     *
     * @return a byte[] containing the token to be sent to the peer.
     *         null indicates that no token is generated (needs more data).
     */
    public byte[] initSecContext(byte[] inBuff, int off, int len) throws GSSException {
        logger.debug("enter initSecContext");

        if (!this.conn) {
            this.role = INITIATE;

            logger.debug("enter initializing in initSecContext");

            if (this.anonymity || this.ctxCred.getName().isAnonymous()) {
                this.anonymity = true;
            } else {
                this.anonymity = false;

                setCredential();

                if (this.ctxCred.getUsage() != GSSCredential.INITIATE_ONLY
                        && this.ctxCred.getUsage() != GSSCredential.INITIATE_AND_ACCEPT) {
                    throw new GlobusGSSException(GSSException.DEFECTIVE_CREDENTIAL, GlobusGSSException.UNKNOWN,
                            "badCredUsage");
                }
            }

            if (getCredDelegState()) {
                if (this.gssMode == GSIConstants.MODE_SSL) {
                    throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_ARGUMENT,
                            "initCtx00");
                }
                if (this.anonymity) {
                    throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_ARGUMENT,
                            "initCtx01");
                }
            }

            try {
                init(this.role);
            } catch (SSLException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            }

            this.conn = true;
            logger.debug("done initializing in initSecContext");
        }

        // Unless explicitly disabled, check if delegation is
        // requested and expected target is null
        logger.debug("Require authz with delegation: " + this.requireAuthzWithDelegation);
        if (!Boolean.FALSE.equals(this.requireAuthzWithDelegation)) {

            if (this.expectedTargetName == null && getCredDelegState()) {
                throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_ARGUMENT, "initCtx02");
            }
        }

        /*DEL
                this.out.reset();
                this.in.putToken(inBuff, off, len);
        */

        this.outByteBuff.clear();
        ByteBuffer inByteBuff;
        if (savedInBytes != null) {
            if (len > 0) {
                byte[] allInBytes = new byte[savedInBytes.length + len];
                logger.debug("ALLOCATED for allInBytes " + savedInBytes.length + " + " + len + " bytes\n");
                System.arraycopy(savedInBytes, 0, allInBytes, 0, savedInBytes.length);
                System.arraycopy(inBuff, off, allInBytes, savedInBytes.length, len);
                inByteBuff = ByteBuffer.wrap(allInBytes, 0, allInBytes.length);
            } else {
                inByteBuff = ByteBuffer.wrap(savedInBytes, 0, savedInBytes.length);
            }
            savedInBytes = null;
        } else {
            inByteBuff = ByteBuffer.wrap(inBuff, off, len);
        }

        switch (state) {

        case HANDSHAKE:
            try {

                logger.debug("STATUS BEFORE: " + this.sslEngine.getHandshakeStatus().toString());
                SSLEngineResult.HandshakeStatus handshake_status = sslEngine.getHandshakeStatus();

                if (handshake_status == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
                    // return null;
                    throw new Exception("GSSAPI in HANDSHAKE state but " + "SSLEngine in NOT_HANDSHAKING state!");
                } else {
                    outByteBuff = this.sslProcessHandshake(inByteBuff, outByteBuff);
                }

                logger.debug("STATUS AFTER: " + this.sslEngine.getHandshakeStatus().toString());

                outByteBuff.flip();
                /*DEL
                                this.conn.getHandshake().processHandshake();
                                if (this.conn.getHandshake().finishedP()) {
                */
                if (this.sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
                    // the wrap/unwrap above has resulted in handshaking
                    // being complete on our end.
                    logger.debug("initSecContext handshake finished");
                    handshakeFinished();

                    /*DEL
                                        Vector chain = this.conn.getCertificateChain();
                                        X509Cert crt = (X509Cert)chain.elementAt(chain.size()-1);
                                        setGoodUntil(crt.getValidityNotAfter());
                    */
                    Certificate[] chain = this.sslEngine.getSession().getPeerCertificates();
                    if (!(chain instanceof X509Certificate[])) {
                        throw new Exception("Certificate chain not of type X509Certificate");
                    }

                    for (X509Certificate cert : (X509Certificate[]) chain) {
                        setGoodUntil(cert.getNotAfter());
                    }

                    // acceptor - peer

                    /*DEL
                                        String identity = verifyChain(chain);
                    */
                    // chain verification would have already been done by
                    // JSSE

                    String identity = BouncyCastleUtil.getIdentity(
                            bcConvert(BouncyCastleUtil.getIdentityCertificate((X509Certificate[]) chain)));
                    this.targetName = new GlobusGSSName(CertificateUtil.toGlobusID(identity, false));

                    this.peerLimited = Boolean.valueOf(ProxyCertificateUtil
                            .isLimitedProxy(BouncyCastleUtil.getCertificateType((X509Certificate) chain[0])));

                    logger.debug("Peer Identity is: " + identity + " Target name is: " + this.targetName
                            + " Limited Proxy: " + this.peerLimited.toString());

                    // initiator
                    if (this.anonymity) {
                        this.sourceName = new GlobusGSSName();
                    } else {
                        for (X509Certificate cert : this.ctxCred.getCertificateChain()) {
                            setGoodUntil(cert.getNotAfter());
                        }
                        this.sourceName = this.ctxCred.getName();
                    }

                    // mutual authentication test
                    if (this.expectedTargetName != null && !this.expectedTargetName.equals(this.targetName)) {
                        throw new GlobusGSSException(GSSException.UNAUTHORIZED, GlobusGSSException.BAD_NAME,
                                "authFailed00", new Object[] { this.expectedTargetName, this.targetName });
                    }

                    if (this.gssMode == GSIConstants.MODE_GSI) {
                        this.state = CLIENT_START_DEL;
                        // if there is data to return then
                        // break. otherwise we fall through!!!
                        if (this.outByteBuff.remaining() > 0) {
                            break;
                        }
                    } else {
                        setDone();
                        break;
                    }

                } else {
                    break;
                }
            } catch (IOException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            } catch (Exception e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            }

        case CLIENT_START_DEL:

            logger.debug("CLIENT_START_DEL");
            // sanity check - might be invalid state
            if (this.state != CLIENT_START_DEL || this.outByteBuff.remaining() > 0) {
                throw new GSSException(GSSException.FAILURE);
            }
            if (inByteBuff.hasRemaining()) {
                throw new GlobusGSSException(GSSException.FAILURE,
                        new Exception(
                                "Not all data processed; Original: " + len + " Remaining: " + inByteBuff.remaining()
                                        + " Handshaking status: " + sslEngine.getHandshakeStatus()));
            }
            this.outByteBuff.clear();

            try {
                String deleg;

                if (getCredDelegState()) {
                    deleg = Character.toString(GSIConstants.DELEGATION_CHAR);
                    this.state = CLIENT_END_DEL;
                } else {
                    deleg = Character.toString('0');
                    setDone();
                }

                byte[] a = deleg.getBytes("US-ASCII");
                inByteBuff = ByteBuffer.wrap(a, 0, a.length);
                outByteBuff = sslDataWrap(inByteBuff, outByteBuff);
                outByteBuff.flip();

            } catch (Exception e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            }

            break;

        case CLIENT_END_DEL:

            logger.debug("CLIENT_END_DEL");
            if (!inByteBuff.hasRemaining()) {
                throw new GSSException(GSSException.DEFECTIVE_TOKEN);
            }

            ByteArrayInputStream byteArrayInputStream = null;
            try {
                /*DEL
                                if (this.in.available() <= 0) {
                return null;
                                }
                */
                outByteBuff = sslDataUnwrap(inByteBuff, outByteBuff);
                outByteBuff.flip();
                if (!outByteBuff.hasRemaining())
                    break;

                byte[] certReq = new byte[outByteBuff.remaining()];
                outByteBuff.get(certReq, 0, certReq.length);

                X509Certificate[] chain = this.ctxCred.getCertificateChain();

                byteArrayInputStream = new ByteArrayInputStream(certReq);
                X509Certificate cert = this.certFactory.createCertificate(byteArrayInputStream, chain[0],
                        this.ctxCred.getPrivateKey(), -1,
                        /*DEL
                                                           getDelegationType(chain[0]));
                        */
                        BouncyCastleCertProcessingFactory.decideProxyType(chain[0], this.delegationType));

                byte[] enc = cert.getEncoded();
                /*DEL
                                this.conn.getOutStream().write(enc, 0, enc.length);
                */
                inByteBuff = ByteBuffer.wrap(enc, 0, enc.length);
                outByteBuff.clear();
                outByteBuff = sslDataWrap(inByteBuff, outByteBuff);
                outByteBuff.flip();

                setDone();
            } catch (GeneralSecurityException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            } catch (IOException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            } finally {
                if (byteArrayInputStream != null) {
                    try {
                        byteArrayInputStream.close();
                    } catch (Exception e) {
                        logger.warn("Unable to close stream.");
                    }
                }
            }

            break;

        default:
            throw new GSSException(GSSException.FAILURE);
        }

        if (inByteBuff.hasRemaining()) {
            // Likely BUFFER_UNDERFLOW; save the
            // inByteBuff bytes here like in the unwrap() case
            logger.debug("Not all data processed; Original: " + len + " Remaining: " + inByteBuff.remaining()
                    + " Handshaking status: " + sslEngine.getHandshakeStatus());
            logger.debug("SAVING unprocessed " + inByteBuff.remaining() + "BYTES\n");
            savedInBytes = new byte[inByteBuff.remaining()];
            inByteBuff.get(savedInBytes, 0, savedInBytes.length);
        }

        logger.debug("exit initSecContext");
        //XXX: Why is here a check for CLIENT_START_DEL?
        // if (this.outByteBuff.hasRemaining() || this.state == CLIENT_START_DEL) {
        if (this.outByteBuff.hasRemaining()) {
            // TODO can we avoid this copy if the ByteBuffer is array based
            // and we return that array, each time allocating a new array
            // for outByteBuff?
            byte[] out = new byte[this.outByteBuff.remaining()];
            this.outByteBuff.get(out, 0, out.length);
            return out;
        } else
            return null;
    }

    private void setDone() {
        logger.debug("DONE with Handshaking and any initial cred delegation");
        this.established = true;
    }

    private void setGoodUntil(Date date) {
        if (this.goodUntil == null) {
            this.goodUntil = date;
        } else if (date.before(this.goodUntil)) {
            this.goodUntil = date;
        }
    }

    private void init(int how) throws GSSException, SSLException {

        /*DEL
                short [] cs;
                if (this.encryption) {
        // always make sure to add NULL cipher at the end
        short [] ciphers = this.policy.getCipherSuites();
        short [] newCiphers = new short[ciphers.length + 1];
        System.arraycopy(ciphers, 0, newCiphers, 0, ciphers.length);
        newCiphers[ciphers.length] = SSLPolicyInt.TLS_RSA_WITH_NULL_MD5;
        cs = newCiphers;
                } else {
        // encryption not requested - accept only one cipher
        // XXX: in the future might want to iterate through
        // all cipher and enable only the null encryption ones
        cs = NO_ENCRYPTION;
                }
                this.policy.setCipherSuites(cs);
                this.policy.requireClientAuth(this.requireClientAuth.booleanValue());
                this.policy.setAcceptNoClientCert(this.acceptNoClientCerts.booleanValue());
            
                setTrustedCertificates();
            
                this.in = new TokenInputStream();
                this.out = new ByteArrayOutputStream();
            
                try {
        this.conn = new SSLConn(null,
                                this.in,
                                this.out,
                                this.context,
                                how);
                } catch (IOException e) {
        throw new GlobusGSSException(GSSException.FAILURE, e);
                }
            
                this.conn.init();
        */
        try {
            // set trust parameters in SSLConfigurator
            if (this.tc == null) {
                KeyStore trustStore = Stores.getDefaultTrustStore();
                sslConfigurator.setTrustAnchorStore(trustStore);

                CertStore crlStore = Stores.getDefaultCRLStore();
                sslConfigurator.setCrlStore(crlStore);

                ResourceSigningPolicyStore sigPolStore = Stores.getDefaultSigningPolicyStore();
                sslConfigurator.setPolicyStore(sigPolStore);
            }

            this.sslConfigurator.setRejectLimitProxy(rejectLimitedProxy);
            if (proxyPolicyHandlers != null)
                sslConfigurator.setHandlers(proxyPolicyHandlers);

            this.sslContext = this.sslConfigurator.getSSLContext();
            this.sslEngine = this.sslContext.createSSLEngine();
        } catch (Exception e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }

        if (this.forceSSLv3AndConstrainCipherSuitesForGram.booleanValue())
            this.sslEngine.setEnabledProtocols(GRAM_PROTOCOLS);
        else
            this.sslEngine.setEnabledProtocols(ENABLED_PROTOCOLS);
        logger.debug("SUPPORTED PROTOCOLS: " + Arrays.toString(this.sslEngine.getSupportedProtocols())
                + "; ENABLED PROTOCOLS: " + Arrays.toString(this.sslEngine.getEnabledProtocols()));

        ArrayList<String> cs = new ArrayList();
        if (this.encryption) {
            if (this.forceSSLv3AndConstrainCipherSuitesForGram.booleanValue())
                for (String cipherSuite : GRAM_ENCRYPTION_CIPHER_SUITES)
                    cs.add(cipherSuite);
            else // Simply retain the default-enabled Cipher Suites
                cs.addAll(Arrays.asList(this.sslEngine.getEnabledCipherSuites()));
        } else {
            if (this.forceSSLv3AndConstrainCipherSuitesForGram.booleanValue())
                for (String cipherSuite : GRAM_NO_ENCRYPTION_CIPHER_SUITES)
                    cs.add(cipherSuite);
            else {
                for (String cipherSuite : NO_ENCRYPTION)
                    cs.add(cipherSuite);
                cs.addAll(Arrays.asList(this.sslEngine.getEnabledCipherSuites()));
            }
        }
        cs.removeAll(Arrays.asList(bannedCiphers));
        String[] testSuite = new String[0];
        this.sslEngine.setEnabledCipherSuites(cs.toArray(testSuite));
        logger.debug("CIPHER SUITE IS: " + Arrays.toString(this.sslEngine.getEnabledCipherSuites()));

        // TODO: Document the following behavior
        // NOTE: requireClientAuth Vs. acceptNoClientCerts
        // which one takes precedence? for now err on the side of security
        if (this.requireClientAuth.booleanValue() == Boolean.TRUE) {
            this.sslEngine.setNeedClientAuth(this.requireClientAuth.booleanValue());
        } else
            this.sslEngine.setWantClientAuth(!this.acceptNoClientCerts.booleanValue());

        this.sslEngine.setUseClientMode(how == INITIATE);

        this.certFactory = BouncyCastleCertProcessingFactory.getDefault();
        this.state = HANDSHAKE;
        int appSize = sslEngine.getSession().getApplicationBufferSize();
        this.outByteBuff = ByteBuffer.allocate(appSize);
        this.sslEngine.beginHandshake();
    }

    /* this is called when handshake is done */
    private void handshakeFinished() throws IOException {
        /*DEL
                // this call just forces some internal library
                // variables to be initailized
                this.conn.finishHandshake();
        */
        String cs = this.sslEngine.getSession().getCipherSuite();
        this.encryption = !cs.contains("WITH_NULL");
        logger.debug("encryption alg: " + cs);
    }

    /*DEL
        // allows bypass of PureTLS checks - since they were
        // already performed during SSL hashshake
        static class GSSProxyPathValidator extends ProxyPathValidator {
    public void validate(X509Certificate [] certPath,
                         TrustedCertificates trustedCerts,
                         CertificateRevocationLists crlsList)
        throws ProxyPathValidatorException {
        super.validate(certPath, trustedCerts, crlsList);
    }
        }
        
        private String verifyChain(Vector peerCerts)
    throws GSSException {
        
    X509Certificate[] peerChain = null;
    try {
        peerChain = PureTLSUtil.certificateChainToArray(peerCerts);
    } catch (GeneralSecurityException e) {
        throw new GlobusGSSException(GSSException.DEFECTIVE_CREDENTIAL,
                                     e);
    }
        
    GSSProxyPathValidator validator = new GSSProxyPathValidator();
        
    if (this.proxyPolicyHandlers != null) {
        Iterator iter = this.proxyPolicyHandlers.keySet().iterator();
        String oid;
        ProxyPolicyHandler handler;
        while(iter.hasNext()) {
            oid = (String)iter.next();
            handler =
                (ProxyPolicyHandler)this.proxyPolicyHandlers.get(oid);
            validator.setProxyPolicyHandler(oid, handler);
        }
    }
        
    CertificateRevocationLists certRevList =
        CertificateRevocationLists.getDefaultCertificateRevocationLists();
        
    validator.setRejectLimitedProxyCheck(
              this.rejectLimitedProxy.booleanValue());
        
    try {
        validator.validate(peerChain, this.tc, certRevList);
    } catch (ProxyPathValidatorException e) {
        // COMMENT FIXME we don't have an error code
        if (e.getErrorCode() ==
            ProxyPathValidatorException.LIMITED_PROXY_ERROR) {
            throw new GlobusGSSException(GSSException.UNAUTHORIZED,
                                         e);
        } else {
            throw new GlobusGSSException(GSSException.DEFECTIVE_CREDENTIAL,
                                         e);
        }
    }
        
    // C code also sets a flag RECEIVED_LIMITED_PROXY
    // when recevied certs is a limited proxy
    this.peerLimited = (validator.isLimited()) ?
        Boolean.TRUE : Boolean.FALSE;
        
    return validator.getIdentity();
        }
    */

    private void setCredential() throws GSSException {
        try {
            /*DEL
                        this.context.setCredential(this.ctxCred.getX509Credential());
            */
            KeyStore keyStore = KeyStore.getInstance("JKS");
            keyStore.load(null, null);
            X509Credential cred = this.ctxCred.getX509Credential();

            keyStore.setKeyEntry("default", cred.getPrivateKey(), "password".toCharArray(),
                    cred.getCertificateChain());
            this.sslConfigurator.setCredentialStore(keyStore);
            this.sslConfigurator.setCredentialStorePassword("password");

        } catch (GeneralSecurityException e) {
            throw new GlobusGSSException(GSSException.DEFECTIVE_CREDENTIAL, e);
        } catch (Exception e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }
    }

    /*DEL
        private void setTrustedCertificates()
    throws GSSException {
    if (this.tc == null) {
        this.tc = PureTLSTrustedCertificates.getDefaultPureTLSTrustedCertificates();
    }
    if (this.tc == null) {
        throw new GlobusGSSException(GSSException.DEFECTIVE_CREDENTIAL,
                                     GlobusGSSException.UNKNOWN,
                                     "noCaCerts");
    }
    try {
        // COMMENT: move use of PureTLS from TrustCertificates
        this.context.setRootList(PureTLSUtil.certificateChainToVector(this.tc.getCertificates()));
    } catch (GeneralSecurityException e) {
        throw new GlobusGSSException(GSSException.FAILURE, e);
    }
        }
    */

    /**
     * Wraps a message for integrity and protection.
     * A regular SSL-wrapped token is returned.
     */
    public byte[] wrap(byte[] inBuf, int off, int len, MessageProp prop) throws GSSException {

        checkContext();

        logger.debug("enter wrap");

        byte[] token = null;
        boolean doGSIWrap = false;

        if (prop != null) {
            if (prop.getQOP() != 0 && prop.getQOP() != GSSConstants.GSI_BIG) {
                throw new GSSException(GSSException.BAD_QOP);
            }
            doGSIWrap = (!prop.getPrivacy() && prop.getQOP() == GSSConstants.GSI_BIG);
        }

        if (doGSIWrap) {
            throw new GSSException(GSSException.UNAVAILABLE);
            /*DEL
                
                        byte [] mic = getMIC(inBuf, off, len, null);
                
                        byte [] wtoken = new byte[5 + len + mic.length];
                        wtoken[0] = GSI_WRAP;
                        wtoken[1] = 3;
                        wtoken[2] = 0;
                        wtoken[3] = (byte)(mic.length >>> 8);
                        wtoken[4] = (byte)(mic.length >>> 0);
                        System.arraycopy(mic, 0, wtoken, 5, mic.length);
                        System.arraycopy(inBuf, off, wtoken, 5+mic.length, len);
                
                        token = wtoken;
            */
        } else {
            token = wrap(inBuf, off, len);

            if (prop != null) {
                prop.setPrivacy(this.encryption);
                prop.setQOP(0);
            }
        }

        logger.debug("exit wrap");
        return token;
    }

    private byte[] wrap(byte[] inBuf, int off, int len) throws GSSException {
        try {
            /*DEL
                        this.conn.getOutStream().write(inBuf, off, len);
            */
            ByteBuffer inByteBuff = ByteBuffer.wrap(inBuf, off, len);
            this.outByteBuff.clear();
            outByteBuff = this.sslDataWrap(inByteBuff, outByteBuff);
            outByteBuff.flip();

            if (inByteBuff.hasRemaining()) {
                throw new Exception("Not all data processed; Original: " + len + " Remaining: "
                        + inByteBuff.remaining() + " Handshaking status: " + sslEngine.getHandshakeStatus());
            }
        } catch (Exception e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }

        if (this.outByteBuff.hasRemaining()) {
            // TODO can we avoid this copy if the ByteBuffer is array based
            // and we return that array, each time allocating a new array
            // for outByteBuff?
            byte[] out = new byte[this.outByteBuff.remaining()];
            this.outByteBuff.get(out, 0, out.length);
            return out;
        } else
            return null;
        /*DEL
                return this.out.toByteArray();
        */
    }

    /**
     * Unwraps a token generated by <code>wrap</code> method on the other side of the context.
     */
    public byte[] unwrap(byte[] inBuf, int off, int len, MessageProp prop) throws GSSException {

        checkContext();

        logger.debug("enter unwrap");

        byte[] token = null;

        /*
         * see if the token is a straight SSL packet or
         * one of ours made by wrap using get_mic
         */
        if (inBuf[off] == GSI_WRAP && inBuf[off + 1] == 3 && inBuf[off + 2] == 0) {
            throw new GSSException(GSSException.UNAVAILABLE);
            /*DEL
                
                        int micLen = SSLUtil.toShort(inBuf[off+3], inBuf[off+4]);
                        int msgLen = len - 5 - micLen;
                
                        if (micLen > len-5 || msgLen < 0) {
            throw new GSSException(GSSException.DEFECTIVE_TOKEN);
                        }
                
                        verifyMIC(inBuf, off+5, micLen,
                  inBuf, off+5+micLen, msgLen,
                  null);
                
                        if (prop != null) {
            prop.setPrivacy(false);
            prop.setQOP(GSSConstants.GSI_BIG);
                        }
                
                        // extract the data
                        token = new byte[msgLen];
                        System.arraycopy(inBuf, off+5+micLen, token, 0, msgLen);
            */

        } else {
            token = unwrap(inBuf, off, len);

            if (prop != null) {
                prop.setPrivacy(this.encryption);
                prop.setQOP(0);
            }
        }

        logger.debug("exit unwrap");
        return token;
    }

    private byte[] unwrap(byte[] inBuf, int off, int len) throws GSSException {

        /*DEL
                ByteArrayInputStream in =
        new ByteArrayInputStream(inBuf, off, len);
                ByteArrayOutputStream out =
        new ByteArrayOutputStream();
            
                // TODO: this might need to be rewritten
                // to catch lower level exceptions
                // e.g. mac too long, etc.
                try {
        while(in.available() > 0) {
            SSLRecord r = new SSLRecord(null);
            r.decode(this.conn, in);
            switch (r.getType().getValue()) {
            case SSLRecord.SSL_CT_APPLICATION_DATA:
                out.write(r.getData().getValue());
                break;
            case SSLRecord.SSL_CT_ALERT:
                this.conn.getRecordReader().processAlert(r.getData().getValue());
                break;
            default:
                throw new Exception(i18n.getMessage("tokenFail03"));
            }
        }
                } catch (IOException e) {
        throw new GlobusGSSException(GSSException.BAD_MIC, e);
                } catch (Exception e) {
        throw new GlobusGSSException(GSSException.DEFECTIVE_TOKEN, e);
                }
            
                return out.toByteArray();
        */
        ByteBuffer inByteBuff;
        if (savedInBytes != null) {
            if (len > 0) {
                byte[] allInBytes = new byte[savedInBytes.length + len];
                logger.debug("ALLOCATED for allInBytes " + savedInBytes.length + " + " + len + " bytes\n");
                System.arraycopy(savedInBytes, 0, allInBytes, 0, savedInBytes.length);
                System.arraycopy(inBuf, off, allInBytes, savedInBytes.length, len);
                inByteBuff = ByteBuffer.wrap(allInBytes, 0, allInBytes.length);
            } else {
                inByteBuff = ByteBuffer.wrap(savedInBytes, 0, savedInBytes.length);
            }
            savedInBytes = null;
        } else {
            inByteBuff = ByteBuffer.wrap(inBuf, off, len);
        }
        this.outByteBuff.clear();
        outByteBuff = this.sslDataUnwrap(inByteBuff, outByteBuff);
        outByteBuff.flip();

        if (inByteBuff.hasRemaining()) {
            logger.debug("Not all data processed; Original: " + len + " Remaining: " + inByteBuff.remaining()
                    + " Handshaking status: " + sslEngine.getHandshakeStatus());
            logger.debug("SAVING unprocessed " + inByteBuff.remaining() + "BYTES\n");
            savedInBytes = new byte[inByteBuff.remaining()];
            inByteBuff.get(savedInBytes, 0, savedInBytes.length);
        }

        if (this.outByteBuff.hasRemaining()) {
            // TODO can we avoid this copy if the ByteBuffer is array based
            // and we return that array, each time allocating a new array
            // for outByteBuff?
            byte[] out = new byte[this.outByteBuff.remaining()];
            this.outByteBuff.get(out, 0, out.length);
            return out;
        } else
            return null;

    }

    public void dispose() throws GSSException {
        // doesn't do anything right now
        logger.debug("dipose");
    }

    public boolean isEstablished() {
        return this.established;
    }

    public void requestCredDeleg(boolean state) throws GSSException {
        this.credentialDelegation = state;
    }

    public boolean getCredDelegState() {
        return this.credentialDelegation;
    }

    public boolean isInitiator() throws GSSException {
        if (this.role == UNDEFINED) {
            throw new GSSException(GSSException.FAILURE);
        }
        return (this.role == INITIATE);
    }

    public boolean isProtReady() {
        return isEstablished();
    }

    public void requestLifetime(int lifetime) throws GSSException {
        if (lifetime == GSSContext.INDEFINITE_LIFETIME) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.UNKNOWN, "badLifetime00");
        }

        if (lifetime != GSSContext.DEFAULT_LIFETIME) {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.SECOND, lifetime);
            setGoodUntil(calendar.getTime());
        }
    }

    public int getLifetime() {
        if (this.goodUntil != null) {
            return (int) ((this.goodUntil.getTime() - System.currentTimeMillis()) / 1000);
        } else {
            return -1;
        }
    }

    public Oid getMech() throws GSSException {
        return GSSConstants.MECH_OID;
    }

    public GSSCredential getDelegCred() throws GSSException {
        return this.delegCred;
    }

    public void requestConf(boolean state) throws GSSException {
        // enabled encryption
        this.encryption = state;
    }

    public boolean getConfState() {
        return this.encryption;
    }

    /**
     * Returns a cryptographic MIC (message integrity check)
     * of a specified message.
     */
    public byte[] getMIC(byte[] inBuf, int off, int len, MessageProp prop) throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
        /*TODO
            
                checkContext();
            
                logger.debug("enter getMic");
            
                if (prop != null && (prop.getQOP() != 0 || prop.getPrivacy())) {
        throw new GSSException(GSSException.BAD_QOP);
                }
            
                SSLCipherState st = this.conn.getWriteCipherState();
                SSLCipherSuite cs = st.getCipherSuite();
                long sequence = this.conn.getWriteSequence();
            
                byte [] mic = new byte[GSI_MESSAGE_DIGEST_PADDING + cs.getDigestOutputLength()];
            
                System.arraycopy(Util.toBytes(sequence), 0, mic, 0, GSI_SEQUENCE_SIZE);
                System.arraycopy(Util.toBytes(len, 4), 0, mic, GSI_SEQUENCE_SIZE, 4);
            
                this.conn.incrementWriteSequence();
            
                int pad_ct = (cs.getDigestOutputLength()==16) ? 48 : 40;
            
                try {
        MessageDigest md =
            MessageDigest.getInstance(cs.getDigestAlg());
            
        md.update(st.getMacKey());
        for(int i=0;i<pad_ct;i++) {
            md.update(SSLHandshake.pad_1);
        }
        md.update(mic, 0, GSI_MESSAGE_DIGEST_PADDING);
        md.update(inBuf, off, len);
            
        byte[] digest = md.digest();
            
        System.arraycopy(digest, 0, mic, GSI_MESSAGE_DIGEST_PADDING, digest.length);
                } catch (NoSuchAlgorithmException e) {
        throw new GlobusGSSException(GSSException.FAILURE, e);
                }
            
                if (prop != null) {
        prop.setPrivacy(false);
        prop.setQOP(0);
                }
            
                logger.debug("exit getMic");
                return mic;
        */
    }

    /**
     * Verifies a cryptographic MIC (message integrity check)
     * of a specified message.
     */
    public void verifyMIC(byte[] inTok, int tokOff, int tokLen, // mic
            byte[] inMsg, int msgOff, int msgLen, // real msg
            MessageProp prop) throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
        /*TODO
            
                checkContext();
            
                logger.debug("enter verifyMic");
            
                SSLCipherState st = this.conn.getReadCipherState();
                SSLCipherSuite cs = st.getCipherSuite();
            
                logger.debug("digest algorithm: " + cs.getDigestAlg());
            
                if (tokLen != (GSI_MESSAGE_DIGEST_PADDING + cs.getDigestOutputLength())) {
        throw new GlobusGSSException(GSSException.DEFECTIVE_TOKEN,
                                     GlobusGSSException.TOKEN_FAIL,
                                     "tokenFail00",
                                     new Object[] {new Integer(tokLen),
                                                   new Integer(GSI_MESSAGE_DIGEST_PADDING +
                                                               cs.getDigestOutputLength())});
                }
            
                int bufLen = SSLUtil.toInt(inTok, tokOff+GSI_SEQUENCE_SIZE);
                if (bufLen != msgLen) {
        throw new GlobusGSSException(GSSException.DEFECTIVE_TOKEN,
                                     GlobusGSSException.TOKEN_FAIL,
                                     "tokenFail01",
                                     new Object[] {new Integer(msgLen), new Integer(bufLen)});
                }
            
                int pad_ct = (cs.getDigestOutputLength()==16) ? 48 : 40;
            
                byte [] digest = null;
            
                try {
        MessageDigest md =
            MessageDigest.getInstance(cs.getDigestAlg());
            
        md.update(st.getMacKey());
        for(int i=0;i<pad_ct;i++) {
            md.update(SSLHandshake.pad_1);
        }
        md.update(inTok, tokOff, GSI_MESSAGE_DIGEST_PADDING);
        md.update(inMsg, msgOff, msgLen);
            
        digest = md.digest();
                } catch (NoSuchAlgorithmException e) {
        throw new GlobusGSSException(GSSException.FAILURE, e);
                }
            
                byte [] token = new byte[tokLen-GSI_MESSAGE_DIGEST_PADDING];
                System.arraycopy(inTok, tokOff+GSI_MESSAGE_DIGEST_PADDING, token, 0, token.length);
            
                if (!Arrays.equals(digest, token)) {
        throw new GlobusGSSException(GSSException.BAD_MIC,
                                     GlobusGSSException.BAD_MIC,
                                     "tokenFail02");
                }
            
                long tokSeq = SSLUtil.toLong(inTok, tokOff);
                long readSeq = this.conn.getReadSequence();
                long seqTest = tokSeq - readSeq;
            
                logger.debug("Token seq#   : " + tokSeq);
                logger.debug("Current seq# : " + readSeq);
            
                if (seqTest > 0) {
        // gap token
        throw new GSSException(GSSException.GAP_TOKEN);
                } else if (seqTest < 0) {
        // old token
        throw new GSSException(GSSException.OLD_TOKEN);
                } else {
        this.conn.incrementReadSequence();
                }
            
                if (prop != null) {
        prop.setPrivacy(false);
        prop.setQOP(0);
                }
            
                logger.debug("exit verifyMic");
        */
    }

    /**
     * It works just like {@link #initSecContext(byte[], int, int) initSecContext} method.
     * It reads one SSL token from input stream, calls
     * {@link #initSecContext(byte[], int, int) initSecContext} method and
     * writes the output token to the output stream (if any)
     * SSL token is not read on the initial call.
     */
    public int initSecContext(InputStream in, OutputStream out) throws GSSException {
        byte[] inToken = null;
        try {
            if (!this.conn) {
                inToken = new byte[0];
            } else {
                inToken = SSLUtil.readSslMessage(in);
            }
            byte[] outToken = initSecContext(inToken, 0, inToken.length);
            if (outToken != null) {
                out.write(outToken);
                return outToken.length;
            } else {
                return 0;
            }
        } catch (IOException e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }
    }

    /**
     * It works just like {@link #acceptSecContext(byte[], int, int) acceptSecContext}
     * method. It reads one SSL token from input stream, calls
     * {@link #acceptSecContext(byte[], int, int) acceptSecContext}
     * method and writes the output token to the output stream (if any)
     */
    public void acceptSecContext(InputStream in, OutputStream out) throws GSSException {
        try {
            byte[] inToken = SSLUtil.readSslMessage(in);
            byte[] outToken = acceptSecContext(inToken, 0, inToken.length);
            if (outToken != null) {
                out.write(outToken);
            }
        } catch (IOException e) {
            throw new GlobusGSSException(GSSException.FAILURE, e);
        }
    }

    public GSSName getSrcName() throws GSSException {
        return this.sourceName;
    }

    public GSSName getTargName() throws GSSException {
        return this.targetName;
    }

    public void requestInteg(boolean state) throws GSSException {
        if (!state) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION, "integOn");
        }
    }

    public boolean getIntegState() {
        return true; // it is always on with ssl
    }

    public void requestSequenceDet(boolean state) throws GSSException {
        if (!state) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION, "seqDet");
        }
    }

    public boolean getSequenceDetState() {
        return true; // it is always on with ssl
    }

    public void requestReplayDet(boolean state) throws GSSException {
        if (!state) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION, "replayDet");
        }
    }

    public boolean getReplayDetState() {
        return true; // is is always on with ssl
    }

    public void requestAnonymity(boolean state) throws GSSException {
        this.anonymity = state;
    }

    public boolean getAnonymityState() {
        return this.anonymity;
    }

    public void requestMutualAuth(boolean state) throws GSSException {
        if (!state) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION, "mutualAuthOn");
        }
    }

    public boolean getMutualAuthState() {
        return true; // always on with gsi i guess
    }

    protected byte[] generateCertRequest(X509Certificate cert) throws GeneralSecurityException {

        int bits = ((RSAPublicKey) cert.getPublicKey()).getModulus().bitLength();

        this.keyPair = keyPairCache.getKeyPair(bits);

        return this.certFactory.createCertificateRequest(cert, this.keyPair);
    }

    protected void verifyDelegatedCert(X509Certificate certificate) throws GeneralSecurityException {
        RSAPublicKey pubKey = (RSAPublicKey) certificate.getPublicKey();
        RSAPrivateKey privKey = (RSAPrivateKey) this.keyPair.getPrivate();

        if (!pubKey.getModulus().equals(privKey.getModulus())) {
            throw new GeneralSecurityException(i18n.getMessage("keyMismatch"));
        }
    }

    protected void checkContext() throws GSSException {
        if (!this.conn || !isEstablished()) {
            throw new GSSException(GSSException.NO_CONTEXT);
        }

        if (this.checkContextExpiration.booleanValue() && getLifetime() <= 0) {
            throw new GSSException(GSSException.CONTEXT_EXPIRED);
        }
    }

    /*DEL
        protected int getDelegationType(X509Certificate issuer)
    throws GeneralSecurityException, GSSException {
        
    // GSIConstants.CertificateType certType = BouncyCastleUtil.getCertificateType(issuer, this.tc);
       // TODO: Is this alright without this.tc being passed?
    GSIConstants.CertificateType certType = BouncyCastleUtil.getCertificateType(issuer);
    int dType = this.delegationType.intValue();
        
    if (logger.isDebugEnabled()) {
        logger.debug("Issuer type: " + certType + " delg. type requested: " + dType);
    }
        
    if (certType == GSIConstants.CertificateType.EEC) {
        if (dType == GSIConstants.DELEGATION_LIMITED) {
            if (VersionUtil.isGsi2Enabled()) {
                return GSIConstants.GSI_2_LIMITED_PROXY;
            } else if (VersionUtil.isGsi3Enabled()) {
                return GSIConstants.GSI_3_LIMITED_PROXY;
            } else {
                return GSIConstants.GSI_4_LIMITED_PROXY;
            }
        } else if (dType == GSIConstants.DELEGATION_FULL) {
            if (VersionUtil.isGsi2Enabled()) {
                return GSIConstants.GSI_2_PROXY;
            } else if (VersionUtil.isGsi3Enabled()) {
                return GSIConstants.GSI_3_IMPERSONATION_PROXY;
            } else {
                return GSIConstants.GSI_4_IMPERSONATION_PROXY;
            }
        } else if (ProxyCertificateUtil.isProxy(GSIConstants.CertificateType.get(dType))) {
            return dType;
        }
    } else if (ProxyCertificateUtil.isGsi2Proxy(certType)) {
        if (dType == GSIConstants.DELEGATION_LIMITED) {
            return GSIConstants.GSI_2_LIMITED_PROXY;
        } else if (dType == GSIConstants.DELEGATION_FULL) {
            return GSIConstants.GSI_2_PROXY;
        } else if (ProxyCertificateUtil.isGsi2Proxy(GSIConstants.CertificateType.get(dType))) {
            return dType;
        }
    } else if (ProxyCertificateUtil.isGsi3Proxy(certType)) {
        if (dType == GSIConstants.DELEGATION_LIMITED) {
            return GSIConstants.GSI_3_LIMITED_PROXY;
        } else if (dType == GSIConstants.DELEGATION_FULL) {
            return GSIConstants.GSI_3_IMPERSONATION_PROXY;
        } else if (ProxyCertificateUtil.isGsi3Proxy(GSIConstants.CertificateType.get(dType))) {
            return dType;
        }
    } else if (ProxyCertificateUtil.isGsi4Proxy(certType)) {
        if (dType == GSIConstants.DELEGATION_LIMITED) {
            return GSIConstants.GSI_4_LIMITED_PROXY;
        } else if (dType == GSIConstants.DELEGATION_FULL) {
            return GSIConstants.GSI_4_IMPERSONATION_PROXY;
        } else if (ProxyCertificateUtil.isGsi4Proxy(GSIConstants.CertificateType.get(dType))) {
            return dType;
        }
    }
    throw new GSSException(GSSException.FAILURE);
        }
    */

    // -----------------------------------

    protected void setGssMode(Object value) throws GSSException {
        if (!(value instanceof Integer)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "GSS mode", Integer.class });
        }
        Integer v = (Integer) value;
        if (v == GSIConstants.MODE_GSI || v == GSIConstants.MODE_SSL) {
            this.gssMode = v;
        } else {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION, "badGssMode");
        }
    }

    protected void setDelegationType(Object value) throws GSSException {
        GSIConstants.DelegationType v;
        if (value instanceof GSIConstants.DelegationType)
            v = (GSIConstants.DelegationType) value;
        else if (value instanceof Integer)
            v = GSIConstants.DelegationType.get(((Integer) value).intValue());
        else {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "delegation type", GSIConstants.DelegationType.class });
        }
        /*DEL
                Integer v = (Integer)value;
        */
        if (v == GSIConstants.DelegationType.FULL || v == GSIConstants.DelegationType.LIMITED) {
            this.delegationType = v;
        } else {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION, "badDelegType");
        }
    }

    protected void setCheckContextExpired(Object value) throws GSSException {
        if (!(value instanceof Boolean)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "check context expired", Boolean.class });
        }
        this.checkContextExpiration = (Boolean) value;
    }

    protected void setRejectLimitedProxy(Object value) throws GSSException {
        if (!(value instanceof Boolean)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "reject limited proxy", Boolean.class });
        }
        this.rejectLimitedProxy = (Boolean) value;
    }

    protected void setRequireClientAuth(Object value) throws GSSException {
        if (!(value instanceof Boolean)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "require client auth", Boolean.class });
        }
        this.requireClientAuth = (Boolean) value;
    }

    protected void setRequireAuthzWithDelegation(Object value) throws GSSException {

        if (!(value instanceof Boolean)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "require authz with delehation", Boolean.class });
        }
        this.requireAuthzWithDelegation = (Boolean) value;
    }

    protected void setAcceptNoClientCerts(Object value) throws GSSException {
        if (!(value instanceof Boolean)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "accept no client certs", Boolean.class });
        }
        this.acceptNoClientCerts = (Boolean) value;
    }

    protected void setForceSslV3AndConstrainCipherSuitesForGram(Object value) throws GSSException {
        if (!(value instanceof Boolean)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "adjust cipher suites for GRAM", Boolean.class });
        }
        this.forceSSLv3AndConstrainCipherSuitesForGram = (Boolean) value;
    }

    /*DEL
        protected void setGrimPolicyHandler(Object value)
    throws GSSException {
    if (!(value instanceof ProxyPolicyHandler)) {
        throw new GlobusGSSException(GSSException.FAILURE,
                                     GlobusGSSException.BAD_OPTION_TYPE,
                                     "badType",
                                     new Object[] {"GRIM policy handler",
                                                   ProxyPolicyHandler.class});
    }
    if (this.proxyPolicyHandlers == null) {
        this.proxyPolicyHandlers = new HashMap();
    }
    this.proxyPolicyHandlers.put("1.3.6.1.4.1.3536.1.1.1.7", value);
        }
    */

    protected void setProxyPolicyHandlers(Object value) throws GSSException {
        if (!(value instanceof Map)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "Proxy policy handlers", Map.class });
        }
        this.proxyPolicyHandlers = (Map) value;
    }

    protected void setTrustedCertificates(Object value) throws GSSException {
        if (!(value instanceof TrustedCertificates)) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_OPTION_TYPE, "badType",
                    new Object[] { "Trusted certificates", TrustedCertificates.class });
        }
        this.tc = (TrustedCertificates) value;
        //TODO: set this in SSLConfigurator before creating SSLContext and engine?
        sslConfigurator.setTrustAnchorStore(((TrustedCertificates) value).getTrustStore());
        sslConfigurator.setCrlStore(((TrustedCertificates) value).getcrlStore());
        sslConfigurator.setPolicyStore(((TrustedCertificates) value).getsigPolStore());
    }

    public void setOption(Oid option, Object value) throws GSSException {
        if (option == null) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_ARGUMENT, "nullOption");
        }
        if (value == null) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_ARGUMENT, "nullOptionValue");
        }

        if (option.equals(GSSConstants.GSS_MODE)) {
            setGssMode(value);
        } else if (option.equals(GSSConstants.DELEGATION_TYPE)) {
            setDelegationType(value);
        } else if (option.equals(GSSConstants.CHECK_CONTEXT_EXPIRATION)) {
            setCheckContextExpired(value);
        } else if (option.equals(GSSConstants.REJECT_LIMITED_PROXY)) {
            setRejectLimitedProxy(value);
        } else if (option.equals(GSSConstants.REQUIRE_CLIENT_AUTH)) {
            setRequireClientAuth(value);
            /*DEL
                    } else if (option.equals(GSSConstants.GRIM_POLICY_HANDLER)) {
                        setGrimPolicyHandler(value);
            */
        } else if (option.equals(GSSConstants.TRUSTED_CERTIFICATES)) {
            setTrustedCertificates(value);
        } else if (option.equals(GSSConstants.PROXY_POLICY_HANDLERS)) {
            setProxyPolicyHandlers(value);
        } else if (option.equals(GSSConstants.ACCEPT_NO_CLIENT_CERTS)) {
            setAcceptNoClientCerts(value);
        } else if (option.equals(GSSConstants.AUTHZ_REQUIRED_WITH_DELEGATION)) {
            setRequireAuthzWithDelegation(value);
        } else if (option.equals(GSSConstants.FORCE_SSLV3_AND_CONSTRAIN_CIPHERSUITES_FOR_GRAM)) {
            setForceSslV3AndConstrainCipherSuitesForGram(value);
        } else {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.UNKNOWN_OPTION, "unknownOption",
                    new Object[] { option });
        }
    }

    public Object getOption(Oid option) throws GSSException {
        if (option == null) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_ARGUMENT, "nullOption");
        }

        if (option.equals(GSSConstants.GSS_MODE)) {
            return this.gssMode;
        } else if (option.equals(GSSConstants.DELEGATION_TYPE)) {
            return this.delegationType;
        } else if (option.equals(GSSConstants.CHECK_CONTEXT_EXPIRATION)) {
            return this.checkContextExpiration;
        } else if (option.equals(GSSConstants.REJECT_LIMITED_PROXY)) {
            return this.rejectLimitedProxy;
        } else if (option.equals(GSSConstants.REQUIRE_CLIENT_AUTH)) {
            return this.requireClientAuth;
        } else if (option.equals(GSSConstants.TRUSTED_CERTIFICATES)) {
            return this.tc;
        } else if (option.equals(GSSConstants.PROXY_POLICY_HANDLERS)) {
            // return this.proxyPolicyHandlers;
            throw new GSSException(GSSException.UNAVAILABLE);
        } else if (option.equals(GSSConstants.ACCEPT_NO_CLIENT_CERTS)) {
            return this.acceptNoClientCerts;
        }

        return null;
    }

    /**
     * Initiate the delegation of a credential.
     *
     * This function drives the initiating side of the credential
     * delegation process. It is expected to be called in tandem with the
     * {@link #acceptDelegation(int, byte[], int, int) acceptDelegation}
     * function.
     * <BR>
     * The behavior of this function can be modified by
     * {@link GSSConstants#DELEGATION_TYPE GSSConstants.DELEGATION_TYPE}
     * and
     * {@link GSSConstants#GSS_MODE GSSConstants.GSS_MODE} context
     * options.
     * The {@link GSSConstants#DELEGATION_TYPE GSSConstants.DELEGATION_TYPE}
     * option controls delegation type to be performed. The
     * {@link GSSConstants#GSS_MODE GSSConstants.GSS_MODE}
     * option if set to
     * {@link GSIConstants#MODE_SSL GSIConstants.MODE_SSL}
     * results in tokens that are not wrapped.
     *
     * @param credential
     *        The credential to be delegated. May be null
     *        in which case the credential associated with the security
     *        context is used.
     * @param mechanism
     *        The desired security mechanism. May be null.
     * @param lifetime
     *        The requested period of validity (seconds) of the delegated
     *        credential.
     * @return A token that should be passed to <code>acceptDelegation</code> if
     *         <code>isDelegationFinished</code> returns false. May be null.
     * @exception GSSException containing the following major error codes:
     *            <code>GSSException.FAILURE</code>
     */
    public byte[] initDelegation(GSSCredential credential, Oid mechanism, int lifetime, byte[] buf, int off,
            int len) throws GSSException {

        logger.debug("Enter initDelegation: " + delegationState);

        if (mechanism != null && !mechanism.equals(getMech())) {
            throw new GSSException(GSSException.BAD_MECH);
        }

        if (this.gssMode != GSIConstants.MODE_SSL && buf != null && len > 0) {
            buf = unwrap(buf, off, len);
            off = 0;
            len = buf.length;
        }

        byte[] token = null;

        switch (delegationState) {

        case DELEGATION_START:

            this.delegationFinished = false;
            token = DELEGATION_TOKEN;
            this.delegationState = DELEGATION_SIGN_CERT;
            break;

        case DELEGATION_SIGN_CERT:

            if (credential == null) {
                // get default credential
                GSSManager manager = new GlobusGSSManagerImpl();
                credential = manager.createCredential(GSSCredential.INITIATE_AND_ACCEPT);
            }

            if (!(credential instanceof GlobusGSSCredentialImpl)) {
                throw new GSSException(GSSException.DEFECTIVE_CREDENTIAL);
            }

            X509Credential cred = ((GlobusGSSCredentialImpl) credential).getX509Credential();

            X509Certificate[] chain = cred.getCertificateChain();

            int time = (lifetime == GSSCredential.DEFAULT_LIFETIME) ? -1 : lifetime;

            ByteArrayInputStream inData = null;
            ByteArrayOutputStream out = null;
            try {
                inData = new ByteArrayInputStream(buf, off, len);
                X509Certificate cert = this.certFactory.createCertificate(inData, chain[0], cred.getPrivateKey(),
                        time,
                        /*DEL
                                                           getDelegationType(chain[0]));
                        */
                        BouncyCastleCertProcessingFactory.decideProxyType(chain[0], this.delegationType));

                out = new ByteArrayOutputStream();

                out.write(cert.getEncoded());
                for (int i = 0; i < chain.length; i++) {
                    out.write(chain[i].getEncoded());
                }

                token = out.toByteArray();
            } catch (Exception e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            } finally {
                if (inData != null) {
                    try {
                        inData.close();
                    } catch (Exception e) {
                        logger.warn("Unable to close stream.");
                    }
                }
                if (out != null) {
                    try {
                        out.close();
                    } catch (Exception e) {
                        logger.warn("Unable to close stream.");
                    }
                }
            }

            this.delegationState = DELEGATION_START;
            this.delegationFinished = true;
            break;

        default:
            throw new GSSException(GSSException.FAILURE);
        }

        logger.debug("Exit initDelegation");

        if (this.gssMode != GSIConstants.MODE_SSL && token != null) {
            // XXX: Why wrap() only when not in MODE_SSL?
            return wrap(token, 0, token.length);
        } else {
            return token;
        }
    }

    /**
     * Accept a delegated credential.
     *
     * This function drives the accepting side of the credential
     * delegation process. It is expected to be called in tandem with the
     * {@link #initDelegation(GSSCredential, Oid, int, byte[], int, int)
     * initDelegation} function.
     * <BR>
     * The behavior of this function can be modified by
     * {@link GSSConstants#GSS_MODE GSSConstants.GSS_MODE} context
     * option. The
     * {@link GSSConstants#GSS_MODE GSSConstants.GSS_MODE}
     * option if set to
     * {@link GSIConstants#MODE_SSL GSIConstants.MODE_SSL}
     * results in tokens that are not wrapped.
     *
     * @param lifetime
     *        The requested period of validity (seconds) of the delegated
     *        credential.
     * @return A token that should be passed to <code>initDelegation</code> if
     *        <code>isDelegationFinished</code> returns false. May be null.
     * @exception GSSException containing the following major error codes:
     *            <code>GSSException.FAILURE</code>
     */
    public byte[] acceptDelegation(int lifetime, byte[] buf, int off, int len) throws GSSException {

        logger.debug("Enter acceptDelegation: " + delegationState);

        if (this.gssMode != GSIConstants.MODE_SSL && buf != null && len > 0) {
            buf = unwrap(buf, off, len);
            off = 0;
            len = buf.length;
        }

        byte[] token = null;

        switch (delegationState) {

        case DELEGATION_START:

            this.delegationFinished = false;

            if (len != 1 && buf[off] != GSIConstants.DELEGATION_CHAR) {
                throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.DELEGATION_ERROR,
                        "delegError00", new Object[] { new Character((char) buf[off]) });
            }

            try {
                /*DEL
                                Vector certChain = this.conn.getCertificateChain();
                */
                Certificate[] certChain;
                try {
                    certChain = this.sslEngine.getSession().getPeerCertificates();
                } catch (SSLPeerUnverifiedException e) {
                    certChain = null;
                }
                if (certChain == null || certChain.length == 0) {
                    throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.DELEGATION_ERROR,
                            "noClientCert");
                }

                X509Certificate tmpCert =
                        /*DEL
                        PureTLSUtil.convertCert((X509Cert)certChain.lastElement());
                        */
                        (X509Certificate) certChain[0];

                token = generateCertRequest(tmpCert);
            } catch (GeneralSecurityException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            }

            this.delegationState = DELEGATION_COMPLETE_CRED;
            break;

        case DELEGATION_COMPLETE_CRED:

            ByteArrayInputStream in = null;
            X509Certificate[] chain = null;
            LinkedList certList = new LinkedList();
            X509Certificate cert = null;
            try {
                in = new ByteArrayInputStream(buf, off, len);
                while (in.available() > 0) {
                    cert = CertificateLoadUtil.loadCertificate(in);
                    certList.add(cert);
                }

                chain = new X509Certificate[certList.size()];
                chain = (X509Certificate[]) certList.toArray(chain);

                verifyDelegatedCert(chain[0]);

            } catch (GeneralSecurityException e) {
                throw new GlobusGSSException(GSSException.FAILURE, e);
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (Exception e) {
                        logger.warn("Unable to close streamreader.");
                    }
                }
            }

            X509Credential proxy = new X509Credential(this.keyPair.getPrivate(), chain);

            this.delegatedCred = new GlobusGSSCredentialImpl(proxy, GSSCredential.INITIATE_AND_ACCEPT);

            this.delegationState = DELEGATION_START;
            this.delegationFinished = true;
            break;

        default:
            throw new GSSException(GSSException.FAILURE);
        }

        logger.debug("Exit acceptDelegation");

        if (this.gssMode != GSIConstants.MODE_SSL && token != null) {
            // XXX: Why wrap() only when not in MODE_SSL?
            return wrap(token, 0, token.length);
        } else {
            return token;
        }
    }

    public GSSCredential getDelegatedCredential() {
        return this.delegatedCred;
    }

    public boolean isDelegationFinished() {
        return this.delegationFinished;
    }

    /**
     * Retrieves arbitrary data about this context.
     * Currently supported oid: <UL>
     * <LI>
     * {@link GSSConstants#X509_CERT_CHAIN GSSConstants.X509_CERT_CHAIN}
     * returns certificate chain of the peer (<code>X509Certificate[]</code>).
     * </LI>
     * </UL>
     *
     * @param oid the oid of the information desired.
     * @return the information desired. Might be null.
     * @exception GSSException containing the following major error codes:
     *            <code>GSSException.FAILURE</code>
     */
    public Object inquireByOid(Oid oid) throws GSSException {
        if (oid == null) {
            throw new GlobusGSSException(GSSException.FAILURE, GlobusGSSException.BAD_ARGUMENT, "nullOption");
        }

        if (oid.equals(GSSConstants.X509_CERT_CHAIN)) {
            if (isEstablished()) {
                // converting certs is slower but keeping coverted certs
                // takes lots of memory.
                try {
                    /*DEL
                                        Vector peerCerts = this.conn.getCertificateChain();
                    */
                    Certificate[] peerCerts;
                    try {
                        peerCerts = this.sslEngine.getSession().getPeerCertificates();
                    } catch (SSLPeerUnverifiedException e) {
                        peerCerts = null;
                    }
                    if (peerCerts != null && peerCerts.length > 0) {
                        /*DEL
                                                return PureTLSUtil.certificateChainToArray(peerCerts);
                        */
                        return (X509Certificate[]) peerCerts;
                    } else {
                        return null;
                    }
                } catch (Exception e) {
                    throw new GlobusGSSException(GSSException.DEFECTIVE_CREDENTIAL, e);
                }
            }
        } else if (oid.equals(GSSConstants.RECEIVED_LIMITED_PROXY)) {
            return this.peerLimited;
        }

        return null;
    }

    public void setBannedCiphers(String[] ciphers) {
        bannedCiphers = new String[ciphers.length];
        System.arraycopy(ciphers, 0, bannedCiphers, 0, ciphers.length);
    }

    // ==================================================================
    // Not implemented below
    // ==================================================================

    /**
     * Currently not implemented.
     */
    public int getWrapSizeLimit(int qop, boolean confReq, int maxTokenSize) throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
    }

    /**
     * Currently not implemented.
     */
    public void wrap(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
    }

    /**
     * Currently not implemented.
     */
    public void unwrap(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
    }

    /**
     * Currently not implemented.
     */
    public void getMIC(InputStream inStream, OutputStream outStream, MessageProp msgProp) throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
    }

    /**
     * Currently not implemented.
     */
    public void verifyMIC(InputStream tokStream, InputStream msgStream, MessageProp msgProp) throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
    }

    /**
     * Currently not implemented.
     */
    public void setChannelBinding(ChannelBinding cb) throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
    }

    /**
     * Currently not implemented.
     */
    public boolean isTransferable() throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
    }

    /**
     * Currently not implemented.
     */
    public byte[] export() throws GSSException {
        throw new GSSException(GSSException.UNAVAILABLE);
    }

}