I found that Kafka 2.7.0 supports PEM certificates and I decided to try setting up the broker with DigiCert SSL certificate. I used new options and I did everything like in example in KIP-651. But I get the error:
[2021-01-20 17:54:55,787] ERROR [KafkaServer id=0] Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer)
org.apache.kafka.common.config.ConfigException: Invalid value javax.net.ssl.SSLHandshakeException: no cipher suites in common for configuration A client SSLEngine created with the provided settings can't connect to a server SSLEngine created with those settings.
at org.apache.kafka.common.security.ssl.SslFactory.configure(SslFactory.java:98)
at org.apache.kafka.common.network.SslChannelBuilder.configure(SslChannelBuilder.java:72)
at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:157)
at org.apache.kafka.common.network.ChannelBuilders.serverChannelBuilder(ChannelBuilders.java:97)
at kafka.network.Processor.<init>(SocketServer.scala:790)
at kafka.network.SocketServer.newProcessor(SocketServer.scala:415)
at kafka.network.SocketServer.$anonfun$addDataPlaneProcessors$1(SocketServer.scala:288)
at kafka.network.SocketServer.addDataPlaneProcessors(SocketServer.scala:287)
at kafka.network.SocketServer.$anonfun$createDataPlaneAcceptorsAndProcessors$1(SocketServer.scala:254)
at kafka.network.SocketServer.$anonfun$createDataPlaneAcceptorsAndProcessors$1$adapted(SocketServer.scala:251)
at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:553)
at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:551)
at scala.collection.AbstractIterable.foreach(Iterable.scala:920)
at kafka.network.SocketServer.createDataPlaneAcceptorsAndProcessors(SocketServer.scala:251)
at kafka.network.SocketServer.startup(SocketServer.scala:125)
at kafka.server.KafkaServer.startup(KafkaServer.scala:303)
at kafka.server.KafkaServerStartable.startup(KafkaServerStartable.scala:44)
at kafka.Kafka$.main(Kafka.scala:82)
at kafka.Kafka.main(Kafka.scala)
openssl x509 -in certificate.pem -text:
Certificate:
...
Signature Algorithm: ecdsa-with-SHA384
...
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
I've tried using different versions of Oracle JDK and OpenJDK, but no effect.
I also tried to set certificate with options:
ssl.keystore.type=PEM
ssl.keystore.location=/opt/kafka/certs/certificate.pem
ssl.key.password=null
And I got а new error:
[2021-01-23 20:33:21,552] ERROR [KafkaServer id=0] Fatal error during KafkaServer startup. Prepare to shutdown (kafka.server.KafkaServer)
org.apache.kafka.common.errors.InvalidConfigurationException: Failed to load PEM SSL keystore /opt/kafka/certs/certificate.pem
Caused by: org.apache.kafka.common.errors.InvalidConfigurationException: Invalid PEM keystore configs
Caused by: java.io.IOException: overrun, bytes = 111
at javax.crypto.EncryptedPrivateKeyInfo.<init>(EncryptedPrivateKeyInfo.java:92)
at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$PemStore.privateKey(DefaultSslEngineFactory.java:512)
at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$PemStore.createKeyStoreFromPem(DefaultSslEngineFactory.java:462)
at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$PemStore.<init>(DefaultSslEngineFactory.java:435)
at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$FileBasedPemStore.load(DefaultSslEngineFactory.java:412)
at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$FileBasedStore.<init>(DefaultSslEngineFactory.java:349)
at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory$FileBasedPemStore.<init>(DefaultSslEngineFactory.java:405)
at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory.createKeystore(DefaultSslEngineFactory.java:293)
at org.apache.kafka.common.security.ssl.DefaultSslEngineFactory.configure(DefaultSslEngineFactory.java:161)
at org.apache.kafka.common.security.ssl.SslFactory.instantiateSslEngineFactory(SslFactory.java:136)
at org.apache.kafka.common.security.ssl.SslFactory.configure(SslFactory.java:93)
at org.apache.kafka.common.network.SslChannelBuilder.configure(SslChannelBuilder.java:72)
at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:157)
at org.apache.kafka.common.network.ChannelBuilders.serverChannelBuilder(ChannelBuilders.java:97)
at kafka.network.Processor.<init>(SocketServer.scala:790)
at kafka.network.SocketServer.newProcessor(SocketServer.scala:415)
at kafka.network.SocketServer.$anonfun$addDataPlaneProcessors$1(SocketServer.scala:288)
at kafka.network.SocketServer.addDataPlaneProcessors(SocketServer.scala:287)
at kafka.network.SocketServer.$anonfun$createDataPlaneAcceptorsAndProcessors$1(SocketServer.scala:254)
at kafka.network.SocketServer.$anonfun$createDataPlaneAcceptorsAndProcessors$1$adapted(SocketServer.scala:251)
at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:553)
at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:551)
at scala.collection.AbstractIterable.foreach(Iterable.scala:920)
at kafka.network.SocketServer.createDataPlaneAcceptorsAndProcessors(SocketServer.scala:251)
at kafka.network.SocketServer.startup(SocketServer.scala:125)
at kafka.server.KafkaServer.startup(KafkaServer.scala:303)
at kafka.server.KafkaServerStartable.startup(KafkaServerStartable.scala:44)
at kafka.Kafka$.main(Kafka.scala:82)
at kafka.Kafka.main(Kafka.scala)
But if convert this certificate to jks:
openssl pkcs12 -export -in certificate.pem -out certificate.p12
keytool -importkeystore -srckeystore certificate.p12 -srcstoretype pkcs12 -destkeystore certificate.jks
echo 'ssl.keystore.location=/opt/kafka/certs/certificate.jks' >>server.properties
echo 'ssl.keystore.password=password' >>server.properties
Broker works correctly.
Could this be a bug or am I doing anything wrong? Full log here
I think this might be because the private key you are using is encrypted with a PBES2 scheme. You can use OpenSSL to convert the original key and use PBES1 instead:
openssl pkcs8 -in old_kafka.key -passout "pass:password" -topk8 -v1 PBE-SHA1-3DES -out kafka.key
You can find more info of which PKCS#5 encryption algorithms are available with PBES1 and PBES2 in OpenSSL's PKCS#8 man page.
Also, PBES2 support in OpenJDK is mentioned in this issue.
Related
I am trying to create self-signed certificate for usage with a Netty (4.1.86) Websocket Server. It's running fine without SSL as well as with a certificate created with the SelfSignedCertificate class. But I am struggling when creating a self signed certificate with openssl.
This:
SslContext sslCtx = SslContextBuilder.forServer(new File(certFile), new File(keyFile), password).build();
Throws the following error:
ERROR Thread-5 com..application.NettyWSServer - Exception caught:
java.lang.IllegalArgumentException: File does not contain valid private key: /home/johnny/testbench/application/app.pkcs8.key
at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:386)
at io.netty.handler.ssl.SslContextBuilder.forServer(SslContextBuilder.java:120)
at com..application.NettyWSServer.start(NettyWSServer.java:78)
at com..application.ApplicationLauncher$2.run(ApplicationLauncherncher.java:315)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.security.NoSuchAlgorithmException: 1.2.840.113549.1.5.13 SecretKeyFactory not available
at java.base/javax.crypto.SecretKeyFactory.<init>(SecretKeyFactory.java:122)
at java.base/javax.crypto.SecretKeyFactory.getInstance(SecretKeyFactory.java:168)
at io.netty.handler.ssl.SslContext.generateKeySpec(SslContext.java:1084)
at io.netty.handler.ssl.SslContext.getPrivateKeyFromByteBuffer(SslContext.java:1170)
at io.netty.handler.ssl.SslContext.toPrivateKey(SslContext.java:1133)
at io.netty.handler.ssl.SslContextBuilder.keyManager(SslContextBuilder.java:384)
... 4 more
Here's how I create certFile (app.pem) and keyFile (app.pkcs8.key):
openssl genrsa -out app.key 2048
openssl pkcs8 -topk8 -in app.key -out app.pkcs8.key
openssl req -x509 -new -nodes -key app.key -sha256 -days 1024 -out app.pem
As per my understanding Netty needs a pkcs8 format key:
https://netty.io/4.1/api/io/netty/handler/ssl/SslContextBuilder.html#forServer-java.io.File-java.io.File-
Working solution thanks to dave_thompson_085:
openssl genrsa -out app.key 2048
openssl pkcs8 -topk8 -v1 PBE-SHA1-3DES -nocrypt -in app.key -out app.pkcs8.key
openssl req -x509 -new -nodes -key app.key -sha256 -days 1024 -out app.pem
More exactly the default SSLContext path needs either PKCS8-unencrypted or PKCS8-encrypted using a password-based encryption (PBE) scheme that is defined uniquely by the OID at the beginning of the AlgorithmIdentifier, like the schemes in PKCS5v1 (now retronymed PBES1) or PKCS12, but not the newer/better family of schemes in PKCS5v2 named PBES2, because the OID for PBES2 -- 1.2.840.113549.1.5.13 which you see in your exception message -- does not identify a single scheme and thus is not sufficient for Java to instantiate a SecretKeyFactory (causing the exception). And openssl pkcs8 -topk8 by default uses a PBES2 scheme since 1.1.0 in 2016.
But the netty 4.1 SSLContext.toPrivateKey method(s) (which you can see and your stacktrace confirms SSLContextBuilder calls) will instead use BouncyCastlePemReader if available, and Bouncy can handle both older PBE schemes and PBES2, and also OpenSSL 'traditional' format files that are not PKCS8 at all (and if/when encrypted use a scheme based on OpenSSL's own PBKDF EVP_BytesToKey).
Thus I think (but can't test) you have 3 options:
supply BouncyCastle. I think you need bcprov (or bcprov-ext) and bcpkix; you may now need bcutil (due to a reorg in recent versions I haven't fully grokked).
encrypt your file with a non-PBES2 scheme. The PBES1 schemes are all now broken or easily breakable; the 'best' (least bad) PKCS12 scheme is officially named pbeWithSHA1And3-KeyTripleDES-CBC (!) but OpenSSL has a more convenient 'shortname':
openssl pkcs8 -topk8 -v1 PBE-SHA1-3DES [-in oldfile -out newfile]
Technically you could also accomplish this by leaving the default on a very old version of OpenSSL, but that's usually more difficult(*) and in my view not really different. (* not for me, because I have my own archive of old versions, but I'm weird)
don't encrypt your keyfile, (only!) if you can adequately secure it otherwise, for example by access controls on your system(s) (and any backups). Use:
openssl pkcs8 -topk8 -nocrypt [-in oldfile -out newfile]
or a bit more simply:
openssl pkey [-in oldfile -out newfile]
I have been provided with:
A private key (-----BEGIN RSA PRIVATE KEY-----)
Intermediate CA cert (-----BEGIN CERTIFICATE-----)
Root CA cert (-----BEGIN CERTIFICATE-----)
SSL connectivity exists and I have proven this successfully using curl;
curl -vv https://thirdparty.service.com --key private.pem --cert cert.crt
However, I wish to establish this SSL connection using Java. Given this, I know I need to import these certificates and key into my Java keystore.
I initially imported the Intermediate and Root CA certs only into my Java keystore but I could not establish a successful SSL connection to the third party service. Based on my curl command, I realised that I need to somehow import the private key into the Java keystore.
I have tried many openssl/keytool commands and this is the current combination/command I have running. I still cannot establish an SSL connection using Java.
cat cert.crt cachain.crt > import.pem
echo "pazzword" > pazzword.txt
openssl pkcs12 -export -in import.pem -inkey privkey.pem -name my_bundle -passout file:pazzword.txt > server.p12
${JAVA_HOME}/bin/keytool -importkeystore -srckeystore server.p12 -destkeystore ${JAVA_HOME}/jre/lib/security/cacerts -srcstoretype pkcs12 -srcstorepass pazzword -deststorepass changeit
Versions:
openjdk version "1.8.0_345"
OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022)
Can someone please help clarify what I should be doing with the certs and key I have above?
A successful SSL connection using my Java
I am trying to implement SSL for my sample Spring Boot application. I was given a example.pem, example.key, example.csr, example.crt and a example.p7b file by the admin for example.com.
I converted my PEM to a DER file by using this command:
openssl x509 -outform der -in example.pem -out example.der
Then, I converted the DER file to a JKS file as:
keytool -import -alias example -keystore example.jks -file example.der
Then, I put the JKS file into the main/resources directory and following in the application.properties file:
server.port=443
# SSL settings
server.ssl.enabled=true
server.ssl.key-store=classpath:example.jks
server.ssl.key-store-password=example
server.ssl.key-password=example
Now, when I hit https://example.com or https://example.com:443 (doesn't matter as HTTPS is on 443), I get the following trace:
04:05:10.667 [qtp555826066-15] DEBUG o.e.jetty.server.HttpConnection -
javax.net.ssl.SSLHandshakeException: no cipher suites in common
at sun.security.ssl.Handshaker.checkThrown(Handshaker.java:1478)
...
Did I converted the wrong file to JKS or did I do something wrong?
EDIT:
I use embedded Jetty server.
UPDATE:
When I try to add the key file to pem and get a pkcs12 file using the command:
openssl pkcs12 -export -inkey example.key -in example.pem -name example -out example.pkcs12
I get example.pkcs12 file but also an error saying:
No certificate matches private key
Is my PEM or KEY files corrupted?
Try as I might, I can't figure out how to use a .p12 file without a password in Java. I've tried setting javax.net.ssl.keyStorePassword to "" but whatever I do I get the following SSL error:
HTTP transport error: javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
All my googling would suggest that the sun implementation will not allow an empty password and of course the keytool won't let you import any certificate without a password for the store.
The Sun API seems to require a password, so you will instead need to add a password to your .p12 file.
This page says that you can do this with openssl by converting the .p12 to a .pem, then converting back to a .p12 (but I have not tried it):
openssl pkcs12 -in cert.p12 -out temp.pem -passin pass: -passout pass:temppassword
openssl pkcs12 -export -in temp.pem -out cert-final.p12 -passin pass:temppassword -passout pass:newpassword
rm -f temp.pem
See also this related question.
I am coding an application where I control the code of both the client and the server.
I am using SSLSockets to implement it.
I have the protocol already running with normal unsecured sockets, but when I try to switch to SSLSockets (using exactly the same protocol), I keep getting the following stack trace:
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:168)
at com.sun.net.ssl.internal.ssl.InputRecord.readFully(InputRecord.java:293)
at com.sun.net.ssl.internal.ssl.InputRecord.read(InputRecord.java:331)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:782)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:739)
For some reason, the exact same code works perfectly with unsecured sockets. Why could this be?
Any feedback would be appreciated. Thank you.
Pablo
From your post it is not possile to detect the problem.
When you switch to secure sockets the most secure ciphers are used by default.
If you have not configured your truststore/keystore correctly (or have not enabled the non-authenticated suites) then the SSL handshake will fail.
The exception seems to indicate that.
What you can do is run your program using javax.net.debug=ssl,handshake to enable SSL debugging info and post the debugging info and your code if you expect someone to help you.
Depending on what OS you are using, it may require admin/root priveledges to bind to or listen to the SSL port. Trying running your application with admin rights (in Windows) or sudo'd (on Linux).
Reasons can vary, -Djavax.net.debug=ssl is your friend, as suggested by Vladimir Dyuzhev.
Anyway, it may be a certificate problem -- make sure you have correct keystore and trustore. You will require one entry in keystore with:
private key
certificate
complete chain of issuer of the certificate
And a truststore:
complete chain of certificates for server certificate
I have problems generating proper keystore (trustore is easy -- just use keytool). For keystore you need st like this (Linux with openssl + java):
# convert all to PEM
openssl x509 -in ${ca}.der -inform DER -outform PEM -out ${ca}.pem
openssl x509 -in ${subca}.der -inform DER -outform PEM -out ${subca}.pem
# create one large PEM file containing certificate chain
cat ${ca}.pem ${subca}.pem > tmp_cert_chain.pem
# generate PKCS#12 BUNDLE
openssl pkcs12 -export -in ${cert}.pem -inkey ${key}.pem -certfile tmp_cert_chain.pem -out tmp_pkcs12.pfx
# convert PKCS#12 bundle to JKS
keytool -importkeystore -srckeystore tmp_pkcs12.pfx -srcstoretype pkcs12 -srcstorepass ${storepass} -destkeystore $keystore -deststoretype jks -deststorepass ${storepass}
# print out JKS keystore
keytool -list -keystore $keystore -storepass $storepass