๋ฃจํŠธ ์ธ์ฆ ๊ธฐ๊ด€ ๋งŒ๋“ค๊ธฐ

์ด ํŽ˜์ด์ง€์—์„œ๋Š” CA ํ’€์—์„œ ๋ฃจํŠธ ์ธ์ฆ ๊ธฐ๊ด€ (CA)์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๋ฃจํŠธ CA๋Š” ๊ณต๊ฐœ ํ‚ค ์ธํ”„๋ผ(PKI) ๊ณ„์ธต ๊ตฌ์กฐ์˜ ๋งจ ์œ„์— ์žˆ์œผ๋ฉฐ PKI์˜ ์‹ ๋ขฐ ์•ต์ปค๋ฅผ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค. PKI์—์„œ ์ธ์ฆ์„œ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ธฐ๊ธฐ, ์†Œํ”„ํŠธ์›จ์–ด ๋˜๋Š” ๊ตฌ์„ฑ์š”์†Œ๊ฐ€ PKI๋ฅผ ์‹ ๋ขฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ๋ฃจํŠธ CA๋ฅผ ์‹ ๋ขฐํ•˜๋„๋ก ๊ธฐ๊ธฐ, ์†Œํ”„ํŠธ์›จ์–ด, ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ฃจํŠธ CA์—์„œ ๋ฐœ๊ธ‰๋œ ๋ชจ๋“  ์ธ์ฆ์„œ๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ ์ „์—

๋ฃจํŠธ CA ๋งŒ๋“ค๊ธฐ

๋ฃจํŠธ CA์—๋Š” ์ž์ฒด ์„œ๋ช… ์ธ์ฆ์„œ๊ฐ€ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ํด๋ผ์ด์–ธํŠธ์˜ ํŠธ๋Ÿฌ์ŠคํŠธ ์ €์žฅ์†Œ์— ๋ฐฐํฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฃจํŠธ CA์˜ ์ธ์ฆ์„œ๋Š” ์ธ์ฆ์„œ ์ฒด์ธ์˜ ๋งจ ์œ„์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ CA๋กœ ์ด CA ์ธ์ฆ์„œ๋ฅผ ํ•ด์ง€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฃจํŠธ CA์˜ CRL์€ ๋ฃจํŠธ CA๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ๋‹ค๋ฅธ ์ธ์ฆ์„œ์—๋งŒ ์ ์šฉ๋˜๋ฉฐ ๋ฃจํŠธ CA ์ž์ฒด์—๋Š” ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ธฐ์กด CA ํ’€ ๋˜๋Š” ์ƒˆ CA ํ’€์— ๋ฃจํŠธ CA๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์•ˆ๋‚ด์—์„œ๋Š” ๊ธฐ์กด ํ’€์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ฝ˜์†”

๊ธฐ์กด CA ํ’€์— ๋ฃจํŠธ CA๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜์„ธ์š”.

  1. Google Cloud ์ฝ˜์†”์—์„œ Certificate Authority Service ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

    Certificate Authority Service๋กœ ์ด๋™

  2. CA ๊ด€๋ฆฌ์ž ํƒญ์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

  3. CA ๋งŒ๋“ค๊ธฐ ํŽผ์น˜๊ธฐ ํ™”์‚ดํ‘œ๋ฅผ ํด๋ฆญํ•œ ๋‹ค์Œ ๊ธฐ์กด CA ํ’€์— CA ๋งŒ๋“ค๊ธฐ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.

CA ํ’€ ์„ ํƒ

๋ชฉ๋ก์—์„œ ๊ธฐ์กด CA ํ’€์„ ์„ ํƒํ•˜๊ณ  ๊ณ„์†์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

CA ์œ ํ˜• ์„ ํƒ

  1. ์œ ํ˜•์—์„œ ๋ฃจํŠธ CA๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
  2. ์œ ํšจ ๊ธฐ๊ฐ„ ํ•„๋“œ์— CA ์ธ์ฆ์„œ์—์„œ ๋ฐœ๊ธ‰ํ•œ ์ธ์ฆ์„œ๊ฐ€ ์œ ํšจํ•ด์•ผ ํ•  ๊ธฐ๊ฐ„์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  3. ์ดˆ๊ธฐํ™”๋œ ์ƒํƒœ์—์„œ ์ƒ์„ฑ๋  CA์˜ ์ž‘์—… ์ƒํƒœ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
  4. ๊ณ„์†์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
CA ์ฃผ์ฒด ์ด๋ฆ„ ๊ตฌ์„ฑ
  1. ์กฐ์ง(O) ํ•„๋“œ์— ํšŒ์‚ฌ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  2. ์„ ํƒ์‚ฌํ•ญ: ์กฐ์ง ๋‹จ์œ„(OU) ํ•„๋“œ์— ํšŒ์‚ฌ ๋ถ€์„œ ๋˜๋Š” ์‚ฌ์—…๋ถ€๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  3. ์„ ํƒ์‚ฌํ•ญ: ๊ตญ๊ฐ€ ์ด๋ฆ„ (C) ํ•„๋“œ์— 2์ž๋ฆฌ ๊ตญ๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  4. ์„ ํƒ์‚ฌํ•ญ: ์ฃผ/๋„ ์ด๋ฆ„ ํ•„๋“œ์— ์ฃผ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  5. ์„ ํƒ์‚ฌํ•ญ: ์ง€์—ญ ์ด๋ฆ„ ํ•„๋“œ์— ๋„์‹œ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  6. CA ์ผ๋ฐ˜ ์ด๋ฆ„(CN) ํ•„๋“œ์— CA ์ด๋ฆ„์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  7. ๊ณ„์†์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
CA ํ‚ค ํฌ๊ธฐ ๋ฐ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌ์„ฑ
  1. ์š”๊ตฌ์‚ฌํ•ญ์— ๊ฐ€์žฅ ๋ถ€ํ•ฉํ•˜๋Š” ํ‚ค ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ํ‚ค ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ฒฐ์ •์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ํ‚ค ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์„ ํƒ์„ ์ฐธ์กฐํ•˜์„ธ์š”.
  2. ๊ณ„์†์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
CA ์•„ํ‹ฐํŒฉํŠธ ๊ตฌ์„ฑ
  1. Google ๊ด€๋ฆฌํ˜• ๋˜๋Š” ๊ณ ๊ฐ ๊ด€๋ฆฌํ˜• Cloud Storage ๋ฒ„ํ‚ท ์‚ฌ์šฉ ์—ฌ๋ถ€๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
    1. Google ๊ด€๋ฆฌํ˜• Cloud Storage ๋ฒ„ํ‚ท์˜ ๊ฒฝ์šฐ CA Service๋Š” CA์™€ ๋™์ผํ•œ ์œ„์น˜์— Google ๊ด€๋ฆฌํ˜• ๋ฒ„ํ‚ท์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
    2. ๊ณ ๊ฐ ๊ด€๋ฆฌ Cloud Storage ๋ฒ„ํ‚ท์˜ ๊ฒฝ์šฐ ์ฐพ์•„๋ณด๊ธฐ๋ฅผ ํด๋ฆญํ•˜๊ณ  ๊ธฐ์กด Cloud Storage ๋ฒ„ํ‚ท ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
  2. ๊ณ„์†์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
๋ผ๋ฒจ ์ถ”๊ฐ€

๋‹ค์Œ ๋‹จ๊ณ„๋Š” ์„ ํƒ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค.

CA์— ๋ผ๋ฒจ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  1. ํ•ญ๋ชฉ ์ถ”๊ฐ€๋ฅผ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
  2. ํ‚ค 1 ํ•„๋“œ์— ๋ผ๋ฒจ ํ‚ค๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  3. ๊ฐ’ 1 ํ•„๋“œ์— ๋ผ๋ฒจ ๊ฐ’์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
  4. ๋‹ค๋ฅธ ๋ผ๋ฒจ์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด ํ•ญ๋ชฉ ์ถ”๊ฐ€๋ฅผ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ 2๋‹จ๊ณ„์™€ 3๋‹จ๊ณ„์— ์„ค๋ช…๋œ ๋Œ€๋กœ ๋ผ๋ฒจ ํ‚ค์™€ ๊ฐ’์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  5. ๊ณ„์†์„ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
์„ค์ • ๊ฒ€ํ† 

๋ชจ๋“  ์„ค์ •์„ ์‹ ์ค‘ํ•˜๊ฒŒ ๊ฒ€ํ† ํ•œ ๋‹ค์Œ ์™„๋ฃŒ๋ฅผ ํด๋ฆญํ•˜์—ฌ CA๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

gcloud

  1. ๊ธฐ์กด CA ํ’€์—์„œ ์ƒˆ ๋ฃจํŠธ CA๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    gcloud privateca roots create ROOT_CA_ID \
        --location=LOCATION \
        --pool=POOL_ID \
        --key-algorithm=KEY_ALGORITHM \
        --subject="CN=my-ca, O=Test LLC"
    

    ๋‹ค์Œ์„ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

    • ROOT_CA_ID: CA์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
    • LOCATION: CA ํ’€์˜ ์œ„์น˜์ž…๋‹ˆ๋‹ค.
    • POOL_ID: CA ํ’€์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
    • KEY_ALGORITHM: Cloud KMS ํ‚ค๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค. ์ด ํ”Œ๋ž˜๊ทธ๋Š” ์„ ํƒ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ์ด ํ”Œ๋ž˜๊ทธ๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฉด ํ‚ค ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๊ธฐ๋ณธ์ ์œผ๋กœ rsa-pkcs1-4096-sha256์œผ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ --key-algorithm ํ”Œ๋ž˜๊ทธ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

    ๊ธฐ๋ณธ์ ์œผ๋กœ CA๋Š” STAGED ์ƒํƒœ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ CA๋ฅผ ์‚ฌ์šฉ ์„ค์ •ํ•˜๋ ค๋ฉด --auto-enable ํ”Œ๋ž˜๊ทธ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

    CA ์ธ์ฆ์„œ ๋ฐ CRL์„ ๊ฒŒ์‹œํ•˜๋Š” ๋ฐ ๊ณ ๊ฐ ๊ด€๋ฆฌ Cloud Storage ๋ฒ„ํ‚ท์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ช…๋ น์–ด์— --bucket bucket-name์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. bucket-name์„ Cloud Storage ๋ฒ„ํ‚ท ์ด๋ฆ„์œผ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

    ์ „์ฒด ์„ค์ • ๋ชฉ๋ก์„ ๋ณด๋ ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

    gcloud privateca roots create --help
    

Terraform

Google ์†Œ์œ  ๋ฐ Google ๊ด€๋ฆฌ ์•”ํ˜ธํ™” ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฃจํŠธ CA๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋‹ค์Œ ์ƒ˜ํ”Œ ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

resource "google_privateca_certificate_authority" "root_ca" {
  // This example assumes this pool already exists.
  // Pools cannot be deleted in normal test circumstances, so we depend on static pools
  pool                                   = "my-pool"
  certificate_authority_id               = "my-certificate-authority-root"
  location                               = "us-central1"
  deletion_protection                    = false # set to true to prevent destruction of the resource
  ignore_active_certificates_on_deletion = true
  config {
    subject_config {
      subject {
        organization = "ACME"
        common_name  = "my-certificate-authority"
      }
    }
    x509_config {
      ca_options {
        # is_ca *MUST* be true for certificate authorities
        is_ca = true
      }
      key_usage {
        base_key_usage {
          # cert_sign and crl_sign *MUST* be true for certificate authorities
          cert_sign = true
          crl_sign  = true
        }
        extended_key_usage {
        }
      }
    }
  }
  key_spec {
    algorithm = "RSA_PKCS1_4096_SHA256"
  }
  // valid for 10 years
  lifetime = "${10 * 365 * 24 * 3600}s"
}

์ž์ฒด ๊ด€๋ฆฌ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฃจํŠธ CA๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋‹ค์Œ ์ƒ˜ํ”Œ ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

resource "google_project_service_identity" "privateca_sa" {
  provider = google-beta
  service  = "privateca.googleapis.com"
}

resource "google_kms_crypto_key_iam_binding" "privateca_sa_keyuser_signerverifier" {
  crypto_key_id = "projects/keys-project/locations/us-central1/keyRings/key-ring/cryptoKeys/crypto-key"
  role          = "roles/cloudkms.signerVerifier"

  members = [
    "serviceAccount:${google_project_service_identity.privateca_sa.email}",
  ]
}

resource "google_kms_crypto_key_iam_binding" "privateca_sa_keyuser_viewer" {
  crypto_key_id = "projects/keys-project/locations/us-central1/keyRings/key-ring/cryptoKeys/crypto-key"
  role          = "roles/viewer"
  members = [
    "serviceAccount:${google_project_service_identity.privateca_sa.email}",
  ]
}

resource "google_privateca_certificate_authority" "default" {
  // This example assumes this pool already exists.
  // Pools cannot be deleted in normal test circumstances, so we depend on static pools
  pool                     = "ca-pool"
  certificate_authority_id = "my-certificate-authority"
  location                 = "us-central1"
  deletion_protection      = false # set to true to prevent destruction of the resource
  key_spec {
    cloud_kms_key_version = "projects/keys-project/locations/us-central1/keyRings/key-ring/cryptoKeys/crypto-key/cryptoKeyVersions/1"
  }

  config {
    subject_config {
      subject {
        organization = "Example, Org."
        common_name  = "Example Authority"
      }
    }
    x509_config {
      ca_options {
        # is_ca *MUST* be true for certificate authorities
        is_ca                  = true
        max_issuer_path_length = 10
      }
      key_usage {
        base_key_usage {
          # cert_sign and crl_sign *MUST* be true for certificate authorities
          cert_sign = true
          crl_sign  = true
        }
        extended_key_usage {
          server_auth = false
        }
      }
    }
  }

  depends_on = [
    google_kms_crypto_key_iam_binding.privateca_sa_keyuser_signerverifier,
    google_kms_crypto_key_iam_binding.privateca_sa_keyuser_viewer,
  ]
}

Go

CA Service์— ์ธ์ฆํ•˜๋ ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ ์ธ์ฆ ์„ค์ •์„ ์ฐธ์กฐํ•˜์„ธ์š”.

import (
	"context"
	"fmt"
	"io"

	privateca "cloud.google.com/go/security/privateca/apiv1"
	"cloud.google.com/go/security/privateca/apiv1/privatecapb"
	"google.golang.org/protobuf/types/known/durationpb"
)

// Create Certificate Authority which is the root CA in the given CA Pool. This CA will be
// responsible for signing certificates within this pool.
func createCa(
	w io.Writer,
	projectId string,
	location string,
	caPoolId string,
	caId string,
	caCommonName string,
	org string,
	caDuration int64) error {
	// projectId := "your_project_id"
	// location := "us-central1"		// For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations.
	// caPoolId := "ca-pool-id"			// The CA Pool id under which the CA should be created.
	// caId := "ca-id"					// A unique id/name for the ca.
	// caCommonName := "ca-name"		// A common name for your certificate authority.
	// org := "ca-org"					// The name of your company for your certificate authority.
	// ca_duration := int64(31536000)	// The validity of the certificate authority in seconds.

	ctx := context.Background()
	caClient, err := privateca.NewCertificateAuthorityClient(ctx)
	if err != nil {
		return fmt.Errorf("NewCertificateAuthorityClient creation failed: %w", err)
	}
	defer caClient.Close()

	// Set the types of Algorithm used to create a cloud KMS key.
	keySpec := &privatecapb.CertificateAuthority_KeyVersionSpec{
		KeyVersion: &privatecapb.CertificateAuthority_KeyVersionSpec_Algorithm{
			Algorithm: privatecapb.CertificateAuthority_RSA_PKCS1_2048_SHA256,
		},
	}

	// Set CA subject config.
	subjectConfig := &privatecapb.CertificateConfig_SubjectConfig{
		Subject: &privatecapb.Subject{
			CommonName:   caCommonName,
			Organization: org,
		},
	}

	// Set the key usage options for X.509 fields.
	isCa := true
	x509Parameters := &privatecapb.X509Parameters{
		KeyUsage: &privatecapb.KeyUsage{
			BaseKeyUsage: &privatecapb.KeyUsage_KeyUsageOptions{
				CrlSign:  true,
				CertSign: true,
			},
		},
		CaOptions: &privatecapb.X509Parameters_CaOptions{
			IsCa: &isCa,
		},
	}

	// Set certificate authority settings.
	// Type: SELF_SIGNED denotes that this CA is a root CA.
	ca := &privatecapb.CertificateAuthority{
		Type:    privatecapb.CertificateAuthority_SELF_SIGNED,
		KeySpec: keySpec,
		Config: &privatecapb.CertificateConfig{
			SubjectConfig: subjectConfig,
			X509Config:    x509Parameters,
		},
		Lifetime: &durationpb.Duration{
			Seconds: caDuration,
		},
	}

	fullCaPoolName := fmt.Sprintf("projects/%s/locations/%s/caPools/%s", projectId, location, caPoolId)

	// Create the CreateCertificateAuthorityRequest.
	// See https://pkg.go.dev/cloud.google.com/go/security/privateca/apiv1/privatecapb#CreateCertificateAuthorityRequest.
	req := &privatecapb.CreateCertificateAuthorityRequest{
		Parent:                 fullCaPoolName,
		CertificateAuthorityId: caId,
		CertificateAuthority:   ca,
	}

	op, err := caClient.CreateCertificateAuthority(ctx, req)
	if err != nil {
		return fmt.Errorf("CreateCertificateAuthority failed: %w", err)
	}

	if _, err = op.Wait(ctx); err != nil {
		return fmt.Errorf("CreateCertificateAuthority failed during wait: %w", err)
	}

	fmt.Fprintf(w, "CA %s created", caId)

	return nil
}

์ž๋ฐ”

CA Service์— ์ธ์ฆํ•˜๋ ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ ์ธ์ฆ ์„ค์ •์„ ์ฐธ์กฐํ•˜์„ธ์š”.


import com.google.api.core.ApiFuture;
import com.google.cloud.security.privateca.v1.CaPoolName;
import com.google.cloud.security.privateca.v1.CertificateAuthority;
import com.google.cloud.security.privateca.v1.CertificateAuthority.KeyVersionSpec;
import com.google.cloud.security.privateca.v1.CertificateAuthority.SignHashAlgorithm;
import com.google.cloud.security.privateca.v1.CertificateAuthorityServiceClient;
import com.google.cloud.security.privateca.v1.CertificateConfig;
import com.google.cloud.security.privateca.v1.CertificateConfig.SubjectConfig;
import com.google.cloud.security.privateca.v1.CreateCertificateAuthorityRequest;
import com.google.cloud.security.privateca.v1.KeyUsage;
import com.google.cloud.security.privateca.v1.KeyUsage.KeyUsageOptions;
import com.google.cloud.security.privateca.v1.Subject;
import com.google.cloud.security.privateca.v1.X509Parameters;
import com.google.cloud.security.privateca.v1.X509Parameters.CaOptions;
import com.google.longrunning.Operation;
import com.google.protobuf.Duration;
import java.io.IOException;
import java.util.concurrent.ExecutionException;

public class CreateCertificateAuthority {

  public static void main(String[] args)
      throws InterruptedException, ExecutionException, IOException {
    // TODO(developer): Replace these variables before running the sample.
    // location: For a list of locations, see:
    // https://cloud.google.com/certificate-authority-service/docs/locations
    // poolId: Set it to the CA Pool under which the CA should be created.
    // certificateAuthorityName: Unique name for the CA.
    String project = "your-project-id";
    String location = "ca-location";
    String poolId = "ca-pool-id";
    String certificateAuthorityName = "certificate-authority-name";
    createCertificateAuthority(project, location, poolId, certificateAuthorityName);
  }

  // Create Certificate Authority which is the root CA in the given CA Pool.
  public static void createCertificateAuthority(
      String project, String location, String poolId, String certificateAuthorityName)
      throws InterruptedException, ExecutionException, IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests. After completing all of your requests, call
    // the `certificateAuthorityServiceClient.close()` method on the client to safely
    // clean up any remaining background resources.
    try (CertificateAuthorityServiceClient certificateAuthorityServiceClient =
        CertificateAuthorityServiceClient.create()) {

      String commonName = "common-name";
      String orgName = "org-name";
      int caDuration = 100000; // Validity of this CA in seconds.

      // Set the type of Algorithm.
      KeyVersionSpec keyVersionSpec =
          KeyVersionSpec.newBuilder().setAlgorithm(SignHashAlgorithm.RSA_PKCS1_4096_SHA256).build();

      // Set CA subject config.
      SubjectConfig subjectConfig =
          SubjectConfig.newBuilder()
              .setSubject(
                  Subject.newBuilder().setCommonName(commonName).setOrganization(orgName).build())
              .build();

      //  Set the key usage options for X.509 fields.
      X509Parameters x509Parameters =
          X509Parameters.newBuilder()
              .setKeyUsage(
                  KeyUsage.newBuilder()
                      .setBaseKeyUsage(
                          KeyUsageOptions.newBuilder().setCrlSign(true).setCertSign(true).build())
                      .build())
              .setCaOptions(CaOptions.newBuilder().setIsCa(true).build())
              .build();

      // Set certificate authority settings.
      CertificateAuthority certificateAuthority =
          CertificateAuthority.newBuilder()
              // CertificateAuthority.Type.SELF_SIGNED denotes that this CA is a root CA.
              .setType(CertificateAuthority.Type.SELF_SIGNED)
              .setKeySpec(keyVersionSpec)
              .setConfig(
                  CertificateConfig.newBuilder()
                      .setSubjectConfig(subjectConfig)
                      .setX509Config(x509Parameters)
                      .build())
              // Set the CA validity duration.
              .setLifetime(Duration.newBuilder().setSeconds(caDuration).build())
              .build();

      // Create the CertificateAuthorityRequest.
      CreateCertificateAuthorityRequest certificateAuthorityRequest =
          CreateCertificateAuthorityRequest.newBuilder()
              .setParent(CaPoolName.of(project, location, poolId).toString())
              .setCertificateAuthorityId(certificateAuthorityName)
              .setCertificateAuthority(certificateAuthority)
              .build();

      // Create Certificate Authority.
      ApiFuture<Operation> futureCall =
          certificateAuthorityServiceClient
              .createCertificateAuthorityCallable()
              .futureCall(certificateAuthorityRequest);
      Operation response = futureCall.get();

      if (response.hasError()) {
        System.out.println("Error while creating CA !" + response.getError());
        return;
      }

      System.out.println(
          "Certificate Authority created successfully : " + certificateAuthorityName);
    }
  }
}

Python

CA Service์— ์ธ์ฆํ•˜๋ ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ ์ธ์ฆ ์„ค์ •์„ ์ฐธ์กฐํ•˜์„ธ์š”.

import google.cloud.security.privateca_v1 as privateca_v1
from google.protobuf import duration_pb2


def create_certificate_authority(
    project_id: str,
    location: str,
    ca_pool_name: str,
    ca_name: str,
    common_name: str,
    organization: str,
    ca_duration: int,
) -> None:
    """
    Create Certificate Authority which is the root CA in the given CA Pool. This CA will be
    responsible for signing certificates within this pool.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations.
        ca_pool_name: set it to the CA Pool under which the CA should be created.
        ca_name: unique name for the CA.
        common_name: a title for your certificate authority.
        organization: the name of your company for your certificate authority.
        ca_duration: the validity of the certificate authority in seconds.
    """

    caServiceClient = privateca_v1.CertificateAuthorityServiceClient()

    # Set the types of Algorithm used to create a cloud KMS key.
    key_version_spec = privateca_v1.CertificateAuthority.KeyVersionSpec(
        algorithm=privateca_v1.CertificateAuthority.SignHashAlgorithm.RSA_PKCS1_4096_SHA256
    )

    # Set CA subject config.
    subject_config = privateca_v1.CertificateConfig.SubjectConfig(
        subject=privateca_v1.Subject(common_name=common_name, organization=organization)
    )

    # Set the key usage options for X.509 fields.
    x509_parameters = privateca_v1.X509Parameters(
        key_usage=privateca_v1.KeyUsage(
            base_key_usage=privateca_v1.KeyUsage.KeyUsageOptions(
                crl_sign=True,
                cert_sign=True,
            )
        ),
        ca_options=privateca_v1.X509Parameters.CaOptions(
            is_ca=True,
        ),
    )

    # Set certificate authority settings.
    certificate_authority = privateca_v1.CertificateAuthority(
        # CertificateAuthority.Type.SELF_SIGNED denotes that this CA is a root CA.
        type_=privateca_v1.CertificateAuthority.Type.SELF_SIGNED,
        key_spec=key_version_spec,
        config=privateca_v1.CertificateConfig(
            subject_config=subject_config,
            x509_config=x509_parameters,
        ),
        lifetime=duration_pb2.Duration(seconds=ca_duration),
    )

    ca_pool_path = caServiceClient.ca_pool_path(project_id, location, ca_pool_name)

    # Create the CertificateAuthorityRequest.
    request = privateca_v1.CreateCertificateAuthorityRequest(
        parent=ca_pool_path,
        certificate_authority_id=ca_name,
        certificate_authority=certificate_authority,
    )

    operation = caServiceClient.create_certificate_authority(request=request)
    result = operation.result()

    print("Operation result:", result)

REST API

  1. ๋ฃจํŠธ CA๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

    HTTP ๋ฉ”์„œ๋“œ ๋ฐ URL:

    POST https://privateca.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/caPools/POOL_ID/certificateAuthorities?certificate_authority_id=ROOT_CA_ID

    JSON ์š”์ฒญ ๋ณธ๋ฌธ:

    {
    "type": "SELF_SIGNED",
    "lifetime": {
      "seconds": 315576000,
      "nanos": 0
    },
    "config": {
      "subject_config": {
        "subject": {
          "organization": "ORGANIZATION_NAME",
          "common_name": "COMMON_NAME"
        }
      },
      "x509_config":{
        "ca_options":{
          "is_ca":true
        },
        "key_usage":{
          "base_key_usage":{
            "cert_sign":true,
            "crl_sign":true
          }
        }
      }
    },
    "key_spec":{
      "algorithm":"RSA_PKCS1_4096_SHA256"
    }
    }
    

    ์š”์ฒญ์„ ๋ณด๋‚ด๋ ค๋ฉด ๋‹ค์Œ ์˜ต์…˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ํŽผ์นฉ๋‹ˆ๋‹ค.

    ๋‹ค์Œ๊ณผ ๋น„์Šทํ•œ JSON ์‘๋‹ต์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

    {
      "name": "projects/PROJECT_ID/locations/LOCATION/operations/operation-UUID",
      "metadata": {...},
      "done": false
    }
    

  2. ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ž‘์—…์„ ํด๋งํ•ฉ๋‹ˆ๋‹ค.

    HTTP ๋ฉ”์„œ๋“œ ๋ฐ URL:

    GET https://privateca.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/operations/operation-UUID

    ์š”์ฒญ์„ ๋ณด๋‚ด๋ ค๋ฉด ๋‹ค์Œ ์˜ต์…˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ํŽผ์นฉ๋‹ˆ๋‹ค.

    ๋‹ค์Œ๊ณผ ๋น„์Šทํ•œ JSON ์‘๋‹ต์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

    {
     "name": "projects/PROJECT_ID/locations/LOCATION/operations/operation-UUID",
     "metadata": {...},
     "done": true,
     "response": {
       "@type": "type.googleapis.com/google.cloud.security.privateca.v1.CertificateAuthority",
       "name": "...",
     }
    }
    

CA๊ฐ€ ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธ๋˜๋ฉด CA ํ’€์„ ์œ„ํ•œ ๋ถ€ํ•˜ ๋ถ„์‚ฐ ์ธ์ฆ์„œ์˜ ๋ฐœ๊ธ‰์„ ์‹œ์ž‘ํ•˜๋„๋ก ์‚ฌ์šฉ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฃจํŠธ CA ์‚ฌ์šฉ ์„ค์ •

gcloud

๋ฃจํŠธ CA๋ฅผ ์‚ฌ์šฉ ์„ค์ •ํ•˜๋ ค๋ฉด ๋‹ค์Œ gcloud ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

gcloud privateca roots enable ROOT_CA_ID --location=LOCATION --pool=POOL_ID

๋‹ค์Œ์„ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

  • ROOT_CA_ID: CA์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
  • LOCATION: CA ํ’€์˜ ์œ„์น˜์ž…๋‹ˆ๋‹ค. ์ „์ฒด ์œ„์น˜ ๋ชฉ๋ก์€ ์œ„์น˜๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.
  • POOL_ID: CA ํ’€์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.

Terraform

Terraform์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฃจํŠธ CA๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒฝ์šฐ ๋ฃจํŠธ CA๋Š” ์ƒ์„ฑ ์‹œ ์‚ฌ์šฉ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. STAGED ์ƒํƒœ์˜ ๋ฃจํŠธ CA๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด CA๋ฅผ ๋งŒ๋“ค ๋•Œ desired_state ํ•„๋“œ๋ฅผ STAGED๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

CA ์ƒ์„ฑ ํ›„ desired_state ํ•„๋“œ๋ฅผ ENABLED ๋˜๋Š” DISABLED๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Go

CA Service์— ์ธ์ฆํ•˜๋ ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ ์ธ์ฆ ์„ค์ •์„ ์ฐธ์กฐํ•˜์„ธ์š”.

import (
	"context"
	"fmt"
	"io"

	privateca "cloud.google.com/go/security/privateca/apiv1"
	"cloud.google.com/go/security/privateca/apiv1/privatecapb"
)

// Enable the Certificate Authority present in the given ca pool.
// CA cannot be enabled if it has been already deleted.
func enableCa(w io.Writer, projectId string, location string, caPoolId string, caId string) error {
	// projectId := "your_project_id"
	// location := "us-central1"	// For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations.
	// caPoolId := "ca-pool-id"		// The id of the CA pool under which the CA is present.
	// caId := "ca-id"				// The id of the CA to be enabled.

	ctx := context.Background()
	caClient, err := privateca.NewCertificateAuthorityClient(ctx)
	if err != nil {
		return fmt.Errorf("NewCertificateAuthorityClient creation failed: %w", err)
	}
	defer caClient.Close()

	fullCaName := fmt.Sprintf("projects/%s/locations/%s/caPools/%s/certificateAuthorities/%s",
		projectId, location, caPoolId, caId)

	// Create the EnableCertificateAuthorityRequest.
	// See https://pkg.go.dev/cloud.google.com/go/security/privateca/apiv1/privatecapb#EnableCertificateAuthorityRequest.
	req := &privatecapb.EnableCertificateAuthorityRequest{Name: fullCaName}

	op, err := caClient.EnableCertificateAuthority(ctx, req)
	if err != nil {
		return fmt.Errorf("EnableCertificateAuthority failed: %w", err)
	}

	var caResp *privatecapb.CertificateAuthority
	if caResp, err = op.Wait(ctx); err != nil {
		return fmt.Errorf("EnableCertificateAuthority failed during wait: %w", err)
	}

	if caResp.State != privatecapb.CertificateAuthority_ENABLED {
		return fmt.Errorf("unable to enable Certificate Authority. Current state: %s", caResp.State.String())
	}

	fmt.Fprintf(w, "Successfully enabled Certificate Authority: %s.", caId)
	return nil
}

์ž๋ฐ”

CA Service์— ์ธ์ฆํ•˜๋ ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ ์ธ์ฆ ์„ค์ •์„ ์ฐธ์กฐํ•˜์„ธ์š”.


import com.google.api.core.ApiFuture;
import com.google.cloud.security.privateca.v1.CertificateAuthority.State;
import com.google.cloud.security.privateca.v1.CertificateAuthorityName;
import com.google.cloud.security.privateca.v1.CertificateAuthorityServiceClient;
import com.google.cloud.security.privateca.v1.EnableCertificateAuthorityRequest;
import com.google.longrunning.Operation;
import java.io.IOException;
import java.util.concurrent.ExecutionException;

public class EnableCertificateAuthority {

  public static void main(String[] args)
      throws InterruptedException, ExecutionException, IOException {
    // TODO(developer): Replace these variables before running the sample.
    // location: For a list of locations, see:
    // https://cloud.google.com/certificate-authority-service/docs/locations
    // poolId: The id of the CA pool under which the CA is present.
    // certificateAuthorityName: The name of the CA to be enabled.
    String project = "your-project-id";
    String location = "ca-location";
    String poolId = "ca-pool-id";
    String certificateAuthorityName = "certificate-authority-name";
    enableCertificateAuthority(project, location, poolId, certificateAuthorityName);
  }

  // Enable the Certificate Authority present in the given ca pool.
  // CA cannot be enabled if it has been already deleted.
  public static void enableCertificateAuthority(
      String project, String location, String poolId, String certificateAuthorityName)
      throws IOException, ExecutionException, InterruptedException {
    try (CertificateAuthorityServiceClient certificateAuthorityServiceClient =
        CertificateAuthorityServiceClient.create()) {
      // Create the Certificate Authority Name.
      CertificateAuthorityName certificateAuthorityParent =
          CertificateAuthorityName.newBuilder()
              .setProject(project)
              .setLocation(location)
              .setCaPool(poolId)
              .setCertificateAuthority(certificateAuthorityName)
              .build();

      // Create the Enable Certificate Authority Request.
      EnableCertificateAuthorityRequest enableCertificateAuthorityRequest =
          EnableCertificateAuthorityRequest.newBuilder()
              .setName(certificateAuthorityParent.toString())
              .build();

      // Enable the Certificate Authority.
      ApiFuture<Operation> futureCall =
          certificateAuthorityServiceClient
              .enableCertificateAuthorityCallable()
              .futureCall(enableCertificateAuthorityRequest);
      Operation response = futureCall.get();

      if (response.hasError()) {
        System.out.println("Error while enabling Certificate Authority !" + response.getError());
        return;
      }

      // Get the current CA state.
      State caState =
          certificateAuthorityServiceClient
              .getCertificateAuthority(certificateAuthorityParent)
              .getState();

      // Check if the CA is enabled.
      if (caState == State.ENABLED) {
        System.out.println("Enabled Certificate Authority : " + certificateAuthorityName);
      } else {
        System.out.println(
            "Cannot enable the Certificate Authority ! Current CA State: " + caState);
      }
    }
  }
}

Python

CA Service์— ์ธ์ฆํ•˜๋ ค๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ ์ธ์ฆ ์„ค์ •์„ ์ฐธ์กฐํ•˜์„ธ์š”.

import google.cloud.security.privateca_v1 as privateca_v1


def enable_certificate_authority(
    project_id: str, location: str, ca_pool_name: str, ca_name: str
) -> None:
    """
    Enable the Certificate Authority present in the given ca pool.
    CA cannot be enabled if it has been already deleted.

    Args:
        project_id: project ID or project number of the Cloud project you want to use.
        location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations.
        ca_pool_name: the name of the CA pool under which the CA is present.
        ca_name: the name of the CA to be enabled.
    """

    caServiceClient = privateca_v1.CertificateAuthorityServiceClient()
    ca_path = caServiceClient.certificate_authority_path(
        project_id, location, ca_pool_name, ca_name
    )

    # Create the Enable Certificate Authority Request.
    request = privateca_v1.EnableCertificateAuthorityRequest(
        name=ca_path,
    )

    # Enable the Certificate Authority.
    operation = caServiceClient.enable_certificate_authority(request=request)
    operation.result()

    # Get the current CA state.
    ca_state = caServiceClient.get_certificate_authority(name=ca_path).state

    # Check if the CA is enabled.
    if ca_state == privateca_v1.CertificateAuthority.State.ENABLED:
        print("Enabled Certificate Authority:", ca_name)
    else:
        print("Cannot enable the Certificate Authority ! Current CA State:", ca_state)

REST API

  1. CA ํ’€์—์„œ ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰ํ•˜๋„๋ก CA๋ฅผ ์‚ฌ์šฉ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    HTTP ๋ฉ”์„œ๋“œ ๋ฐ URL:

    POST https://privateca.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/caPools/POOL_ID/certificateAuthorities/ROOT_CA_ID:enable

    ์š”์ฒญ์„ ๋ณด๋‚ด๋ ค๋ฉด ๋‹ค์Œ ์˜ต์…˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ํŽผ์นฉ๋‹ˆ๋‹ค.

    ๋‹ค์Œ๊ณผ ๋น„์Šทํ•œ JSON ์‘๋‹ต์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

    {
        "name": "projects/PROJECT_ID/locations/LOCATION/operations/operation-UUID",
        "metadata": {...},
        "done": false
    }
    

  2. ์ž‘์—…์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ž‘์—…์„ ํด๋งํ•ฉ๋‹ˆ๋‹ค.

    HTTP ๋ฉ”์„œ๋“œ ๋ฐ URL:

    GET https://privateca.googleapis.com/v1/projects/PROJECT_ID/locations/LOCATION/operations/operation-UUID

    ์š”์ฒญ์„ ๋ณด๋‚ด๋ ค๋ฉด ๋‹ค์Œ ์˜ต์…˜ ์ค‘ ํ•˜๋‚˜๋ฅผ ํŽผ์นฉ๋‹ˆ๋‹ค.

    ๋‹ค์Œ๊ณผ ๋น„์Šทํ•œ JSON ์‘๋‹ต์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

    {
        "name": "projects/PROJECT_ID/locations/LOCATION/operations/operation-UUID",
        "metadata": {...},
        "done": true,
        "response": {
          "@type": "type.googleapis.com/google.cloud.security.privateca.v1.CertificateAuthority",
          "name": "...",
        }
    }
    

CA ํ…Œ์ŠคํŠธ

CA๊ฐ€ ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋ ค๋ฉด ์—ฐ๊ฒฐ๋œ CA ํ’€์—์„œ ์ธ์ฆ์„œ๋ฅผ ์š”์ฒญํ•˜๊ณ  --ca ํ”Œ๋ž˜๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธํ•  CA ์ด๋ฆ„์„ ๋ช…์‹œ์ ์œผ๋กœ ์–ธ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ CA ํ’€์—์„œ ์ธ์ฆ์„œ๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. CA Service์—์„œ ์ž๋™์œผ๋กœ ๋น„๊ณต๊ฐœ ๋˜๋Š” ๊ณต๊ฐœ ํ‚ค๋ฅผ ๋งŒ๋“ค๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ž์ฒด ๋น„๊ณต๊ฐœ/๊ณต๊ฐœ ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ธ์ฆ์„œ ์„œ๋ช… ์š”์ฒญ(CSR) ์ œ์ถœํ•˜๊ธฐ

์ž๋™ ์ƒ์„ฑ๋œ ๋น„๊ณต๊ฐœ ํ‚ค๋‚˜ ๊ณต๊ฐœ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์‰ฝ๊ฒŒ CA ํ’€์˜ CA์—์„œ ์ธ์ฆ์„œ๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์„น์…˜์—์„œ๋Š” ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•œ CA ํ…Œ์ŠคํŠธ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ž๋™ ์ƒ์„ฑ๋œ ๋น„๊ณต๊ฐœ ํ‚ค๋‚˜ ๊ณต๊ฐœ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CA ํ’€์˜ CA์—์„œ ์ธ์ฆ์„œ๋ฅผ ์š”์ฒญํ•˜๋ ค๋ฉด ๋‹ค์Œ gcloud ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

gcloud privateca certificates create \
    --issuer-pool=POOL_ID \
    --issuer-location=ISSUER_LOCATION \
    --ca=ROOT_CA_ID \
    --generate-key \
    --key-output-file=KEY_FILENAME \
    --cert-output-file=CERT_FILENAME \
    --dns-san=DNS_NAME

๋‹ค์Œ์„ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

  • POOL_ID: CA ํ’€์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
  • ISSUER_LOCATION: ๋””์ง€ํ„ธ ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰ํ•œ ์ธ์ฆ ๊ธฐ๊ด€ (CA)์˜ ์œ„์น˜์ž…๋‹ˆ๋‹ค.
  • ROOT_CA_ID: ํ…Œ์ŠคํŠธํ•  CA์˜ ๊ณ ์œ  ์‹๋ณ„์ž์ž…๋‹ˆ๋‹ค.
  • KEY_FILENAME: ์ƒ์„ฑ๋œ ํ‚ค๊ฐ€ PEM ํ˜•์‹์œผ๋กœ ์ž‘์„ฑ๋˜๋Š” ํŒŒ์ผ์ž…๋‹ˆ๋‹ค.
  • CERT_FILENAME: ๊ฒฐ๊ณผ๋กœ ์ƒ์„ฑ๋œ PEM ์ธ์ฝ”๋”ฉ ์ธ์ฆ์„œ ์ฒด์ธ ํŒŒ์ผ์ด ์ž‘์„ฑ๋˜๋Š” ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. ์ธ์ฆ์„œ ์ฒด์ธ์˜ ์ˆœ์„œ๋Š” ๋ฆฌํ”„์—์„œ ๋ฃจํŠธ์ž…๋‹ˆ๋‹ค.
  • DNS_NAME: ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋œ ํ•˜๋‚˜ ์ด์ƒ์˜ DNS ์ฃผ์ฒด ๋Œ€์ฒด ์ด๋ฆ„ (SAN)์ž…๋‹ˆ๋‹ค.

    --generate-key ํ”Œ๋ž˜๊ทธ๋Š” ๋จธ์‹ ์— ์ƒˆ RSA-2048 ๋น„๊ณต๊ฐœ ํ‚ค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ธ์ฆ์„œ ์„œ๋ช… ์š”์ฒญ (CSR)์„ ์‚ฌ์šฉํ•˜์—ฌ CA ํ’€์˜ CA์—์„œ ์ธ์ฆ์„œ๋ฅผ ์š”์ฒญํ•˜๊ฑฐ๋‚˜ ์ธ์ฆ์„œ ์š”์ฒญ์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ ค๋ฉด ์ธ์ฆ์„œ ์š”์ฒญ ๋ฐ ๋ฐœ๊ธ‰๋œ ์ธ์ฆ์„œ ๋ณด๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

์ธ์ฆ ๊ธฐ๊ด€ ํด๋ก 

๊ธฐ์กด CA๋ฅผ ํด๋ก ํ•˜์—ฌ ํ•ด๋‹น CA๋ฅผ ๊ฐฑ์‹ ํ•˜๊ฑฐ๋‚˜ ๋™์ผํ•œ ๊ตฌ์„ฑ์œผ๋กœ ์ƒˆ CA๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

gcloud privateca roots create NEW_CA_ID \
    --location=LOCATION \
    --pool=POOL_ID \
    --from-ca=EXISTING_CA_ID \
    --key-algorithm "ec-p384-sha384"

๋‹ค์Œ์„ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

  • NEW_CA_ID: ์ƒˆ CA์˜ ๊ณ ์œ  ์‹๋ณ„์ž์ž…๋‹ˆ๋‹ค.
  • LOCATION: CA ํ’€์˜ ์œ„์น˜์ž…๋‹ˆ๋‹ค.
  • POOL_ID: ์ƒˆ CA๋ฅผ ๋งŒ๋“ค CA ํ’€์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
  • EXISTING_CA_ID: ์†Œ์Šค CA์˜ ID ๋˜๋Š” ์†Œ์Šค CA์˜ ์ •๊ทœํ™”๋œ ์‹๋ณ„์ž์ž…๋‹ˆ๋‹ค.

--from-ca ํ”Œ๋ž˜๊ทธ๋Š” ๋ฃจํŠธ ๋ฐ ํ•˜์œ„ CA ๋งŒ๋“ค๊ธฐ์— ์ง€์›๋ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด CA๋Š” ์ƒˆ CA์™€ ๋™์ผํ•œ CA ํ’€์— ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

--key-algorithm ํ”Œ๋ž˜๊ทธ๋Š” ๊ธฐ์กด CA์—์„œ (Cloud KMS ํ‚ค ๋ฒ„์ „ ๋ฐ Cloud Storage ๋ฒ„ํ‚ท์„ ์ œ์™ธํ•œ) ๋ชจ๋“  CA ๊ตฌ์„ฑ์„ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ ์ ˆํ•œ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ œ๊ณตํ•˜์—ฌ ์ƒˆ CA์˜ ๊ตฌ์„ฑ ๊ฐ’์„ ์žฌ์ •์˜ํ•  ์ˆ˜๋Š” ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ƒˆ ์ฃผ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก `--subject SUBJECT๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

--key-algorithm ํ”Œ๋ž˜๊ทธ๋ฅผ ์ƒ๋žตํ•˜๋ฉด ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง€์ •๋ฉ๋‹ˆ๋‹ค.

  • ๋ฃจํŠธ CA์˜ ๊ฒฝ์šฐ rsa-pkcs1-4096-sha256
  • ํ•˜์œ„ CA์˜ ๊ฒฝ์šฐ rsa-pkcs1-2048-sha256

์ด gcloud ๋ช…๋ น์–ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ gcloud privateca roots create๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

๋‹ค์Œ ๋‹จ๊ณ„