Replace javapinning dependency with new PinnedPubKeyTrustManager
This commit is contained in:
parent
557a33fda8
commit
7492f5d23d
@ -9,14 +9,15 @@
|
||||
<artifactId>jDnsProxy</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<name>${project.artifactId}</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>eu.geekplace.javapinning</groupId>
|
||||
<artifactId>java-pinning-java7</artifactId>
|
||||
<version>1.1.0</version>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<name>${project.artifactId}</name>
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
|
@ -1,14 +1,11 @@
|
||||
package com.moparisthebest.dns.net;
|
||||
|
||||
import eu.geekplace.javapinning.java7.Java7Pinning;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.net.*;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
|
||||
import static com.moparisthebest.dns.tls.PinnedPubKeyTrustManager.pinSha256SSLContext;
|
||||
|
||||
public class ParsedUrl {
|
||||
|
||||
private final String urlStr;
|
||||
@ -57,18 +54,7 @@ public class ParsedUrl {
|
||||
SSLSocketFactory sslSocketFactory = null;
|
||||
final String pubKeyPinsSha256 = props.get("pubKeyPinsSha256");
|
||||
if (pubKeyPinsSha256 != null) {
|
||||
final String[] pins = pubKeyPinsSha256.split(",");
|
||||
// todo: ugh java-pinning only supports hex not base64 *and* hashes the cert one time per pin, fix this
|
||||
for (int x = 0; x < pins.length; ++x) {
|
||||
pins[x] = "SHA256:" + bytesToHex(Base64.getDecoder().decode(pins[x]));
|
||||
}
|
||||
final SSLContext sslContext;
|
||||
try {
|
||||
sslContext = Java7Pinning.forPins(pins);
|
||||
} catch (KeyManagementException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("invalid pins", e);
|
||||
}
|
||||
sslSocketFactory = sslContext.getSocketFactory();
|
||||
sslSocketFactory = pinSha256SSLContext(pubKeyPinsSha256.split(",")).getSocketFactory();
|
||||
}
|
||||
if(sslSocketFactory == null && url.getScheme().equals("tls"))
|
||||
sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
|
||||
|
@ -0,0 +1,127 @@
|
||||
package com.moparisthebest.dns.tls;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Given a MessageDigest algorithm name and a list of public key hashes, implements secure TLS certificate verification
|
||||
* via public key pinning.
|
||||
*/
|
||||
public class PinnedPubKeyTrustManager implements X509TrustManager {
|
||||
|
||||
/**
|
||||
* Create TLS SSLContext for a single TrustManager
|
||||
*
|
||||
* @param trustManager TrustManager instance
|
||||
* @return SSLContext
|
||||
*/
|
||||
public static SSLContext sslContext(final TrustManager trustManager) {
|
||||
try {
|
||||
final SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, new TrustManager[]{trustManager}, new SecureRandom());
|
||||
return sslContext;
|
||||
} catch (KeyManagementException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("TLS not supported or ?", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create pinned pubkey SSLContext
|
||||
*
|
||||
* @param sha256Pins HPKP RFC7469 style base64'd sha256 pins
|
||||
* @return SSLContext that only accepts servers with these pubkeys
|
||||
*/
|
||||
public static SSLContext pinSha256SSLContext(final Stream<String> sha256Pins) {
|
||||
return sslContext(pinSha256TrustManager(sha256Pins));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create pinned pubkey SSLContext
|
||||
*
|
||||
* @param sha256Pins HPKP RFC7469 style base64'd sha256 pins
|
||||
* @return SSLContext that only accepts servers with these pubkeys
|
||||
*/
|
||||
public static SSLContext pinSha256SSLContext(final String... sha256Pins) {
|
||||
return pinSha256SSLContext(Arrays.stream(sha256Pins));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create pinned pubkey TrustManager
|
||||
*
|
||||
* @param sha256Pins HPKP RFC7469 style base64'd sha256 pins
|
||||
* @return TrustManager that only accepts servers with these pubkeys
|
||||
*/
|
||||
public static TrustManager pinSha256TrustManager(final Stream<String> sha256Pins) {
|
||||
return new PinnedPubKeyTrustManager("SHA-256", sha256Pins
|
||||
.map(sha256Pin -> Base64.getDecoder().decode(sha256Pin))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
private static final X509Certificate[] acceptedIssuers = new X509Certificate[0];
|
||||
|
||||
private final String algorithm;
|
||||
private final Iterable<byte[]> pinnedPubKeys;
|
||||
|
||||
/**
|
||||
* PinnedPubKeyTrustManager constructor
|
||||
*
|
||||
* @param algorithm Algorithm to create instance of MessageDigest to hash server public key with before comparing to pinnedPubKeys
|
||||
* @param pinnedPubKeys Hashes of public keys to trust, if any match the leaf certificate the connection is trusted
|
||||
*/
|
||||
public PinnedPubKeyTrustManager(final String algorithm, final Iterable<byte[]> pinnedPubKeys) {
|
||||
this.algorithm = algorithm;
|
||||
this.pinnedPubKeys = pinnedPubKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
|
||||
if(chain.length < 1)
|
||||
throw new CertificateException("no certificates provided");
|
||||
// first cert is the leaf one????
|
||||
final byte[] hashedServerPubKey = getMessageDigest().digest(chain[0].getPublicKey().getEncoded());
|
||||
for(final byte[] pinnedPubKey : pinnedPubKeys) {
|
||||
if (Arrays.equals(hashedServerPubKey, pinnedPubKey)) {
|
||||
return; // success!
|
||||
}
|
||||
}
|
||||
throw new CertificateException("pinnedpubkey authentication failed, server pin (" + algorithm + "): " +
|
||||
Base64.getEncoder().encodeToString(hashedServerPubKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return acceptedIssuers;
|
||||
}
|
||||
|
||||
protected MessageDigest getMessageDigest() throws CertificateException {
|
||||
try {
|
||||
return MessageDigest.getInstance(algorithm);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new CertificateException(algorithm + " not supported", e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public Iterable<byte[]> getPinnedPubKeys() {
|
||||
return pinnedPubKeys;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.moparisthebest.dns.tls;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class PinnedPubKeyTrustManagerTest {
|
||||
|
||||
@Test
|
||||
public void pinSha256SSLContext() {
|
||||
// todo: make these not rely on remote server, stand up local TLS server with hard-coded certs?
|
||||
assertEquals("pinnedpubkey authentication failed, server pin (SHA-256): t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=",
|
||||
testPin("moparisthebest.com", 443, "bla").getCause().getMessage());
|
||||
assertEquals("pinnedpubkey authentication failed, server pin (SHA-256): t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=",
|
||||
testPin("moparisthebest.com", 443, "eEHQC9au2QRAP1FnvcYEsmvXT7511EXQ2gw8ppBfseM=").getCause().getMessage());
|
||||
assertEquals("read: 1024",
|
||||
testPin("moparisthebest.com", 443, "t62CeU2tQiqkexU74Gxa2eg7fRbEgoChTociMee9wno=").getMessage());
|
||||
}
|
||||
|
||||
private static Exception testPin(final String hostname, final int port, final String... sha256Pins) {
|
||||
final SSLSocketFactory sf = PinnedPubKeyTrustManager.pinSha256SSLContext(sha256Pins).getSocketFactory();
|
||||
try(Socket s = sf.createSocket()) {
|
||||
s.connect(new InetSocketAddress(hostname, port), 5000);
|
||||
try (InputStream is = s.getInputStream();
|
||||
OutputStream os = s.getOutputStream()) {
|
||||
os.write("GET /\r\n".getBytes(UTF_8));
|
||||
os.flush();
|
||||
final byte[] resp = new byte[1024];
|
||||
final int read = is.read(resp);
|
||||
return new Exception("read: " + read);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user