SSL(Secure Sokets Layer)はインターネット上でデータを暗号化して送受信するトランスポート層のプロトコル1

TLS(Transport Layer Security)はSSLを元に作られたプロトコル。SSLという名称が広く普及していたことからSSLと呼ばれることも多い。

多くのサイトではより安全性の高いTLSが使われている。

現在のTLSの最新バージョンは1.2で、TLSがネゴシエーション時に使うバージョン番号はSSL時代からの通し番号で「3.3」になっている2

HTTPS (Hypertext Transfer Protocol Secure)はTLSを使って、セキュアなHTTP通信をしている。

サーバ証明書を取得する

SSLにはサーバ証明書が必要。

opensslコマンドなどを使って、証明書発行に必要な証明書要求データを作る。

作り方についてはサーバー証明書のつくりかたと、その原理 - kirinwikiblogが詳しいので参考にした。

まず、opensslコマンドでRSA方式で2048ビットの鍵ペア(秘密鍵)を作る。

$ openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
.............................................+++
...+++
e is 65537 (0x10001)

“2048 bit long modulus”と書いてあるように2048ビットなのはmodulusである。modulusといっているのはRSA暗号 - WikipediaRSA (cryptosystem) - Wikipediaで言うところの $n$ である3

暗号化に使う $e$ は65537になっていて、これは固定っぽい。

server.keyの中身を見ていこう。2048ビットは長いので32ビットで見ていく。32ビット未満は小さすぎるというエラーが出て生成できない。

$ openssl genrsa -out server.key 32
Generating RSA private key, 32 bit long modulus
.+++++++++++++++++++++++++++
.+++++++++++++++++++++++++++
e is 65537 (0x10001)
$ cat server.key
-----BEGIN RSA PRIVATE KEY-----
MCwCAQACBQC1ydvVAgMBAAECBCZXXPUCAwDpYwIDAMdnAgMAhZkCAnE/AgICOg==
-----END RSA PRIVATE KEY-----

このファイルはPEM(Privacy-enhanced Electronic Mail)形式になっている。PEMはBEGINとENDのラベルとその間にBASE64でエンコードされた値を持つファイル。

エンコードしているのは秘密鍵の情報を DER (Distinguished Encoding Rule) という形式でエンコードしたバイナリ。

このDERのバイナリについて、詳しく知りたい場合はRSA 秘密鍵/公開鍵ファイルのフォーマット - bearmini’s blogを参照して下さい。

バイナリを直接見なくても、以下のコマンドで秘密鍵の内容を知ることができる4

$ openssl rsa -in server.key -text -noout
Private-Key: (32 bit)
modulus: 3049905109 (0xb5c9dbd5)
publicExponent: 65537 (0x10001)
privateExponent: 643259637 (0x26575cf5)
prime1: 59747 (0xe963)
prime2: 51047 (0xc767)
exponent1: 34201 (0x8599)
exponent2: 28991 (0x713f)
coefficient: 570 (0x23a)

色々な例を見ると、publicExponentは65537で固定、privateExponentはmodulusと同じビット数(ここでは32ビット = 4バイト)、primeはmodulusの半分ビット数(ここでは16ビット = 2バイト)になるっぽい。

秘密鍵(RSA PRIVATE KEY)というと、復号化

$$ a = b^d \mod n $$

に必要な $d$ (privateExponent)の情報のみが入っいるのかと思っていたが、公開鍵に必要な情報 $n, e$を入っているので紛らわしい。

入っているからこそ、次のコマンドで証明書要求データに必要な公開鍵を含めることができるのだが……。

では、証明書要求データ、CSR(Certificate Signing Request)を作ってみる。

$ openssl req -new -key server.key -out server.req
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) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:tmsanrinsha.net
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Common Nameだけtmsanrinsha.netを入力して他は空で作ってみた。ここで出力されるserver.reqもPEM形式。拡張子をcsrで作成する場合もあり。server.keyは32ビットだと作成に失敗するので2048ビットにした。

中身を確認する。

$ openssl req -in server.req -text -noout
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=tmsanrinsha.net
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:b9:0d:07:3e:d1:75:ba:66:b2:fe:5e:c0:2f:d8:
                    (中略)
                    f2:ef
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha1WithRSAEncryption
        9c:ad:20:a2:d5:3b:02:e5:d2:a2:f7:bd:db:2e:ff:49:7f:06:
        (中略)
        b0:98:9f:77

DATAの部分に先程入力した値と公開鍵の情報が入っている。

Signature Algorithm: sha1WithRSAEncryptionの部分は電子署名で、秘密鍵を使って署名されている5

このファイルを認証局に送る。

認証局は証明書要求データの提出者がドメインの所有者であるかを検査する。例えば、提出者にメールで秘密のパスワードを送り、提出者はそのパスワードをサイトのトップディレクトリに置く。 認証局の人がそのファイルを確認できればOKのような審査だ6

審査が通るとサーバ証明書が取得できる。

雰囲気をつかむために自分で証明書を作成してみよう。

$ openssl x509 -req -in server.req -signkey server.key -out server.crt -days 3650
Signature ok
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/CN=tmsanrinsha.net
Getting Private key

X.509という形式でserver.reqというCSRファイルから、自分の秘密鍵server.keyでserver.crtという証明書を作った。期間は3650日。

自分の秘密鍵で署名をしているため、これは自己署名証明書(オレオレ証明書)と呼ばれる証明書になっている。

server.crtの中身を見てみる。

$ openssl x509 -in server.crt -text -noout
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            ce:13:48:d3:8d:0a:dc:f8
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=tmsanrinsha.net
        Validity
            Not Before: Oct 22 03:34:15 2017 GMT
            Not After : Oct 20 03:34:15 2027 GMT
        Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd, CN=tmsanrinsha.net
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:b9:0d:07:3e:d1:75:ba:66:b2:fe:5e:c0:2f:d8:
                    (中略)
                    f2:ef
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha1WithRSAEncryption
        1a:0e:de:6c:34:d0:ca:03:e0:17:53:74:f8:f1:f5:dd:e1:a2:
        (中略)
        6c:bc:d6:34

CSRにIssuerや有効期限が追加されてたような構造になっている。署名はサーバ証明書の最初の部分をハッシュ化して、秘密鍵(通常だと認証局のもの)で暗号化して作られる。

サーバ証明書の正当性は、署名を認証局の公開鍵で復号して出てきたハッシュ値と、証明書の最初の部分をハッシュ化して出てきたハッシュ値が一致するかで検証される。

認証局の公開鍵は中間証明書という証明書から取得できて、これはSSL通信時にはサーバ証明書とともにサーバから送られてくる。

では中間証明書の正当性の検証はどう行われるか。

中間証明書にも署名が入ってあり、それはルート認証局という最上位の認証局の秘密鍵で署名されている。ルート認証局の公開鍵はルート証明書から取得できて、ルート証明書はブラウザに最初から入っているので検証することができる。

ルート認証局より上位の認証局はないためルート証明書は自己署名証明書になっている。

このように証明書を検証する仕組みがチェーンのように連なっているので、一連の証明書のことを証明書チェーンといったりする。

中間証明書はサーバ証明書とルート証明書の間にあるので、中間証明書と呼ばれている。ルート証明書と中間証明書にクロスルート設定用証明書というものが存在する場合もあるようだ。

イントラネットなどでは中間証明書を介さず、いきなりルート証明書になることもあるようだ。イントラネットのルート証明書は自分でブラウザにインストールする必要がある。

サーバの設定

サーバには

  • サーバ証明書
  • 中間証明書(必要な場合)
  • サーバ証明書を作るために使った秘密鍵

を置く。置き場所や設定はapacheだのtomcatだのhttpサーバの設定に従う。

SSL通信

クライアントとサーバのやり取りは以下の通り7。ここではブラウザとウェブサーバを例にとるが、アプリとAPIサーバ、ウェブサーバとAPIサーバといった組み合わせも同じ感じだと思われる。

資料

  1. クライアントがセキュアなウェブサイト(https// …)にアクセスして、SSL暗号化通信で接続要求する。このときクライアントは自身が使用可能な暗号化の仕様(暗号方式、鍵長、圧縮方式)をウェブサーバーへ伝える。
  2. ウェブサーバはクライアントから提示された暗号化の仕様から実際に利用するものを選択して、クライアントに通知する。
  3. ウェブサーバはサーバ証明書をクライアントに送る。中間証明書があるならそれも送るはず。ただし同じタイミングかは不明。
  4. クライアントは受け取った証明書の署名検証を行う。
  5. クライアントは証明書の内容の検証する。
    • Coomon Name (FQDN)や有効期限などに問題がないか
    • 証明書が失効してないか(認証内容の変更や暗号鍵の紛失などで認証局が証明書の失効を宣言することがある)8
  6. クライアントは証明書の検証が完了すると、サーバ証明書にあるウェブサーバの公開鍵を用いて、プリマスタシークレット(共通鍵を生成するもととなる乱数データ)を暗号化して、ウェブサーバへ送付する。
  7. ウェブサーバは受け取った暗号化されたプリマスタシークレットを自分の秘密鍵を用いて復号化する。
  8. クライアントとウェブサーバは共有されたプリマスタシークレットを基に共通鍵を生成し、以降のセッションはこの共通鍵を用いて、暗号化、復号化をして通信する。

共通鍵暗号化方式を使うのは公開鍵暗号化方式よりも処理速度が早いため9

クライアントがクライアント証明書をもって、「その証明書保有者のみが閲覧可能なサイト」を閲覧する仕組みもあるそうな10