我需要将某些bash
脚本转换为java
,并且其中一个这样的脚本使用openssl
和vanity-url
作为参数连接到服务器,以检查是否可使用该vanity-url
连接。参见下面的命令
/usr/bin/openssl s_client -connect api.sys.found1.cf.company.com:443 -servername www.app.company.com 2>/dev/null
我想在java
中进行类似的活动并测试连通性。关于如何使用Java建立open-ssl
连接的任何想法。这是我需要使用外部库吗?
答案 0 :(得分:7)
我能够通过参考文档over here
实现这一目标基本上,需要创建SSLEngine
并与SNI
进行成功的握手
private SocketChannel createSocketChannel() throws IOException {
InetSocketAddress socketAddress = new InetSocketAddress(PROXY_ADDRESS, PROXY_PORT);
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(socketAddress);
socketChannel.configureBlocking(false);
return socketChannel;
}
private SSLContext createSSLContext() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sslContext = SSLContext.getInstance(TLS_VERSION);
sslContext.init(null,null,null);
return sslContext;
}
private SSLEngine createSSLEngine() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sslContext = createSSLContext();
SSLEngine sslEngine = sslContext.createSSLEngine(PROXY_ADDRESS, PROXY_PORT);
sslEngine.setUseClientMode(true);
List<SNIServerName> serverNameList = new ArrayList<>();
serverNameList.add(new SNIHostName(SNI_HOST_NAME));
SSLParameters sslParameters = sslEngine.getSSLParameters();
sslParameters.setServerNames(serverNameList);
sslEngine.setSSLParameters(sslParameters);
return sslEngine;
}
创建SSLEngine之后,handShake
必须开始
SocketChannel channel = createSocketChannel();
SSLEngine sslEngine = createSSLEngine();
doHandShake(sslEngine,channel);
private void doHandShake(SSLEngine sslEngine, SocketChannel socketChannel) throws Exception {
System.out.println("Going to do Handshake");
SSLSession session = sslEngine.getSession();
ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
sslEngine.beginHandshake();
SSLEngineResult result;
handshakeStatus = sslEngine.getHandshakeStatus();
while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED &&
handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
switch (handshakeStatus) {
case NEED_UNWRAP:
if (! (socketChannel.read(peerNetData) <0)) {
peerNetData.flip();
result = sslEngine.unwrap(peerNetData,peerAppData);
peerNetData.compact();
handshakeStatus = result.getHandshakeStatus();
switch (result.getStatus()) {
case OK:
break;
}
}
break;
case NEED_WRAP:
myNetData.clear() ;// Empty the local network packet buffer
result = sslEngine.wrap(myAppData,myNetData);
handshakeStatus = result.getHandshakeStatus();
switch (result.getStatus()) {
case OK:
myNetData.flip();
while (myNetData.hasRemaining()) {
socketChannel.write(myNetData);
}
}
break;
case NEED_TASK:
Runnable task = sslEngine.getDelegatedTask();
if (null!=task) {
task.run();
}
handshakeStatus = sslEngine.getHandshakeStatus();
break;
}
}
完成handShake
之后。您可以获得Principal
对象
Principal principal = sslEngine.getSession().getPeerPrincipal();
if (principal.getName().contains(SNI_HOST_NAME)) {
System.out.println("available ... ");
}else {
System.out.println("Not available");
}
答案 1 :(得分:2)
使用您的值调用isAliasExists,
isAliasExists(“ api.sys.found1.cf.company.com”,“ www.app.company.com”);
如果您的别名(服务器名)是证书的一部分,则返回true,
private static boolean isAliasExists(String hostName, String alias) throws Exception {
String host;
int port;
String[] parts = hostName.split(":");
host = parts[0];
port = (parts.length == 1) ? 443 : Integer.parseInt(parts[1]);
// key store password
char[] passphrase = "changeit".toCharArray();
File file = new File("jssecacerts");
if (file.isFile() == false) {
char SEP = File.separatorChar;
File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security");
file = new File(dir, "jssecacerts");
if (file.isFile() == false) {
file = new File(dir, "cacerts");
}
}
InputStream in = new FileInputStream(file);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(in, passphrase);
in.close();
SSLContext context = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
context.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.setSoTimeout(10000);
try {
System.out.println("Starting SSL handshake...");
socket.startHandshake();
socket.close();
System.out.println("Certificate is already trusted");
} catch (SSLException e) {
e.printStackTrace();
}
X509Certificate[] chain = tm.chain;
List<String> altNames=new ArrayList<String>();
for (X509Certificate cert: chain)
{
altNames.addAll(getSubjectAltNames(cert));
}
for(String altName: altNames) {
if(altName.trim().contains(alias))
return true;
}
if (chain == null) {
System.out.println("Could not obtain server certificate chain");
return false;
}
return false;
}
从cert返回备用名称列表,
private static List<String> getSubjectAltNames(X509Certificate certificate) throws CertificateParsingException {
List<String> result = new ArrayList<>();
try {
Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames();
if (subjectAltNames == null) {
return Collections.emptyList();
}
for (Object subjectAltName : subjectAltNames) {
List<?> entry = (List<?>) subjectAltName;
if (entry == null || entry.size() < 2) {
continue;
}
Integer altNameType = (Integer) entry.get(0);
if (altNameType == null) {
continue;
}
String altName = (String) entry.get(1);
if (altName != null) {
result.add(altName);
}
}
return result;
} catch (CertificateParsingException e) {
return Collections.emptyList();
}
}
客户信任管理器,
private static class SavingTrustManager implements X509TrustManager {
private final X509TrustManager tm;
private X509Certificate[] chain;
SavingTrustManager(X509TrustManager tm) {
this.tm = tm;
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
// throw new UnsupportedOperationException();
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
throw new UnsupportedOperationException();
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
this.chain = chain;
tm.checkServerTrusted(chain, authType);
}
}
答案 2 :(得分:0)
在不真正了解SNI是什么的情况下,我试图通过下面显示的测试程序来获得一些见识。
我不知道openssl s_client
命令的输出,但是测试程序可能被证明是一个起点。当javax.net.debug
输出打开时,将转储很多输出,其中只有几行是相关的(另请参见注释)。这有点烦人,我对此没有一个简单的解决方案。可以重新制作TrustAllServers
类,以检查您希望从特定域的服务器(也称为主机)接收到的证书。可能还有其他选择(例如套接字的握手方法),但这是我所掌握的。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
// https://stackoverflow.com/questions/56005883/java-equivalent-to-openssl-s-client-command
// Please use latest Java 8 version, bugs are around in earlier versions.
public class ServerNameTest {
public static void main(String[] args) {
// SSL debug options, see https://stackoverflow.com/q/23659564/3080094 and https://access.redhat.com/solutions/973783
// System.setProperty("javax.net.debug", "all");
// System.setProperty("javax.net.debug", "ssl:handshake");
// System.setProperty("jsse.enableSNIExtension", "true"); // "true" is the default
try {
ServerNameTest sn = new ServerNameTest();
// This will show 2 different server certificate chains.
// Note this is a random server - please pick your own one.
sn.test("major.io", "rackerhacker.com");
sn.test("major.io", "major.io");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done");
}
/*
* With javax.net.debug output you should see something like:
* <pre>
* *** ClientHello
* ...
* Extension server_name, server_name: [type=host_name (0), value=DOMAIN;]
* ...
* *** ServerHello
* ...
* Extension server_name, server_name:
* ...
* </pre>
* Note that if the server does not provide a value for server_name,
* it does not actually mean the server does not support SNI/server_name (see https://serverfault.com/a/506303)
*/
void test(String host, String domain) throws Exception {
SSLParameters sslParams = new SSLParameters();
if (domain != null && !domain.isEmpty()) {
sslParams.setServerNames(Arrays.asList(new SNIHostName(domain)));
}
// Only for webservers: set endpoint algorithm to HTTPS
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
SSLSocketFactory sslsf = serverTrustingSSLFactory();
try (SSLSocket socket = (SSLSocket) sslsf.createSocket()) {
socket.setSSLParameters(sslParams);
socket.setSoTimeout(3_000);
System.out.println("Connecting to " + host + " for domain " + domain);
socket.connect(new InetSocketAddress(host, 443), 3_000);
// Trigger actual connection by getting the session.
socket.getSession();
System.out.println("Connected to remote " + socket.getRemoteSocketAddress());
try (BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
try (OutputStream out = socket.getOutputStream()) {
System.out.println(">> OPTIONS");
out.write("OPTIONS * HTTP/1.1\r\n\r\n".getBytes(StandardCharsets.UTF_8));
System.out.println("<< " + input.readLine());
}
} catch (Exception e) {
System.err.println("No line read: " + e);
}
}
}
SSLSocketFactory serverTrustingSSLFactory() throws Exception {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustManager(), null);
return ctx.getSocketFactory();
}
TrustManager[] trustManager() throws Exception {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init( (KeyStore) null);
// Must use "extended" type versus the default javax.net.ssl.X509TrustManager,
// otherwise the error "No subject alternative DNS name matching" keeps showing up.
X509ExtendedTrustManager defaultManager = null;
for (TrustManager trustManager : tmf.getTrustManagers()) {
if (trustManager instanceof X509ExtendedTrustManager) {
defaultManager = (X509ExtendedTrustManager) trustManager;
break;
}
}
if (defaultManager == null) {
throw new RuntimeException("Cannot find default X509ExtendedTrustManager");
}
return new TrustManager[] { new TrustAllServers(defaultManager) };
}
static void printChain(X509Certificate[] chain) {
try {
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = chain[i];
System.out.println("Cert[" + i + "] " + cert.getSubjectX500Principal() + " :alt: " + cert.getSubjectAlternativeNames());
}
} catch (Exception e) {
e.printStackTrace();
}
}
static class TrustAllServers extends X509ExtendedTrustManager {
final X509ExtendedTrustManager defaultManager;
public TrustAllServers(X509ExtendedTrustManager defaultManager) {
this.defaultManager = defaultManager;
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultManager.checkServerTrusted(chain, authType);
} catch (Exception e) {
System.err.println("Untrusted server: " + e);
}
printChain(chain);
}
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
try {
defaultManager.checkServerTrusted(chain, authType, socket);
} catch (Exception e) {
System.err.println("Untrusted server for socket: " + e);
}
printChain(chain);
}
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
try {
defaultManager.checkServerTrusted(chain, authType, engine);
} catch (Exception e) {
System.err.println("Untrusted server for engine: " + e);
}
printChain(chain);
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
defaultManager.checkClientTrusted(chain, authType);
}
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
defaultManager.checkClientTrusted(chain, authType, socket);
}
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
defaultManager.checkClientTrusted(chain, authType, engine);
}
public X509Certificate[] getAcceptedIssuers() {
return defaultManager.getAcceptedIssuers();
}
}
}