この記事の流れ

  1. opensslで秘密鍵を発行してみる
  2. 秘密鍵の中身を見る
  3. RSA暗号の解説。証明。
  4. rsa暗号でサーバ証明書を発行するまで
  5. SAN付きのサーバ証明書の作り方
  6. lets encryptでsan付きのサーバ証明書が作れるか

そもそもsslサーバ証明書とは?

「サーバー証明書」とは、「通信の暗号化」「Webサイトの運営者・運営組織の実在証明」の2つの役割をもつ電子証明書です。

です。つまり、証明書には、暗号化用の鍵+証明書が入っている、ということになる。いいですね。

秘密鍵の中身をのぞいてみる。

秘密鍵の作成

$ sudo mkdir /etc/nginx/ssl
$ sudo openssl genrsa -out /etc/nginx/ssl/server.key 512

秘密鍵を作っているけど、公開鍵はどこにあるのか??というのが非常に気になります。ていうか秘密鍵が因数分解されたものなんだっけ? だからいいのか。掛け算したら一瞬でわかるって話か。おけ。ってことでさ、適当に鍵を一つ作ってみたんだよ。 ちなみに、512bitより短い鍵は作れません。これすごいよね。

-----BEGIN PRIVATE KEY-----
MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA4dNuxX/SzX3wEhde
FmNFXLWlNkbGq4xaU6CfKy2nI9K+SWSKueJWSjoanmEG1eb6cXozmAk4eiuKm7zv
+FdQowIDAQABAkB+1CdnRoXXIT7eej8+ZZyEGARkulVD7XyhcRlTv70aMWjC29cG
1LIeSScmy5ChqoGwH9Ow/BRzZXax3yeqMQFJAiEA9m+ardfTOK/xVoSn3I5pkzoR
prW2xLQi0IDsv5lEu3UCIQDqlxAo6aDT0KpBBe5HbxBgw7uf34G+m7LJs/JnoMYQ
twIgALXvr0KpFEfFnWdCiKtMeKU5Oc7aWRTf6NQGWsMZZKUCIFxk6wRyH9nNEYFS
qKqR3818yeUJzrwX7q7qpMqT0+65AiEAqWRxDU6t8ZjPmJd6B7w481FuwrFmF9cq
mq+QXX8gjRw=
-----END PRIVATE KEY-----
echo "MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA4dNuxX/SzX3wEhde
FmNFXLWlNkbGq4xaU6CfKy2nI9K+SWSKueJWSjoanmEG1eb6cXozmAk4eiuKm7zv
+FdQowIDAQABAkB+1CdnRoXXIT7eej8+ZZyEGARkulVD7XyhcRlTv70aMWjC29cG
1LIeSScmy5ChqoGwH9Ow/BRzZXax3yeqMQFJAiEA9m+ardfTOK/xVoSn3I5pkzoR
prW2xLQi0IDsv5lEu3UCIQDqlxAo6aDT0KpBBe5HbxBgw7uf34G+m7LJs/JnoMYQ
twIgALXvr0KpFEfFnWdCiKtMeKU5Oc7aWRTf6NQGWsMZZKUCIFxk6wRyH9nNEYFS
qKqR3818yeUJzrwX7q7qpMqT0+65AiEAqWRxDU6t8ZjPmJd6B7w481FuwrFmF9cq
mq+QXX8gjRw=" | base64 --decode

これでデコードしてもバイナリだからなんもわからんわ。

いかのスクリプトでデコードできるみたい。

from Crypto.PublicKey import RSA

# Load the private key
with open("private_key.pem", "r") as file:
    private_key = file.read()

# Parse the private key
key = RSA.import_key(private_key)

# Extract the components
n = key.n  # Modulus
e = key.e  # Public exponent
d = key.d  # Private exponent
p = key.p  # Prime 1
q = key.q  # Prime 2

print(f"Modulus (n): {n}")
print(f"Public exponent (e): {e}")
print(f"Private exponent (d): {d}")
print(f"Prime 1 (p): {p}")
print(f"Prime 2 (q): {q}")
Modulus (n): 11827462552050103756019456673259686146276364164688350469602565108471900615793415256864500417710852720839063036792768824435280979352135219586691204539437219
Public exponent (e): 65537
Private exponent (d): 6642559381010851411383007183311248119169234719466623674178885422700539328402280041863549141435055457596044186231508711647457747564061370576607226853654857
Prime 1 (p): 111466148331411145531384799415569793304697439187343498431136600736302912289653
Prime 2 (q): 106108112006209210762976910352826494821357000184205525073929931397132009738423

ということですね。はい。 はい、でさ、RSA暗号の仕組みも今勉強してしまいましょうか。

これが非常にわかりやすく説明してくれていた フェルマーの小定理と金、使われているんだよね。競プロやっていた時は結構意識していたけど、最近は全くわからんな。

rsa暗号の仕組み

rsa暗号を理解するには

  1. 不定方程式の性質
  2. 中国剰余定理
  3. フェルマーの小定理

の三つの理解が必要です。 それぞれ簡単に説明していくか。

不定方程式の性質

m,nが互いに素である時、
mx + ny = 1
は整数解を持つ。

中国剰余定理

よくある問題だよね。

5で割って3あまり、7で割って2余る整数を、0 < n <=35の範囲で求めよ。

n = 5x + 3
n = 7y + 2

5x - 7y = -1
を解けばいいんだよね。
これは高校数学でやったやつでさ、一つ買いを見つけてってやつだよね。
で、まあ一つ見つかるんだけど、これを一般化するとこんな感じになる。

# 中国剰余定理
与えられた二つの整数 m, n が互いに素ならば、任意に与えられる整数 a, b に対し、連立合同式

x ≡ a (mod m),
x ≡ b (mod n)
を満たす整数 x が mn を法として一意的に存在する

フェルマーの小定理

a^(p - 1) ≡ 1 (mod p)

これを見つけたフェルマーはすごいなー。

まずはrsa暗号の仕組み

以下の整数を定義する

p,q : 秘密鍵の二つの素数
n := p*q
e := p - 1, q - 1と互いに素である整数。
L := p - 1と q - 1の最小公倍数 (gcd)
d :=  de - Ly = 1を満たす整数。(Lは指定されていて、eもわかっているので、(d,y)の一時不定方程式になる。実は、これは自然数回を持つことがわかる。) 

で、暗号化の手順だけど、 整数xをe乗して、nで割ったあまりをf(x) とする。これが暗号文です。つまり、

f(x) = x^(e) % n 

これが暗号文になる。

復号の仕方だけど、

f(x)をd乗してnで割ったものが元のxになっている。つまり

x^(e*d) ≡ x (mod n)

という話なんだが、これ結構すごいよね。全くわからんって話です。

rsa暗号の原理の証明

で、 上で説明した3つ定理を使ってこの

x^(e*d) ≡ x (mod n)

が証明できるという話なのだが、これはスキップですみません。

さっきのpythonのスクリプトもう一回見てみましょうか。

Modulus (n): 11827462552050103756019456673259686146276364164688350469602565108471900615793415256864500417710852720839063036792768824435280979352135219586691204539437219
Public exponent (e): 65537
Private exponent (d): 6642559381010851411383007183311248119169234719466623674178885422700539328402280041863549141435055457596044186231508711647457747564061370576607226853654857
Prime 1 (p): 111466148331411145531384799415569793304697439187343498431136600736302912289653
Prime 2 (q): 106108112006209210762976910352826494821357000184205525073929931397132009738423

うん、公開鍵は結局、nとeを持っているって話な。で、dは別に公開する必要ないって話だな。 dは公開してはダメだね。これが結局暗号を解くための鍵になるって話ですからね。

まずはopensslを使って単一のサーバ証明書

参考サイト

秘密鍵の生成

sudo openssl genrsa -out /etc/nginx/ssl/server.key 2048

という感じでまず最初に秘密鍵を生成します。

CSR(証明書署名要求)の作成

$ sudo openssl req -new -key /etc/nginx/ssl/server.key -out /etc/nginx/ssl/server.csr

これを入力すると色々聞かれるんですよね。

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value,
If you enter '', the field will be left blank.
Country Name (2 letter code) [XX]: JP
State or Province Name (full name) |]: Tokyo
Locality Name (eg, city) [Default Cityl:
company) Organization Name (eg, company) [Default Company Ltdl
Organizational Unit Name (eg, section) |]:
Common Name (eg, your name or your server's hostname) [l
Email Address []: Please enter the following 'extra' attributes
to be sent with your certificate request
[A challenge password [:
An optional company name I:

これ、何やっているかってすごく大事なところよね。 まず、csrは暗号技術の仕組みそのものに関わるものではなく、完全に人の営みの中で必要なものになってくる。ということ。 「CSRの中には公開鍵や識別情報(組織名、部署名、国名など)、重要な署名が含まれています。 CSRを受け取ったCAはこれらの情報を検証し、CSRの正当性を確認します。」 ということですね。「csr = 公開鍵 + 公開鍵発行者の情報」というわけです。 これをCAに提出するわけですね。そこで、この公開鍵が発行者のものである!という署名をもらうわけです。 その署名をする機関がまあlets encryptだったり、cyber trustだったりするわけですね。理解です。 で、署名するときも鍵を秘密鍵を使って署名するわけですね。

CRT(SSLサーバ証明書)の作成 (自己署名)

$ sudo openssl x509 -days 3650 -req -signkey /etc/nginx/ssl/server.key -in /etc/nginx/ssl/server.csr -out /etc/nginx/ssl/server.crt
$ ls /etc/nginx/ssl/
server.crt  server.csr  server.key

こちら何をやっているか、という話なんですが、 /etc/nginx/ssl/server.keyという鍵で、server.csrに対して、署名をし、server.crtという鍵を生成している、という流れになります。 そんな感じです。はい。

「まずCSRのデータをハッシュ関数にかけてハッシュ値を算出します。次にCSRのハッシュ値を署名生成鍵(秘密鍵)で暗号化します。その暗号化したハッシュ値と、元のCSRを合わせたデータが「署名済み証明書」となります。」

ちなみに、証明書にも色々と種類があるっては暗視でov証明書とか、dv証明書とか、どこまで証明するかによって名前が違う。」

サイトにアクセスしてくるユーザの動き

「署名済み証明書の発行後は、サーバーに証明書のインストールが行われます。このサーバーに対してクライアントがアクセスすると、署名済み証明書の検証が行われます。証明書の検証では、正当な証明書であるか、改竄されていないか、などが確認されます。」

「署名済み証明書の検証の流れを以下の図に表しました。署名済み証明書を受け取ったクライアントは、署名済み証明書に含まれるCSRのデータからハッシュ値を算出します。次に暗号化されたハッシュ値を検証鍵(公開鍵)で復号します。CSRから算出したハッシュ値と、復号したハッシュ値を比較することで検証を行います。一致すれば正しいサーバーとして認識されます。反対に、もし証明書に改竄があればハッシュ値が一致せず、クライアントのブラウザに警告が表示されます。」

という感じで、 実は、サーバ証明書 = CSR + CSRのハッシュ値をCAの秘密鍵で暗号化したもの。 ハッシュ関数にはSHA-256が使われ、暗号化には秘密鍵が使われる。(これは間違いなのである。)で、 ユーザは証明書をハッシュにかけ 電子署名が秘密鍵で暗号化と言い右翼ある誤解の話 はい、誤解なんですね。

「これは、受け取った署名に対して「公開鍵と一緒に暗号化アルゴリズム」に渡して処理をすると「秘密鍵と一緒に復号化アルゴリズム」に渡して処理する前のデータが得られるという、RSA公開鍵暗号の特殊な性質を利用しています。」 ここは実は結構難しいポイントだったりする。上のRSA暗号の仕組みを知る必要がありそうだね。と思ったんだけど、めっちゃ当たり前のことを言っているだけだった。

平文:=xとすると、

x^(d)e ≡ x^(e)d ≡ x

ということで、復号処理と暗号処理の順番は関係ないんですね。 だから、秘密鍵でメッセージに処理をかけて、そのあと公開鍵で処理をかけて、ってやると原文が得られるんですよね。これはまあ結構便利だともいます。はい。

ただ、注意が必要なのが、TLS v1.2ではssl/tls通信に置いてRSA暗号は非推奨となり、ecdhが推奨されるようになったという話です。1.3ではrsaは廃止されたという話です。

参考

このサイトが非常にわかりやすい。 あと、

nginxの設定はこうする

$ sudo vi /etc/nginx/conf.d/[任意のファイル].conf
server {
    # 443番ポートを許可し、SSL機能をON
    # listen 80;
    listen 443 ssl;

    # 証明書を設定
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
}

opensslをつかって俺俺証明書でsanのサーバ証明書を作る方法

参考

まず、sanが何かだけど、subect alt nameだね。これを使うと、一つのサーバ証明書で複数のドメインに対応できるようになる。 nginxで仮想サーバの機能を使って複数のEPを作るときに、一つの証明書で大丈夫になるって話だ。

作り方。

sanファイルを作る¥

$cat san.txt 
subjectAltName = DNS:*.my-test-domain.local, DNS:my-test-domain.local

秘密鍵を作成

$openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus

certificate signature requestを作る

$openssl req -out server.csr -key server.key -new
$ openssl x509 -req -days 3650 -signkey server.key -in server.csr -out server.crt -extfile san.txt
Signature ok
subject=/C=jp/ST=osaka/L=osaka/O=my-company/CN=my-test-domain.local
Getting Private key

という感じですね。はい。

cname レコードについて

別名は、例えば1台のサーバーで複数のサービスを提供している場合に、サービスごとにサーバーの名前を変えるときに使用します。サーバーの正式なドメイン名はservice.example.jpであるが、将来のことを考えて情報提供ではinfo.example.jpを、ショッピングではshop.example.jpというドメイン名で運用したいといったケースが該当します。この例のような場合、DNSではCNAMEリソースレコードを用いて以下のように定義できます。

ちな、cname のcはcanonicalであり、commonではないので注意。

lets encryptでsan付きのサーバ証明書を発行する方法

ここに書いてあるので割愛します。はい。