10

I implemented a Java program that will connect and execute a command in a remote server using JSCH. The problem is that whenever I tried to connect to the server, I got the following exception:

com.jcraft.jsch.JSchException: Session.connect: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 256 to 2048 (inclusive)

I tried the solution of adding the Bouncy Castle provider in jre/lib and security.provider and it works. But I need to make it project dependent, so I tried to add Bouncy Castle in my build path and add manually the Bouncy Castle provider in my program. But after exporting it to jar, I am still receiving the exception.

package services;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.Security;
import java.util.Iterator;
import java.util.Properties;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class MainService {

public MainService() {
    Security.addProvider(new BouncyCastleProvider()); //Adding BouncyCastlePRovider in security
    // TODO Auto-generated constructor stub
    String report = "";
    StringBuilder sb = new StringBuilder();

    System.out.println("Running the monitoring...");
    System.out.println("Starting printer monitoring...");

    PrinterService ps = new PrinterService(); //A service that connects to the server and executes the commands
    System.out.println("Building report for printer");

    sb.append(ps.buildReport());

    System.out.println("Done building report for printer");
    System.out.println("Finish printer Monitoring...");
    report = sb.toString();
    writeToFile(report,"fai");
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    MainService msrv = new MainService();
}

public void writeToFile(String contents,String report_name){
    try {
        System.out.println("Writing to file...");
        PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(report_name+".html",false)));
        pw.println(contents);
        pw.close();
        System.out.println("Done writing...");
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

Here is my Server utilities that handles server connection:

package utilities;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Properties;

import javax.swing.JOptionPane;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

import entity.Server;

public class ServerUtil {

public ServerUtil() {
    // TODO Auto-generated constructor stub
}

public static Session createSession(Server srv){
    JSch js = new JSch();
    try {
        Session s = js.getSession(srv.getUser().getUsername(), srv.getAddress(), 22);
        s.setPassword(srv.getUser().getPassword());
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        config.put("PreferredAuthentications", "password");
        s.setConfig(config);
        s.connect();
        return s;
    } catch (JSchException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return null;
    }
}

public static ArrayList<String> executeCommands(Session s, String commands){
    ArrayList<String> result = new ArrayList<String>();
    try {
        System.out.println("Creating channel...");
        ChannelExec channel = (ChannelExec) s.openChannel("exec");
        System.out.println("Channel created.");
        System.out.println("Setting commands...");
        channel.setCommand(commands);
        System.out.println("Commands set.");
        System.out.println("Connecting to channel...");
        channel.connect();
        System.out.println("Channel connected.");

        System.out.println("Retrieving output...");
        BufferedReader reader = new BufferedReader(new InputStreamReader(channel.getInputStream()));
        String line;
        while((line = reader.readLine()) != null){
                result.add(line);
        }
        System.out.println("Output retrieved.");
        channel.disconnect();
        System.out.println("Returning result...");
        return result;
    } catch (JSchException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return result;
    }catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return result;
    }
}
}

While debugging, I find out that the error occurs when the printer service tries to connect to the server.

    package services;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.StringTokenizer;

import com.jcraft.jsch.Session;

import entity.Server;
import utilities.DatabaseUtil;
import utilities.ServerUtil;

public class PrinterService {

    private ArrayList<String> server_names;
    private ArrayList<ArrayList<String>> result_server;

    public PrinterService() {
        // TODO Auto-generated constructor stub
        executePrinterMonitoring();
    }

    //Connect to the printer server and process printer monitoring
    public void executePrinterMonitoring(){
        Iterator<Server> it_s = DatabaseUtil.getServers("PRINTER").iterator();
        server_names = new ArrayList<String>();
        result_server = new ArrayList<ArrayList<String>>();
        while(it_s.hasNext()){
            Server svr = it_s.next();
            System.out.println("***********START PRINTER SERVER***********");
            String commands = "lpstat -t | sed '/READY/d'; lpstat -W | sed '/READY/d'";
            Session connect = ServerUtil.createSession(svr);
            StringTokenizer tokenize = new StringTokenizer(commands, ";");
            ArrayList<String> res;
            ArrayList<ArrayList<String>> res2 = new ArrayList<ArrayList<String>>();
            System.out.println("Executing commands...");
            while(tokenize.hasMoreTokens()){
                String comm = tokenize.nextToken().trim();
                res = ServerUtil.executeCommands(connect, comm);
                res2.add(res);
            }
            System.out.println("Done executing commands...");
            System.out.println("Processing results...");
            processPMonitoring(res2,svr.getName());
            connect.disconnect();
            System.out.println("***********END PRINTER SERVER***********");
        }
   }

    //Get the current date, date - 1, and date - 2
    public String getDate(Calendar cal){
        String mon;
        String dy;
        String dy2;
        String dy3;
        String yr;
        int month = cal.get(Calendar.MONTH)+1;
        int day = cal.get(Calendar.DATE);
        int year = cal.get(Calendar.YEAR);
        if(month < 10)
             mon = "0"+month;
        else
             mon = ""+month;
        if(day < 10){
            dy = "0"+day;
        }
        else{
            dy = ""+day;
        }
        yr = (year+"").substring(2, 4);
        String date =  mon+ "/"+dy+"/"+yr;

        return date;
    }

    //Split and process the result from the server.
public void processPMonitoring(ArrayList<ArrayList<String>> s,String servername){

        Iterator<String> res1 = s.get(0).iterator();
        Iterator<String> res2 = s.get(1).iterator();
        ArrayList<String> as = new ArrayList<String>();
        ArrayList<String> fres = new ArrayList<String>();

        Calendar cal = Calendar.getInstance();
        String date1 = getDate(cal);
        cal.add(Calendar.DATE, -1);
        String date2 = getDate(cal);
        cal.add(Calendar.DATE, -1);
        String date3 = getDate(cal);
        int header = 1;

        System.out.println("Checking server:"+servername);
        System.out.println("Getting queued results...");
        while(res1.hasNext()){
            if(header <= 3){
                //as.add(res1.next());
                header++;
            }
            else{
                String curr = res1.next();
                if(curr.contains("@")){
                    if(curr.contains("STDIN")){
                        String f4 = "";
                        String f5 = "";
                        if(res1.hasNext())
                            f4 = res1.next();
                        if(res1.hasNext())
                            f5 = res1.next();

                        if(f4.contains(date1)){
                            as.add(curr);
                        }
                        else if(f4.contains(date2)){
                            as.add(curr);
                        }
                        else if(f4.contains(date3)){
                            as.add(curr);
                        }
                    }
                }
                else{
                    String f1 = curr;
                    String f2 = "";
                    String f3 = "";
                    if(res1.hasNext())
                        f2 = res1.next();
                    if(res1.hasNext())
                        f3 = res1.next();
                    if(f2.contains(date1)){
                        as.add(f1);
                    }
                    else if(f2.contains(date2)){
                        as.add(f1);
                    }
                    else if(f2.contains(date3)){
                        as.add(f1 + " - 3 DAYS OLD!");
                    }
                }
            }
        }

        System.out.println("Done queued results...");
        Iterator<String> g = as.iterator();
        boolean flag = true;
        String cl = "";
        String std = "";
        header = 1;
        System.out.println("Processing queued results...");
        while(res2.hasNext() && g.hasNext()){
            if(header <=2){
                fres.add(res2.next());
                header++;
            }
            else{
                String curr = res2.next();
                if(curr.contains("@")){
                        fres.add(curr);
                        continue;
                }
                if(flag){
                    cl = g.next();
                    if(cl.contains("@") && cl.contains("STDIN")){
                        continue;
                    }
                    int first_st = cl.indexOf("STDIN");
                    int last_ind = 0;
                    for(last_ind = first_st+1;;last_ind++){
                        //System.out.println("Value of CL:"+cl);
                        //System.out.println("Checking for spaces");
                        //System.out.println("STD CURRENT CHAR:"+cl.charAt(last_ind));
                        if(cl.charAt(last_ind) == ' '){
                            break;
                        }
                    }
                    std = cl.substring(first_st, last_ind);

                    flag = false;

                    if(fres.get(fres.size()-1).contains(std)){
                        flag = true;
                        continue;
                    }

                }
                if(curr.contains(std)){
                    fres.add(curr);
                    flag = true;
                }
            }
        }

        System.out.println("Done processing queued results...");
        System.out.println("Post-process queued results...");
        int size = fres.size();
        boolean down = false;
        for(int i=0;i<size;i++){
            if(fres.get(i).contains("@") && fres.get(i).contains("DOWN")){
                down = true;
                fres.remove(i);
                i--;
                size--;
                continue;
            }
            if(down){
                if(fres.get(i).contains("@") && !fres.get(i).contains("DOWN")){
                    down = false;
                    continue;
                }
                fres.remove(i);
                i--;
                size--;
            }
        }
        System.out.println("Done post-processing queued results...");
        //Post-process
        server_names.add(servername);
        result_server.add(fres);
        //fres.add(0,servername);
        //writeToFile(fres,3);
    }

    public String buildReport(){
        String report = "";
        StringBuilder sb = new StringBuilder();
        Timestamp ts = new Timestamp(new Date().getTime());
        sb.append("<table style=\"border:1px solid black; text-align:center;\" rules=\"all\">");
        sb.append("<h1 style=\"margin:0px 0px 0px 50px\">Printer Monitoring as of "+ts.toString()+"</h1>");
        sb.append("<tr style=\"background-color: seagreen\">"
                + "<th style=\"padding: 6px\">SERVER</th>"
                + "<th style=\"padding: 6px\">QUEUE</th>"
                + "<th style=\"padding: 6px\">DEV</th>"
                + "<th style=\"padding: 6px\">STATUS</th>"
                + "<th style=\"padding: 6px\">JOB FILES</th>"
                + "<th style=\"padding: 6px\">USER</th>"
                + "<th style=\"padding: 6px\">PP</th>"
                + "<th style=\"padding: 6px\">%</th>"
                + "<th style=\"padding: 6px\">BLKS</th>"
                + "<th style=\"padding: 6px\">CP</th>"
                + "<th style=\"padding: 6px\">RNK</th>"
                + "</tr>");
        int counter = 0;
        Iterator<String> it_s = server_names.iterator();
        while(it_s.hasNext()){
            sb.append("<tr style=\"background-color: green\"><td style=\"padding: 6px\"><b>"+it_s.next().toUpperCase()+"</b></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>");
            Iterator<String> it_res = result_server.get(counter).iterator();
            if(result_server.get(counter).isEmpty()){
                sb.append("<tr><td></td><td style=\"padding: 6px\"><h5>CLEAN</h5></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>");
            }
            else if(result_server.get(counter).size() == 2){
                sb.append("<tr><td></td><td style=\"padding: 6px\"><h5>CLEAN</h5></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>");
            }
            else{
                while(it_res.hasNext()){
                    String res = it_res.next();
                    if(!res.contains("Dev") && !res.contains("----")){
                        StringTokenizer tok = new StringTokenizer(res," ");
                        sb.append("<tr>");

                        if(tok.countTokens() == 11){
                            sb.append("<td style=\"padding: 6px\"></td>");
                            for(int x=0;x<10;x++){
                                if(x == 3){
                                    sb.append("<td style=\"padding: 6px\">"+tok.nextToken()+" "+tok.nextToken()+"</td>");
                                }
                                else{
                                    sb.append("<td style=\"padding: 6px\">"+tok.nextToken()+"</td>");
                                }
                            }
                        }
                        else{
                            sb.append("<td style=\"padding: 6px\"></td>");
                            sb.append("<td style=\"padding: 6px\"></td>");
                            sb.append("<td style=\"padding: 6px\"></td>");
                            sb.append("<td style=\"padding: 6px\">"+tok.nextToken()+"</td>");
                            sb.append("<td style=\"padding: 6px\">"+tok.nextToken()+" "+tok.nextToken()+"</td>");
                            sb.append("<td style=\"padding: 6px\">"+tok.nextToken()+"</td>");
                            sb.append("<td style=\"padding: 6px\"></td>");
                            sb.append("<td style=\"padding: 6px\"></td>");
                            sb.append("<td style=\"padding: 6px\">"+tok.nextToken()+"</td>");
                            sb.append("<td style=\"padding: 6px\">"+tok.nextToken()+"</td>");
                            sb.append("<td style=\"padding: 6px\">"+tok.nextToken()+"</td>");
                        }
                        sb.append("</tr>");
                    }
                }
            }
            counter++;
        }
        sb.append("</table>");
        sb.append("</br></br>");
        report = sb.toString();
        return report;
    }
}

I would also like to know if there is a way to make Bouncy Castle a light weight, since I need my jar to be < 2MB since I need to transfer it to a server, and I don't have a permission to transfer a file > 2MB.

Thank you for the help. This is my first post in stackoverflow, and I really love this community. Thanks.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Vaanz
  • 175
  • 1
  • 2
  • 14
  • Post your `PrinterService` as well, it should give some clue. – free6om Mar 30 '16 at 04:49
  • 1
    Possible duplicate of [Session.connect: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 256 to 2048 (inclusive)](http://stackoverflow.com/questions/33210630/session-connect-java-security-invalidalgorithmparameterexception-prime-size-mu) –  Mar 30 '16 at 05:22
  • 1
    or http://stackoverflow.com/questions/6851461/java-why-does-ssl-handshake-give-could-not-generate-dh-keypair-exception –  Mar 30 '16 at 05:23
  • How did you generate the keys for the server? The similar questions seem to point to the server's key being too large. – erickson Mar 30 '16 at 05:53
  • Thank you for the reply. I'm using my password as my authentication. I will also try to use the solution in the links that you posted. Thanks. – Vaanz Mar 30 '16 at 06:31
  • I posted a simple workaround that works for us as: https://stackoverflow.com/a/44212319/3225887 – jeffbuhrt May 27 '17 at 02:24

9 Answers9

10

I didn't have the benefit of switching to Ganymed, so I installed the "Bouncy Castle" libraries to replace the security on the JVM. For some reason the Java 8 JVM still does not allow for security keys to be larger than 1024 in length.

  1. Download the jar files from https://www.bouncycastle.org/latest_releases.html (look for jar files that start with 'bcprov-jdk')

  2. Place the jar files under $JAVA_HOME/jre/lib/ext

  3. Edit the java.security file located in $JAVA_HOME/jre/lib/security
  4. Scroll down past the middle of the file and you will find a numbered list of security providers (around 9 or 8). Place a comment for the line of the seecond provider (with a #)
  5. Replace the commented line with this:

    security.provider.2=org.bouncycastle.jce.provider.BouncyCastleProvider

  6. Restart what you must, and try again.

I'm baffled as to why we need to hack the JDK this way. It doesn't inspire a lot of confidence to anybody I mentioned it at work. But since there is poor documentation (or education) on anything relating to security we are treating it as a 'temporary' fix.

Salvador Valencia
  • 1,330
  • 1
  • 17
  • 26
3

I solved a similar problem on oracle java 8 by switching to bouncycastle provider for ssl/tls:

  1. Added bouncycastle to my project

    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.54</version>
    </dependency>
    
  2. Before I do any SSL stuff, I add the BouncyCastle provider as 1st provider to the list:

    Security.insertProviderAt(new BouncyCastleProvider(),1);
    

This works with most stuff that uses sun's SSLSocketFactory, so it might also work with JSch.

Michael Wyraz
  • 3,638
  • 1
  • 27
  • 25
2

I was getting the same error with JGit's use of JSch. I tried a lot of suggestions in this thread to no avail.

But then recently, I noticed that if I used a slightly newer jre than I used before, the error went away.

Just for the record, I was using "jsch-0.1.55.jar" and the two jre's I tried were:

  • JRE 1.7.0_80 (experienced the exception)
  • JRE 1.8.0_191 (made the problem go away)

I can't say for sure whether it was merely the JRE upgrade that resolved the problem or the suggested tweaks from this thread that I made in addition.

All the same, just wanted to share the experience in case it helps someone else.

Gurce
  • 592
  • 7
  • 18
1

I tried using a 2048 bit key that I generate in a server, still I am receiving those error. The solution that I found is to use a different SSH library and the one that works is Ganymed SSH-2, instead of JSch. Thank you for all the suggestions and comments.

Edited: In addition, this library is also light weight ~1MB.

Vaanz
  • 175
  • 1
  • 2
  • 14
1

My workaround was changing this registry key to allow 1024 bit DH keys in Windows 10 (2048 was/is the minimum bit size per https://learn.microsoft.com/en-us/security-updates/securityadvisories/2016/3174644)

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\KeyExchangeAlgorithms\Diffie-Hellman]
"ServerMinKeyBitLength"=dword:00000400
Beau Harder
  • 1,062
  • 1
  • 7
  • 7
  • It works for me for a "java.lang.RuntimeException: Could not generate DH keypair " caused by "java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)" – KJBTech Nov 24 '21 at 11:43
1

My solution to this problem was putting in the first line of my main method the following:

public static void main(String[] args) {
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    ...
}

If you are using Spring you can add instead:

@Configuration
public class AppConfig {
    @PostConstruct
    public void init(){
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }
}
Aliuk
  • 1,249
  • 2
  • 17
  • 32
0

I too faced same issue and resolved it by degrading jar file from jsch-1.5.4 to jsch-1.5.0. Try changing jsch jar file and see which version is suitable for your code. The root cause of issue is due to some method in jsch jar file upgrade, expecting extra input parameter and it is missing in your code.

-1

I was getting this error

ERROR: Message form the remote server : Session.connect: java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive)

I was able to solve it by setting the client version as in this line

session.setClientVersion("SSH-2.0-OpenSSH_2.5.3");

The default value that was causing the error is

session.setClientVersion("SSH-2.0-JSCH-0.1.54");
Tunaki
  • 132,869
  • 46
  • 340
  • 423
-1

Add this line of code after you have the Session instance:

public static Session createSession(Server srv){
   JSch js = new JSch();
   try {
      Session s = js.getSession(srv.getUser().getUsername(), srv.getAddress(), 22);
      s.setPassword(srv.getUser().getPassword());
      Properties config = new Properties();
      config.put("StrictHostKeyChecking", "no");
      config.put("PreferredAuthentications", "password");
      config.put("kex", "diffie-hellman-group1-sha1");
      s.setConfig(config);
      ...

The line to be added is: config.put("kex", "diffie-hellman-group1-sha1");

  • 2
    Please don't suggest some random settings that decreases security level (both `StrictHostKeyChecking=no` and `kex=diffie-hellman-group1-sha1`), without explaining the consequences. – Martin Prikryl May 01 '19 at 16:14
  • Thank you, for my problem the solution "kex=diffie-hellman-group1-sha1" solved my problem – DrakonHaSh Mar 21 '23 at 13:16