Ошибка [Ljava.lang.Object; нельзя преобразовать в [Ljava.security.cert.X509Certificate при преобразовании массива X509Certificate в массив

Я очень новичок в этом SSL и сертификаты. У меня есть файл JKS, который состоит из некоторых сертификатов, которым доверяет сервер. Я пытаюсь прочитать все сертификаты из файла JKS и вернуть его в метод getAcceptedIssuers(). Сертификаты имеют тип X509Сертификат. Метод, который я реализовал, правильно читает файл jks и создает массив сертификатов X509Certificate. Далее, когда я пытаюсь преобразовать массив в массив, я получаю это исключение

[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)

Мой код как следует

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();
    }
}

3 ответа

Решение

Ваша ошибка здесь:

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

Это перегрузка toArray() вернет Object[] и Object[] НЕ МОЖЕТ быть приведен к другому типу массива.

Измените эту строку на эту:

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

Здесь вы выделяете массив правильного типа и размера и передаете его toArray быть заполненным из списка.

(Вы также можете написать это...

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

... но это приведет к ненужному распределению. Пожалуйста, прочитайте Javadocs для toArray(...) для лучшего понимания. (Следует признать, что стоимость такого дополнительного распределения невелика, и вы можете избежать этого, передав предварительно выделенный / совместно используемый массив нулевого размера.))


Пока вы это делаете, измените имена переменных, чтобы они соответствовали соглашениям стиля Java. Это ужасно сложно использовать один и тот же идентификатор для типа и переменной в одной строке кода!

То, что вы хотите привести - это элементы массива, а не сам массив, и поэтому приведение не выполняется. Когда вы делаете toArray из списка без указания типа, вы получаете массив Object, и хотя объекты имеют тип X509Certificate, и поэтому вы можете выгружать каждый из них по отдельности, вы не можете привести сам массив. Если вы попытаетесь разыграть это, то столкнетесь с этим исключением. Ваш список должен быть преобразован в массив элементов типа X509Certificate.

В качестве альтернативы в Java 8 вы можете сделать это:

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

Я также предложил бы продолжать использовать нотацию CamelCase, когда это возможно. Вы используете заглавную букву в качестве первой буквы имени вашей переменной. Это также должно быть более наглядным, поскольку вы возвращаете массив сертификатов, а не только один.

Это:

X509Certificate должно быть что-то вроде этого x509Certificates

Менять

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

в

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

потому что toArray возвращает Object[], который не может быть преобразован в X509Certificate[], даже каждый элемент массива может быть приведен к X509Certificate

Другие вопросы по тегам