1. はじめに
続きです。クライアントからClientHelloを受け取ったら、ServerHello をクライアントに返します。でもその際に「サーバー証明書」が必要になります。
自己署名証明書というものを作っていきます。「テスト用ならこれでいいんだけれども...」と何度もChatGPT君に言われてました。テストだから良いのです。
俗に「オレオレ証明書」と言われているものです。
pfx でエクスポートすると あとで使う時に非常に楽になるのでオプションで出すようにします。
2. サーバー自己署名証明書を作ろう
2-1. opensslで作る
これがたぶん一番楽です。
2-1-1.秘密鍵を作成
サーバーの秘密鍵を作ってserver.keyというファイル名で保存するようにします。
openssl genrsa -out server.key 2048
2-1-2.CSR(証明書署名要求)を作る
サーバーの秘密鍵からCSR(証明書署名要求)を作ります。
本来なら「サーバーの公開鍵で中身を作る」のですが、公開鍵は秘密鍵から作れるので、秘密鍵さえ判ればいいのです。
CN(CommonName) は後でクライアントがサーバー認証をするときに使う(可能性がある)ので、ドメイン名を入れます。
openssl req -new -key server.key -out server.csr -subj "/CN=www.mydomain.com"
他のパラメータもこんな感じで追加できます。
openssl req -new -key server.key -out server.csr -subj "/C=JP/ST=Tokyo/L=Chiyoda/O=Example Inc./CN=www.maydomain.com"
使わないなら単純に
openssl req -new -key server.key -out server.csr
2-1-3.サーバーの自己署名証明書(server.crt)を作る
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
ここでは有効期限を365日としています。そのうち47日になります。
2-1-4.pfx形式で保存する
作成した サーバーの秘密鍵 (server.key) とサーバーの自己署名証明書 (server.crt) をまとめて PFX(PKCS#12)形式 に変換します。PFX は Windows の IIS や多くのアプリケーションで使いやすい形式です。もちろん後でサーバー認証の時に使います。
openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt
パスワードを求められます。このパスワードはpfxファイルのパスワードであり、それ以外の用途はありません。
パスワードをいちいち聞かれるのも面倒です。セキュリティ的にアレなのですが、コマンドラインからパスワードを指定できます。
openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt -passout pass:p@ssw0rd
パスワードをコマンドラインに書きたくなければ環境変数に書いておくといいでしょう。
openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt -passout env:PFX_PASS
2-2.どうしてもプログラムで書きたい人向け
.NET Framework でサーバー自己署名証明書が作れます。
2-2-1. RSA版とECDSA版
ChatGPT君によるとECDSA版の方がよりモダンなのだとか。
せっかくなので両方作ってもらいましょう。
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
class Program
{
static void Main()
{
string ecdsaPath = @"server_ecdsa.pfx";
string rsaPath = @"server_rsa.pfx";
string password = "p@ssw0rd";
// ECDSA 証明書作成
CreateEcdsaCertificate(ecdsaPath, password);
// RSA 証明書作成
CreateRsaCertificate(rsaPath, password);
}
static void CreateEcdsaCertificate(string path, string password)
{
using (ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256))
{
var req = new CertificateRequest(
"CN=localhost",
ecdsa,
HashAlgorithmName.SHA256);
AddStandardExtensions(req);
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1));
File.WriteAllBytes(path, cert.Export(X509ContentType.Pfx, password));
Console.WriteLine($"ECDSA PFX 作成完了: {path}");
}
}
static void CreateRsaCertificate(string path, string password)
{
using (RSA rsa = RSA.Create(4096))
{
var req = new CertificateRequest(
"CN=localhost",
rsa,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1);
AddStandardExtensions(req);
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1));
File.WriteAllBytes(path, cert.Export(X509ContentType.Pfx, password));
Console.WriteLine($"RSA PFX 作成完了: {path}");
}
}
static void AddStandardExtensions(CertificateRequest req)
{
// SAN
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("localhost");
req.CertificateExtensions.Add(sanBuilder.Build());
// 基本制約(CA ではない)
req.CertificateExtensions.Add(
new X509BasicConstraintsExtension(false, false, 0, false));
// KeyUsage と EKU(TLS サーバー用)
req.CertificateExtensions.Add(
new X509KeyUsageExtension(
X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, false));
req.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
}
}
2-2.サーバー自己署名証明書の確認
openssl で 作ったサーバー自己証明書を確認できます。
openssl pkcs12 -in server.pfx -nokeys -out cert.pem
openssl x509 -in cert.pem -noout -text
確認ポイント:
・Signature Algorithm → 設定したアルゴリズムであること
・Public-Key: (256 bit) → 設定したアルゴリズムであること
・Not Before / Not After → 有効期限
・SAN -> 設定したSANであること
3. 終わりに
サーバー自己署名証明書 が作れたので次回に続きます。