Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IllegalStateException thrown by Maven (SSL-related?) when downloading project dependencies

Tags:

java

ssl

maven

I'm setting up a development environment on an ARM machine, with the following versions of Java and Maven, both installed via apt-get:

(xenial)craig@localhost:~$ mvn -version
Apache Maven 3.3.9
Maven home: /usr/share/maven
Java version: 1.8.0_91, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-8-openjdk-armhf/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "3.14.0", arch: "arm", family: "unix"

(xenial)craig@localhost:~$ java -version
openjdk version "1.8.0_91"
OpenJDK Runtime Environment (build 1.8.0_91-8u91-b14-0ubuntu4~16.04.1-b14)
OpenJDK Zero VM (build 25.91-b14, interpreted mode)

However, when I run a mvn clean install on my project, it fails attempting to download a POM file that does exist. (I can visit it in my browser.)

The stacktrace is quite large, but the root seems to be:

Caused by: java.lang.IllegalStateException
    at sun.security.ec.ECDHKeyAgreement.deriveKey(Native Method)
    at sun.security.ec.ECDHKeyAgreement.engineGenerateSecret(ECDHKeyAgreement.java:130)
    at sun.security.ec.ECDHKeyAgreement.engineGenerateSecret(ECDHKeyAgreement.java:163)
    at javax.crypto.KeyAgreement.generateSecret(KeyAgreement.java:648)
    at sun.security.ssl.ECDHCrypt.getAgreedSecret(ECDHCrypt.java:101)
    at sun.security.ssl.ClientHandshaker.serverHelloDone(ClientHandshaker.java:1067)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:348)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
    at sun.security.ssl.Handshaker.process_record(Handshaker.java:914)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1062)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)

There unfortunately isn't much more to it - Maven fails with:

Caused by: org.eclipse.aether.resolution.ArtifactDescriptorException: Failed to read artifact descriptor for org.jacoco:jacoco-maven-plugin:jar:0.7.6.201602180812

And the stack ends with the exception thrown in deriveKey(). Am I missing some crypto library on my machine?

This is a fresh install of Xenial (16.04 LTS).

like image 955
Craig Otis Avatar asked May 11 '16 15:05

Craig Otis


1 Answers

While installing the Oracle JRE is the easy way out, here are the instructions in case you want to use the OpenJDK Zero VM specifically, be it with Maven, SSL, the ECDH key agreement or any other crypto method that's implemented in native code in the OpenJDK default crypto provider.

I've assumed that the ECDHKeyAgreement.deriveKey method fails because it is a native method and the OpenJDK Zero VM as packaged in Ubuntu for Raspberry Pi just can't handle it; I'm not equipped to debug that failure.

Ubuntu packages the BouncyCastle crypto provider which is implemented in pure Java. You need to install it the usual way:

sudo apt install libbcprov-java*

(this also installs the docs)

Then follow the instructions in /usr/share/doc/libbcprov-java/README.Debian to make it the default crypto provider. Specifically, you need to make a link to the provider jar from the ext directory of the JRE, so do a update-java-alternatives -l followed by (in my case - I've been using the defaults provided in Ubuntu 16.04 Raspberry Pi server install - the specific JRE directory may change in time):

sudo ln -s /usr/share/java/bcprov.jar /usr/lib/jvm/java-1.8.0-openjdk-armhf/jre/lib/ext/bcprov.jar

Then edit the /usr/lib/jvm/java-1.8.0-openjdk-armhf/jre/lib/security/java.security file (invoking the editor with sudo), adding the line:

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

...where the crypto providers are listed, and adding 1 to the priority of the ones already there, so that the list looks similar to this:

security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.2=sun.security.provider.Sun
security.provider.3=sun.security.rsa.SunRsaSign
security.provider.4=sun.security.ec.SunEC
security.provider.5=com.sun.net.ssl.internal.ssl.Provider
security.provider.6=com.sun.crypto.provider.SunJCE
security.provider.7=sun.security.jgss.SunProvider
security.provider.8=com.sun.security.sasl.Provider
security.provider.9=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.10=sun.security.smartcardio.SunPCSC

Crypto in Java should now work, albeit very slowly. Here's an example program I've used to confirm that it does, if you don't want to use Maven for that:

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class URLGet {
        public static void main(String[] args) {
                try {
                        URL url = new URL(args[0]);
                        URLConnection urlConnection = url.openConnection();
                        try (
                                InputStream stream = urlConnection.getInputStream();
                        ) {
                                byte[] buf = new byte[4096];
                                int read;
                                while ((read = stream.read(buf, 0, buf.length)) > 0) {
                                        System.out.write(buf, 0, read);
                                }
                        }
                } catch (Exception e) {
                        e.printStackTrace(System.err);
                }
        }
}

Point it at any https URL (e.g. java URLGet https://www.google.com/) to confirm that Java can handle SSL.

like image 190
cynic Avatar answered Nov 10 '22 21:11

cynic