'Error [Ljava.lang.Object; cannot be cast to [Ljava.security.cert.X509Certificate when converting an arraylist of X509Certificate to array

I am very new to this ssl and certificates. I have a jks file which consists of some certificates which server trusts. I am trying to read all the certificates from the jks file and return it to getAcceptedIssuers() method. The certificates are of type X509Certificate. The method which I have implemented reads the jks file properly and creates an arraylist of X509Certificate certificates. Next when I try to convert the arraylist to array, I get this exception

[Ljava.lang.Object; cannot be cast to [Ljava.security.cert.X509Certificate;
    at com.sample.ssl.GetCertificates.loadCertificatesFromCompanJks(GetCertificates.java:125)
    at com.sample.ssl.GetCertificates$1.getAcceptedIssuers(GetCertificates.java:44)
    at sun.security.ssl.AbstractTrustManagerWrapper.checkAlgorithmConstraints(Unknown Source)
    at sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(Unknown Source)
    at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(Unknown Source)
    at sun.security.ssl.ClientHandshaker.serverCertificate(Unknown Source)
    at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source)
    at sun.security.ssl.Handshaker.processLoop(Unknown Source)
    at sun.security.ssl.Handshaker.process_record(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
    at sun.security.ssl.SSLSocketImpl.getSession(Unknown Source)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:91)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:397)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121)
    at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:573)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)
    at com.sample.ssl.GetCertificates.postMessage(GetCertificates.java:82)
    at com.sample.ssl.GetCertificates.main(GetCertificates.java:138)

My code is as Follows

public class GetCertificates {
    static private TrustManager[] trustmgr = new TrustManager[]{new X509TrustManager() {

        private X509Certificate[] certs = null;

        public void checkClientTrusted(X509Certificate[] certs, String authType) {
            System.out.println("checkClientTrusted");
        }

        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            System.out.println("checkServerTrusted");
        }

        public X509Certificate[] getAcceptedIssuers() {
            System.out.println("getAcceptedIssuers");
            certs = loadCertificatesFromCompanJks("C:/Users/vinod/Desktop/keystore.jks", "mypassword");
            // return new
            // X509Certificate[]{};
            return certs;
        }
    }};

    public void postMessage() {
        try {
            // here I prepare Url to execute and make a call
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static X509Certificate[] loadCertificatesFromCompanJks(String jksPath, String keyStorePassword) {
        try {
            X509Certificate X509Certificate[] = null;
            Certificate[] certs = null;

            ArrayList<X509Certificate> serverCerts = new ArrayList<X509Certificate>();
            FileInputStream is = new FileInputStream(jksPath);
            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
            String password = keyStorePassword;
            keystore.load(is, password.toCharArray());

            Enumeration e = keystore.aliases();
            for (; e.hasMoreElements(); ) {

                String alias = (String) e.nextElement();
                Certificate cert = keystore.getCertificate(alias);
                X509Certificate cert1 = (X509Certificate) cert;
                serverCerts.add(cert1);
            }
            is.close();
            System.out.println("Number of server certificates : " + serverCerts.size());
            X509Certificate = (java.security.cert.X509Certificate[]) serverCerts.toArray();
            return X509Certificate;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        new GetCertificates().postMessage();
    }
}


Solution 1:[1]

Your mistake is here:

X509Certificate = (java.security.cert.X509Certificate[]) serverCerts.toArray();

That overload of toArray() will return an Object[] and an Object[] CANNOT be cast to a different array type.

Change that line to this:

X509Certificate = serverCerts.toArray(new X509Certificate[serverCerts.size()]);

Here, you are allocating the array of the correct type and size, and passing it to toArray to be filled from the list.

(You could also write this ...

X509Certificate = serverCerts.toArray(new X509Certificate[0]);

... but that will result in an unnecessary allocation. Please read the javadocs for toArray(...) for a better understanding. (Admittedly, the cost of that extra allocation is small, and you could avoid it by passing a pre-allocated / shared zero-sized array.) )


While you are at it, change your variable names to conform to the Java style conventions. It is horribly confusing to use the same identifier for a type and a variable in the same line of code!

Solution 2:[2]

change

        X509Certificate = (java.security.cert.X509Certificate[]) serverCerts.toArray();

to

        X509Certificate = serverCerts.toArray(new java.security.cert.X509Certificate[0]);

because toArray return Object[] which cannot be convert to X509Certificate[] even every element of array can be cast to X509Certificate

Solution 3:[3]

What you want to cast are the elements of the array and not the array itself, and that's why casting fails. When you do a toArray from a list without specifying the type you get an array of Object and though the objects are of type X509Certificate and so you can upcast each of them individually, you cannot cast the array itself. If you try to cast that then you will run into this exception. Your list needs to be converted to an array of elements of type X509Certificate.

As an alternative in Java 8 you can do this:

X509Certificate[] x509Certificates = serverCerts.parallelStream().toArray(X509Certificate[]::new);

I would also suggest to keep using CamelCase notation whenever possible. You are using uppercase letter as a first letter of your variable name. It should also be more descriptive as you are returning an array of certificates and not just one.

This:

X509Certificate should be something like this x509Certificates

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 andy
Solution 3