summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNick Hudson <nick.hudson@isode.com>2013-02-22 12:49:11 (GMT)
committerNick Hudson <nick.hudson@isode.com>2013-03-26 11:04:42 (GMT)
commit00a381a2121fe68d318cddceed32d8bb230935ea (patch)
tree30eb70265b6eacaf2c08069ca1718024e0a525f1 /src/com/isode/stroke/tls/java/CAPIKeyManager.java
parentebef58ff180acc9d760ea0137216ef6b4f16a9b6 (diff)
downloadstroke-00a381a2121fe68d318cddceed32d8bb230935ea.zip
stroke-00a381a2121fe68d318cddceed32d8bb230935ea.tar.bz2
Support TLS use of certificates from CAPI keystores
This patch adds a new "CAPICertificate" class, which can be used to configure TLS connections that use a client certificate from a Windows CAPI keystore, including certificates on smart cards. The JSSEContext class is updated so that "setClientCertificate()" checks to see whether the CertificateWithKey object that it's been given is a PKCS12Certificate or a CAPICertificate, and initializes the appropriate type of KeyStore. Note that the default behaviour of the KeyStore returned by SunMSCAPI when choosing a client certificate for TLS authentication is for it to choose the "most suitable" certificate it finds. This "most suitable" certificate may not be the one that the user has chosen, and in fact various certificates in CAPI are not considered by SunMSCAPI in this case - for example, certificates issued by CAs who don't appear in the list of acceptable CAs in the server's CertificateRequest (RFC5246 7.4.4). The CAPIKeyManager class provided here allows a caller to override the default behaviour, and force the use of a specific client certificate (whether it's "suitable" or not) based on the value specified by the caller when the CAPICertificate object was created. This also means that it is possible for a user to specify a particular certificate and use that, even if SunMSCAPI would have thought a "more suitable" one was found in CAPI. Test-information: Tested that P12 based TLS still works Tested on Windows that I can specify a "CAPICertificate" which is a reference to a certificate in the Windows keystore whose private key is held on a smartcard, and that I am prompted to insert the card (if necessary() and enter the PIN before the TLS handshake proceeds. Tested on Windows that I can specify a "CAPICertificate" which is a reference to an imported P12 file where certificate and key are in CAPI, and the TLS handshake proceeds without asking me for a PIN Tested that the "CAPIKeyManager" class is correctly forcing use of the certificate specified by the user, rather than the one which would be returned by the default SunMSCAPI implementation. Tested that I can still use "PKCS12Certificate"s to authenticate Tested that if I try and use a CAPICertificate on a non-Windows platform, then I can't authenticate, and get errors emitted from Stroke complaining of "no such provider: SunMSCAPI" Change-Id: Iff38e459f60c0806755820f6989c516be37cbf08 Signed-off-by: Nick Hudson <nick.hudson@isode.com>
Diffstat (limited to 'src/com/isode/stroke/tls/java/CAPIKeyManager.java')
-rw-r--r--src/com/isode/stroke/tls/java/CAPIKeyManager.java110
1 files changed, 110 insertions, 0 deletions
diff --git a/src/com/isode/stroke/tls/java/CAPIKeyManager.java b/src/com/isode/stroke/tls/java/CAPIKeyManager.java
new file mode 100644
index 0000000..84e0d97
--- /dev/null
+++ b/src/com/isode/stroke/tls/java/CAPIKeyManager.java
@@ -0,0 +1,110 @@
+/* Copyright (c) 2013, Isode Limited, London, England.
+ * All rights reserved.
+ *
+ * Acquisition and use of this software and related materials for any
+ * purpose requires a written licence agreement from Isode Limited,
+ * or a written licence from an organisation licensed by Isode Limited Limited
+ * to grant such a licence.
+ *
+ */
+
+package com.isode.stroke.tls.java;
+
+import java.net.Socket;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.X509ExtendedKeyManager;
+
+import com.isode.stroke.base.NotNull;
+
+/**
+ * This class is used to provide a way of overriding the behaviour of a KeyManager
+ * returned from SunMSCAPI.
+ * <p>Specifically, this implementation allows callers to specify what should
+ * be returned by {@link #chooseEngineClientAlias(String[], Principal[], SSLEngine)
+ */
+public class CAPIKeyManager extends X509ExtendedKeyManager {
+
+ X509ExtendedKeyManager parentKeyManager = null;
+ String engineClientAlias = null;
+
+ /**
+ * Create a new object.
+ * @param parent the actual X509ExtendedKeyManager to which work will
+ * be delegated unless overridden by caller-specified values. Must
+ * not be null.
+ */
+ public CAPIKeyManager(X509ExtendedKeyManager parent) {
+ NotNull.exceptIfNull(parent,"parent");
+ this.parentKeyManager = parent;
+ }
+
+ /**
+ * Set the value which should be returned by
+ * {@link #chooseEngineClientAlias(String[], Principal[], SSLEngine)}.
+ *
+ * <p>The default behaviour of the SunMSCAPI KeyManager is to pick what it
+ * thinks is the most suitable client certificate for the session.
+ * However, this may not be the same as the certificate which was specified
+ * by the client. This method allows callers to override the default
+ * behaviour and force a specific certificate to be used.
+ *
+ * @param engineClientAlias the alias of an entry in the KeyStore. This
+ * may be null, in which case when
+ * {@link #chooseEngineClientAlias(String[], Principal[], SSLEngine) is
+ * called, it will return whatever value the original KeyManager returns.
+ */
+ public void setEngineClientAlias(String engineClientAlias) {
+ this.engineClientAlias = engineClientAlias;
+ }
+
+ @Override
+ public String[] getServerAliases(String keyType, Principal[] issuers) {
+ return parentKeyManager.getServerAliases(keyType, issuers);
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(String alias) {
+ return parentKeyManager.getPrivateKey(alias);
+ }
+
+ @Override
+ public String[] getClientAliases(String keyType, Principal[] issuers) {
+ return parentKeyManager.getClientAliases(keyType, issuers);
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain(String alias) {
+ return parentKeyManager.getCertificateChain(alias);
+ }
+
+ @Override
+ public String chooseServerAlias(String keyType, Principal[] issuers,
+ Socket socket) {
+ return parentKeyManager.chooseServerAlias(keyType, issuers, socket);
+
+ }
+
+ @Override
+ public String chooseClientAlias(String[] keyType, Principal[] issuers,
+ Socket socket) {
+ return parentKeyManager.chooseClientAlias(keyType, issuers, socket);
+ }
+
+ @Override
+ public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
+ if (engineClientAlias != null) {
+ return engineClientAlias;
+ }
+ return parentKeyManager.chooseEngineClientAlias(keyType, issuers, engine);
+ }
+ @Override
+ public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
+ return parentKeyManager.chooseEngineServerAlias(keyType, issuers, engine);
+
+ }
+
+}