Skip to content

Instantly share code, notes, and snippets.

@pich4ya
Last active November 29, 2019 07:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pich4ya/c7eaaa90e6de37a4a4c067026fa83ffe to your computer and use it in GitHub Desktop.
Save pich4ya/c7eaaa90e6de37a4a4c067026fa83ffe to your computer and use it in GitHub Desktop.
วิธีการไม่ trust all HTTPS cert เวลาต่อ internal API ที่ API server ใช้ cert ที่ issue มาจาก internal root CA
/*
ต้อง
- แน่ใจว่ามีไฟล์ root certificate แล้ว
- ตั้ง $JAVA_HOME ให้ถูกที่ และแน่ใจว่ามีโฟล์ $JAVA_HOME/jre/lib/security/cacerts
- รหัสผ่านของ Java keystore เป็นคำว่า changeit โดยค่า default
วิธีการ
1. ต้องไปดาวน์โหลดหรือขอ root certificate ที่จะใช้มาก่อน
Root certificates contain public information and CAs always make them available for anyone.
$ wget https://sth.sh/demo/STH_Root_CA.pem -O ca.pem
2. แปลง root certificate เป็น DER format
If you were able to obtain the root certificate in DER format, skip this step.
For the root certificate in PEM format:
$ openssl x509 -in ca.pem -inform pem -out ca.der -outform der
3. ยืนยันว่า root certificate ที่แปลงมาถูกต้อง
Ensure that the Java keytool can parse the certificate and display its content:
$ keytool -v -printcert -file ca.der
... output ...
Owner: CN=*.p7z.pw, O=Siam Thanat Hack, L=Bangkok, ST=Bangkok, C=TH
Issuer: CN=*.p7z.pw, O=Siam Thanat Hack, L=Bangkok, ST=Bangkok, C=TH
Serial number: 58af62466935090fa4a43b0206799cd956e28a07
Valid from: Mon Oct 21 20:04:21 ICT 2019 until: Thu Oct 18 20:04:21 ICT 2029
Certificate fingerprints:
SHA1: 5E:F4:48:63:83:E2:D8:4E:6D:C1:B2:0C:07:07:7E:19:04:41:2B:95
SHA256: CB:06:7E:A5:7B:F3:F6:C3:FF:9E:9F:3A:81:F3:94:AA:D5:3B:BE:31:06:87:24:82:37:AD:D4:AC:F5:E8:BA:A7
...
4. Import ไฟล์ root certificate เข้าไปใน Java system root CA store
$ sudo keytool -importcert -alias STHinternalCA -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -file ca.der
... output ...
Trust this certificate? [no]: yes
Certificate was added to keystore
5. ยืนยันว่า root certificate ถูก import สำเร็จแล้ว
$ keytool -keystore "$JAVA_HOME/jre/lib/security/cacerts" -storepass changeit -list | grep -i STHinternalCA
... output ...
sthinternalca, Nov 29, 2019, trustedCertEntry,
ต่อมาตอนรัน web หรือ app ที่เขียนด้วย Java ต้องใส่ออฟชั่น -D และใส่ค่าดังนี้ (ขึ้นกับวิธีการ deployment)
javax.net.ssl.trustStore=/path/to/jre/lib/security/cacerts
javax.net.ssl.trustStorePassword=changeit
ต่อมา Java จะต่อ HTTPS โดยยืนยันผ่าน root cert ใน Java keystore ให้เองเลยไม่ต้องทำอะไร
ตัวอย่างนี้ใช้ javax.net.ssl.HttpsURLConnection
*/
String params = "client_id=" + clientID + "&client_secret=" + clientSecret + "&grant_type=client_credentials";
URL obj = new URL(url);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
BufferedReader in;
// add reuqest header
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "Mozilla/5.0");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(params);
wr.flush();
wr.close();
int responseCode = con.getResponseCode();
if (responseCode >= 400)
in = new BufferedReader(new InputStreamReader(con.getErrorStream()));
else
in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
@pich4ya
Copy link
Author

pich4ya commented Nov 29, 2019

-- วิธีที่ 2

ถ้าไม่ต้องการใส่ custom root cert ใน Java system CAs ก็ใส่แยกเป็นไฟล์

  1. สร้างไฟล์ truststore.jks
    $ keytool -genkey -alias internalAPI -keyalg RSA -keystore truststore.jks -keysize 2048
    ... output ...
    Enter keystore password: // รหัสผ่านนี้ใช้แค่เพื่อเข้า public information + public key
    Re-enter new password:
    What is your first and last name?
    ...
  2. เอา certificate ของ server หรือ root certificate ที่เป็น issuer มาใส่
    $ keytool -import -v -trustcacerts -alias server-alias -file server.cer -keystore truststore.jks -keypass changeit -storepass changeit

แล้วทำตาม https://stackoverflow.com/a/24561444 โดยให้ ยืนยัน HTTPS ด้วย cert ใน keystore ที่เราสร้างมา

FileInputStream myKeys = new FileInputStream("/path/to/truststore.jks");
KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
myTrustStore.load(myKeys, "changeit".toCharArray());
myKeys.close();

tmf = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(myTrustStore);

X509TrustManager myTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        myTm = (X509TrustManager) tm;
        break;
    }
}

final X509TrustManager finalDefaultTm = defaultTm;
final X509TrustManager finalMyTm = myTm;
X509TrustManager customTm = new X509TrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return finalDefaultTm.getAcceptedIssuers();
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        try {
            finalMyTm.checkServerTrusted(chain, authType);
        } catch (CertificateException e) {
            finalDefaultTm.checkServerTrusted(chain, authType);
        }
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        finalDefaultTm.checkClientTrusted(chain, authType);
    }
};


SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { customTm }, null);

SSLContext.setDefault(sslContext);
// แล้วก็เอา SSLContext มาใช้ต่อ API เช่นผ่าน HttpsURLConnection
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

-- วิธีที่ 3
ทำ certificate pinning
https://github.com/Flowdalic/java-pinning

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment