Невозможно выполнить аутентификацию клиента с помощью закрытого ключа PKCS # 1 с хранилищем ключей Java. - PullRequest
0 голосов
/ 03 июля 2019

У меня есть открытый ключ и закрытый ключ (PKCS # 1: т.е. с «BEGIN RSA PRIVATE KEY») в качестве отдельных входных данных String. Эти ключи создаются с помощью следующих команд:

openssl req -new -x509 -keyout ca-key.pem -out ca-cert.pem -days 365 -passout pass:abcd123 -subj "/C=IN/ST=KAD/L=CAT/O=MIN/OU=OPQ/CN=*.xyz.com/emailAddress=xxx.xxxx@xyz.com"
openssl genrsa -out client_private.key 2048
openssl req -new -sha256 -key client_private.key -subj "/C=IN/ST=KAD/L=CAT/O=MIN/OU=OPQ/CN=client.xyx.com" -out client.csr
openssl x509 -req -in client.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client.crt -days 365 -sha256
openssl x509 -in client.crt -out client.pem -outform PEM

где содержимое файла client.csr предоставляется в виде строки открытого ключа и содержимое файла client_private.key предоставляется в виде строки закрытого ключа

Теперь мне нужно создать хранилище ключей java, добавить эти ключи и прагматично экспортировать их в JKS-файл. Я просмотрел несколько ссылок и попробовал пример надувного замка, но я получаю ошибку ниже.

javax.net.ssl.SSLProtocolException: Handshake message sequence violation, 2

Я пишу клиент kafka с включенным SSL и аутентификацией клиента, а также установлен сервер со всеми необходимыми параметрами. Создание доверенного хранилища и командной строки хранилища ключей работает нормально. Но с помощью Java-программы я не могу выполнить аутентификацию клиента (SSL работает нормально). Ниже приведен фрагмент кода:

public void initClientAuthStore(String certString, String keyString)
        throws GeneralSecurityException, IOException {
    clientAuthStore = KeyStore.getInstance(getType());
    clientAuthStore.load(null, getPassword().toCharArray());

    Certificate clientCert = CertificateHandler.getCertificate(certString);
    Certificate[] certificateChain = {trustStoreCertificate, clientCert};

    log.debug("initClientAuthStore: Certificate is created");
    // keyString.getBytes(StandardCharsets.UTF_8);
    //    clientAuthStore.setKeyEntry(
    //        keyStoreAlias, getPrivateKey(keyString), getPassword().toCharArray(), certificate);
    PrivateKey privateKey = getPrivateKey(keyString);
    clientAuthStore.setKeyEntry(
            keyStoreAlias,
            privateKey,
            getPassword().toCharArray(),
            certificateChain);

    log.debug("initClientAuthStore: setting certificate and private key is success.");
}

public PrivateKey getPrivateKey(String key) throws IOException {
    PemObject privateKeyObject;
    PemReader pemReader =
            new PemReader(
                    new InputStreamReader(
                            new ByteArrayInputStream(key.getBytes(Charset.forName("UTF-8"))),
                            StandardCharsets.UTF_8));
    privateKeyObject = pemReader.readPemObject();
    RSAPrivateCrtKeyParameters privateKeyParameter;
    if (privateKeyObject.getType().endsWith("RSA PRIVATE KEY")) {
        log.info("PRIVATE KEY TYPE: pkcs#1");
        // PKCS#1 key
        RSAPrivateKey rsa = RSAPrivateKey.getInstance(privateKeyObject.getContent());
        privateKeyParameter =
                new RSAPrivateCrtKeyParameters(
                        rsa.getModulus(),
                        rsa.getPublicExponent(),
                        rsa.getPrivateExponent(),
                        rsa.getPrime1(),
                        rsa.getPrime2(),
                        rsa.getExponent1(),
                        rsa.getExponent2(),
                        rsa.getCoefficient());
    } else if (privateKeyObject.getType().endsWith("PRIVATE KEY")) {
        log.info("PRIVATE KEY TYPE: pkcs#8");
        // PKCS#8 key
        privateKeyParameter =
                (RSAPrivateCrtKeyParameters) PrivateKeyFactory.createKey(privateKeyObject.getContent());
    } else {
        throw new RuntimeException("Unsupported key type: " + privateKeyObject.getType());
    }

    JcaPEMKeyConverter jcaPemKeyConverter = new JcaPEMKeyConverter();
    PrivateKey privateKey =
            jcaPemKeyConverter.getPrivateKey(
                    PrivateKeyInfoFactory.createPrivateKeyInfo(privateKeyParameter));
    log.info("PRIVATE KEY generated. {}", privateKey.getFormat());
    return privateKey;
}

Ошибка ниже:

javax.net.ssl.SSLProtocolException: Handshake message sequence violation, 2

Не уверен, что не так, и если мне не хватает кодирования / декодирования и т. Д. Я получаю закрытый ключ PKCS # 1 в качестве входного параметра String, и мне приходится использовать надувной замок для преобразования в PKCS # 8, генерации privatekey и добавления в хранилище. Примечание: закрытый ключ является содержимым client_private.key

1 Ответ

1 голос
/ 04 июля 2019

(Не ответ, но слишком много комментариев).

, где содержимое client.csr предоставляется в виде строки открытого ключа ...

CSRэто не открытый ключ, это CSR.Но код, который вы показываете, явно не использует его.Он, очевидно, использует сертификат, который также не является открытым ключом, это сертификат, который содержит открытый ключ.Тем не менее, сертификат, а НЕ открытый ключ ИЛИ CSR, является правильной вещью, которую следует использовать для аутентификации SSL / TLS.

Я получаю закрытый ключ PKCS # 1 в качестве ввода строки, и я должен использоватьНадежный замок для конвертации в PKCS # 8 и генерации privatekey и добавления в хранилище ....

На самом деле вам не нужно конвертировать в PKCS8;PEMParser plus JcaPEMKeyConvertor может конвертировать PKCS1 напрямую в KeyPair, содержащую пригодный для использования (RSA) PrivateKey, что намного проще.Или OpenSSL может легко создать ключ PKCS8, который может прочитать стандартный JCE (без BouncyCastle) - или, в этом отношении, PKCS12, который уже является хранилищем ключей Java без какого-либо преобразования.Но то, что у вас работает, работает.

Теперь мне нужно создать хранилище ключей java, добавить эти ключи и прагматично экспортировать в файл JKS.

В сторону: вы, вероятно, имеете в виду программно, а не прагматично.Ваш код не экспортирует файл хранилища ключей и не нуждается в этом;и вам не нужно специально использовать хранилище ключей JKS, любое поддерживаемое хранилище ключей в памяти должно работать, хотя вы не показываете код, выполняющий соединение и использующий хранилище ключей.Начиная с Java 9 (почти два года назад), Oracle рекомендует , чтобы люди прекратили использовать JKS и вместо этого использовали PKCS12, хотя я сомневаюсь, что они действительно отбросят JKS в ближайшее время.

javax.net.ssl.SSLProtocolException: нарушение последовательности сообщений рукопожатия, 2

Это ваша настоящая проблема, и это не хранилище ключей или ключ, и не должно быть сертификатом.Даже если вы отправляете ключ и подтверждаете, что он не нравится, сервер должен ответить, отклонив аутентификацию в пределах протокола (ов), не нарушая протокол.И 2 - ServerHello, которое должно происходить только за до , когда сертификат отправляется или проверяется в направлении или .Выполните хотя бы одно из следующих действий:

  • стандартная отладка Java SSL / TLS : запустите с помощью sysprop javax.net.debug=ssl,handshake и перехватите / зарегистрируйте вывод.(Ниже Java 11 вы на самом деле можете использовать только ssl, и он автоматически включает handshake.) Это точно покажет, что ваша система отправляет и получает, и позволяет определить, как это неправильно, что должно, по крайней мере, помочь определить, почему.

  • захватите рукопожатие с помощью сетевой трассировки, например wireshark, tcpdump или аналогичной, и посмотрите на нее.Это работает, даже если в Java что-то серьезно не так (чего не должно быть), но его, по крайней мере, немного сложнее декодировать, и если вы не очень осторожны, рискуете включить другие данные в сеть в течение того же периода времени,может быть чувствительным и недоступным для обмена.

  • попытайтесь соединиться с другим инструментом (или инструментами), используя тот же ключ + сертификат и с того же компьютера, если это вообще возможно - это может быть проблема, связанная с сетью, которая зависитна том, где вы подключаетесь.Поскольку у вас есть openssl и у вас уже есть файлы в форматах OpenSSL, проще всего использовать

    openssl s_client -connect $host:$port -key keyfile -cert certfile [see text]

    s_client также является необычным (уникальным AFAIK) в том смысле, что вы можете контролировать, отправляется ли SNI (расширение индикации имени сервера), и в настоящее время со многими серверами это может хорошо влиять на поведение сервера (возможно, вызывая или подавляяошибка).То, отправляет ли Java (JSSE) SNI, зависит от комбинации обычно нескольких факторов, которые вы нам не сообщили: всегда версия Java и, как правило, некоторые детали кода, выполняющего попытку подключения, особенно, использует ли он HttpsURLConnection, новыйj11 + HttpClient, сторонний код, такой как Apache или Google, или пользовательский код.Для выпусков OpenSSL до 1.1.0 SNI не отправляется по умолчанию, и вы должны добавить -servername $host, чтобы отправить его.Для 1.1.1 он отправляется по умолчанию, но не отправляется, если вы добавляете -noservername.

    В этом примере я не указал склад доверенных сертификатов.s_client по умолчанию будет проверять хранилище доверенных сертификатов по умолчанию, которое может или может быть правильным для вашего сервера, но проигнорирует любую ошибку и продолжит работу в любом случае.Если вы отлаживали проблему доверия сертификата, тогда было бы важно сопоставить хранилище доверенных сертификатов OpenSSL с тем, которое используется вашей Java, но не для вашей проблемы.

  • уточните у оператора сервера) как эта проблема воспринимается с их стороны: думают ли они, что с вашим рукопожатием что-то не так, и если да, то что?Какое программное обеспечение (или, как правило, более подходящее промежуточное программное обеспечение) они используют, с какими настройками или опциями?

Если вы добавите (некоторую / достаточную) эту информацию в свой вопрос, я постараюсь обновить ее, чтобы она соответствовала действительности.

...