package com.greenaddress.greenapi; import android.util.Log; import com.blockstream.libwally.Wally; import java.security.KeyStore; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import io.netty.handler.ssl.util.SimpleTrustManagerFactory; import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.InternalThreadLocalMap; public class FingerprintTrustManagerFactorySHA256 extends SimpleTrustManagerFactory { private static final Pattern FINGERPRINT_PATTERN = Pattern.compile("^[0-9a-fA-F:]+$"); private static final Pattern FINGERPRINT_STRIP_PATTERN = Pattern.compile(":"); private static final int SHA256_HEX_LEN = Wally.SHA256_LEN * 2; private final TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(final X509Certificate[] chain, final String s) throws CertificateException { checkTrusted("client", chain); } @Override public void checkServerTrusted(final X509Certificate[] chain, final String s) throws CertificateException { checkTrusted("server", chain); } private void checkTrusted(final String type, final X509Certificate[] chain) throws CertificateException { boolean found = false; for (final X509Certificate cert : chain) { for (final byte[] allowedFingerprint: fingerprints) { if (Arrays.equals(fingerprint(cert), allowedFingerprint)) { found = true; break; } } } if (!found) { Log.e("SSL", type + " certificate with unknown fingerprint: " + chain[0].getSubjectDN()); throw new CertificateException( type + " certificate with unknown fingerprint: " + chain[0].getSubjectDN()); } } private byte[] fingerprint(final X509Certificate cert) throws CertificateEncodingException { return Wally.sha256(cert.getEncoded()); } @Override public X509Certificate[] getAcceptedIssuers() { return EmptyArrays.EMPTY_X509_CERTIFICATES; } }; private final byte[][] fingerprints; public FingerprintTrustManagerFactorySHA256(final String... fp) { final byte[][] fingerprints = toFingerprintArray(Arrays.asList(fp)); final List<byte[]> list = InternalThreadLocalMap.get().arrayList(); for (final byte[] f: fingerprints) { if (f == null) { break; } if (f.length != Wally.SHA256_LEN) { throw new IllegalArgumentException("malformed fingerprint: " + Wally.hex_from_bytes(f) + " (expected: SHA256)"); } list.add(f.clone()); } this.fingerprints = list.toArray(new byte[list.size()][]); } private static byte[][] toFingerprintArray(final Iterable<String> fingerprints) { if (fingerprints == null) { throw new NullPointerException("fingerprints"); } final List<byte[]> list = InternalThreadLocalMap.get().arrayList(); for (String f: fingerprints) { if (f == null) { break; } if (!FINGERPRINT_PATTERN.matcher(f).matches()) { throw new IllegalArgumentException("malformed fingerprint: " + f); } f = FINGERPRINT_STRIP_PATTERN.matcher(f).replaceAll(""); if (f.length() != SHA256_HEX_LEN) { throw new IllegalArgumentException("malformed fingerprint: " + f + " (expected: SHA256)"); } final byte[] farr = new byte[Wally.SHA256_LEN]; for (int i = 0; i < farr.length; i++) { final int strIdx = i << 1; farr[i] = (byte) Integer.parseInt(f.substring(strIdx, strIdx + 2), 16); } list.add(farr); } return list.toArray(new byte[list.size()][]); } @Override protected void engineInit(final KeyStore keyStore) throws Exception { } @Override protected void engineInit(final ManagerFactoryParameters managerFactoryParameters) throws Exception { } @Override protected TrustManager[] engineGetTrustManagers() { return new TrustManager[] { tm }; } }