-->

Java: InvalidAlgorithmParameterException Prime siz

2019-02-15 05:52发布

问题:

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.

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

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");


回答5:

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.



回答6:

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://docs.microsoft.com/en-us/security-updates/securityadvisories/2016/3174644)

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\KeyExchangeAlgorithms\Diffie-Hellman]
"ServerMinKeyBitLength"=dword:00000400