## PHP 5.6.x における OpenSSL 関連の変更

### ストリームラッパーが、SSL/TLS を使っている場合のピア証明書とホスト名の検証にデフォルトで対応

暗号化されたすべてのクライアントストリームで、ピア検証がデフォルトで有効になりました。 デフォルトでは、OpenSSL のデフォルト CA バンドルを使ってピア証明書を検証します。 たいていの場合は、正しい SSL 証明書を持つサーバーと通信するならこれを変更する必要はありません。 OpenSSL が、よく知られた CA バンドルを使うように設定されているからです。

デフォルトの CA バンドルを上書きすることもできます。 openssl.cafile あるいは openssl.capath を設定すればグローバルに変更でき、コンテキストオプション `cafile` あるいは `capath` を使えばリクエスト単位で変更できます。

一般的にはおすすめできませんが、 コンテキストオプション `verify_peer` を `false` にしてリクエストでのピア証明書の検証を無効化することもできます。 また同じく、ピア名の検証も、コンテキストオプション `verify_peer_name` を `false` にすれば無効化できます。

### 証明書のフィンガープリント

証明書のフィンガープリントの抽出と、その検証に対応しました。 新たに追加された openssl_x509_fingerprint() 関数が、 X.509 証明書からフィンガープリントを抽出します。また、 SSL ストリームコンテキスト に二つのオプションが追加されました。 `capture_peer_cert` はピアの X.509 証明書を取り込み、 `peer_fingerprint` はピアの証明書が指定したフィンガープリントにマッチすることを確かめます。

### デフォルトの暗号の更新

PHP がデフォルトで使う暗号が更新され、 » Mozilla cipher recommendations に基づいた、よりセキュアなリストになりました。 リストにない例外は、anonymous Diffie-Hellman ciphers と RC4 です。

新しい定数 `OPENSSL_DEFAULT_STREAM_CIPHERS` で、この一覧にアクセスできます。 また、(過去のバージョンの PHP のように) 上書きもできます。上書きをするには、コンテキストオプション `ciphers` を設定します。

### デフォルトでの圧縮の無効化

SSL/TLS の圧縮が、デフォルトで無効になりました。これは、CRIME 攻撃への対応です。 PHP 5.4.13 で新たにコンテキストオプション `disable_compression` が加わり、圧縮を無効にできるようになりました。そのデフォルト値が `true` （つまり圧縮が無効になっている) に変わったのです。

### サーバーの暗号の優先が可能に

SSL コンテキストオプション `honor_cipher_order` が追加されました。これによって、暗号化されたストリームサーバーでの BEAST 脆弱性を除去するために、サーバーの暗号をクライアントよりも優先させられるようになります。

### ネゴシエート済みのプロトコルと暗号へのアクセス

暗号化ストリームでネゴシエート済みのプロトコルと暗号へのアクセスが、 stream_get_meta_data() あるいは stream_context_get_options() でできるようになりました。 これは、SSL コンテキストオプション `capture_session_meta` が `true` になっている場合に有効です。

    <?php
    $ctx = stream_context_create(['ssl' => [
        'capture_session_meta' => TRUE
    ]]);

    $html = file_get_contents('https://google.com/', FALSE, $ctx);
    $meta = stream_context_get_options($ctx)['ssl']['session_meta'];
    var_dump($meta);
    ?>

上の例の出力は以下となります。

    array(4) {
      ["protocol"]=>
      string(5) "TLSv1"
      ["cipher_name"]=>
      string(20) "ECDHE-RSA-AES128-SHA"
      ["cipher_bits"]=>
      int(128)
      ["cipher_version"]=>
      string(11) "TLSv1/SSLv3"
    }

### 暗号化ストリームサーバーでの前方秘匿性に関する新たなオプション

暗号化クライアントストリームはすでに前方秘匿性に対応しています。 というのも、一般に前方秘匿性は、サーバーが制御するものだからです。 PHP の暗号化サーバーストリームが使っている証明書は前方秘匿性に対応しており、 何もしなくても有効になっています。 しかし、前方秘匿性に関してより決め細やかな制御をしたり、 互換性に関する問題に対応したりするために、 新たな SSL コンテキストオプションがいくつか追加されました。

`ecdh_curve`

  このオプションで、ECDH 暗号で使う曲線を選択できるようになります。 省略した場合は `prime256v1` を使います。

`dh_param`

  Diffie-Hellman 鍵交換用のパラメータを含むファイルへのパス。 このファイルは、たとえば以下のようなコマンドで生成したものです。

      openssl dhparam -out /path/to/my/certs/dh-2048.pem 2048

`single_dh_use`

  `true` にすると、Diffie-Hellman パラメータを使うときに、 新たな鍵ペアを作成します。これにより、前方秘匿性が向上します。

`single_ecdh_use`

  `true` にすると、ECDH 暗号スイートでのネゴシエート時に 新たな鍵ペアを作成します。これにより、前方秘匿性が向上します。

### SSL/TLS のバージョンの選択

SSL や TLS の、特定のバージョンを指定できるようになりました。SSL コンテキストオプション `crypto_method` を利用するか、 あるいはストリームラッパーの作成時に (stream_socket_client() あるいは stream_socket_server() を呼んで) トランスポートを指定します。

SSL コンテキストオプション `crypto_method` には、 stream_socket_enable_crypto() 関数の `crypto_type` と同様に、 許可するプロトコルをビットマスクで指定します。

プロトコルのバージョンと、対応するオプション
プロトコル | クライアントのフラグ | サーバーのフラグ | トランスポート
--- | --- | --- | ---
任意のバージョンの TLS あるいは SSL | `STREAM_CRYPTO_METHOD_ANY_CLIENT` | `STREAM_CRYPTO_METHOD_ANY_SERVER` | `ssl://`
任意のバージョンの TLS | `STREAM_CRYPTO_METHOD_TLS_CLIENT` | `STREAM_CRYPTO_METHOD_TLS_SERVER` | `tls://`
TLS 1.0 | `STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT` | `STREAM_CRYPTO_METHOD_TLSv1_0_SERVER` | `tlsv1.0://`
TLS 1.1 | `STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT` | `STREAM_CRYPTO_METHOD_TLSv1_1_SERVER` | `tlsv1.1://`
TLS 1.2 | `STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT` | `STREAM_CRYPTO_METHOD_TLSv1_2_SERVER` | `tlsv1.2://`
SSL 3 | `STREAM_CRYPTO_METHOD_SSLv3_CLIENT` | `STREAM_CRYPTO_METHOD_SSLv3_SERVER` | `sslv3://`

    <?php

    // file_get_contents() を使うには TLS 1.0 以降が必要です
    $ctx = stream_context_create([
        'ssl' => [
            'crypto_method' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
        ],
    ]);
    $html = file_get_contents('https://google.com/', false, $ctx);

    // TLS 1.1 あるいは 1.2 が必要です
    $ctx = stream_context_create([
        'ssl' => [
            'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT |
                               STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
        ],
    ]);
    $html = file_get_contents('https://google.com/', false, $ctx);

    // 接続時に tlsv1.2:// ストリームソケットトランスポートを利用します
    $sock = stream_socket_client('tlsv1.2://google.com:443/');

    ?>

### openssl_get_cert_locations() の追加

openssl_get_cert_locations() 関数が追加されました。 この関数は、PHP が CA バンドルを探す際のデフォルトの場所を返します。

    <?php
    var_dump(openssl_get_cert_locations());
    ?>

上の例の出力は以下となります。

    array(8) {
      ["default_cert_file"]=>
      string(21) "/etc/pki/tls/cert.pem"
      ["default_cert_file_env"]=>
      string(13) "SSL_CERT_FILE"
      ["default_cert_dir"]=>
      string(18) "/etc/pki/tls/certs"
      ["default_cert_dir_env"]=>
      string(12) "SSL_CERT_DIR"
      ["default_private_dir"]=>
      string(20) "/etc/pki/tls/private"
      ["default_default_cert_area"]=>
      string(12) "/etc/pki/tls"
      ["ini_cafile"]=>
      string(0) ""
      ["ini_capath"]=>
      string(0) ""
    }

### SPKI のサポート

署名された公開鍵およびチャレンジ (SPKAC) の生成や抽出、そして検証のサポートが追加されました。 新たに追加された関数 openssl_spki_new()、 openssl_spki_verify()、 openssl_spki_export_challenge() そして openssl_spki_export() で、 SPKAC が HTML5 の `KeyGen` 要素から生成した PEM 公開鍵とチャレンジの作成、検証、エクスポートを行います。

`openssl_spki_new`

  秘密鍵とチャレンジ文字列、そしてハッシュアルゴリズムを指定して、 新しい SPKAC を生成します。

      <?php
      $pkey = openssl_pkey_new();
      openssl_pkey_export($pkey, 'secret passphrase');

      $spkac = openssl_spki_new($pkey, 'challenge string');
      ?>

  上の例の出力は以下となります。

      SPKAC=MIIBXjCByDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA3L0IfUijj7+A8CPC8EmhcdNoe5fUAog7OrBdhn7EkxFButUp40P7+LiYiygYG1TmoI/a5EgsLU3s9twEz3hmgY9mYIqb/rb+SF8qlD/K6KVyUORC7Wlz1Df4L8O3DuRGzx6/+3jIW6cPBpfgH1sVuYS1vDBsP/gMMIxwTsKJ4P0CAwEAARYkYjViMzYxMTktNjY5YS00ZDljLWEyYzctMGZjNGFhMjVlMmE2MA0GCSqGSIb3DQEBAwUAA4GBAF7hu0ifzmjonhAak2FhhBRsKFDzXdKIkrWxVNe8e0bZzMrWOxFM/rqBgeH3/gtOUDRS5Fnzyq425UsTYbjfiKzxGeCYCQJb1KJ2V5Ij/mIJHZr53WYEXHQTNMGR8RPm7IxwVXVSHIgAfXsXZ9IXNbFbcaLRiSTr9/N4U+MXUWL7

`openssl_spki_verify`

  指定した SPKAC を検証します。

      <?php
      $pkey = openssl_pkey_new();
      openssl_pkey_export($pkey, 'secret passphrase');

      $spkac = openssl_spki_new($pkey, 'challenge string');
      var_dump(openssl_spki_verify($spkac));
      ?>

`openssl_spki_export_challenge`

  指定した SPKAC から、関連付けられたチャレンジをエクスポートします。

      <?php
      $pkey = openssl_pkey_new();
      openssl_pkey_export($pkey, 'secret passphrase');

      $spkac = openssl_spki_new($pkey, 'challenge string');
      $challenge = openssl_spki_export_challenge($spkac);
      echo $challenge;
      ?>

  上の例の出力は以下となります。

      challenge string

`openssl_spki_export`

  PEM フォーマットされた RSA 公開鍵を、SPKAC からエクスポートします。

      <?php
      $pkey = openssl_pkey_new();
      openssl_pkey_export($pkey, 'secret passphrase');

      $spkac = openssl_spki_new($pkey, 'challenge string');
      echo openssl_spki_export($spkac);
      ?>

  上の例の出力は以下となります。

      -----BEGIN PUBLIC KEY-----
      MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcvQh9SKOPv4DwI8LwSaFx02h7
      l9QCiDs6sF2GfsSTEUG61SnjQ/v4uJiLKBgbVOagj9rkSCwtTez23ATPeGaBj2Zg
      ipv+tv5IXyqUP8ropXJQ5ELtbXPUN/gvw7cO5EbPHr/7eMhbpw8Gl+AfWxW5hLW8
      MGw/+AwwjHBOwong/QIDAQAB
      -----END PUBLIC KEY-----

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open