CliProtocol2.java 3.1 KB
Newer Older
1 2 3 4
package hudson.cli;

import hudson.Extension;
import jenkins.model.Jenkins;
K
Kohsuke Kawaguchi 已提交
5
import org.jenkinsci.Symbol;
6
import org.jenkinsci.remoting.nio.NioChannelHub;
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.Signature;

/**
 * {@link CliProtocol} Version 2, which adds transport encryption.
 *
 * @author Kohsuke Kawaguchi
 * @since 1.467
 */
K
Kohsuke Kawaguchi 已提交
24
@Extension @Symbol("cli2")
25 26 27 28 29 30 31 32
public class CliProtocol2 extends CliProtocol {
    @Override
    public String getName() {
        return "CLI2-connect";
    }

    @Override
    public void handle(Socket socket) throws IOException, InterruptedException {
33
        new Handler2(nio.getHub(), socket).run();
34 35 36
    }

    protected static class Handler2 extends Handler {
37 38 39 40
        /**
         * @deprecated as of 1.559
         *      Use {@link #Handler2(NioChannelHub, Socket)}
         */
41
        @Deprecated
42 43 44 45
        public Handler2(Socket socket) {
            super(socket);
        }

46 47 48 49
        public Handler2(NioChannelHub hub, Socket socket) {
            super(hub, socket);
        }

50 51 52 53 54 55 56 57 58 59 60 61 62 63
        @Override
        public void run() throws IOException, InterruptedException {
            try {
                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
                out.writeUTF("Welcome");

                // perform coin-toss and come up with a session key to encrypt data
                Connection c = new Connection(socket);
                byte[] secret = c.diffieHellman(true).generateSecret();
                SecretKey sessionKey = new SecretKeySpec(Connection.fold(secret,128/8),"AES");
                c = c.encryptConnection(sessionKey,"AES/CFB8/NoPadding");

                try {
                    // HACK: TODO: move the transport support into modules
64
                    Class<?> cls = Jenkins.getActiveInstance().pluginManager.uberClassLoader.loadClass("org.jenkinsci.main.modules.instance_identity.InstanceIdentity");
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
                    Object iid = cls.getDeclaredMethod("get").invoke(null);
                    PrivateKey instanceId = (PrivateKey)cls.getDeclaredMethod("getPrivate").invoke(iid);

                    // send a signature to prove our identity
                    Signature signer = Signature.getInstance("SHA1withRSA");
                    signer.initSign(instanceId);
                    signer.update(secret);
                    c.writeByteArray(signer.sign());
                } catch (ClassNotFoundException e) {
                    throw new Error(e);
                } catch (IllegalAccessException e) {
                    throw new Error(e);
                } catch (InvocationTargetException e) {
                    throw new Error(e);
                } catch (NoSuchMethodException e) {
                    throw new Error(e);
                }

                runCli(c);
            } catch (GeneralSecurityException e) {
85
                throw new IOException("Failed to encrypt the CLI channel",e);
86 87 88 89
            }
        }
    }
}