I have an oracle database that has a server certificate signed by a root ca. I have a client that also has a client certificate that is signed by the same root CA.
I can configure a java application to open a TLS encrypted connection to the server when SSL_CLIENT_AUTHENTICATION is off. However, after I load the CA into the oracle wallet, and try to use the client cert/key to do mtls, the call fails with “unknown ca”
it seems to me that the server is still rejecting the client certificate even though the CA of the client is in the server’s trust store.
Run the oracle express database
podman run -d --name oracle-db --replace -p 1521:1521 -p 2484:2484 -p 1532:1532 -e ORACLE_PWD=password container-registry.oracle.com/database/express
Configure the certificate authority and certificates.
Step 1: Create a Certificate Authority
openssl genrsa -out ca-private-key.pem 2048
openssl req -x509 -new -nodes -key ca-private-key.pem -subj "/C=US/ST=State/L=Locality/O=Organization/CN=localhost" -out ca-cert.pem -days 3650
openssl genrsa -out oracle-private-key.pem 2048
openssl req -new -newkey rsa:2048 -nodes -keyout oracle-private-key.pem -out oracle-csr.pem -subj "/CN=0.0.0.0"
Step 3: Sign the CSR and Issue a Certificate
To sign the CSR and issue a certificate, you can use the openssl
command to use the CA private key to sign the CSR.
openssl x509 -req -in oracle-csr.pem -CA ca-cert.pem -CAkey ca-private-key.pem -CAcreateserial -out oracle-cert.pem -days 3650
Step 4: Add the Certificate to the Oracle Wallet
Copy the certificates into the pod:
podman cp oracle-ssl/oracle-cert.pem oracle-db:cert.crt
podman cp oracle-ssl/server.key oracle-db:client.key
Exec into the pod to run the tls configuration.
podman exec -it oracle-db /bin/bash
export TCPS_CERTS_LOCATION=/home/oracle
/opt/oracle/configTcps.sh 1532 0.0.0.0 pass_word
Manually add the CA.
Step 5: Add the CA Authority to the Java Trust Store of the client.
To add the CA authority to the Java trust store, you can use the keytool
command to import the CA certificate.
You can use the default truststore of the java runtime.
keytool -importcert -v -trustcacerts -alias myauthority -file ca-cert.pem -cacerts $JAVA_HOME/lib/security/cacerts -storepass changeitn
This will add the CA certificate to the Java trust store.
Test
openssl s_client -connect 0.0.0.0:1532 -CAfile ca/server_cert.pem
We can now see in the output that the server is presenting a certificate. That certificate is signed by a trusted ca with respect to us, the client.
We can see a succesfully established SSL session.
CONNECTED(00000003)
Can't use SSL_get_servername
depth=1 C = US, ST = State, L = Locality, O = Organization, CN = localhost
verify return:1
depth=0 CN = 0.0.0.0
verify return:1
---
Certificate chain
0 s:CN = 0.0.0.0
i:C = US, ST = State, L = Locality, O = Organization, CN = localhost
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIC9DCCAdwCFFIj0zwc3kWP23kdU3Xu8sGESzaSMA0GCSqGSIb3DQEBCwUAMFsx
CzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVTdGF0ZTERMA8GA1UEBwwITG9jYWxpdHkx
FTATBgNVBAoMDE9yZ2FuaXphdGlvbjESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0
MDkyMDE1MTMzNFoXDTM0MDkxODE1MTMzNFowEjEQMA4GA1UEAwwHMC4wLjAuMDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ3++Gd/fubc1aHvcjer2+/Z
+9aJ/luA1Utg4n6HC92kh9jcC1DOTx2QLIU1ZRswsRIrrHqsTT7MWGL81WcBczx7
8ql97gL3tPwFxZgelXwu7nm/C4zy6tZJb08gkzRMQ6sqvXr4WrQCZDJxbqXXibwX
m/CEzOY4rAODowbuEXx1EusKf5NUu9p9yh0zJP5w8jZJLLipie7EQrBppPBhGyX0
4VD1N92QO9p4auJsx6H6H60Mak+KbbZU4r+01HZuUya2ooJg/5RGiLCm+NShcfYh
Yxqd5jKStE1X78AGcigyw5BqQcmKKP2cjqH8wbUlGRWv0MEjMgzcl36IuYAvUycC
AwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfT0mwOMEylD3rYTznrYnobjYZjSPtMol
WSr8nf2NKrGJzSO0ziCPPyao2ea9ryvja7CHg+EnBDbnEO7N01jAPNPsaEU6O2Ry
vfWE+xeC7owshhVR+OKcy9vtJALR3R6sx0UiuYWX8CtsABwfv5S7ZUey5PGpNxV/
nwvKVU5TH7EpmxvEjYtHPKj32t/efEsr+IGakUmJzsgZvIVDiCnOKYZsk/1tx8Xi
e2DNsnLmTgpxqzeDWwfB9WbIRCv5hVgh3PxX+41D+2ktr+4MNw8VGYF6lWE9f+qK
S27mRUdy2N69l/TqhK9MtUJVzT1rrJOy6mp/uEpDaZPI8ezjSawACg==
-----END CERTIFICATE-----
subject=CN = 0.0.0.0
issuer=C = US, ST = State, L = Locality, O = Organization, CN = localhost
---
No client certificate CA names sent
Peer signing digest: SHA384
Peer signature type: RSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 1259 bytes and written 415 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: 85984CE01C015B7BCE54A5E27592358FAD096934067DD7DDB482534F3DD089EE
Session-ID-ctx:
Master-Key: F1D08FF9E32D830D17A6909D10953577921D0F15C46280E8A88B294C9FB3076930CA5E28199DE5EE939CE162D66C62E8
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1727018204
Timeout : 7200 (sec)
Verify return code: 0 (ok)
Extended master secret: no
Turn SSL_CLIENT_AUTHENTICATION=TRUE
If we turn the parameter SSL_CLIENT_AUTHENTICATION=TRUE
we will see the following error:
In the oracle trace
TNS-00542: SSL Handshake failed
Oracle error 1: 28860
ORA-28860: Fatal SSL error
nt secondary err code: 29024
nt OS err code: 0
opiodr aborting process unknown ospid (4002) as a result of ORA-609
2024-09-20T15:41:56.282807+00:00
Errors in file /opt/oracle/diag/rdbms/xe/XE/trace/XE_ora_4002.trc:
ORA-00609: could not attach to incoming connection
ORA-28860: Fatal SSL error
In the Java trace:
"CertificateRequest": {
"certificate types": [ecdsa_sign, rsa_sign, dss_sign]
"supported signature algorithms": [ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha224, rsa_sha224, dsa_sha224, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]
"certificate authorities": []
}
)
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.265 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.265 CEST|CertificateRequest.java:809|Unavailable authentication scheme: ecdsa_secp256r1_sha256
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.265 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.265 CEST|CertificateRequest.java:809|Unavailable authentication scheme: ecdsa_secp384r1_sha384
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.265 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.265 CEST|CertificateRequest.java:809|Unavailable authentication scheme: ecdsa_secp521r1_sha512
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.265 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.266 CEST|CertificateRequest.java:809|Unavailable authentication scheme: rsa_pkcs1_sha256
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.266 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.266 CEST|CertificateRequest.java:809|Unavailable authentication scheme: rsa_pkcs1_sha384
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.266 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.266 CEST|CertificateRequest.java:809|Unavailable authentication scheme: rsa_pkcs1_sha512
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.266 CEST|X509Authentication.java:246|No X.509 cert selected for DSA
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.266 CEST|CertificateRequest.java:809|Unavailable authentication scheme: dsa_sha256
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.266 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.266 CEST|CertificateRequest.java:809|Unavailable authentication scheme: ecdsa_sha224
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.267 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.267 CEST|CertificateRequest.java:809|Unavailable authentication scheme: rsa_sha224
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.267 CEST|X509Authentication.java:246|No X.509 cert selected for DSA
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.267 CEST|CertificateRequest.java:809|Unavailable authentication scheme: dsa_sha224
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.267 CEST|X509Authentication.java:246|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.267 CEST|CertificateRequest.java:809|Unavailable authentication scheme: ecdsa_sha1
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.267 CEST|X509Authentication.java:246|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.267 CEST|CertificateRequest.java:809|Unavailable authentication scheme: rsa_pkcs1_sha1
javax.net.ssl|ALL|01|main|2024-09-20 17:41:56.267 CEST|X509Authentication.java:246|No X.509 cert selected for DSA
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.267 CEST|CertificateRequest.java:809|Unavailable authentication scheme: dsa_sha1
javax.net.ssl|WARNING|01|main|2024-09-20 17:41:56.267 CEST|CertificateRequest.java:819|No available authentication scheme
javax.net.ssl|DEBUG|01|main|2024-09-20 17:41:56.268 CEST|ServerHelloDone.java:151|Consuming ServerHelloDone handshake message (
<empty>
)
javax.net.ssl|DEBUG|01|main|2024-09-20 17:41:56.268 CEST|CertificateMessage.java:299|No X.509 certificate for client authentication, use empty Certificate message instead
javax.net.ssl|DEBUG|01|main|2024-09-20 17:41:56.268 CEST|CertificateMessage.java:330|Produced client Certificate handshake message (
"Certificates": <empty list>
)
javax.net.ss
Client authentication
We first need to create a certificate and key for our client.
openssl req -newkey rsa:2048 -keyout client-key.pem -out client-csr.pem -nodes -subj "/CN=Client"
Then we can create a CSR for the certificate.
openssl req -new -key client-key.pem -out client-csr.pem
We sign the certificate using the CA’s private key.
openssl x509 -req -in client-csr.pem -CA ../ca/ca-cert.pem -CAkey ../ca/ca-private-key.pem -CAcreateserial -out client-cert.pem -days 365
We then need to import the certificate into the truststore of our java runtime.
openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name client-cert
keytool -importkeystore -deststorepass changeit -destkeypass changeit -destkeystore $JAVA_HOME/lib/security/cacerts -srckeystore client/client.p12 -srcstoretype PKCS12 -srcstorepass password -alias client-cert
We can then try to run out application:
/home/pat/.sdkman/candidates/java/11.0.23-sem/bin/java -Djavax.net.debug=all -Djavax.net.ssl.keyStore=java/mykeystore.jks -Djavax.net.ssl.keyStorePassword=password -javaagent:/home/pat/.local/share/idea-ce/lib/idea_rt.jar=38277:/home/pat/.local/share/idea-ce/bin -Dfile.encoding=UTF-8 -classpath /home/pat/Projects/clients/blm/oracle-ssl/target/classes:/home/pat/.m2/repository/com/oracle/database/jdbc/ojdbc8/19.8.0.0/ojdbc8-19.8.0.0.jar org.ibm.OracleConnection
Copy the root certificate into the pod
podman cp ca/ca-cert.pem oracle-db:ca-cert.pem
Add the trusted cert to the wallet and restart the listener
orapki wallet add -wallet ${ORACLE_BASE}/oradata/clientWallet/${ORACLE_SID} -trusted_cert -cert /home/oracle/ca-cert.pem
At this point I expect that everything should be fine – server has ROOT CA to verify the client certificate. I see this in my client logs:
javax.net.ssl|DEBUG|01|main|2024-09-23 09:29:17.061 CEST|ServerHelloDone.java:151|Consuming ServerHelloDone handshake message (
<empty>
)
javax.net.ssl|DEBUG|01|main|2024-09-23 09:29:17.061 CEST|CertificateMessage.java:330|Produced client Certificate handshake message (
"Certificates": [
"certificate" : {
"version" : "v1",
"serial number" : "52 23 D3 3C 1C DE 45 8F DB 79 1D 53 75 EE F2 C1 84 4B 36 96",
"signature algorithm": "SHA256withRSA",
"issuer" : "CN=localhost, O=Organization, L=Locality, ST=State, C=US",
"not before" : "2024-09-23 09:15:51.000 CEST",
"not after" : "2025-09-23 09:15:51.000 CEST",
"subject" : "[email protected], CN=localhost, OU=IBM, O=IBM, L=New York, ST=New York, C=US",
"subject public key" : "RSA"}
]
)
but then i see this:
javax.net.ssl|DEBUG|01|main|2024-09-23 09:29:17.074 CEST|SSLEngineInputRecord.java:214|READ: TLSv1.2 alert, length = 2
javax.net.ssl|DEBUG|01|main|2024-09-23 09:29:17.074 CEST|Alert.java:238|Received alert message (
"Alert": {
"level" : "fatal",
"description": "unknown_ca"
}
)
javax.net.ssl|ERROR|01|main|2024-09-23 09:29:17.075 CEST|TransportContext.java:352|Fatal (UNKNOWN_CA): Received fatal alert: unknown_ca (
"throwable" : {
javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:117)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:347)
at java.base/sun.security.ssl.Alert$AlertConsumer.consume(Alert.java:293)
at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:186)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:172)
at java.base/sun.security.ssl.SSLEngineImpl.decode(SSLEngineImpl.java:681)
at java.base/sun.security.ssl.SSLEngineImpl.readReco
I know the CA is in the client side trust store so i assume this is coming from the server, but its not clear from the logs.