package hudson.cli;
import hudson.Extension;
import jenkins.model.Jenkins;
import org.jenkinsci.Symbol;
import org.jenkinsci.remoting.nio.NioChannelHub;
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
*/
@Extension @Symbol("cli2")
public class CliProtocol2 extends CliProtocol {
@Override
public String getName() {
return "CLI2-connect";
}
/**
* {@inheritDoc}
*/
@Override
public boolean isOptIn() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public String getDisplayName() {
return "Jenkins CLI Protocol/2";
}
@Override
public void handle(Socket socket) throws IOException, InterruptedException {
new Handler2(nio.getHub(), socket).run();
}
protected static class Handler2 extends Handler {
/**
* @deprecated as of 1.559
* Use {@link #Handler2(NioChannelHub, Socket)}
*/
@Deprecated
public Handler2(Socket socket) {
super(socket);
}
public Handler2(NioChannelHub hub, Socket socket) {
super(hub, socket);
}
@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
Class<?> cls = Jenkins.getActiveInstance().pluginManager.uberClassLoader.loadClass("org.jenkinsci.main.modules.instance_identity.InstanceIdentity");
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 | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new Error(e);
}
runCli(c);
} catch (GeneralSecurityException e) {
throw new IOException("Failed to encrypt the CLI channel",e);
}
}
}
}