証明書の検証

鍵が Cloud HSM で作成されたものであれば、必要に応じて、その鍵が実際に HSM によって保護されていることを証明する証明書ステートメントをリクエストできます。このステートメントは、物理的な機器によって暗号で直接署名されたトークンであり、ユーザーがそのトークンを検証できます。このトピックでは、証明書ステートメントの署名を検証し、このステートメントが鍵バージョンと一致しているかどうかを判断する方法について説明します。

このトピックに含まれる例は、Linux 環境を想定したものです。

始める前に

証明書を検証するためのスクリプト

証明書を検証するには、検証に使用するコマンドラインで verify.py を作成します。Cloud Shell を使用する場合は、コードエディタを使用してこのファイルを作成できます。このファイルに次のような内容を含めます。

#!/usr/bin/env python
import sys, base64, hashlib

public_key_file = sys.argv.pop()
parsed_attest_file = sys.argv.pop()
with open(public_key_file, 'r') as public_key_data:
  # remove the "BEGIN" and "END" line.
  publickey = "".join(public_key_data.read().split('\n')[1:-2])
  publickey = base64.b64decode(publickey)
  # KCV is the first 6 bytes of the SHA1 digest of the key.
  kcv = hashlib.sha1(publickey).hexdigest()[:6]
  # EKCV is the SHA-256 digest of the key
  ekcv = hashlib.sha256(publickey).hexdigest()

  with open(parsed_attest_file, 'r') as parsed_attest_data:
    attest_data = parsed_attest_data.read()
    if kcv in attest_data and ekcv in attest_data:
      print 'Public key matched with attestation.'
    else:
      print 'Public key not matched with attestation.'
      attest_lines = attest_data.split('\n')
      # field 0x0173 corresponds to KCV,
      # field 1003 (Custom Cavium) corresponds to EKCV
      kcv_lines = [line for line in attest_lines if line.startswith('0x0173:') or line.startswith('0x1003:')]
      print "KCV in attestation: %s" %(kcv_lines)

証明書を解析するためのスクリプト

CAVIUM_V1_COMPRESSEDCAVIUM_V2_COMPRESSED の 2 つの証明書の形式があるため、証明書を解析するためのスクリプトは 2 種類あります。

CAVIUM_V1_COMPRESSED 証明書を解析するためのスクリプト

次の内容のファイルを parse_v1.py という名前で作成します。

#!/usr/bin/python
import io, gzip, sys, struct

HEADER = ">III"
TLV = ">II"
_CAVIUM_ATTESTATION_ASYM_OFFSET = 984
_CAVIUM_ATTESTATION_SYM_OFFSET = 24

def parse(attestation):
  # Parse Object (Cavium stores its structures in big endian)
  obj_handle, attr_count, obj_size = struct.unpack_from(HEADER, attestation, 0)
  obj_header_size = struct.calcsize(HEADER)
  attestation = attestation[obj_header_size:]
  while attr_count > 0:
    # Parse each Attribute.
    attr_type, attr_len = struct.unpack_from(TLV, attestation, 0)
    attestation = attestation[struct.calcsize(TLV):]
    print '0x%04x: %r' % (attr_type, attestation[:attr_len])
    attr_count -= 1
    attestation = attestation[attr_len:]
  return obj_header_size + obj_size

attest_unzipped = gzip.open(sys.argv.pop(), 'rb').read()
key_type = sys.argv.pop()
if key_type == 'symmetric':
  print "Symmetric Key Attestation"
  parse(attest_unzipped[_CAVIUM_ATTESTATION_SYM_OFFSET:])
else:
  print "Public Key Attestation"
  offset = parse(attest_unzipped[_CAVIUM_ATTESTATION_ASYM_OFFSET:])
  print 'Private Key Attestation'
  parse(attest_unzipped[(_CAVIUM_ATTESTATION_ASYM_OFFSET + offset):])

CAVIUM_V2_COMPRESSED 証明書を解析するためのスクリプト

次の内容のファイルを parse_v2.py という名前で作成します。

#!/usr/bin/python
import io, gzip, sys, struct

RESPONSE_HEADER = ">IIII"
INFO_HEADER = ">HHHH"
OBJ_HEADER = ">III"
TLV = ">II"
SIGNATURE_SIZE = 256

def parse(attestation):
  # Parse Object
  obj_handle, attr_count, obj_size = struct.unpack_from(OBJ_HEADER, attestation, 0)
  obj_header_size = struct.calcsize(OBJ_HEADER)
  attestation = attestation[obj_header_size:]
  while attr_count > 0:
    # Parse each Attribute.
    attr_type, attr_len = struct.unpack_from(TLV, attestation, 0)
    attestation = attestation[struct.calcsize(TLV):]
    attr_val = "".join(['%02x' % ord(ch) for ch in attestation[:attr_len]])
    print '0x%04x: %r' % (attr_type, attr_val)
    attr_count -= 1
    attestation = attestation[attr_len:]
  return obj_header_size + obj_size

attest_unzipped = gzip.open(sys.argv.pop(), 'rb').read()

## Parse headers (Cavium stores its structures in big endian)
_, _, totalsize, bufsize = struct.unpack_from(RESPONSE_HEADER, attest_unzipped, 0)
attribute_offset = totalsize - (bufsize + SIGNATURE_SIZE)
attestation = attest_unzipped[attribute_offset:]
version, _, offset1, offset2 = struct.unpack_from(INFO_HEADER, attestation, 0)

if offset2 > 0:
  print "Public Key Attestation"
  parse(attestation[offset1:])
  print "Private Key Attestation"
  parse(attestation[offset2:])
else:
  print "Symmetric Key Attestation"
  parse(attestation[offset1:])

公開鍵を検証するためのスクリプト

公開鍵を検証するには、次の内容を含む verify_pubkey.py というファイルを作成します。

#!/usr/bin/env python
import sys

public_key_file = sys.argv.pop()
parsed_attest_file = sys.argv.pop()
with open(public_key_file, 'r') as public_key_data:
  # remove the "BEGIN" and "END" line.
  publickey = "".join(public_key_data.read().split('\n')[1:-2])
  publickey = base64.b64decode(publickey)
  # KCV is the first 6 bytes of the SHA1 digest of the key.
  kcv = hashlib.sha1(publickey).hexdigest()[:6]
  # EKCV is the SHA-256 digest of the key
  ekcv = hashlib.sha256(publickey).hexdigest()

  with open(parsed_attest_file, 'r') as parsed_attest_data:
    if kcv in parsed_attest_data.read() and ekcv in parsed_attest_data.read():
      print 'Public key matched with attestation.'
    else:
      print 'Public key not matched with attestation.'
      attest_lines = parsed_attest_data.readlines()
      # field 0x0173 corresponds to KCV,
      # field 1003 (Custom Cavium) corresponds to EKCV
      kcv_lines = [line for line in attest_lines (if line.startswith('0x0173:') or if line.startswith('0x1003:'))]
      print(kcv_lines)

証明書ファイルの取得

暗号鍵のバージョンの証明書を取得するには、Google Cloud Platform Console かコマンドラインを使用します。

Console

  1. GCP Console で [暗号鍵] ページに移動します。
    [暗号鍵] ページに移動

  2. 証明を行う鍵バージョンの鍵が含まれた鍵リングの名前をクリックします。

  3. 証明を行う鍵バージョンの鍵をクリックします。

  4. 証明を行う鍵バージョンで、その他アイコン(3 つの点が縦に並んだアイコン)をクリックします。

  5. [証明書を取得] をクリックします。

  6. [証明書を取得] ダイアログで [ダウンロード] をクリックします。証明書ファイルがお使いのパソコンにダウンロードされます。

証明書ファイルの名前の形式は [KEYRING]-[KEY]-[KEYVERSION]-[ATTESTATION-FORMAT]-attestation.dat です。

コマンドライン

  1. コンソール ウィンドウの上部にある「Google Cloud Shell を有効にする」ボタンをクリックします。 Google Cloud Shell の有効化 コンソールの下部の新しいフレーム内で Cloud Shell セッションが開き、コマンドライン プロンプトが表示されます。shell セッションが初期化されるまで、数秒かかる場合があります。 Cloud Shell セッション

  2. Cloud Shell コマンドライン プロンプトで gcloud kms keys versions describe コマンドを使用して、証明を行う鍵の証明書の形式を取得します。

    gcloud kms keys versions describe [KEY_VERSION] \
      --key [KEY_NAME] \
      --location [LOCATION] \
      --keyring [KEY_RING]
    

    このコマンドの出力には、鍵バージョンの証明書の形式が表示されます。この情報は次の手順で必要になります。

  3. Cloud Shell コマンドライン プロンプトで gcloud kms keys versions describe コマンドを使用して、証明を行う鍵の証明書を取得します。[ATTESTATION-FORMAT] の部分は、前の手順で取得した認証書の形式に置き換えます。--attestation-file フラグには、取得した証明書を保存するパスとファイル名を指定します。

    gcloud kms keys versions describe [KEY_VERSION] \
      --key [KEY_NAME] \
      --location [LOCATION] \
      --keyring [KEY_RING] \
      --attestation-file \
      [KEYRING]-[KEY]-[KEYVERSION]-[ATTESTATION-FORMAT]-attestation.dat
    

鍵の証明

証明書の署名の検証には、2 つの証明書を使用します。1 つは Google の認証局(CA)から発行されたもの、もう 1 つは Cavium の CA から発行されたものです。

  1. 使用する CA の証明書チェーンを取得します。

    curl -O https://www.gstatic.com/cloudhsm/cloud-kms-prod-[LOCATION]-[google|cavium].pem
    

    [LOCATION] は、Cloud HSM でサポートされているいずれかのリージョンに置き換えます。[google|cavium] は、google または cavium に置き換えます。ロケーションが us-west で CA が Google の場合の例を次に示します。

    curl -O https://www.gstatic.com/cloudhsm/cloud-kms-prod-us-west1-google.pem
    

    証明書の内容を表示するには、以下を実行します。

    openssl x509 -in cloud-kms-prod-[LOCATION]-[google|cavium].pem -text
    
  2. OpenSSL 用の Python ラッパーをインストールします。

    pip install --user pyopenssl
    
  3. Cloud Shell のコマンドライン プロンプトで検証ツールを実行します。

    python verify.py \
      cloud-kms-prod-[LOCATION]-[google|cavium].pem \
      [KEYRING]-[KEY]-[KEYVERSION]-[ATTESTATION-FORMAT]-attestation.dat
    

    この検証ツールは、証明書ファイルの末尾に含まれている署名を、HSM の証明書バンドルに入っている公開鍵を使用して検証します。現在このバンドルには、HSM から認証局(CA)証明書に至るすべての(ただし CA 証明書は含まれない)証明書チェーンが含まれています。

証明書ファイル内の値の検証

証明書の署名の検証に加え、証明書の他の部分も検証できます。たとえば、証明書内の鍵バージョン ID を、Cloud KMS に表示される鍵バージョン ID によって検証できます。

CAVIUM_V1_COMPRESSED 形式の証明書を解析する場合は、次のコマンドを実行します。

python parse_v1.py [symmetric|asymmetric] \
[KEYRING]-[KEY]-[KEYVERSION]-[ATTESTATION-FORMAT]-attestation.dat > parsed.dat

CAVIUM_V2_COMPRESSED 形式の証明書を解析する場合は、次のコマンドを実行します。

python parse_v2.py \
[KEYRING]-[KEY]-[KEYVERSION]-[ATTESTATION-FORMAT]-attestation.dat > parsed.dat

以降のセクションでは、この parsed.dat ファイルを使用します。

鍵バージョン ID を検証するには

鍵バージョンのリソース ID の SHA-256 ハッシュが証明書内に存在するかどうかを検証できます。鍵のリソース名は、証明書ファイル内の 0x0102 フィールドか鍵 ID フィールドの一部です。鍵 ID は、16 進数の SHA-256 ハッシュ ダイジェストが 2 つ連結されて構成されます。2 つ目のものは鍵のリソース名と一致する必要があります。

  1. 鍵バージョンのリソース ID を取得します。GCP Console を使用して鍵バージョンのリソース ID を取得するか、次の gcloud コマンドを実行します。

    gcloud kms keys versions list \
      --location [LOCATION] \
      --keyring [KEY_RING] --key [KEY]
    
  2. コマンドラインで、先ほど取得した鍵バージョンのリソース ID に resource_name を割り当てます。

    resource_name=projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[KEY_RING]/cryptoKeys/[KEY]/cryptoKeyVersions/[VERSION]
    
  3. 解析スクリプトはすべての証明書フィールドを 16 進数でダンプするため、鍵 ID は 2 回 16 進数にフォーマットされることになります(keyID の作成時に 1 回、証明書の解析時に 1 回)。リソース名が鍵 ID と一致することを確認するには、リソース名を 16 進数の SHA-256 ダイジェストに変換し、証明書ファイル内の鍵 ID の 16 進数変換を 1 回元に戻して、それら 2 つを比較します。

    resource_name_hex=$(echo -n $resource_name | openssl dgst -sha256 -hex | awk '{print $2}')
    
    keyid_hex=$(grep -m 1 0x0102 /tmp/parsed.dat | awk '{print $2}' | xxd -p -r)
    
    echo $resource_name_hex | grep $keyid_hex
    

鍵のプロパティを検証するには

鍵が抽出可能かどうか、鍵でサポートされる暗号オペレーションは何かといった、鍵のさまざまなプロパティを表示できます。こうしたプロパティは PKCS #11 標準で規定されているフィールドに対応しています。

鍵が抽出可能かどうかを判別するには、0x0162 フィールドを調べます。

grep '0x0162:' parsed.dat

鍵の種類を判別するには、0x0100 フィールドを調べます。鍵の種類は、PCKS#11 標準内にも CKK_* という接頭部を付けて列挙されています。

grep '0x0100:' parsed.dat

鍵がインポートされたかどうかを判別するには、0x0163 フィールドを調べます。この属性は、Cloud HSM を使用して作成された鍵であれば true、インポートされた鍵であれば false となります。

grep '0x0163:' parsed.dat

公開鍵を検証するには

非対称鍵の場合、生成された鍵ペアが公開鍵と一致するかどうかを検証できます。公開鍵の鍵チェックサム値(KCV)と拡張 KCV(EKCV)を比較し、それらを証明書内にあるものと照合してください。

  1. 公開鍵をローカルで取得します。

    gcloud kms keys versions get-public-key \
      [KEY_VERSION] --location [LOCATION] \
      --keyring [KEY_RING] --key [KEY] \
      --output-file /tmp/key.pub
    

    gcloud を使用するほかに、GCP Console や Cloud KMS API を使用して公開鍵を取得することもできます。

  2. 次のスクリプトを実行して公開鍵を検証します。

    python verify_pubkey.py parsed.dat /tmp/key.pub
    

追加情報

  • 証明書を検証することで、鍵バージョンが HSM 内部で作成されたかどうかを判別できます。この検証は意図的に Google から独立して実施されるようになっているため、Google Cloud Platform Console、Cloud KMS API、コマンドラインの gcloud を使用して証明書を検証することはできません。
このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...