証明書の検証

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

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

始める前に

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

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

#!/usr/bin/env python
from OpenSSL import crypto
import gzip,sys

_END_DELIM = "-----END CERTIFICATE-----"
def verify(blob, signature, certs_file):
  with open(certs_file, 'r') as cert_data:
    certs = [cert + _END_DELIM for cert in cert_data.read().split(_END_DELIM)
             if cert.strip(' \t\n\r')]
    for cert in certs:
      cert_obj = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
      try:
        crypto.verify(cert_obj, signature, blob, 'sha256')
        return True
      except crypto.Error as er:
        continue
    return False

attest_unzipped = gzip.open(sys.argv.pop(), 'rb').read()
certs_file = sys.argv.pop()
blob, signature = attest_unzipped[:-256], attest_unzipped[-256:]
verified = verify(blob, signature, certs_file)
print('Signature verified!' if verified else 'Signature failed!')

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

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):]
    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(attest_filename, '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 SHA256 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 alpha kms keys version describe コマンドを使用して、証明を行う鍵の証明書の形式を取得します。

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

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

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

    gcloud alpha 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 ハッシュが証明書内に存在するかどうかを検証できます。

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

    gcloud alpha 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. 証明書スクリプトを使用して、手順 1 で取得した鍵バージョンのリソース ID の SHA-256 ハッシュを、証明書内にあるものと照合します。

    echo -n $resource_name | sha256sum | cut -d ' ' -f 1 | grep -f - parsed.dat
    

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

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

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

grep '0x0162:' parsed.dat

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

grep '0x0100:' parsed.dat

公開鍵を検証するには

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

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

    gcloud alpha 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 を使用して証明書を検証することはできません。
このページは役立ちましたか?評価をお願いいたします。

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