搭建http2 mTLS通道,实现本地/远程端口转发(gost)

一、制作证书

mTLS认证要求两端都提供证书并证明拥有对应的key,并且要求对方的证书是由自己认可的CA签发的

(一)自制根CA证书

https://learn.microsoft.com/zh-cn/azure/application-gateway/self-signed-certificates
1、准备RSA密钥
输出的key文件里实际同时包含公钥和私钥

openssl genrsa -out wzp-ca.key 2048
# 查看上述密钥文件里的公钥;可用于检查“证书里的公钥”和“密钥文件的公钥”是否一致
openssl rsa -pubout -in wzp-ca.key

2、准备签名请求
输出的csr文件里包含CA的公钥、CA的名称

openssl req -new -key wzp-ca.key -out wzp-ca.csr -config <(
cat <<-EOF
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[ dn ]
CN=wzp-ca
EOF
)
# 查看上述签名请求文件里的名称、公钥信息
openssl req -in wzp-ca.csr -noout -text

3、准备证书扩展配置
主要是申明是CA,并可以用于签发证书

tee wzp-ca_ext.cnf <<-'EOF'
basicConstraints = CA:TRUE
keyUsage = digitalSignature, keyEncipherment, keyCertSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
EOF

4、制作CA

openssl x509 -req -days 1000 -in wzp-ca.csr -extfile wzp-ca_ext.cnf -signkey wzp-ca.key -out wzp-ca.crt

(二)自签发服务端证书

1、准备RSA密钥

2、准备签名请求
提供给签发方的csr文件,不含私钥信息,无需担心安全问题

openssl req -new -key wzp-server.key -out wzp-server.csr -config <(
cat <<-EOF
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn

[ dn ]
CN=wzp.com
EOF
)

3、准备证书扩展配置
申明增强密钥用法:serverAuth
请求端验证证书合法性时,是检查服务端证书里的备用名称subjectAltName是否和请求地址匹配,而不是检查Subject里的CN

tee wzp-server_ext.cnf <<-'EOF'
basicConstraints = CA:FALSE
nsCertType = server
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1= wzp.com
DNS.2 = *.wzp.com
IP.1 = 127.0.0.1
EOF

4、签发证书

openssl x509 -req -days 1000 -in wzp-server.csr -extfile wzp-server_ext.cnf -CA wzp-ca.crt -CAkey wzp-ca.key -CAcreateserial -out wzp-server.crt
#验证证书是否由某个CA签发
openssl verify -CAfile wzp-ca.crt -verbose wzp-server.crt

(三)客户端证书

1、准备RSA密钥

2、准备签名请求

3、准备证书扩展配置
关键是申明增强密钥用法:clientAuth

tee wzp-client_ext.cnf <<-'EOF'
basicConstraints = CA:FALSE
nsCertType = client, email
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, emailProtection
EOF

4、签发证书

5、制作PKCS#12证书安装文件
pfx文件里包含证书和私钥,可被浏览器等请求端导入,用于在mTLS通讯时向服务端证明自己身份

openssl pkcs12 -export -out wzp-client.pfx -inkey wzp-client.key -in wzp-client.crt

二、使用gost进行端口转发

gost v2

(一)本地端口转发

访问转发客户端所在机器的端口,相当于从转发服务器发起对指定地址的访问
1、服务端:准备转发通道

gost -L="http2://:443?cert=/wzp-server.crt&key=/wzp-server.key&ca=/wzp-ca.crt"

转发通道要求客户端进行TLS握手时,持有wzp-ca签发的证书

2、客户端:申请监听并映射客户端机器的端口
假设转发服务器地址是a.a.a.a,在转发客户端本地hosts配置里把mtls.wzp.com解析到地址a.a.a.a;

gost -L="tcp://:2222/b.b.b.b:443" \
     -L="tcp://:3333/c.c.c.c:443" \
     -F="http2://mtls.wzp.com:443?cert=/wzp-client.crt&key=/wzp-client.key&secure=true&ca=/wzp-ca.crt"

用户访问转发客户端机器的2222端口,相当于从转发服务器a.a.a.a发起对b.b.b.b:443的访问;
用户访问转发客户端机器的3333端口,相当于从转发服务器a.a.a.a发起对c.c.c.c:443的访问

(二)远程端口转发

访问转发服务器的端口,相当于从转发客户端机器发起对指定地址的访问
1、服务端:准备转发通道

gost -L="h2://:443?cert=/wzp-server.crt&key=/wzp-server.key&ca=/wzp-ca.crt"
gost -L="socks5://127.0.0.1:18080"

第一个转发通道用于和客户端建立mTLS握手的多路复用长连接;
第二个转发通道用于在转发服务器本地打开映射端口,因为只有socks5协议支持远程端口转发

2、客户端:申请监听并映射转发服务器的端口
假设转发服务器地址是a.a.a.a,在转发客户端本地hosts配置里把mtls.wzp.com解析到地址a.a.a.a;

gost -L="rtcp://:28080/d.d.d.d:8080" \
     -F="h2://mtls.wzp.com:443?cert=/wzp-client.crt&key=/wzp-client.key&secure=true&ca=/wzp-ca.crt" \
     -F="socks5://127.0.0.1:18080"

第二个-F参数表示进行第二级转发,即由转发服务器本地的socks5服务在转发服务器上开启监听28080端口;
用户访问转发服务器a.a.a.a的28080端口,相当于在转发客户端机器发起对d.d.d.d:8080的访问

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MTLS(Mutual TLS)是一种双向的TLS(Transport Layer Security)认证方式,即客户端和服务端都需要验证对方的身份。Java中可以使用JSSE(Java Secure Socket Extension)实现MTLS。 下面是一个简单的实现MTLS的示例代码: ```java import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; public class MTLSExample { private static final String KEYSTORE_PATH = "keystore.jks"; private static final String KEYSTORE_PASSWORD = "password"; private static final String TRUSTSTORE_PATH = "truststore.jks"; private static final String TRUSTSTORE_PASSWORD = "password"; private static final int PORT = 8443; public static void main(String[] args) throws Exception { // Load the key store KeyStore keyStore = KeyStore.getInstance("JKS"); try (InputStream is = new FileInputStream(KEYSTORE_PATH)) { keyStore.load(is, KEYSTORE_PASSWORD.toCharArray()); } // Create key manager factory KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, KEYSTORE_PASSWORD.toCharArray()); // Load the trust store KeyStore trustStore = KeyStore.getInstance("JKS"); try (InputStream is = new FileInputStream(TRUSTSTORE_PATH)) { trustStore.load(is, TRUSTSTORE_PASSWORD.toCharArray()); } // Create trust manager factory TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); // Create SSL context SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null); // Create a server socket SSLServerSocketFactory sslServerSocketFactory = sslContext.getServerSocketFactory(); SSLServerSocket sslServerSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(); sslServerSocket.bind(new InetSocketAddress(PORT)); // Set up a trust manager that trusts all certificates sslServerSocket.setNeedClientAuth(true); TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { } } }; sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, null); // Start accepting connections while (true) { System.out.println("Waiting for client connection..."); try (var socket = sslServerSocket.accept()) { System.out.println("Client connected"); } catch (IOException e) { System.out.println("Failed to accept client connection: " + e.getMessage()); } } } } ``` 在这个示例中,我们加载了一个包含服务端和客户端证书的JKS格式的密钥库和信任库,然后创建了一个SSLContext对象,用于创建SSLServerSocket。`sslContext.init()`方法的第二个参数是一个TrustManager数组,用于验证客户端证书。在这个示例中,我们使用了一个简单的实现,它信任所有的客户端证书。在实际应用中,你需要使用一个更安全的实现。 在SSLServerSocket上调用`sslServerSocket.setNeedClientAuth(true)`方法,表示必须要验证客户端证书。如果客户端没有提供证书或者证书验证失败,那么连接将被拒绝。 当客户端与服务端建立连接后,服务端可以使用`socket.getPeerCertificates()`方法获取客户端证书。你可以使用这个证书来验证客户端的身份。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值