自签名证书错误的 SSL 连接(C# 套接字连接到 Java 服务器套接字)
SSL Connection with self-signed certificate error (C# Socket connect to Java Server Socket)
一开始...这个程序只是 4-fun,永远不会发布。它只是为了测试Sockets,所以不用关心这个连接的安全性。
我编写了一个小信使,您可以在文本框中输入一条消息,它将发送到服务器,然后从服务器发送到所有客户端。到目前为止,一切都很好。只是为了好玩,我想为此连接使用 SSL。
但是当我尝试连接到服务器套接字时,它 returns RemoteCertificateNameMismatch at ValidateServerCertificate。
添加一些调试消息后,我发现 Sslstream.LocalCertificate 为空。我该如何修复这个丢失的证书?我使用的是自签名证书。
请帮忙,这是我的代码...是的...抱歉我的英语不好:/ :
C# 客户端
[...]
public bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
//sslPolicyErrors returns RemoteCertificateNameMismatch
return true; //Code shortened
}
public X509CertificateCollection getCertificates()
{
X509CertificateCollection cCollection = new X509CertificateCollection();
cCollection.Add(getCertificate());
return cCollection;
}
private X509Certificate getCertificate()
{
string Certificate = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.crt";
string ClientCertificatePassword = "...";
return new X509Certificate(Certificate, ClientCertificatePassword);
}
public X509Certificate SelectLocalCertificate(
object sender,
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers)
{
foreach(X509Certificate cer in localCertificates) {
return cer;
}
return getCertificate();
}
public void connect(String username)
{
XMLConfigManager xml = XMLConfigManager.getInstance();
String ip = xml.get("ip");
tc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ;
tc.Connect(ip, int.Parse(xml.get("port")));
networdstream = new NetworkStream(tc);
using(sslstream = new SslStream(networdstream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate), new LocalCertificateSelectionCallback(SelectLocalCertificate)))
{
sslstream.AuthenticateAsClient(ip, getCertificates(), SslProtocols.Default, true);
}
sendMessage(username);
t = new Thread(new ThreadStart(checkInput));
t.Start();
tm.addThread(t);
th = new Thread(new ThreadStart(userTimer));
th.Start();
tm.addThread(th);
}
public void disconnect()
{
if(sslstream != null) {
sslstream.Close();
}
if(tc != null) {
tc.Close();
}
}
[...]
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public void sendMessage(String s)
{
try
{
if ((s != null) && (s.Length > 0))
{
try
{
sslstream.Write(GetBytes(s));
}
catch (Exception)
{
}
}
}
catch (Exception ex)
{
endError(ex);
return;
}
}
public void userTimer()
{
Thread.Sleep(2000);
sendCommand("usercount");
th = new Thread(new ThreadStart(userTimer));
th.Start();
tm.addThread(th);
}
[...]
static String ReadMessage(SslStream sslStream)
{
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
} while (bytes != 0);
return messageData.ToString();
}
public void checkInput()
{
try
{
Form2 f2 = f1.f;
if(!isConnected()) {
t.Abort();
return;
}
String s = ReadMessage(sslstream);
if(s == null) {
t.Abort();
return;
}
CommandHandler ch = new CommandHandler();
Boolean handle = ch.handle(s, f1.f);
if (!handle) {
if (s == "Der Benutzername ist schon vergeben.")
{
endDuplicatename();
return;
}
f2.Invoke(new Action(() => f2.chat.Items.Add(s)));
f2.Invoke(new Action(() => f2.select_newest()));
}
}
finally
{
t = new Thread(new ThreadStart(checkInput));
t.Start();
tm.addThread(t);
}
}
Java 服务器套接字
package de.wladhd.server;
import java.net.Socket;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import de.wladhd.client.Client;
import de.wladhd.client.ClientManager;
import de.wladhd.logs.Log;
import de.wladhd.logs.LogType;
public class DateServer {
private SSLServerSocket server;
private boolean running = true;
private String keyStore = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.pfx";
private String keyStorePassword = "...";
private String keyStoreType = "PKCS12";
private String trustStore = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.pfx";
private String trustStorePassword = "...";
private String trustStoreType = "PKCS12";
public void execute() throws Exception {
//System.setProperty("javax.net.debug","all");
System.setProperty("javax.net.ssl.keyStoreType", keyStoreType);
System.setProperty("javax.net.ssl.keyStore", keyStore);
System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
System.setProperty("javax.net.ssl.trustStoreType", trustStoreType);
System.setProperty("javax.net.ssl.trustStore", trustStore);
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
//System.setProperty("javax.net.ssl.trustStore", "de.wladhd.server.Trusting");
SSLServerSocketFactory serverFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
if(serverFactory == null) {
new Log("Error occured... Server Factory == null!", LogType.ERROR);
return;
}
server = (SSLServerSocket) serverFactory.createServerSocket(9090);
running = true;
new Log("Server now running on query: " + server.getInetAddress().getHostAddress() + ":" + server.getLocalPort(), LogType.INFO);
while (running) {
try {
if(server.isClosed()) {
return;
}
new Log("Client connecting...", LogType.DEBUG);
final Socket rawsocket = server.accept();
if(!(rawsocket instanceof SSLSocket)) {
new Log("Client isnt an instance of SSLSocket!", LogType.DEBUG);
return;
}
final SSLSocket socket = (SSLSocket) rawsocket;
try {
socket.startHandshake();
} catch (Exception ex) {
}
new Log("Client - Connected: " + socket.isConnected(), LogType.DEBUG);
new Log("Client - Protocol: " + socket.getSession().getProtocol(), LogType.DEBUG);
new Log("Client - Session valid: " + socket.getSession().isValid(), LogType.DEBUG);
new Log("Client - CipherSuite: " + socket.getSession().getCipherSuite(), LogType.DEBUG);
new Log("Client - NeedClientAuth: " + socket.getNeedClientAuth(), LogType.DEBUG);
new Log("Client - WantClientAuth: " + socket.getWantClientAuth(), LogType.DEBUG);
Client c = new Client(socket);
//socket.getHandshakeSession() returns null and peer not authenticated exception...
} catch (Exception ex) {
ex.printStackTrace();
continue;
}
}
}
public void disconnect() throws Exception {
new Log("Server disconnecting...", LogType.INFO);
setRunning(false);
ClientManager.getInstance().disconnectClients();
if(server != null) {
server.close();
}
new Log("Server successfully disconnected!", LogType.INFO);
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean state) {
running = state;
}
}
如果你需要什么请告诉我...
RemoteCertificateNameMismatch 策略错误与丢失的本地证书无关。此错误只是告诉您收到的服务器证书中的 SAN(主题备用名称),或者,如果没有 SAN,则收到的服务器证书的主题名称的通用名称与您连接的主机名不匹配。如果您不希望在这两个地方之一的证书中看到主机名,则可以忽略此错误。
我猜你没有本地证书,因为你是从文件中加载证书,而Windows私钥库中没有此证书对应的私钥。
尝试使用 MMC 将证书和私钥(例如,从同时具有证书和私钥的 PFX 文件)导入本地计算机个人证书存储,然后加载 此处使用 .NET 证书存储 API 的证书。只要您有权访问私钥(如果 您 是导入 PFX 的 Windows 用户,您就可以访问),那么您应该能够连接到此客户端证书.
在你的代码中,不是从文件中读取证书,而是从证书存储中加载它,像这样:
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
try
{
X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
return TakeFirstCertificate(collection);
}
finally
{
store.Close();
}
您可以使用指纹以外的其他东西来查找它 -- 这只是一个示例。这就是您拥有的 SelectLocalCertificate return.
一开始...这个程序只是 4-fun,永远不会发布。它只是为了测试Sockets,所以不用关心这个连接的安全性。 我编写了一个小信使,您可以在文本框中输入一条消息,它将发送到服务器,然后从服务器发送到所有客户端。到目前为止,一切都很好。只是为了好玩,我想为此连接使用 SSL。 但是当我尝试连接到服务器套接字时,它 returns RemoteCertificateNameMismatch at ValidateServerCertificate。 添加一些调试消息后,我发现 Sslstream.LocalCertificate 为空。我该如何修复这个丢失的证书?我使用的是自签名证书。
请帮忙,这是我的代码...是的...抱歉我的英语不好:/ :
C# 客户端
[...]
public bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
//sslPolicyErrors returns RemoteCertificateNameMismatch
return true; //Code shortened
}
public X509CertificateCollection getCertificates()
{
X509CertificateCollection cCollection = new X509CertificateCollection();
cCollection.Add(getCertificate());
return cCollection;
}
private X509Certificate getCertificate()
{
string Certificate = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.crt";
string ClientCertificatePassword = "...";
return new X509Certificate(Certificate, ClientCertificatePassword);
}
public X509Certificate SelectLocalCertificate(
object sender,
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers)
{
foreach(X509Certificate cer in localCertificates) {
return cer;
}
return getCertificate();
}
public void connect(String username)
{
XMLConfigManager xml = XMLConfigManager.getInstance();
String ip = xml.get("ip");
tc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); ;
tc.Connect(ip, int.Parse(xml.get("port")));
networdstream = new NetworkStream(tc);
using(sslstream = new SslStream(networdstream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate), new LocalCertificateSelectionCallback(SelectLocalCertificate)))
{
sslstream.AuthenticateAsClient(ip, getCertificates(), SslProtocols.Default, true);
}
sendMessage(username);
t = new Thread(new ThreadStart(checkInput));
t.Start();
tm.addThread(t);
th = new Thread(new ThreadStart(userTimer));
th.Start();
tm.addThread(th);
}
public void disconnect()
{
if(sslstream != null) {
sslstream.Close();
}
if(tc != null) {
tc.Close();
}
}
[...]
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public void sendMessage(String s)
{
try
{
if ((s != null) && (s.Length > 0))
{
try
{
sslstream.Write(GetBytes(s));
}
catch (Exception)
{
}
}
}
catch (Exception ex)
{
endError(ex);
return;
}
}
public void userTimer()
{
Thread.Sleep(2000);
sendCommand("usercount");
th = new Thread(new ThreadStart(userTimer));
th.Start();
tm.addThread(th);
}
[...]
static String ReadMessage(SslStream sslStream)
{
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
} while (bytes != 0);
return messageData.ToString();
}
public void checkInput()
{
try
{
Form2 f2 = f1.f;
if(!isConnected()) {
t.Abort();
return;
}
String s = ReadMessage(sslstream);
if(s == null) {
t.Abort();
return;
}
CommandHandler ch = new CommandHandler();
Boolean handle = ch.handle(s, f1.f);
if (!handle) {
if (s == "Der Benutzername ist schon vergeben.")
{
endDuplicatename();
return;
}
f2.Invoke(new Action(() => f2.chat.Items.Add(s)));
f2.Invoke(new Action(() => f2.select_newest()));
}
}
finally
{
t = new Thread(new ThreadStart(checkInput));
t.Start();
tm.addThread(t);
}
}
Java 服务器套接字
package de.wladhd.server;
import java.net.Socket;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLSocket;
import de.wladhd.client.Client;
import de.wladhd.client.ClientManager;
import de.wladhd.logs.Log;
import de.wladhd.logs.LogType;
public class DateServer {
private SSLServerSocket server;
private boolean running = true;
private String keyStore = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.pfx";
private String keyStorePassword = "...";
private String keyStoreType = "PKCS12";
private String trustStore = "D:/Wlad/Programmierung/Zertifikat/CA/CertificateAuthority.pfx";
private String trustStorePassword = "...";
private String trustStoreType = "PKCS12";
public void execute() throws Exception {
//System.setProperty("javax.net.debug","all");
System.setProperty("javax.net.ssl.keyStoreType", keyStoreType);
System.setProperty("javax.net.ssl.keyStore", keyStore);
System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
System.setProperty("javax.net.ssl.trustStoreType", trustStoreType);
System.setProperty("javax.net.ssl.trustStore", trustStore);
System.setProperty("javax.net.ssl.trustStorePassword", trustStorePassword);
//System.setProperty("javax.net.ssl.trustStore", "de.wladhd.server.Trusting");
SSLServerSocketFactory serverFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
if(serverFactory == null) {
new Log("Error occured... Server Factory == null!", LogType.ERROR);
return;
}
server = (SSLServerSocket) serverFactory.createServerSocket(9090);
running = true;
new Log("Server now running on query: " + server.getInetAddress().getHostAddress() + ":" + server.getLocalPort(), LogType.INFO);
while (running) {
try {
if(server.isClosed()) {
return;
}
new Log("Client connecting...", LogType.DEBUG);
final Socket rawsocket = server.accept();
if(!(rawsocket instanceof SSLSocket)) {
new Log("Client isnt an instance of SSLSocket!", LogType.DEBUG);
return;
}
final SSLSocket socket = (SSLSocket) rawsocket;
try {
socket.startHandshake();
} catch (Exception ex) {
}
new Log("Client - Connected: " + socket.isConnected(), LogType.DEBUG);
new Log("Client - Protocol: " + socket.getSession().getProtocol(), LogType.DEBUG);
new Log("Client - Session valid: " + socket.getSession().isValid(), LogType.DEBUG);
new Log("Client - CipherSuite: " + socket.getSession().getCipherSuite(), LogType.DEBUG);
new Log("Client - NeedClientAuth: " + socket.getNeedClientAuth(), LogType.DEBUG);
new Log("Client - WantClientAuth: " + socket.getWantClientAuth(), LogType.DEBUG);
Client c = new Client(socket);
//socket.getHandshakeSession() returns null and peer not authenticated exception...
} catch (Exception ex) {
ex.printStackTrace();
continue;
}
}
}
public void disconnect() throws Exception {
new Log("Server disconnecting...", LogType.INFO);
setRunning(false);
ClientManager.getInstance().disconnectClients();
if(server != null) {
server.close();
}
new Log("Server successfully disconnected!", LogType.INFO);
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean state) {
running = state;
}
}
如果你需要什么请告诉我...
RemoteCertificateNameMismatch 策略错误与丢失的本地证书无关。此错误只是告诉您收到的服务器证书中的 SAN(主题备用名称),或者,如果没有 SAN,则收到的服务器证书的主题名称的通用名称与您连接的主机名不匹配。如果您不希望在这两个地方之一的证书中看到主机名,则可以忽略此错误。
我猜你没有本地证书,因为你是从文件中加载证书,而Windows私钥库中没有此证书对应的私钥。
尝试使用 MMC 将证书和私钥(例如,从同时具有证书和私钥的 PFX 文件)导入本地计算机个人证书存储,然后加载 此处使用 .NET 证书存储 API 的证书。只要您有权访问私钥(如果 您 是导入 PFX 的 Windows 用户,您就可以访问),那么您应该能够连接到此客户端证书.
在你的代码中,不是从文件中读取证书,而是从证书存储中加载它,像这样:
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
try
{
X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
return TakeFirstCertificate(collection);
}
finally
{
store.Close();
}
您可以使用指纹以外的其他东西来查找它 -- 这只是一个示例。这就是您拥有的 SelectLocalCertificate return.