【TLS】OpenSSLについて調べてみた

openssl

HeartBeatとHeartBleed-Bug

OpenSSL 1.0.1~1.0.1f のHeartBeat実装には, 重大な脆弱性 (HeartBleed-Bug) が存在しました。

$ openssl version

HeartBeat拡張機能 (RFC6520) は 鼓動/生存確認 という意味からその名前がついています。
TLSハンドシェイク中に, まず ClientHello で HeartBeat拡張 を使うことを伝えます。サーバから ServerHello が応答され, クライアントは以下のように TLS_version,payload,payload_length を指定して HeartBeatRequest を送信します。

# RFC6520
struct {
      HeartbeatMessageType type;
      uint16 payload_length;
      opaque payload[HeartbeatMessage.payload_length];
      opaque padding[padding_length];
   } HeartbeatMessage;

HeartbeatMessageTypeはClientの場合0x01を指定します。

# RFC6520
enum {
      heartbeat_request(1),
      heartbeat_response(2),
      (255)
   } HeartbeatMessageType;

サーバはクライアントの生存を確認し HeartBeatRequest の payload をそのまま echo のように応答 (HeartBeatResponse) しないといけません。

HeartBleed-Bug は以下のように 64KB の payload_length を指定されつつ実際にはそれに満たない payload (以下例ではpayloadに何も入れない) を送信されたときに, OpenSSLをリンクしているプロセスが確保しているメモリをランダムにpayloadに格納して応答してしまうバグです。

0x18 (heartbeat_extension)
0x0302 (TLS1.1)
0x0003 (message_length -> HeartbeatMessageType + payload_length)
0x01 (HeartbeatMessageType)
0xffff (payload_length)

heartbeat

この時, 運悪くメモリに秘密鍵/ユーザID/パスワード/セッションCookieが入っていたら攻撃者に漏洩してしまいます。

脆弱性の発表と同時にパッチ版(1.0.1g)も公開され, 同日中に CentOS/Debian ともにパッチ対応版が配布されています。
最近出た Ubutu14.04LTS (LTS) の OpenSSLは デフォルトで脆弱性対策されていると思います。

話題の OpenSSL について特徴や開発体制,そして使い方について調べてみました。

SSL/TLS

SSL/TLS は安全な通信を行うために生まれたプロトコルでTCPをラップして使用されます。
SSLは1995年にネットスケープから発表され主要ブラウザに広まっていきました, それを受けて IETF の TLSWG が SSL を拡張した TLS の標準化作業を開始し1997年にRFCを公開しました。現在はTLS 1.2が最新です。

SSL/TLS の実装としてOSSとして公開され有名なのが, OpenSSL/GnuTLS です。

OpenSSLの特徴

2014年時点で OpenSSL の開発メンバーは11人程度でほとんどがボランティアで常勤は1人, 年間予算100万ドルに満たないとされています。
米国内に拠点を置くと武器輸出管理法の適用対象となるため, それを回避する目的で OpenSSL の拠点は米国外にあるそうです。OSSとはいえ, 世界の2/3で使われている規模に対して人員や予算の規模が小さすぎる気がします。OpenBSD は OpenSSL を独自に開発する方針に決めたようです。

  • BIOによるI/Oの抽象化
  • コマンドラインインターフェイス
  • 豊富な暗号方式 (暗号スイート一覧)
  • マルチスレッド対応
  • Re-Negotiation

OpenSSLが対応している公開鍵暗号

暗号アルゴリズムは公開鍵暗号の桁数が大きい数の素因数分解問題が現代の計算速度では困難であることを担保としているRSA暗号, DSA, 鍵交換方式として有名なDiffie-Hellman鍵に対応していています。
公開鍵暗号方式は共通鍵暗号方式で問題であった鍵の配送問題を克服したという点で画期的な暗号方式です。
また, 公開鍵の正当性を証明するためのCAやPKIを構築することもできます。(オレオレ証明書)

OpenSSLが対応している共通鍵暗号

共通鍵暗号方式は公開鍵暗号方式と比較して高速な処理が可能です。
共通鍵暗号方式は, ブロック暗号方式 と ストリーム暗号方式 の2つに分類できます。

ブロック暗号方式

ブロック暗号方式は, 数十ビット以上の比較的長いデータブロックごとに暗号化および復号を行う暗号方式です。
1960年代に開発された DES は1977年に 米国商務省標準技術局 (NIST) によって政府公式暗号になりました。その後, DESの危殆化から AES が生まれました。
ブロック暗号を使ったハッシュ関数, つまり一方向性関数を使った方法として メッセージダイジェスト(MD) が有名です。
MDはデータに対して短いダイジェストとなる文字列を生成し改ざんを検出するために使われます。(MD5, MD2, SHA-1, SHA-2, MDC-2)
ブロック暗号のモードとして OpenSSL には CBC (Cipher Block Chaining) などが用意されています。

ストリーム暗号方式

ストリーム暗号方式における暗号化および復号は, 小さいデータブロックごとに逐次に行われるためブロック暗号に比べ速い処理が特徴です。
擬似乱数の乱数性が暗号強度に直接的に影響するので周期性 / パターン性に注意を払う必要があります。
RC4等が有名です。(Blowfish, Camellia, RC2, RC4, RC5, SEED)

OpenSSLをコマンドラインから使う

MD(メッセージダイジェスト)を生成します。

$ openssl dgst [command]
-c              to output the digest with separating colons
-r              to output the digest in coreutils format
-d              to output debug info
-hex            output as hex dump
-binary         output in binary form
-hmac arg       set the HMAC key to arg
-non-fips-allow allow use of non FIPS digest
-sign   file    sign digest using private key in file
-verify file    verify a signature using public key in file
-prverify file  verify a signature using private key in file
-keyform arg    key file format (PEM or ENGINE)
-out filename   output to filename rather than stdout
-signature file signature to verify
-sigopt nm:v    signature parameter
-hmac key       create hashed MAC with key
-mac algorithm  create MAC (not neccessarily HMAC)
-macopt nm:v    MAC algorithm parameters or key
-engine e       use engine e, possibly a hardware device.
-md4            to use the md4 message digest algorithm
-md5            to use the md5 message digest algorithm
-ripemd160      to use the ripemd160 message digest algorithm
-sha            to use the sha message digest algorithm
-sha1           to use the sha1 message digest algorithm
-sha224         to use the sha224 message digest algorithm
...

RSA鍵を生成します。これは頻繁に使います。

$ openssl genrsa [command]
usage: genrsa [args] [numbits]
 -des            encrypt the generated key with DES in cbc mode
 -des3           encrypt the generated key with DES in ede cbc mode (168 bit key)
 -seed
                 encrypt PEM output with cbc seed
 -aes128, -aes192, -aes256
                 encrypt PEM output with cbc aes
 -camellia128, -camellia192, -camellia256
                 encrypt PEM output with cbc camellia
 -out file       output the key to 'file'
 -passout arg    output file pass phrase source
 -f4             use F4 (0x10001) for the E value
 -3              use 3 for the E value
 -engine e       use engine e, possibly a hardware device.
 -rand file:file:...
                 load the file (or the files in the directory) into
                 the random number generator
...

ciphersはSSL/TLSで使用できる暗号スイート一覧を表示します。

$ openssl ciphers  [command]
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=RSA  Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
...

絞り込みで一覧を表示できます。例えば以下は, MEDIUM で RSA/DSA で128bit以上の暗号鍵のみを表示します。

$ openssl ciphers -v "MEDIUM HIGH"
DHE-RSA-SEED-SHA        SSLv3 Kx=DH       Au=RSA  Enc=SEED(128) Mac=SHA1
DHE-DSS-SEED-SHA        SSLv3 Kx=DH       Au=DSS  Enc=SEED(128) Mac=SHA1
ADH-SEED-SHA            SSLv3 Kx=DH       Au=None Enc=SEED(128) Mac=SHA1
SEED-SHA                SSLv3 Kx=RSA      Au=RSA  Enc=SEED(128) Mac=SHA1
IDEA-CBC-SHA            SSLv3 Kx=RSA      Au=RSA  Enc=IDEA(128) Mac=SHA1
IDEA-CBC-MD5            SSLv2 Kx=RSA      Au=RSA  Enc=IDEA(128) Mac=MD5 
RC2-CBC-MD5             SSLv2 Kx=RSA      Au=RSA  Enc=RC2(128)  Mac=MD5 
ECDHE-RSA-RC4-SHA       SSLv3 Kx=ECDH     Au=RSA  Enc=RC4(128)  Mac=SHA1
...

caは CA(認証局) を扱います。

$ openssl ca [command]
 -verbose        - Talk a lot while doing things
 -config file    - A config file
 -name arg       - The particular CA definition to use
 -gencrl         - Generate a new CRL
 -crldays days   - Days is when the next CRL is due
 -crlhours hours - Hours is when the next CRL is due
 -startdate YYMMDDHHMMSSZ  - certificate validity notBefore
 -enddate YYMMDDHHMMSSZ    - certificate validity notAfter (overrides -days)
 -days arg       - number of days to certify the certificate for
 -md arg         - md to use, one of md2, md5, sha or sha1
 -policy arg     - The CA 'policy' to support
 -keyfile arg    - private key file
 -keyform arg    - private key file format (PEM or ENGINE)
 -key arg        - key to decode the private key if it is encrypted
 -cert file      - The CA certificate
...

s_client は SSL/TLS でサーバに接続します。

$ openssl s_client  [command]
 -host host     - use -connect instead
 -port port     - use -connect instead
 -connect host:port - who to connect to (default is localhost:4433)
 -verify arg   - turn on peer certificate verification
 -cert arg     - certificate file to use, PEM format assumed
 -certform arg - certificate format (PEM or DER) PEM default
 -key arg      - Private key file to use, in cert file if
                 not specified but cert file is.
 -keyform arg  - key format (PEM or DER) PEM default
 -pass arg     - private key file pass phrase source
 -CApath arg   - PEM format directory of CA's
 -CAfile arg   - PEM format file of CA's
...

例えば以下で接続できます。

$ openssl s_client -host localhost -port 443

s_serverはSSLサーバを起動します。デバッグなどで便利です。


$ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out cert.pem  
$ openssl s_server -accept 443  -cert cert.pem -www

他にも OpenSSL には DSA秘密鍵生成 の gendsa や乱数を生成する rand などの機能が用意されています。

OpenSSLをアプリケーションから使う

ソースコードはtarballはopenssl-1.0.1g, git版はgithubにあります。
OpenSSLはコマンドラインからも使えますが, アプリケーションから使いたい時には BIO というI/Oを抽象化する OpenSSL の仕組みが用意されています。
BIOはメモリやファイルやソケットといったものに対してソース(読み込み)したりシンク(書き込み)したりできます。
これは crypto/bio.h にあります。BIOオブジェクトを宣言して使います。

typedef struct bio_st BIO;

bio_st構造体はこんな感じです。

struct bio_st
    {
    BIO_METHOD *method;
    /* bio, mode, argp, argi, argl, ret */
    long (*callback)(struct bio_st *,int,const char *,int, long,long);
    char *cb_arg; /* first argument for the callback */

    int init;
    int shutdown;
    int flags;    /* extra storage */
    int retry_reason;
    int num;
    void *ptr;
    struct bio_st *next_bio;    /* used by filter BIOs */
    struct bio_st *prev_bio;    /* used by filter BIOs */
    int references;
    unsigned long num_read;
    unsigned long num_write;

    CRYPTO_EX_DATA ex_data;
};

BIO_METHOD はその直前にあります。

typedef struct bio_method_st
	{
	int type;
	const char *name;
	int (*bwrite)(BIO *, const char *, int);
	int (*bread)(BIO *, char *, int);
	int (*bputs)(BIO *, const char *);
	int (*bgets)(BIO *, char *, int);
	long (*ctrl)(BIO *, int, long, void *);
	int (*create)(BIO *);
	int (*destroy)(BIO *);
        long (*callback_ctrl)(BIO *, int, bio_info_cb *);
	} BIO_METHOD;

ちなみに, BIOのSSL関連のメソッドは以下です。

// bio_ssl.c
static BIO_METHOD methods_sslp=
	{
	BIO_TYPE_SSL,"ssl",
	ssl_write,
	ssl_read,
	ssl_puts,
	NULL, /* ssl_gets, */
	ssl_ctrl,
	ssl_new,
	ssl_free,
	ssl_callback_ctrl,
};

BIOには Chain機能 があり, 例えばファイルを暗号化, 符号化などしてネットワークに流し込むことができます。

今回の件からも, OSSのセキュリティ情報はこまめに確認した方が良いと思います。

OpenSSL
GnuTLS
OpenSSH
NTP
vsftpd
iptables

SSL/TLS関連本

OpenSSL 暗号・PKI・SSL/TLSライブラリの詳細 – ちょっと古いですが OpenSSL の仕組みが詳細にのっていてライブラリの全体像を掴むのに役立ちます。

暗号解読 – 暗号の歴史がわかり個々のストーリーがとても面白いです。古代文字の解読からエニグマ, RSA, 量子暗号 についても触れられています。