org.apache.airavata.gfac.impl.task.utils.bes.MyProxyLogon.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.airavata.gfac.impl.task.utils.bes.MyProxyLogon.java

Source

/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.apache.airavata.gfac.impl.task.utils.bes;

import eu.emi.security.authn.x509.CommonX509TrustManager;
import eu.emi.security.authn.x509.X509CertChainValidator;
import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.util.encoders.Base64;

import javax.net.ssl.*;
import javax.security.auth.login.FailedLoginException;
import java.io.*;
import java.net.ProtocolException;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * The MyProxyLogon class provides an interface for retrieving credentials from
 * a MyProxy server.
 * <p/>
 * First, use <code>setHost</code>, <code>setPort</code>,
 * <code>setUsername</code>, <code>setPassphrase</code>,
 * <code>setCredentialName</code>, <code>setLifetime</code> and
 * <code>requestTrustRoots</code> to configure. Then call <code>connect</code>,
 * <code>logon</code>, <code>getCredentials</code>, then
 * <code>disconnect</code>. Use <code>getCertificates</code> and
 * <code>getPrivateKey</code> to access the retrieved credentials, or
 * <code>writeProxyFile</code> or <code>saveCredentialsToFile</code> to
 * write them to a file. Use <code>writeTrustRoots</code>,
 * <code>getTrustedCAs</code>, <code>getCRLs</code>,
 * <code>getTrustRootData</code>, and <code>getTrustRootFilenames</code>
 * for trust root information.
 *
 * (modified for use with UNICORE)
 *
 * @version 1.1
 * @see <a href="http://myproxy.ncsa.uiuc.edu/">MyProxy Project Home Page</a>
 * 
 */
public class MyProxyLogon {

    public final static String version = "1.1";

    private enum State {
        READY, CONNECTED, LOGGEDON, DONE
    }

    public final static String VERSION = "VERSION=MYPROXYv2";
    private final static String GETCOMMAND = "COMMAND=0";
    private final static String TRUSTROOTS = "TRUSTED_CERTS=";
    private final static String USERNAME = "USERNAME=";
    private final static String PASSPHRASE = "PASSPHRASE=";
    private final static String LIFETIME = "LIFETIME=";
    private final static String CREDNAME = "CRED_NAME=";
    public final static String RESPONSE = "RESPONSE=";
    private final static String ERROR = "ERROR=";
    private final static String DN = "CN=ignore";

    public final int DEFAULT_KEY_SIZE = 2048;
    private int keySize = DEFAULT_KEY_SIZE;
    private final static String keyAlg = "RSA";
    private State state = State.READY;
    private String host = "localhost";
    private String username;
    private String credname;
    private char[] passphrase;
    private int port = 7512;
    private int lifetime = 43200;
    private SSLSocket socket;
    private BufferedInputStream socketIn;
    private BufferedOutputStream socketOut;
    private KeyPair keypair;
    private Collection<X509Certificate> certificateChain;
    private String[] trustrootFilenames;
    private String[] trustrootData;
    private KeyManagerFactory keyManagerFactory;
    private TrustManager trustManager;

    /**
     * Constructs a MyProxyLogon object.
     */
    public MyProxyLogon() {
        super();
        host = System.getenv("MYPROXY_SERVER");
        if (host == null) {
            host = "myproxy.teragrid.org";
        }
        String portString = System.getenv("MYPROXY_SERVER_PORT");
        if (portString != null) {
            port = Integer.parseInt(portString);
        }
        username = System.getProperty("user.name");
    }

    /**
     * sets the internal trust manager using the supplied validator
     */
    public void setValidator(X509CertChainValidator validator) {
        CommonX509TrustManager mtm = new CommonX509TrustManager(validator);
        setTrustManager(mtm);
    }

    /**
     * Sets the hostname of the MyProxy server. Defaults to localhost.
     *
     * @param host MyProxy server hostname
     */
    public void setHost(String host) {
        this.host = host;
    }

    /**
     * Sets the port of the MyProxy server. Defaults to 7512.
     *
     * @param port MyProxy server port
     */
    public void setPort(int port) {
        this.port = port;
    }

    /**
     * Sets the key size.
     *
     * @param keySize
     */
    public void setKeySize(int keySize) {
        this.keySize = keySize;
    }

    /**
     * Gets the MyProxy username.
     *
     * @return MyProxy server port
     */
    public String getUsername() {
        return username;
    }

    /**
     * Sets the MyProxy username. Defaults to user.name.
     *
     * @param username MyProxy username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * Sets the optional MyProxy credential name.
     *
     * @param credname credential name
     */
    public void setCredentialName(String credname) {
        this.credname = credname;
    }

    /**
     * Sets the MyProxy passphrase.
     *
     * @param passphrase MyProxy passphrase
     */
    public void setPassphrase(char[] passphrase) {
        this.passphrase = passphrase;
    }

    /**
     * Sets the requested credential lifetime. Defaults to 43200 seconds (12
     * hours).
     *
     * @param seconds Credential lifetime
     */
    public void setLifetime(int seconds) {
        lifetime = seconds;
    }

    /**
     * Gets the certificates returned from the MyProxy server by
     * getCredentials().
     *
     * @return Collection of java.security.cert.Certificate objects
     */
    public Collection<X509Certificate> getCertificates() {
        return certificateChain;
    }

    // for unit testing
    static PrivateKey testingPrivateKey;

    /**
     * Gets the private key generated by getCredentials().
     *
     * @return PrivateKey
     */
    public PrivateKey getPrivateKey() {
        if (testingPrivateKey != null) {
            //for unit testing
            return testingPrivateKey;
        }
        return keypair.getPrivate();
    }

    /**
     * Connects to the MyProxy server at the desired host and port. Requires
     * host authentication via SSL. The host's certificate subject must
     * match the requested hostname. If CA certificates are found in the
     * standard GSI locations, they will be used to verify the server's
     * certificate. If trust roots are requested and no CA certificates are
     * found, the server's certificate will still be accepted.
     */
    public void connect() throws IOException, GeneralSecurityException {
        SSLContext sc = SSLContext.getInstance("SSL");
        if (trustManager == null) {
            throw new IllegalStateException("No trust manager has been set!");
        }
        TrustManager[] trustAllCerts = new TrustManager[] { trustManager };
        sc.init(getKeyManagers(), trustAllCerts, new java.security.SecureRandom());
        SSLSocketFactory sf = sc.getSocketFactory();
        socket = (SSLSocket) sf.createSocket(host, port);
        socket.startHandshake();
        socketIn = new BufferedInputStream(socket.getInputStream());
        socketOut = new BufferedOutputStream(socket.getOutputStream());
        state = State.CONNECTED;
    }

    /**
     * Set the key manager factory for use in client-side SSLSocket
     * certificate-based authentication to the MyProxy server.
     * Call this before connect().
     *
     * @param keyManagerFactory Key manager factory to use
     */
    public void setKeyManagerFactory(KeyManagerFactory keyManagerFactory) {
        this.keyManagerFactory = keyManagerFactory;
    }

    public void setTrustManager(TrustManager trustManager) {
        this.trustManager = trustManager;
    }

    /**
      * Disconnects from the MyProxy server.
      */
    public void disconnect() throws IOException {
        socket.close();
        socket = null;
        socketIn = null;
        socketOut = null;
        state = State.READY;
    }

    /**
     * Logs on to the MyProxy server by issuing the MyProxy GET command.
     */
    public void logon() throws IOException, GeneralSecurityException {
        String line;
        char response;

        if (state != State.CONNECTED) {
            connect();
        }

        socketOut.write('0');
        socketOut.flush();
        socketOut.write(VERSION.getBytes());
        socketOut.write('\n');
        socketOut.write(GETCOMMAND.getBytes());
        socketOut.write('\n');
        socketOut.write(USERNAME.getBytes());
        socketOut.write(username.getBytes());
        socketOut.write('\n');
        socketOut.write(PASSPHRASE.getBytes());
        socketOut.write(new String(passphrase).getBytes());
        socketOut.write('\n');
        socketOut.write(LIFETIME.getBytes());
        socketOut.write(Integer.toString(lifetime).getBytes());
        socketOut.write('\n');
        if (credname != null) {
            socketOut.write(CREDNAME.getBytes());
            socketOut.write(credname.getBytes());
            socketOut.write('\n');
        }
        socketOut.flush();

        line = readLine(socketIn);
        if (line == null) {
            throw new EOFException();
        }
        if (!line.equals(VERSION)) {
            throw new ProtocolException("bad MyProxy protocol VERSION string: " + line);
        }
        line = readLine(socketIn);
        if (line == null) {
            throw new EOFException();
        }
        if (!line.startsWith(RESPONSE) || line.length() != RESPONSE.length() + 1) {
            throw new ProtocolException("bad MyProxy protocol RESPONSE string: " + line);
        }
        response = line.charAt(RESPONSE.length());
        if (response == '1') {
            StringBuffer errString;

            errString = new StringBuffer("MyProxy logon failed");
            while ((line = readLine(socketIn)) != null) {
                if (line.startsWith(ERROR)) {
                    errString.append('\n');
                    errString.append(line.substring(ERROR.length()));
                }
            }
            throw new FailedLoginException(errString.toString());
        } else if (response == '2') {
            throw new ProtocolException("MyProxy authorization RESPONSE not implemented");
        } else if (response != '0') {
            throw new ProtocolException("unknown MyProxy protocol RESPONSE string: " + line);
        }
        while ((line = readLine(socketIn)) != null) {
            if (line.startsWith(TRUSTROOTS)) {
                String filenameList = line.substring(TRUSTROOTS.length());
                trustrootFilenames = filenameList.split(",");
                trustrootData = new String[trustrootFilenames.length];
                for (int i = 0; i < trustrootFilenames.length; i++) {
                    String lineStart = "FILEDATA_" + trustrootFilenames[i] + "=";
                    line = readLine(socketIn);
                    if (line == null) {
                        throw new EOFException();
                    }
                    if (!line.startsWith(lineStart)) {
                        throw new ProtocolException(
                                "bad MyProxy protocol RESPONSE: expecting " + lineStart + " but received " + line);
                    }
                    trustrootData[i] = new String(Base64.decode(line.substring(lineStart.length())));
                }
            }
        }
        state = State.LOGGEDON;
    }

    /**
     * Retrieves credentials from the MyProxy server.
     */
    public void getCredentials() throws IOException, GeneralSecurityException {

        KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(keyAlg);
        keyGenerator.initialize(keySize);
        keypair = keyGenerator.genKeyPair();
        Security.addProvider(new BouncyCastleProvider());

        org.bouncycastle.pkcs.PKCS10CertificationRequest pkcs10 = null;
        try {
            pkcs10 = generateCertificationRequest(DN, keypair);
        } catch (Exception ex) {
            throw new GeneralSecurityException(ex);
        }
        getCredentials(pkcs10.getEncoded());
    }

    public X509Certificate getCertificate() {
        if (certificateChain == null) {
            return null;
        }
        Iterator<X509Certificate> iter = this.certificateChain.iterator();
        return iter.next();
    }

    private KeyManager[] getKeyManagers() {
        return keyManagerFactory != null ? keyManagerFactory.getKeyManagers() : null;
    }

    private void getCredentials(byte[] derEncodedCertRequest) throws IOException, GeneralSecurityException {
        if (state != State.LOGGEDON) {
            logon();
        }
        socketOut.write(derEncodedCertRequest);
        socketOut.flush();
        int numCertificates = socketIn.read();
        if (numCertificates == -1) {
            throw new IOException("Error: connection aborted");
        } else if (numCertificates == 0 || numCertificates < 0) {
            throw new GeneralSecurityException("Error: bad number of certificates sent by server");
        }
        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        certificateChain = new ArrayList<X509Certificate>();
        for (int i = 0; i < numCertificates; i++) {
            X509Certificate c = (X509Certificate) certFactory.generateCertificate(socketIn);
            certificateChain.add(c);
        }
        state = State.DONE;
    }

    private String readLine(InputStream is) throws IOException {
        StringBuffer sb = new StringBuffer();
        for (int c = is.read(); c > 0 && c != '\n'; c = is.read()) {
            sb.append((char) c);
        }
        if (sb.length() > 0) {
            return new String(sb);
        }
        return null;
    }

    private org.bouncycastle.pkcs.PKCS10CertificationRequest generateCertificationRequest(String dn, KeyPair kp)
            throws Exception {
        X500Name subject = new X500Name(dn);
        PublicKey pubKey = kp.getPublic();
        PrivateKey privKey = kp.getPrivate();
        AsymmetricKeyParameter pubkeyParam = PublicKeyFactory.createKey(pubKey.getEncoded());
        SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(pubkeyParam);
        PKCS10CertificationRequestBuilder builder = new PKCS10CertificationRequestBuilder(subject, publicKeyInfo);
        AlgorithmIdentifier signatureAi = new AlgorithmIdentifier(OIWObjectIdentifiers.sha1WithRSA);
        BcRSAContentSignerBuilder signerBuilder = new BcRSAContentSignerBuilder(signatureAi,
                AlgorithmIdentifier.getInstance(OIWObjectIdentifiers.idSHA1));
        AsymmetricKeyParameter pkParam = PrivateKeyFactory.createKey(privKey.getEncoded());
        ContentSigner signer = signerBuilder.build(pkParam);
        return builder.build(signer);
    }
}