Dodanie niezaufanego certyfikatu https do keystore

Opis problemu

Umożliwienie zestawienia połączenia https do niezaufanej usługi za pomocą aplikacji uruchomionej na maszynie wirtualnej java.

Często na etapie tworzenia usług mamy potrzebę do połączenia do usługi która jest udostępniona po protokole https, ale certyfikat takiej strony nie jest zaufany. Gdy uruchomimy taką w aplikacje na maszynie wirtualnej java która łączy się do niezaufanego adresu https to dostaniemy błąd.

Przykład

Przykładowy skrypt w groovym.

def content = new URL("https://self-signed.badssl.com/").text
println content
~ groovy scratch_1.groovy                                                     
Caught: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        at scratch_1.run(scratch_1.groovy:1)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        ... 1 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
        ... 1 more

Wymagania

  • jre
  • openssl

Rozwiązanie

Aby maszyna wirtualna pozwoliła się połączyć do niezaufanej usługi https wymagane jest:

  • Pobranie certyfikatu niezaufanej usługi
  • (Opcjonalny) stworzenie keystore
  • Dodanie pobranego certyfikatu do keystore

Przydatny może się okazać poniższy skrypt (addToJavaKeystore.sh):

#!/bin/bash

set -e
set -u

command_exists ()
{
    type "$1" >/dev/null 2>&1 || { echo "I require ${1} but it's not installed.  Aborting." >&2; exit 1; }
}

command_exists openssl
command_exists sed

HOST=${1}
PORT=443
JAVA_HOME=${2}
KEYSTORE_FILE=${JAVA_HOME}/jre/lib/security/cacerts
KEYSTORE_PASS=changeit
KEYTOOL_FILE=${JAVA_HOME}/bin/keytool
CERT_FILE=${HOST}.cert

# save the SSL certificate to file
openssl s_client -connect ${HOST}:${PORT} </dev/null \
| sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ${CERT_FILE}

# create a keystore and import certificate
${KEYTOOL_FILE} -import -noprompt -trustcacerts \
-alias ${HOST} -file ${CERT_FILE} \
-keystore ${KEYSTORE_FILE} -storepass ${KEYSTORE_PASS}

# verify we have got it.
${KEYTOOL_FILE} -list -v -keystore ${KEYSTORE_FILE} -storepass ${KEYSTORE_PASS} -alias ${HOST}

# remove cert file
rm -f ${CERT_FILE}

Użycie

Po uruchomieniu skryptu dla domeny self-signed.badssl.com

sudo ./addToJavaKeystore.sh self-signed.badssl.com $JAVA_HOME
~ sudo ./addToJavaKeystore.sh self-signed.badssl.com $JAVA_HOME
depth=0 C = US, ST = California, L = San Francisco, O = BadSSL, CN = *.badssl.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = BadSSL, CN = *.badssl.com
verify error:num=10:certificate has expired
notAfter=Aug  8 21:17:05 2018 GMT
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = BadSSL, CN = *.badssl.com
notAfter=Aug  8 21:17:05 2018 GMT
verify return:1
DONE
Certificate was added to keystore
Alias name: self-signed.badssl.com
Creation date: 2019-10-09
Entry type: trustedCertEntry

Owner: CN=*.badssl.com, O=BadSSL, L=San Francisco, ST=California, C=US
Issuer: CN=*.badssl.com, O=BadSSL, L=San Francisco, ST=California, C=US
Serial number: 86fb4dc8e5dd0f18
Valid from: Mon Aug 08 23:17:05 CEST 2016 until: Wed Aug 08 23:17:05 CEST 2018
Certificate fingerprints:
         MD5:  46:10:F4:1F:93:A3:EE:58:E0:CC:69:BE:1C:71:E0:C0
         SHA1: 64:14:50:D9:4A:65:FA:EB:3B:63:10:28:D8:E8:6C:95:43:1D:B8:11
         SHA256: 28:C9:E8:BA:A6:03:EE:94:00:2E:CA:CD:37:C1:50:91:DC:A6:E1:AC:8E:D4:29:E3:11:89:7C:6C:72:20:34:B0
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3

Extensions: 

#1: ObjectId: 2.5.29.19 Criticality=false
BasicConstraints:[
  CA:false
  PathLen: undefined
]

#2: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  DNSName: *.badssl.com
  DNSName: badssl.com
]

Wykonanie skyptu po dodaniu domeny do keystore:

~ groovy scratch_1.groovy                                      
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="shortcut icon" href="/icons/favicon-red.ico"/>
  <link rel="apple-touch-icon" href="/icons/icon-red.png"/>
  <title>self-signed.badssl.com</title>
  <link rel="stylesheet" href="/style.css">
  <style>body { background: red; }</style>
</head>
<body>
<div id="content">
  <h1 style="font-size: 12vw;">
    self-signed.<br>badssl.com
  </h1>
</div>

</body>
</html>

Udało się połączyć do niezaufanej usługi https. :)