首先放出几个参考文档:
数字证书
如果想深入理解数字证书,有几个概念必须要搞清楚
- 数字证书标准 x.509
- 数字证书格式 PEM(字符/ASCII格式)、DER(二进制格式)
- 证书链
制作证书需要用到几个工具:
- makecert.exe证书制作工具
- Cert2Spc.exe公钥证书格式转换成SPC
- pvk2pfx.exe将公钥证书和私钥合并成一个PFX格式的证书文件
制作根证书
根证书就是自签名的证书,在证书链的顶端,自己验证自己。
证书最初生成时是一个自签名证书。自签名证书是其签发者(签名者)与主题(其公共密钥由该证书进行验证的实体)相同的证书。如果拥有者向 CA 发送证书签名请求 (CSR),然后输入响应,自签名证书将被证书链替换。链的底部是由 CA 发布的、用于验证主题的公共密钥的证书(回复)。链中的下一个证书是验证 CA 的公共密钥的证书。通常,这是一个自签名证书(即,来自 CA、用于验证其自身的公共密钥的证书)并且是链中的最后一个证书。
makecert -n “CN=Root” -r -sv RootIssuer.pvk RootIssuer.cer
注意: 服务端和客户端的电脑上都需要先导入根证书
制作https数字证书
我们需要使用刚才生成的根证书签发一张https数字证书(其实并没有指定一定是https使用的,socket一样可以用,下文ssl socket也是使用的此数字证书)
makecert -n “CN=www.lwwl.tech” -b 06/22/2018 -e 06/23/2019 -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localmachine -sky exchange -sp “Microsoft RSA SChannel Cryptographic Provider” -sy 12 -$ commercial -cy authority -iv RootIssuer.pvk -ic RootIssuer.cer -sv lwwl.pvk lwwl.cer
注意:CN=www.lwwl.tech必须是你服务器绑定的域名,否则浏览器会提示安全验证问题。
-n 中还可以带其他参数:
-n “CN=公司名称, E=E-MAIL地址, O=组织名称, OU=组织单位, C=国家, S=省份(州), P=县城”
签发证书的较理想方式是:
(1)首先生成一张根证书,根证书需安装在各个设备中,永远不能改变;
(2)使用根证书签发一张用于签发证书的证书A(姑且称为签发证书A);
(3)然后使用签发证书A去签发实际使用的数字证书B(姑且称为叶子证书B);
证书链:根证书 << 签发证书A << 叶子证书B
我们可以看一下csdn的证书链:
好处是方便使用签发证书来管理此证书签发的所有证书,还有其他的可以留言补充。
制作pfx并添加到iis
在iis中导入证书必须为.pfx格式,所以我们需要将之前生成的lwwl.pvk和lwwl.cer。
(1)首先生成.spc格式证书(需要使用cert2spc.exe工具)
cert2spc lwwl.cer lwwl.spc
.spc 意思是 软件发布者证书(Software Pulisher Cerificate).
(2)将公钥证书和私钥合并成一个PFX格式的证书文件。pvk2pfx.exe
pvk2pfx -pvk lwwl.pvk -spc lwwl.spc -pfx lwwl.pfx
然后再进入iis导入.pfx证书即可:
注意: 密码 一栏不填
将网站设为https:
C# ssl socket 通信
可以参考:C# Socket SSL通讯笔记
服务端代码:
// ssl socket server
class SocketServer
{
static X509Certificate serverCertificate = null;
Socket socket { get; set; }
public void startListen()
{
X509Store store = new X509Store(StoreName.My);
store.Open(OpenFlags.ReadWrite);
// 检索证书
X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySubjectName, "www.lwwl.tech", false); // vaildOnly = true时搜索无结果。
if (certs.Count == 0) throw new Exception("没有找到合法的数字证书");
serverCertificate = certs[0];
IPAddress ip = IPAddress.Any;
//端口号
IPEndPoint point = new IPEndPoint(ip, 8080);
//创建监听用的Socket
/*
* AddressFamily.InterNetWork:使用 IP4地址。
SocketType.Stream:支持可靠、双向、基于连接的字节流,而不重复数据。此类型的 Socket 与单个对方主机进行通信,并且在通信开始之前需要远程主机连接。Stream 使用传输控制协议 (Tcp) ProtocolType 和 InterNetworkAddressFamily。
ProtocolType.Tcp:使用传输控制协议。
*/
//使用IPv4地址,流式socket方式,tcp协议传递数据
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//创建好socket后,必须告诉socket绑定的IP地址和端口号。
//让socket监听point
try
{
//socket监听哪个端口
socket.Bind(point);
//同一个时间点过来10个客户端,排队
socket.Listen(10);
ShowMsg("服务器开始监听");
Thread thread = new Thread(AcceptInfo);
thread.IsBackground = true;
thread.Start(socket);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
}
}
void AcceptInfo(object o)
{
Socket socket = o as Socket;
while (true)
{
try
{
//创建通信用的Socket
Socket tSocket = socket.Accept();
// 配置ssl
var m_DataStream = new NetworkStream(tSocket, true);
SslStream sslStream = new SslStream(m_DataStream, false);
sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Default, true);
Console.WriteLine("Waiting for client message...");
string point = tSocket.RemoteEndPoint.ToString();
ShowMsg(point + "连接成功!");
Thread th = new Thread(ReceiveSslMsg);
th.IsBackground = true;
th.Start(sslStream);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
break;
}
}
}
void ReceiveSslMsg(object o)
{
SslStream sslStream = o as SslStream;
while (true)
{
try
{
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
}
while (bytes != 0);
ShowMsg("Server SSL Data = " + messageData.ToString());
sslStream.Write(Encoding.UTF8.GetBytes("Server Receive SSL Data Success<EOF>"));
sslStream.Flush();
}
catch (Exception ex)
{
Console.WriteLine("Server ReceiveSslMsg :" + ex.Message);
}
}
}
void ShowMsg(string msg)
{
Console.WriteLine(msg);
}
public void StopListen()
{
if (socket != null)
{
socket.Close();
}
}
}
客户端代码:
// ssl socket client
class SocketClient
{
private SslStream sslStream;
private Socket client;
public void SendMsg(string msg)
{
byte[] messsage = Encoding.UTF8.GetBytes(msg + "<EOF>");
sslStream.Write(messsage);
sslStream.Flush();
}
public void Connect()
{
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Connection(client);
}
private void DisConnect(Socket client)
{
try
{
if (client != null)
{
client.Close();
}
}
catch (Exception ex)
{
}
}
private void Connection(Socket client)
{
//连接到的目标IP
IPAddress ip = IPAddress.Parse("127.0.0.1");
//IPAddress ip = IPAddress.Any;
//连接到目标IP的哪个应用(端口号!)
IPEndPoint point = new IPEndPoint(ip, 8080);
try
{
//连接到服务器
client.Connect(point);
ShowMsg("连接成功");
// 验证ssl
var m_DataStream = new NetworkStream(client, true);
sslStream = new SslStream(m_DataStream, false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
try
{
sslStream.AuthenticateAsClient("www.lwwl.tech");
}
catch (AuthenticationException e)
{
Console.WriteLine("Exception: {0}", e.Message);
if (e.InnerException != null)
{
Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
}
Console.WriteLine("Authentication failed - closing the connection.");
client.Close();
return;
}
ShowMsg("服务器" + client.RemoteEndPoint.ToString());
ShowMsg("客户端:" + client.LocalEndPoint.ToString());
//连接成功后,就可以接收服务器发送的信息了
Thread th = new Thread(new ParameterizedThreadStart(ReceiveSslMsg));
th.IsBackground = true;
th.Start(sslStream);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
}
}
private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// Do not allow this client to communicate with unauthenticated servers.
return false;
}
void ReceiveSslMsg(object o)
{
SslStream sslStream = o as SslStream;
while (true)
{
try
{
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
}
while (bytes != 0);
ShowMsg("Client SSL Data = " + messageData.ToString());
}
catch (Exception ex)
{
Console.WriteLine("Client ReceiveSslMsg :" + ex.Message);
}
}
}
void ShowMsg(string msg)
{
Console.WriteLine(msg);
}
private void send(Socket client, string msg)
{
//客户端给服务器发消息
if (client != null)
{
try
{
byte[] buffer = Encoding.UTF8.GetBytes(msg);
client.Send(buffer);
}
catch (Exception ex)
{
ShowMsg(ex.Message);
}
}
}
}
Main函数测试:
static class Program
{
static void Main()
{
SocketServer server = new SocketServer();
server.startListen();
SocketClient _client = new SocketClient();
_client.Connect();
_client.SendMsg("ABC");
}
}