com.geoxp.oss.client.OSSClient.java Source code

Java tutorial

Introduction

Here is the source code for com.geoxp.oss.client.OSSClient.java

Source

/*
 * Copyright 2012-2013 Mathias Herberts
 *
 *    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 com.geoxp.oss.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
import java.net.ProxySelector;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
import org.apache.http.util.EntityUtils;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;

import com.geoxp.oss.CryptoHelper;
import com.geoxp.oss.CryptoHelper.SSHAgentClient;
import com.geoxp.oss.CryptoHelper.SSHAgentClient.SSHKey;
import com.geoxp.oss.MasterSecretGenerator;
import com.geoxp.oss.OSS;
import com.geoxp.oss.OSSException;
import com.geoxp.oss.servlet.GuiceServletModule;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class OSSClient {

    /**
     * Name of property containing the OSS instance's RSA public key modulus and public exponent.
     */
    public static final String OSS_RSA = "oss.rsa";

    /**
     * Get an HttpClient able to use a the Java configured proxyHost/Port if needed
     *
     * @returns HttpClient
     */
    private static HttpClient newHttpClient() {
        DefaultHttpClient httpclient = new DefaultHttpClient();
        ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
                httpclient.getConnectionManager().getSchemeRegistry(), ProxySelector.getDefault());
        httpclient.setRoutePlanner(routePlanner);
        return httpclient;
    }

    public static Map<String, String> genMasterSecret(byte[] secret, List<String> pubrings, List<String> pgpkeyids,
            int k) throws OSSException {

        try {

            //
            // Extract public keys from key rings
            //

            List<PGPPublicKey> keys = new ArrayList<PGPPublicKey>();

            for (String pubring : pubrings) {
                List<PGPPublicKey> pubkeys = CryptoHelper.PGPPublicKeysFromKeyRing(pubring);

                for (PGPPublicKey key : pubkeys) {
                    //
                    // Generate hex version of key id
                    //
                    String id = "000000000000000" + Long.toHexString(key.getKeyID()).toLowerCase();
                    id = id.substring(id.length() - 16);

                    //
                    // Add the key if it is in 'keyids'
                    //

                    for (String keyid : pgpkeyids) {
                        if (id.endsWith(keyid.toLowerCase())) {
                            keys.add(key);
                            break;
                        }
                    }
                }
            }

            //
            // If we have fewer keys than the number of ids specified, throw an error
            //

            if (keys.size() < pgpkeyids.size()) {
                throw new OSSException("Some keys not present in the provided key rings.");
            }

            //
            // Generate the master secret
            //

            Map<PGPPublicKey, byte[]> shares = MasterSecretGenerator.generate(secret, keys, k);

            //
            // Produce JSON output
            //

            Map<String, String> strshares = new HashMap<String, String>();

            for (Entry<PGPPublicKey, byte[]> entry : shares.entrySet()) {
                String id = "000000000000000" + Long.toHexString(entry.getKey().getKeyID());
                id = id.substring(id.length() - 16);

                strshares.put(id, new String(entry.getValue(), "UTF-8"));
            }

            return strshares;
        } catch (Exception e) {
            throw new OSSException(e);
        }
    }

    public static void genSecret(String ossURL, String secretName, String sshKeyFingerprint) throws OSSException {

        SSHAgentClient agent = null;

        HttpClient httpclient = null;

        try {

            agent = new SSHAgentClient();

            List<SSHKey> sshkeys = agent.requestIdentities();

            //
            // If no SSH Key fingerprint was provided, try all SSH keys available in the agent
            //

            List<String> fingerprints = new ArrayList<String>();

            if (null == sshKeyFingerprint) {
                for (SSHKey key : sshkeys) {
                    fingerprints.add(key.fingerprint);
                }
            } else {
                fingerprints.add(sshKeyFingerprint.toLowerCase().replaceAll("[^0-9a-f]", ""));
            }

            int idx = 0;

            for (String fingerprint : fingerprints) {
                idx++;

                //
                // Check if the signing key is available in the agent
                //

                byte[] keyblob = null;

                for (SSHKey key : sshkeys) {
                    if (key.fingerprint.equals(fingerprint)) {
                        keyblob = key.blob;
                        break;
                    }
                }

                //
                // Throw an exception if this condition is encountered as it can only happen if
                // there was a provided fingerprint which is not in the agent.
                //

                if (null == keyblob) {
                    throw new OSSException("SSH Key " + sshKeyFingerprint + " was not found by your SSH agent.");
                }

                //
                // Build OSS Token
                //
                // <TS> <SECRET_NAME> <SSH Signing Key Blob> <SSH Signature Blob>
                //

                ByteArrayOutputStream token = new ByteArrayOutputStream();

                byte[] tsdata = nowBytes();

                token.write(CryptoHelper.encodeNetworkString(tsdata));

                token.write(CryptoHelper.encodeNetworkString(secretName.getBytes("UTF-8")));

                token.write(CryptoHelper.encodeNetworkString(keyblob));

                //
                // Generate signature
                //

                byte[] sigblob = agent.sign(keyblob, token.toByteArray());

                token.write(CryptoHelper.encodeNetworkString(sigblob));

                String b64token = new String(Base64.encode(token.toByteArray()), "UTF-8");

                //
                // Send request
                //

                httpclient = newHttpClient();

                URIBuilder builder = new URIBuilder(ossURL + GuiceServletModule.SERVLET_PATH_GEN_SECRET);

                builder.addParameter("token", b64token);

                URI uri = builder.build();

                String qs = uri.getRawQuery();

                HttpPost post = new HttpPost(
                        uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + uri.getPath());

                post.setHeader("Content-Type", "application/x-www-form-urlencoded");

                post.setEntity(new StringEntity(qs));

                HttpResponse response = httpclient.execute(post);
                post.reset();

                if (HttpServletResponse.SC_OK != response.getStatusLine().getStatusCode()) {
                    // Only throw an exception if this is the last SSH key we could try
                    if (idx == fingerprints.size()) {
                        throw new OSSException("None of the provided keys (" + idx
                                + ") could be used to generate secret. Latest error message was: "
                                + response.getStatusLine().getReasonPhrase());
                    } else {
                        continue;
                    }
                }

                return;
            }
        } catch (OSSException osse) {
            throw osse;
        } catch (Exception e) {
            throw new OSSException(e);
        } finally {
            if (null != httpclient) {
                httpclient.getConnectionManager().shutdown();
            }
            if (null != agent) {
                agent.close();
            }
        }
    }

    public static byte[] getSecret(String ossURL, String secretName, String sshKeyFingerprint) throws OSSException {

        HttpClient httpclient = null;

        SSHAgentClient agent = null;

        try {
            agent = new SSHAgentClient();

            List<SSHKey> sshkeys = agent.requestIdentities();

            //
            // If no SSH Key fingerprint was provided, try all SSH keys available in the agent
            //

            List<String> fingerprints = new ArrayList<String>();

            if (null == sshKeyFingerprint) {
                for (SSHKey key : sshkeys) {
                    fingerprints.add(key.fingerprint);
                }
            } else {
                fingerprints.add(sshKeyFingerprint.toLowerCase().replaceAll("[^0-9a-f]", ""));
            }

            int idx = 0;

            for (String fingerprint : fingerprints) {
                idx++;

                //
                // Check if the signing key is available in the agent
                //

                byte[] keyblob = null;

                for (SSHKey key : sshkeys) {
                    if (key.fingerprint.equals(fingerprint)) {
                        keyblob = key.blob;
                        break;
                    }
                }

                //
                // Throw an exception if this condition is encountered as it can only happen if
                // there was a provided fingerprint which is not in the agent.
                //

                if (null == keyblob) {
                    throw new OSSException("SSH Key " + sshKeyFingerprint + " was not found by your SSH agent.");
                }

                //
                // Generate temporary RSA key pair
                //

                RSAKeyPairGenerator rsagen = new RSAKeyPairGenerator();
                RSAKeyGenerationParameters params = new RSAKeyGenerationParameters(new BigInteger("65537"),
                        CryptoHelper.getSecureRandom(), OSS.DEFAULT_RSA_STRENGTH, 64);
                rsagen.init(params);
                final AsymmetricCipherKeyPair keypair = rsagen.generateKeyPair();

                RSAPrivateKey rsapriv = new RSAPrivateKey() {
                    public BigInteger getModulus() {
                        return ((RSAKeyParameters) keypair.getPrivate()).getModulus();
                    }

                    public String getFormat() {
                        return "PKCS#8";
                    }

                    public byte[] getEncoded() {
                        return null;
                    }

                    public String getAlgorithm() {
                        return "RSA";
                    }

                    public BigInteger getPrivateExponent() {
                        return ((RSAKeyParameters) keypair.getPrivate()).getExponent();
                    }
                };

                RSAPublicKey rsapub = new RSAPublicKey() {
                    public BigInteger getModulus() {
                        return ((RSAKeyParameters) keypair.getPublic()).getModulus();
                    }

                    public String getFormat() {
                        return "PKCS#8";
                    }

                    public byte[] getEncoded() {
                        return null;
                    }

                    public String getAlgorithm() {
                        return "RSA";
                    }

                    public BigInteger getPublicExponent() {
                        return ((RSAKeyParameters) keypair.getPublic()).getExponent();
                    }
                };

                //
                // Build OSS Token
                //
                // <TS> <<SECRET_NAME> <RSA_ENC_KEY>> <SSH Signing Key Blob> <SSH Signature Blob>
                //

                ByteArrayOutputStream token = new ByteArrayOutputStream();

                byte[] tsdata = nowBytes();

                token.write(CryptoHelper.encodeNetworkString(tsdata));

                ByteArrayOutputStream subtoken = new ByteArrayOutputStream();

                subtoken.write(CryptoHelper.encodeNetworkString(secretName.getBytes("UTF-8")));
                subtoken.write(CryptoHelper.encodeNetworkString(CryptoHelper.sshKeyBlobFromPublicKey(rsapub)));

                token.write(CryptoHelper.encodeNetworkString(subtoken.toByteArray()));

                token.write(CryptoHelper.encodeNetworkString(keyblob));

                //
                // Generate signature
                //

                byte[] sigblob = agent.sign(keyblob, token.toByteArray());

                token.write(CryptoHelper.encodeNetworkString(sigblob));

                String b64token = new String(Base64.encode(token.toByteArray()), "UTF-8");

                //
                // Send request
                //

                httpclient = newHttpClient();

                URIBuilder builder = new URIBuilder(ossURL + GuiceServletModule.SERVLET_PATH_GET_SECRET);

                builder.addParameter("token", b64token);

                URI uri = builder.build();

                String qs = uri.getRawQuery();

                HttpPost post = new HttpPost(
                        uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + uri.getPath());

                post.setHeader("Content-Type", "application/x-www-form-urlencoded");

                post.setEntity(new StringEntity(qs));

                HttpResponse response = httpclient.execute(post);
                HttpEntity resEntity = response.getEntity();
                String content = EntityUtils.toString(resEntity, "UTF-8");
                post.reset();

                if (HttpServletResponse.SC_OK != response.getStatusLine().getStatusCode()) {
                    // Only throw an exception if this is the last SSH key we could try
                    if (idx == fingerprints.size()) {
                        throw new OSSException("None of the provided keys (" + idx
                                + ") could be used to retrieve secret. Latest error message was: "
                                + response.getStatusLine().getReasonPhrase());
                    } else {
                        continue;
                    }
                }

                //
                // Extract encrypted secret and sealed key
                //

                byte[] secretandsealedkey = Base64.decode(content);

                byte[] encryptedsecret = CryptoHelper.decodeNetworkString(secretandsealedkey, 0);
                byte[] sealedkey = CryptoHelper.decodeNetworkString(secretandsealedkey, 4 + encryptedsecret.length);

                //
                // Unseal key
                //

                byte[] wrappingkey = CryptoHelper.decryptRSA(rsapriv, sealedkey);

                //
                // Unwrap secret
                //

                return CryptoHelper.unwrapAES(wrappingkey, encryptedsecret);
            }
        } catch (OSSException osse) {
            throw osse;
        } catch (Exception e) {
            throw new OSSException(e);
        } finally {
            if (null != httpclient) {
                httpclient.getConnectionManager().shutdown();
            }
            if (null != agent) {
                agent.close();
            }
        }

        return null;
    }

    public static boolean init(String ossURL, byte[] secret, String sshKeyFingerprint) throws OSSException {

        SSHAgentClient agent = null;

        HttpClient httpclient = newHttpClient();

        try {
            agent = new SSHAgentClient();

            List<SSHKey> sshkeys = agent.requestIdentities();

            //
            // If no SSH Key fingerprint was provided, try all SSH keys available in the agent
            //

            List<String> fingerprints = new ArrayList<String>();

            if (null == sshKeyFingerprint) {
                for (SSHKey key : sshkeys) {
                    fingerprints.add(key.fingerprint);
                }
            } else {
                fingerprints.add(sshKeyFingerprint.toLowerCase().replaceAll("[^0-9a-f]", ""));
            }

            int idx = 0;

            for (String fingerprint : fingerprints) {
                idx++;

                //
                // Ask the SSH agent for the SSH key blob
                //

                byte[] keyblob = null;

                for (SSHKey key : sshkeys) {
                    if (key.fingerprint.equals(fingerprint)) {
                        keyblob = key.blob;
                        break;
                    }
                }

                //
                // Throw an exception if this condition is encountered as it can only happen if
                // there was a provided fingerprint which is not in the agent.
                //

                if (null == keyblob) {
                    throw new OSSException("SSH Key " + sshKeyFingerprint + " was not found by your SSH agent.");
                }

                //
                // Retrieve OSS RSA key
                //

                RSAPublicKey pubkey = getOSSRSA(ossURL);

                //
                // Build the initialization token
                //
                // <TS> <SECRET> <SSH Signing Key Blob> <SSH Signature Blob>
                //

                ByteArrayOutputStream token = new ByteArrayOutputStream();

                byte[] tsdata = nowBytes();

                token.write(CryptoHelper.encodeNetworkString(tsdata));

                token.write(CryptoHelper.encodeNetworkString(secret));

                token.write(CryptoHelper.encodeNetworkString(keyblob));

                byte[] sigblob = agent.sign(keyblob, token.toByteArray());

                token.write(CryptoHelper.encodeNetworkString(sigblob));

                //
                // Encrypt the token with a random AES256 key
                //

                byte[] aeskey = new byte[32];
                CryptoHelper.getSecureRandom().nextBytes(aeskey);

                byte[] wrappedtoken = CryptoHelper.wrapAES(aeskey, token.toByteArray());

                //
                // Encrypt the random key with OSS' RSA key
                //

                byte[] sealedaeskey = CryptoHelper.encryptRSA(pubkey, aeskey);

                //
                // Create the token
                //

                token.reset();

                token.write(CryptoHelper.encodeNetworkString(wrappedtoken));
                token.write(CryptoHelper.encodeNetworkString(sealedaeskey));

                //
                // Base64 encode the encryptedtoken
                //

                String b64token = new String(Base64.encode(token.toByteArray()), "UTF-8");

                //
                // Send request to OSS
                //

                URIBuilder builder = new URIBuilder(ossURL + GuiceServletModule.SERVLET_PATH_INIT);

                builder.addParameter("token", b64token);

                URI uri = builder.build();

                String qs = uri.getRawQuery();

                HttpPost post = new HttpPost(
                        uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + uri.getPath());

                post.setHeader("Content-Type", "application/x-www-form-urlencoded");

                post.setEntity(new StringEntity(qs));

                httpclient = newHttpClient();

                HttpResponse response = httpclient.execute(post);
                HttpEntity resEntity = response.getEntity();
                String content = EntityUtils.toString(resEntity, "UTF-8");

                post.reset();

                if (HttpServletResponse.SC_ACCEPTED == response.getStatusLine().getStatusCode()) {
                    return false;
                } else if (HttpServletResponse.SC_OK != response.getStatusLine().getStatusCode()) {
                    // Only throw an exception if this is the last SSH key we could try
                    if (idx == fingerprints.size()) {
                        throw new OSSException("None of the provided keys (" + idx
                                + ") could be used to initialize this Open Secret Server. Latest error message was: "
                                + response.getStatusLine().getReasonPhrase());
                    } else {
                        continue;
                    }
                }

                return true;
            }

        } catch (OSSException osse) {
            throw osse;
        } catch (Exception e) {
            throw new OSSException(e);
        } finally {
            if (null != httpclient) {
                httpclient.getConnectionManager().shutdown();
            }
            if (null != agent) {
                agent.close();
            }
        }

        return false;
    }

    public static void putSecret(String ossURL, String secretname, byte[] secret, String sshKeyFingerprint)
            throws OSSException {

        SSHAgentClient agent = null;

        HttpClient httpclient = null;

        try {
            agent = new SSHAgentClient();

            List<SSHKey> sshkeys = agent.requestIdentities();

            //
            // If no SSH Key fingerprint was provided, try all SSH keys available in the agent
            //

            List<String> fingerprints = new ArrayList<String>();

            if (null == sshKeyFingerprint) {
                for (SSHKey key : sshkeys) {
                    fingerprints.add(key.fingerprint);
                }
            } else {
                fingerprints.add(sshKeyFingerprint.toLowerCase().replaceAll("[^0-9a-f]", ""));
            }

            int idx = 0;

            for (String fingerprint : fingerprints) {
                idx++;

                //
                // Ask the SSH agent for the SSH key blob
                //

                byte[] keyblob = null;

                for (SSHKey key : sshkeys) {
                    if (key.fingerprint.equals(fingerprint)) {
                        keyblob = key.blob;
                        break;
                    }
                }

                //
                // Throw an exception if this condition is encountered as it can only happen if
                // there was a provided fingerprint which is not in the agent.
                //

                if (null == keyblob) {
                    throw new OSSException("SSH Key " + sshKeyFingerprint + " was not found by your SSH agent.");
                }

                //
                // Retrieve OSS RSA key
                //

                RSAPublicKey pubkey = getOSSRSA(ossURL);

                //
                // Build the token
                //
                // <TS> <<WRAPPED_SECRET><ENCRYPTED_WRAPPING_KEY>> <SSH Signing Key Blob> <SSH Signature Blob>
                //

                ByteArrayOutputStream token = new ByteArrayOutputStream();

                byte[] tsdata = nowBytes();

                token.write(CryptoHelper.encodeNetworkString(tsdata));

                ByteArrayOutputStream subtoken = new ByteArrayOutputStream();

                subtoken.write(CryptoHelper.encodeNetworkString(secretname.getBytes("UTF-8")));
                subtoken.write(CryptoHelper.encodeNetworkString(secret));

                token.write(CryptoHelper.encodeNetworkString(subtoken.toByteArray()));

                token.write(CryptoHelper.encodeNetworkString(keyblob));

                byte[] sigblob = agent.sign(keyblob, token.toByteArray());

                token.write(CryptoHelper.encodeNetworkString(sigblob));

                //
                // Encrypt the token with a random AES256 key
                //

                byte[] aeskey = new byte[32];
                CryptoHelper.getSecureRandom().nextBytes(aeskey);

                byte[] wrappedtoken = CryptoHelper.wrapAES(aeskey, token.toByteArray());

                //
                // Encrypt the random key with OSS' RSA key
                //

                byte[] sealedaeskey = CryptoHelper.encryptRSA(pubkey, aeskey);

                //
                // Create the token
                //

                token.reset();

                token.write(CryptoHelper.encodeNetworkString(wrappedtoken));
                token.write(CryptoHelper.encodeNetworkString(sealedaeskey));

                //
                // Base64 encode the encryptedtoken
                //

                String b64token = new String(Base64.encode(token.toByteArray()), "UTF-8");

                //
                // Send request to OSS
                //

                URIBuilder builder = new URIBuilder(ossURL + GuiceServletModule.SERVLET_PATH_PUT_SECRET);

                builder.addParameter("token", b64token);

                URI uri = builder.build();

                String qs = uri.getRawQuery();

                HttpPost post = new HttpPost(
                        uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + uri.getPath());

                post.setHeader("Content-Type", "application/x-www-form-urlencoded");

                post.setEntity(new StringEntity(qs));

                httpclient = newHttpClient();

                HttpResponse response = httpclient.execute(post);
                HttpEntity resEntity = response.getEntity();
                String content = EntityUtils.toString(resEntity, "UTF-8");

                post.reset();

                if (HttpServletResponse.SC_OK != response.getStatusLine().getStatusCode()) {
                    // Only throw an exception if this is the last SSH key we could try
                    if (idx == fingerprints.size()) {
                        throw new OSSException("None of the provided keys (" + idx
                                + ") could be used to store the secret. Latest error message was: "
                                + response.getStatusLine().getReasonPhrase());
                    } else {
                        continue;
                    }
                }

                return;
            }
        } catch (OSSException osse) {
            throw osse;
        } catch (Exception e) {
            throw new OSSException(e);
        } finally {
            if (null != httpclient) {
                httpclient.getConnectionManager().shutdown();
            }
            if (null != agent) {
                agent.close();
            }
        }
    }

    public static void addACL(String ossURL, String sshKeyFingerprint, String secretname, List<String> keyfpr)
            throws OSSException {
        changeACL(false, ossURL, sshKeyFingerprint, secretname, keyfpr);
    }

    public static void removeACL(String ossURL, String sshKeyFingerprint, String secretname, List<String> keyfpr)
            throws OSSException {
        changeACL(true, ossURL, sshKeyFingerprint, secretname, keyfpr);
    }

    private static void changeACL(boolean remove, String ossURL, String sshKeyFingerprint, String secretname,
            List<String> keyfpr) throws OSSException {

        SSHAgentClient agent = null;

        HttpClient httpclient = null;

        try {
            agent = new SSHAgentClient();

            List<SSHKey> sshkeys = agent.requestIdentities();

            //
            // If no SSH Key fingerprint was provided, try all SSH keys available in the agent
            //

            List<String> fingerprints = new ArrayList<String>();

            if (null == sshKeyFingerprint) {
                for (SSHKey key : sshkeys) {
                    fingerprints.add(key.fingerprint);
                }
            } else {
                fingerprints.add(sshKeyFingerprint.toLowerCase().replaceAll("[^0-9a-f]", ""));
            }

            int idx = 0;

            for (String fingerprint : fingerprints) {
                idx++;

                //
                // Ask the SSH agent for the SSH key blob
                //

                byte[] keyblob = null;

                for (SSHKey key : sshkeys) {
                    if (key.fingerprint.equals(fingerprint)) {
                        keyblob = key.blob;
                        break;
                    }
                }

                //
                // Throw an exception if this condition is encountered as it can only happen if
                // there was a provided fingerprint which is not in the agent.
                //

                if (null == keyblob) {
                    throw new OSSException("SSH Key " + sshKeyFingerprint + " was not found by your SSH agent.");
                }

                //
                // Retrieve OSS RSA key
                //

                RSAPublicKey pubkey = getOSSRSA(ossURL);

                //
                // Build the token
                //
                // <TS> <<SECRET_NAME><FINGERPRINT1>....<FINGERPRINTN>> <SSH Signing Key Blob> <SSH Signature Blob>
                //

                ByteArrayOutputStream token = new ByteArrayOutputStream();

                byte[] tsdata = nowBytes();

                token.write(CryptoHelper.encodeNetworkString(tsdata));

                ByteArrayOutputStream subtoken = new ByteArrayOutputStream();

                subtoken.write(CryptoHelper.encodeNetworkString(secretname.getBytes("UTF-8")));

                for (String fpr : keyfpr) {
                    subtoken.write(CryptoHelper
                            .encodeNetworkString(fpr.toLowerCase().replaceAll("[^a-f0-9]", "").getBytes("UTF-8")));
                }

                token.write(CryptoHelper.encodeNetworkString(subtoken.toByteArray()));
                token.write(CryptoHelper.encodeNetworkString(keyblob));

                byte[] sigblob = agent.sign(keyblob, token.toByteArray());

                token.write(CryptoHelper.encodeNetworkString(sigblob));

                //
                // Encrypt the token with a random AES256 key
                //

                byte[] aeskey = new byte[32];
                CryptoHelper.getSecureRandom().nextBytes(aeskey);

                byte[] wrappedtoken = CryptoHelper.wrapAES(aeskey, token.toByteArray());

                //
                // Encrypt the random key with OSS' RSA key
                //

                byte[] sealedaeskey = CryptoHelper.encryptRSA(pubkey, aeskey);

                //
                // Create the token
                //

                token.reset();

                token.write(CryptoHelper.encodeNetworkString(wrappedtoken));
                token.write(CryptoHelper.encodeNetworkString(sealedaeskey));

                //
                // Base64 encode the encryptedtoken
                //

                String b64token = new String(Base64.encode(token.toByteArray()), "UTF-8");

                //
                // Send request to OSS
                //

                URIBuilder builder = new URIBuilder(ossURL + (remove ? GuiceServletModule.SERVLET_PATH_REMOVE_ACL
                        : GuiceServletModule.SERVLET_PATH_ADD_ACL));

                builder.addParameter("token", b64token);

                URI uri = builder.build();

                String qs = uri.getRawQuery();

                HttpPost post = new HttpPost(
                        uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + uri.getPath());

                post.setHeader("Content-Type", "application/x-www-form-urlencoded");

                post.setEntity(new StringEntity(qs));

                httpclient = newHttpClient();

                HttpResponse response = httpclient.execute(post);
                HttpEntity resEntity = response.getEntity();
                String content = EntityUtils.toString(resEntity, "UTF-8");

                post.reset();

                if (HttpServletResponse.SC_OK != response.getStatusLine().getStatusCode()) {
                    // Only throw an exception if this is the last SSH key we could try
                    if (idx == fingerprints.size()) {
                        throw new OSSException("None of the provided keys (" + idx
                                + ") could be used to modify ACL. Latest error message was: "
                                + response.getStatusLine().getReasonPhrase());
                    } else {
                        continue;
                    }
                }

                return;
            }
        } catch (OSSException osse) {
            throw osse;
        } catch (Exception e) {
            throw new OSSException(e);
        } finally {
            if (null != httpclient) {
                httpclient.getConnectionManager().shutdown();
            }
            if (null != agent) {
                agent.close();
            }
        }
    }

    public static List<String> getACL(String ossURL, String sshKeyFingerprint, String secretName)
            throws OSSException {

        SSHAgentClient agent = null;

        HttpClient httpclient = null;

        try {
            agent = new SSHAgentClient();

            List<SSHKey> sshkeys = agent.requestIdentities();

            //
            // If no SSH Key fingerprint was provided, try all SSH keys available in the agent
            //

            List<String> fingerprints = new ArrayList<String>();

            if (null == sshKeyFingerprint) {
                for (SSHKey key : sshkeys) {
                    fingerprints.add(key.fingerprint);
                }
            } else {
                fingerprints.add(sshKeyFingerprint.toLowerCase().replaceAll("[^0-9a-f]", ""));
            }

            int idx = 0;

            for (String fingerprint : fingerprints) {
                idx++;

                //
                // Check if the signing key is available in the agent
                //

                byte[] keyblob = null;

                for (SSHKey key : sshkeys) {
                    if (key.fingerprint.equals(fingerprint)) {
                        keyblob = key.blob;
                        break;
                    }
                }

                //
                // Throw an exception if this condition is encountered as it can only happen if
                // there was a provided fingerprint which is not in the agent.
                //

                if (null == keyblob) {
                    throw new OSSException("SSH Key " + sshKeyFingerprint + " was not found by your SSH agent.");
                }

                //
                // Generate temporary RSA key pair
                //

                RSAKeyPairGenerator rsagen = new RSAKeyPairGenerator();
                RSAKeyGenerationParameters params = new RSAKeyGenerationParameters(new BigInteger("65537"),
                        CryptoHelper.getSecureRandom(), OSS.DEFAULT_RSA_STRENGTH, 64);
                rsagen.init(params);
                final AsymmetricCipherKeyPair keypair = rsagen.generateKeyPair();

                RSAPrivateKey rsapriv = new RSAPrivateKey() {
                    public BigInteger getModulus() {
                        return ((RSAKeyParameters) keypair.getPrivate()).getModulus();
                    }

                    public String getFormat() {
                        return "PKCS#8";
                    }

                    public byte[] getEncoded() {
                        return null;
                    }

                    public String getAlgorithm() {
                        return "RSA";
                    }

                    public BigInteger getPrivateExponent() {
                        return ((RSAKeyParameters) keypair.getPrivate()).getExponent();
                    }
                };

                RSAPublicKey rsapub = new RSAPublicKey() {
                    public BigInteger getModulus() {
                        return ((RSAKeyParameters) keypair.getPublic()).getModulus();
                    }

                    public String getFormat() {
                        return "PKCS#8";
                    }

                    public byte[] getEncoded() {
                        return null;
                    }

                    public String getAlgorithm() {
                        return "RSA";
                    }

                    public BigInteger getPublicExponent() {
                        return ((RSAKeyParameters) keypair.getPublic()).getExponent();
                    }
                };

                //
                // Build OSS Token
                //
                // <TS> <<SECRET_NAME> <RSA_ENC_KEY>> <SSH Signing Key Blob> <SSH Signature Blob>
                //

                ByteArrayOutputStream token = new ByteArrayOutputStream();

                byte[] tsdata = nowBytes();

                token.write(CryptoHelper.encodeNetworkString(tsdata));

                ByteArrayOutputStream subtoken = new ByteArrayOutputStream();

                subtoken.write(CryptoHelper.encodeNetworkString(secretName.getBytes("UTF-8")));
                subtoken.write(CryptoHelper.encodeNetworkString(CryptoHelper.sshKeyBlobFromPublicKey(rsapub)));

                token.write(CryptoHelper.encodeNetworkString(subtoken.toByteArray()));

                token.write(CryptoHelper.encodeNetworkString(keyblob));

                //
                // Generate signature
                //

                byte[] sigblob = agent.sign(keyblob, token.toByteArray());

                token.write(CryptoHelper.encodeNetworkString(sigblob));

                String b64token = new String(Base64.encode(token.toByteArray()), "UTF-8");

                //
                // Send request
                //

                httpclient = newHttpClient();

                URIBuilder builder = new URIBuilder(ossURL + GuiceServletModule.SERVLET_PATH_GET_ACL);

                builder.addParameter("token", b64token);

                URI uri = builder.build();

                String qs = uri.getRawQuery();

                HttpPost post = new HttpPost(
                        uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort() + uri.getPath());

                post.setHeader("Content-Type", "application/x-www-form-urlencoded");

                post.setEntity(new StringEntity(qs));

                HttpResponse response = httpclient.execute(post);
                HttpEntity resEntity = response.getEntity();
                String content = EntityUtils.toString(resEntity, "UTF-8");
                post.reset();

                if (HttpServletResponse.SC_OK != response.getStatusLine().getStatusCode()) {
                    // Only throw an exception if this is the last SSH key we could try
                    if (idx == fingerprints.size()) {
                        throw new OSSException("None of the provided keys (" + idx
                                + ") could be used to retrieve ACLs. Latest error message was: "
                                + response.getStatusLine().getReasonPhrase());
                    } else {
                        continue;
                    }
                }

                //
                // Extract encrypted list of fingerprints and sealed key
                //

                byte[] fprandsealedkey = Base64.decode(content);

                byte[] encryptedfpr = CryptoHelper.decodeNetworkString(fprandsealedkey, 0);
                byte[] sealedkey = CryptoHelper.decodeNetworkString(fprandsealedkey, 4 + encryptedfpr.length);

                //
                // Unseal key
                //

                byte[] wrappingkey = CryptoHelper.decryptRSA(rsapriv, sealedkey);

                //
                // Unwrap fingerprints
                //

                byte[] fpr = CryptoHelper.unwrapAES(wrappingkey, encryptedfpr);

                int offset = 0;

                List<String> res = new ArrayList<String>();

                while (offset < fpr.length) {
                    byte[] f = CryptoHelper.decodeNetworkString(fpr, offset);

                    if (null == f) {
                        break;
                    }

                    offset += 4 + f.length;

                    if (0 < f.length) {
                        res.add(new String(Hex.encode(f), "UTF-8").replaceAll("([0-9a-f]{2})", "$1:"));
                    }
                }

                return res;
            }
        } catch (OSSException osse) {
            throw osse;
        } catch (Exception e) {
            throw new OSSException(e);
        } finally {
            if (null != httpclient) {
                httpclient.getConnectionManager().shutdown();
            }
            if (null != agent) {
                agent.close();
            }
        }

        return null;
    }

    public static RSAPublicKey getOSSRSA(String ossURL) {

        if (null != System.getProperty(OSS_RSA)) {
            final String[] tokens = System.getProperty(OSS_RSA).split(":");

            RSAPublicKey pubkey = new RSAPublicKey() {
                public BigInteger getModulus() {
                    return new BigInteger(tokens[0]);
                }

                public String getFormat() {
                    return "PKCS#8";
                }

                public byte[] getEncoded() {
                    return null;
                }

                public String getAlgorithm() {
                    return "RSA";
                }

                public BigInteger getPublicExponent() {
                    return new BigInteger(tokens[1]);
                }
            };

            return pubkey;
        } else {
            System.err.println(
                    "Unsecure OSS Instance RSA public key retrieval, you're not protected from Man-In-The-Middle type attacks.");
        }

        HttpClient httpclient = newHttpClient();

        try {
            String getrsauri = ossURL + GuiceServletModule.SERVLET_PATH_GET_OSS_RSA;

            HttpGet get = new HttpGet(getrsauri);

            HttpResponse response = httpclient.execute(get);

            HttpEntity resEntity = response.getEntity();
            String content = EntityUtils.toString(resEntity, "UTF-8");

            get.reset();

            JsonParser parser = new JsonParser();
            JsonElement elt = parser.parse(content);
            final JsonObject rsa = elt.getAsJsonObject();

            RSAPublicKey pubkey = new RSAPublicKey() {
                public BigInteger getModulus() {
                    return new BigInteger(rsa.get("modulus").getAsString());
                }

                public String getFormat() {
                    return "PKCS#8";
                }

                public byte[] getEncoded() {
                    return null;
                }

                public String getAlgorithm() {
                    return "RSA";
                }

                public BigInteger getPublicExponent() {
                    return new BigInteger(rsa.get("exponent").getAsString());
                }
            };

            return pubkey;
        } catch (ClientProtocolException cpe) {
            return null;
        } catch (IOException ioe) {
            return null;
        } finally {
            httpclient.getConnectionManager().shutdown();
        }
    }

    private static byte[] nowBytes() {
        byte[] tsdata = new byte[8];
        long ts = System.currentTimeMillis();

        tsdata[0] = (byte) ((ts >> 56) & 0xff);
        tsdata[1] = (byte) ((ts >> 48) & 0xff);
        tsdata[2] = (byte) ((ts >> 40) & 0xff);
        tsdata[3] = (byte) ((ts >> 32) & 0xff);
        tsdata[4] = (byte) ((ts >> 24) & 0xff);
        tsdata[5] = (byte) ((ts >> 16) & 0xff);
        tsdata[6] = (byte) ((ts >> 8) & 0xff);
        tsdata[7] = (byte) (ts & 0xff);

        return tsdata;
    }
}