-
Notifications
You must be signed in to change notification settings - Fork 136
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The NonBlockingSocketFactory has been added to provide a non-blocking socket factory for PKIConnection. Eventually it will replace the DefaultSocketFactory once the support for OCSP and CRL has been added into JSSTrustManager. The test for HTTPS connector with NSS has been updated to use the non-blocking socket factory and validate the new error messages generated by JSSTrustManager. The test for HTTPS connector with PKCS #12 file will continue to use the blocking socket factory to prevent regressions.
- Loading branch information
Showing
3 changed files
with
231 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
189 changes: 189 additions & 0 deletions
189
base/common/src/main/java/org/dogtagpki/client/NonBlockingSocketFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
// | ||
// Copyright Red Hat, Inc. | ||
// | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
// | ||
package org.dogtagpki.client; | ||
|
||
import java.io.IOException; | ||
import java.net.InetAddress; | ||
import java.net.InetSocketAddress; | ||
import java.net.Socket; | ||
import java.net.UnknownHostException; | ||
import java.util.Arrays; | ||
|
||
import javax.net.ssl.KeyManager; | ||
import javax.net.ssl.KeyManagerFactory; | ||
import javax.net.ssl.SSLContext; | ||
import javax.net.ssl.SSLSocketFactory; | ||
import javax.net.ssl.TrustManager; | ||
|
||
import org.apache.http.conn.scheme.SchemeLayeredSocketFactory; | ||
import org.apache.http.params.HttpParams; | ||
import org.mozilla.jss.CryptoManager; | ||
import org.mozilla.jss.provider.javax.crypto.JSSTrustManager; | ||
import org.mozilla.jss.ssl.SSLAlertDescription; | ||
import org.mozilla.jss.ssl.SSLAlertEvent; | ||
import org.mozilla.jss.ssl.SSLAlertLevel; | ||
import org.mozilla.jss.ssl.SSLHandshakeCompletedEvent; | ||
import org.mozilla.jss.ssl.SSLSocketListener; | ||
import org.mozilla.jss.ssl.javax.JSSSocket; | ||
|
||
import com.netscape.certsrv.client.PKIConnection; | ||
|
||
/** | ||
* This class provides non-blocking socket factory for PKIConnection. | ||
*/ | ||
public class NonBlockingSocketFactory implements SchemeLayeredSocketFactory { | ||
|
||
public static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(NonBlockingSocketFactory.class); | ||
|
||
PKIConnection connection; | ||
|
||
public NonBlockingSocketFactory(PKIConnection connection) { | ||
this.connection = connection; | ||
} | ||
|
||
@Override | ||
public Socket createSocket(HttpParams params) throws IOException { | ||
return null; | ||
} | ||
|
||
@Override | ||
public Socket connectSocket(Socket socket, | ||
InetSocketAddress remoteAddress, | ||
InetSocketAddress localAddress, | ||
HttpParams params) | ||
throws IOException, | ||
UnknownHostException { | ||
|
||
String hostname = null; | ||
int port = 0; | ||
if (remoteAddress != null) { | ||
hostname = remoteAddress.getHostName(); | ||
port = remoteAddress.getPort(); | ||
} | ||
|
||
int localPort = 0; | ||
InetAddress localAddr = null; | ||
|
||
if (localAddress != null) { | ||
localPort = localAddress.getPort(); | ||
localAddr = localAddress.getAddress(); | ||
} | ||
|
||
SSLSocketFactory socketFactory; | ||
try { | ||
CryptoManager.getInstance(); | ||
|
||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("NssX509", "Mozilla-JSS"); | ||
KeyManager[] kms = kmf.getKeyManagers(); | ||
|
||
// Create JSSTrustManager since the default JSSNativeTrustManager | ||
// does not support hostname validation and cert approval callback. | ||
// | ||
// JSSTrustManager currently does not support cert validation with | ||
// OCSP and CRL. | ||
// | ||
// TODO: Fix JSSTrustManager to support OCSP and CRL, then replace | ||
// DefaultSocketFactory with this class. | ||
|
||
JSSTrustManager trustManager = new JSSTrustManager(); | ||
trustManager.setHostname(hostname); | ||
trustManager.setCallback(connection.getCallback()); | ||
|
||
TrustManager[] tms = new TrustManager[] { trustManager }; | ||
|
||
SSLContext ctx = SSLContext.getInstance("TLS", "Mozilla-JSS"); | ||
ctx.init(kms, tms, null); | ||
|
||
socketFactory = ctx.getSocketFactory(); | ||
|
||
} catch (Exception e) { | ||
throw new IOException("Unable to create SSL socket factory: " + e.getMessage(), e); | ||
} | ||
|
||
JSSSocket jssSocket; | ||
try { | ||
if (socket == null) { | ||
logger.info("Creating new SSL socket"); | ||
jssSocket = (JSSSocket) socketFactory.createSocket( | ||
InetAddress.getByName(hostname), | ||
port, | ||
localAddr, | ||
localPort); | ||
|
||
} else { | ||
logger.info("Creating SSL socket with existing socket"); | ||
jssSocket = (JSSSocket) socketFactory.createSocket( | ||
socket, | ||
hostname, | ||
port, | ||
true); | ||
} | ||
|
||
} catch (Exception e) { | ||
throw new IOException("Unable to create SSL socket: " + e.getMessage(), e); | ||
} | ||
|
||
jssSocket.setUseClientMode(true); | ||
|
||
String certNickname = connection.getConfig().getCertNickname(); | ||
if (certNickname != null) { | ||
logger.info("Client certificate: "+certNickname); | ||
jssSocket.setCertFromAlias(certNickname); | ||
} | ||
|
||
jssSocket.setListeners(Arrays.asList(new SSLSocketListener() { | ||
|
||
@Override | ||
public void alertReceived(SSLAlertEvent event) { | ||
|
||
int intLevel = event.getLevel(); | ||
SSLAlertLevel level = SSLAlertLevel.valueOf(intLevel); | ||
|
||
int intDescription = event.getDescription(); | ||
SSLAlertDescription description = SSLAlertDescription.valueOf(intDescription); | ||
|
||
if (level == SSLAlertLevel.FATAL || logger.isInfoEnabled()) { | ||
logger.error(level + ": SSL alert received: " + description); | ||
} | ||
} | ||
|
||
@Override | ||
public void alertSent(SSLAlertEvent event) { | ||
|
||
int intLevel = event.getLevel(); | ||
SSLAlertLevel level = SSLAlertLevel.valueOf(intLevel); | ||
|
||
int intDescription = event.getDescription(); | ||
SSLAlertDescription description = SSLAlertDescription.valueOf(intDescription); | ||
|
||
if (level == SSLAlertLevel.FATAL || logger.isInfoEnabled()) { | ||
logger.error(level + ": SSL alert sent: " + description); | ||
} | ||
} | ||
|
||
@Override | ||
public void handshakeCompleted(SSLHandshakeCompletedEvent event) { | ||
} | ||
})); | ||
|
||
jssSocket.startHandshake(); | ||
|
||
return jssSocket; | ||
} | ||
|
||
@Override | ||
public boolean isSecure(Socket sock) { | ||
// We only use this factory in the case of SSL Connections. | ||
return true; | ||
} | ||
|
||
@Override | ||
public Socket createLayeredSocket(Socket socket, String target, int port, HttpParams params) | ||
throws IOException, UnknownHostException { | ||
// This method implementation is required to get SSL working. | ||
return null; | ||
} | ||
} |