Skip to content

Custom TrustManager

ulvii edited this page Sep 22, 2017 · 1 revision

TrustManagers are responsible for managing the trust material that is used when making trust decisions, and for deciding whether credentials presented by a peer should be accepted. As of 6.3.3-preview release, Microsoft JDBC Driver for SQL Server lets the users specify a custom TrustManager to perform additional trust checking, which is based upon the needs of the environment. Newly introduced connection properties:

trustManagerClass - The fully qualified class name of a custom javax.net.ssl.TrustManager.
trustManagerConstructorArg - An optional argument to pass to the constructor of the trustManagerClass.

If trustManagerClass is specified and an encrypted connection is requested, the custom TrustManager will be used rather than the default system JVM KeyStore based TrustManager.

Below is an example of a TrustManager that takes a single certificate as an argument and validates against it. Note that the remote certificate does not have to byte-for-byte match the constructor argument. The chain is what is being validated so you can specify a parent certificate as the constructor argument and all child certificates signed by that parent will be trusted as well.

public class SingleCertTrustManager implements X509TrustManager {
    X509Certificate cert;
    X509TrustManager trustManager;        

    public SingleCertTrustManager(String certToTrust) throws IOException, GeneralSecurityException {
    	InputStream in = new ByteArrayInputStream(certToTrust.getBytes());
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        try {
            // Note: KeyStore requires it be loaded even if you don't load anything into it:
            ks.load(null);
        } catch (Exception e) {
        }
        CertificateFactory cf = CertificateFactory.getInstance("X509");
        cert = (X509Certificate) cf.generateCertificate(in);
        ks.setCertificateEntry(UUID.randomUUID().toString(), cert);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        for (TrustManager tm : tmf.getTrustManagers()) {
            if (tm instanceof X509TrustManager) {
                trustManager = (X509TrustManager) tm;
                break;
            }
        }
        if (trustManager == null) {
            throw new GeneralSecurityException("No X509TrustManager found");
        }
    }

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        trustManager.checkServerTrusted(chain, authType);
    }

    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[] { cert };
    }
}

You can specify the custom TrustManager using connection URL.

String url = connectionString + ";trustManagerClass=" + SingleCertTrustManager.class.getName()
                + ";trustManagerConstructorArg=cert;" + ";encrypt=true;";

Another way is to use a SQLServerDataSource object.

SQLServerDataSource ds = new SQLServerDataSource();
ds.setTrustManagerClass(SingleCertTrustManager.class.getName());  
ds.setTrustManagerConstructorArg("cert");
ds.setEncrypt(true);