3

OpenSSL と TLS を使用して、SSH のような信頼モデルを提供しようとしています。つまり、2 つのピアにはそれぞれ RSA キー ペアが保存されており、証明書はありません。また、TLS セッションが確立される前に、公開鍵も交換されています。これを達成するために、OpenSSLの文書化されていない機能を使用しているため、まだ疑問があります。方法は次のとおりです。

  • 初期化中、ピアはメモリ内に一時的な x509 証明書を生成します。これには、ダミーの CN、発行者、および有効期限が切れる何年も前の、私が回避できる最小限のダミー データが含まれています。いずれにせよ、これらは決してチェックされません。SSL コンテキストは、両方の証明書を双方向で交換するように設定されています。

  • 重要なステップは、証明書にホストの公開鍵が含まれており、秘密鍵を使用して署名されていることです。

    EVP_PKEY *pkey = EVP_PKEY_new();
    EVP_PKEY_assign_RSA(pkey, PRIVKEY);
    X509_set_pubkey(x509, pkey);
    X509_sign(x509, pkey, EVP_sha384());
    
  • SSL_CTXこの証明書は、アプリケーションの存続期間全体にわたって割り当てられ、使用されます。

一番気になるのはTLSセッション確立時の検証処理です。OpenSSL を使用して実行されるすべての検証を無効にしSSL_CTX_set_cert_verify_callback(always_true_func)、次のように独自にロールします。

X509 *received_cert = SSL_get_peer_certificate(conn_info->ssl);
EVP_PKEY *received_pubkey = X509_get_pubkey(received_cert);
if (EVP_PKEY_type(received_pubkey->type) != EVP_PKEY_RSA)
    error();
ret = X509_verify(received_cert, received_pubkey);
if (ret <= 0)
    error("trust_failed");

// Compare received public key with expected one
RSA *expected_rsa_key = read_RSA_key_from_disk();
EVP_PKEY expected_pubkey = { 0 };
EVP_PKEY_assign_RSA(&expected_pubkey, expected_rsa_key);
EVP_PKEY_cmp(received_pubkey, &expected_pubkey);

if (ret == 1)
    return true; // identity verified!
else
    return false;

質問:これは OpenSSL API の適切な使用法ですか? 特に最後の部分、受信したキーの検証で、セキュリティ ホールが見られますか? 同じ結果を達成するためのより良い方法はありますか?

編集: 答え: TLS ハンドシェイク中に受信した自己署名証明書が保存されている RSA キーと一致することを確認するには、証明書の署名を確認する必要はありません。つまり、X509_verify. 受信した公開鍵と期待される公開鍵を比較するだけで十分です。

その理由は、(OpenSSL プロジェクトのコア開発者である Stephen Henson 博士の言葉を引用して) 「暗号スイートに応じて、RSA 復号化操作または RSA 署名操作のいずれかがサーバーによって実行されるためです。したがって、ハンドシェイクが正常に完了すると、同じキーが使用されていることを確認できます。証明書に存在するものとして使用されます」。

4

1 に答える 1

2

証明書は、公開鍵と追加情報 (識別子やその他の属性など) を結び付けます。公開鍵と追加情報との間のこの関連付けを証明書にするのは、証明書が署名されているという事実です。

X.509 証明書が CA によって署名および発行される理由は、CA が公開鍵と証明書の残りのコンテンツ (特にそのサブジェクト) との間のバインディングを主張するためです。これの目的は、CA を知っているが、証明書が発行されたエンティティを必ずしも知らない当事者が、証明書の内容が正しいこと、特に公開鍵が証明書のサブジェクトに属していることを確認できるようにすることです。

認証方式は公開鍵の所有者に関する事前に確立された知識に依存しているため、(公開鍵以外の) 証明書の内容を無視するため、公開鍵と公開鍵の間の関連付けを検証しても意味がありません。残りの証明書は真です (自己署名かどうかに関係なく)。

確認する必要があるのは、受け取った公開鍵が既に知っている公開鍵のいずれかと一致することです。

編集:

しかし、2 つ目は、自己署名証明書が有効に署名されていることを実際に確認することです。これは、ホストの公開鍵 (公開鍵です...) は誰でも送信できるためです。

誰でもホストの公開鍵を使用して証明書を送信できますが、その公開鍵と一致する秘密鍵を持つエンティティのみがマスター シークレットをネゴシエートできます。これがどのように行われるかは暗号スイートによって異なりますが、秘密鍵を持つサーバーのみが TLS 接続を続行できます。

クライアントは、サーバー証明書の公開鍵の秘密鍵を持つエンティティとの TLS 接続を確立したことを少なくとも常に知っています (その証明書が誰に属しているかは関係ありません)。

これは、サーバーの ID を検証するために使用されるメカニズム (従来は PKI + ホスト名の検証) から完全に独立しています。

ホストの公開鍵を確実に知っている場合 (たとえば、既に知っているリストで調べることによって)、証明書の署名を検証することは意味がありません (それが自己署名されているか、知らない人によって署名されているかに関係なく)。

同じことがクライアント証明書にも当てはまります。証明書を送信したクライアントが、送信したクライアント証明書と一致する秘密鍵を使用して作成された正しい署名を (Certificate Verify メッセージで) 送信できない場合、ハンドシェイクは完了しません。

于 2013-08-13T17:16:54.597 に答える