Использование BouncyCastle для простого HTTPS-запроса

Вот упрощенная версия кода, который я использую для выполнения простых HTTPS-запросов:

// Assume the variables host, file and postData have valid String values

final URL url = new URL("https", host, file);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-length", String.valueOf(postData.length()));

final DataOutputStream output = new DataOutputStream(connection.getOutputStream());
output.writeBytes(postData);
output.close();

final InputStream input = new DataInputStream(connection.getInputStream());

for (int c = input.read(); c != -1; c = input.read()) {
  System.out.print((char) c);
}

System.out.println();

input.close();

Раньше это хорошо работало для подключения к нашему серверу (и до сих пор работает, если я использую http в качестве протокола) до недавнего времени, когда были сделаны некоторые обновления безопасности.

Теперь это дает мне ошибки «Не удалось сгенерировать пару ключей DH» и «Размер основного числа должен быть кратен 64 и может варьироваться только от 512 до 1024 (включительно)», упомянутые в этом вопросе:

Java: Почему рукопожатие SSL дает ' Не удалось сгенерировать исключение пары ключей Диффи-Хелмана?

Оказывается, это известная ошибка в Java, и рекомендуется использовать реализацию JCE BouncyCastle.

Мой вопрос... как мне использовать BouncyCastle для чего-то подобного? Или есть еще альтернативы?

Отказ от ответственности: у меня очень мало знаний и интереса к криптологии и базовой технологии, которая делает возможными HTTPS-запросы. Скорее, я бы предпочел сосредоточиться на логике своего приложения и позволить различным библиотекам решать проблемы низкого уровня.

Я проверил веб-сайт и документацию BouncyCastle и погуглил, чтобы узнать больше о JCE и т. Д., Но в целом это довольно сложно, и я не смог найти простых примеров кода для выполнения чего-то вроде приведенного выше кода.


person mjomble    schedule 17.11.2011    source источник


Ответы (4)


В следующем примере кода используются jdk1.6.0_45 и bcprov-jdk15on-153.jar для выполнения простого запроса https:

import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;

import org.bouncycastle.crypto.tls.CertificateRequest;
import org.bouncycastle.crypto.tls.DefaultTlsClient;
import org.bouncycastle.crypto.tls.TlsAuthentication;
import org.bouncycastle.crypto.tls.TlsClientProtocol;
import org.bouncycastle.crypto.tls.TlsCredentials;

public class TestHttpClient {
    // Reference: http://boredwookie.net/index.php/blog/how-to-use-bouncy-castle-lightweight-api-s-tlsclient/
    //            bcprov-jdk15on-153.tar\src\org\bouncycastle\crypto\tls\test\TlsClientTest.java
    public static void main(String[] args) throws Exception {
        java.security.SecureRandom secureRandom = new java.security.SecureRandom();
        Socket socket = new Socket(java.net.InetAddress.getByName("www.google.com"), 443);
        TlsClientProtocol protocol = new TlsClientProtocol(socket.getInputStream(), socket.getOutputStream(),secureRandom);
        DefaultTlsClient client = new DefaultTlsClient() {
            public TlsAuthentication getAuthentication() throws IOException {
                TlsAuthentication auth = new TlsAuthentication() {
                    // Capture the server certificate information!
                    public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate) throws IOException {
                    }

                    public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException {
                        return null;
                    }
                };
                return auth;
            }
        };
        protocol.connect(client);

        java.io.OutputStream output = protocol.getOutputStream();
        output.write("GET / HTTP/1.1\r\n".getBytes("UTF-8"));
        output.write("Host: www.google.com\r\n".getBytes("UTF-8"));
        output.write("Connection: close\r\n".getBytes("UTF-8")); // So the server will close socket immediately.
        output.write("\r\n".getBytes("UTF-8")); // HTTP1.1 requirement: last line must be empty line.
        output.flush();

        java.io.InputStream input = protocol.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        String line;
        while ((line = reader.readLine()) != null)
        {
            System.out.println(line);
        }
    }
}
person oraclesoon    schedule 19.10.2015
comment
Как мне использовать это с apache HTTP cleint 3.1 - person Arvind; 11.06.2018
comment
В этом ответе используется простой JDK с Bouncy Castle. Вы можете найти свой вопрос в apache httpclient bouncy Castle. На мой взгляд, поскольку Httpclient и Bouncy Castle развиваются с разными версиями и поддержкой JDK, может быть сложно найти идеальное соответствие. - person oraclesoon; 12.06.2018
comment
Ваш код заканчивается org.bouncycastle.crypto.tls.TlsNoCloseNotifyException: предупреждение close_notify не получено перед закрытием соединения после распечатки части ответа от Google. - person Mader Levap; 01.07.2018
comment
Я вспомнил, что TlsNoCloseNotifyException как-то связан с Connection: close. Если бы мы явно не запрашивали у сервера закрытие соединения, сервер фактически все еще поддерживал бы активное соединение с этим клиентом Java Socket, но этот пример кода выполнил System.out.println и вышел. Это разорвало активное соединение на уровне TLS и вызвало исключение TlsNoCloseNotifyException. - person oraclesoon; 10.07.2018
comment
@oraclesoon правильно сказал, это вызвано только тем, что поток не закрыт; в остальном все работает идеально. Так что добавьте строку reader.close(); в конце, и все будет хорошо. - person HarsH; 26.12.2019

Здесь находится страница о том, как зарегистрировать поставщика JCE Bouncy Castle. Если вы выберете второй вариант, убедитесь, что значение N (порядок предпочтения) таково, что оно предшествует поставщику Sun JCE (и соответствующим образом измените порядок предпочтения регистрации поставщика Sun JCE).

person srkavin    schedule 17.11.2011
comment
Похоже, самой полезной частью этой страницы было примечание, которое я изначально пропустил. Примечание: чтобы в полной мере использовать поставщика, вы должны установить неограниченное количество файлов политик в используемой вами JVM — их можно загрузить с java.sun.com.. После этого мне даже не понадобился BouncyCastle :D. Я опубликую это как полный ответ. - person mjomble; 18.11.2011

Я нашел другое решение (хотя и по ссылке, опубликованной srkavin), которое даже не требует использования BouncyCastle:

После загрузки «Файлов политик юрисдикции неограниченной силы Java Cryptography Extension (JCE)» с Сайт загрузки Java и замена файлов в моей JRE, приведенный выше код заработал без каких-либо изменений.

person mjomble    schedule 18.11.2011

У меня такая же проблема и решение, включая эти строки

Security.addProvider(new BouncyCastleProvider());
System.setProperty("https.protocols", "TLSv1");
person jrabasilio    schedule 12.07.2018