この記事の流れ
- opensslで秘密鍵を発行してみる
- 秘密鍵の中身を見る
- RSA暗号の解説。証明。
- rsa暗号でサーバ証明書を発行するまで
- SAN付きのサーバ証明書の作り方
- 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暗号を理解するには
- 不定方程式の性質
- 中国剰余定理
- フェルマーの小定理
の三つの理解が必要です。 それぞれ簡単に説明していくか。
不定方程式の性質
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付きのサーバ証明書を発行する方法
ここに書いてあるので割愛します。はい。