# -*- coding: utf-8 -*- # # Copyright 2017 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Create a key.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.api_lib.cloudkms import base as cloudkms_base from googlecloudsdk.calliope import base from googlecloudsdk.command_lib.kms import exceptions as kms_exceptions from googlecloudsdk.command_lib.kms import flags from googlecloudsdk.command_lib.kms import maps from googlecloudsdk.command_lib.kms import resource_args from googlecloudsdk.command_lib.util.args import labels_util @base.UniverseCompatible @base.ReleaseTracks( base.ReleaseTrack.ALPHA, base.ReleaseTrack.BETA, base.ReleaseTrack.GA ) class Create(base.CreateCommand): r"""Create a new key. Creates a new key within the given keyring. The flag `--purpose` is always required when creating a key. The flag `--default-algorithm` is required when creating a symmetric signing key, an asymmetric key, or an external key. Algorithm and purpose should be compatible. The optional flags `--rotation-period` and `--next-rotation-time` define a rotation schedule for the key. A schedule can also be defined by the `--create-rotation-schedule` command. The flag `--next-rotation-time` must be in ISO 8601 or RFC3339 format, and `rotation-period` must be in the form INTEGER[UNIT], where units can be one of seconds (s), minutes (m), hours (h) or days (d). The optional flag `--protection-level` specifies the physical environment where crypto operations with the key happen. The default is ``software''; use ``hsm'' to create a hardware-backed key, ``external'' to create an externally backed key, or ``external-vpc'' to create an external key over vpc. The optional flag `--labels` defines a user specified key/value pair for the given key. The flag `--skip-initial-version-creation` creates a CryptoKey with no versions. If you import into the CryptoKey, or create a new version in that CryptoKey, there will be no primary version until one is set using the `--set-primary-version` command. You must include `--skip-initial-version-creation` when creating a CryptoKey with protection level ``external'' or ``external-vpc''. The optional flag `--import-only` restricts the key to imported key versions only. To do so, the flag `--skip-initial-version-creation` must also be set. The optional flag `--destroy-scheduled-duration` defines the destroy schedule for the key, and must be in the form INTEGER[UNIT], where units can be one of seconds (s), minutes (m), hours (h) or days (d). The flag `--crypto-key-backend` defines the resource name for the backend where the key resides. Required for ``external-vpc'' keys. The optional flag `--allowed-access-reasons` defines the Key Access Justifications Policy for the key, and is specified as a comma separated list of zero or more justification codes defined in https://cloud.google.com/assured-workloads/key-access-justifications/docs/justification-codes. The key must be enrolled in Key Access Justifications to use this flag. ## EXAMPLES The following command creates a key named ``frodo'' with protection level ``software'' within the keyring ``fellowship'' and location ``us-east1'': $ {command} frodo \ --location=us-east1 \ --keyring=fellowship \ --purpose=encryption The following command creates a key named ``strider'' with protection level ``software'' within the keyring ``rangers'' and location ``global'' with a specified rotation schedule: $ {command} strider \ --location=global --keyring=rangers \ --purpose=encryption \ --rotation-period=30d \ --next-rotation-time=2017-10-12T12:34:56.1234Z The following command creates a raw encryption key named ``foo'' with protection level ``software'' within the keyring ``fellowship'' and location ``us-east1'' with two specified labels: $ {command} foo \ --location=us-east1 \ --keyring=fellowship \ --purpose=raw-encryption \ --default-algorithm=aes-128-cbc --labels=env=prod,team=kms The following command creates an asymmetric key named ``samwise'' with protection level ``software'' and default algorithm ``ec-sign-p256-sha256'' within the keyring ``fellowship'' and location ``us-east1'': $ {command} samwise \ --location=us-east1 \ --keyring=fellowship \ --purpose=asymmetric-signing \ --default-algorithm=ec-sign-p256-sha256 The following command creates a key named ``gimli'' with protection level ``hsm'' and default algorithm ``google-symmetric-encryption'' within the keyring ``fellowship'' and location ``us-east1'': $ {command} gimli \ --location=us-east1 \ --keyring=fellowship \ --purpose=encryption \ --protection-level=hsm The following command creates a key named ``legolas'' with protection level ``external'' and default algorithm ``external-symmetric-encryption'' within the keyring ``fellowship'' and location ``us-central1'': $ {command} legolas \ --location=us-central1 \ --keyring=fellowship \ --purpose=encryption \ --default-algorithm=external-symmetric-encryption \ --protection-level=external --skip-initial-version-creation The following command creates a key named ``bilbo'' with protection level ``external-vpc'' and default algorithm ``external-symmetric-encryption'' and an EkmConnection of ``eagles'' within the keyring ``fellowship'' and location ``us-central1'': $ {command} bilbo \ --location=us-central1 \ --keyring=fellowship \ --purpose=encryption \ --default-algorithm=external-symmetric-encryption \ --protection-level=external-vpc --skip-initial-version-creation --crypto-key-backend="projects/$(gcloud config get project)/ locations/us-central1/ekmConnections/eagles" The following command creates a key named ``arwen'' with protection level ``software'' within the keyring ``fellowship'' and location ``us-east1'' with a Key Access Justifications policy that allows access reasons ``customer-initiated-access'' and ``google-initiated-system-operation'': $ {command} arwen \ --location=us-east1 \ --keyring=fellowship \ --purpose=encryption \ --allowed-access-reasons=customer-initiated-access,google-initiated-system-operation """ @staticmethod def Args(parser): resource_args.AddKmsKeyResourceArgForKMS(parser, True, 'key') flags.AddRotationPeriodFlag(parser) flags.AddNextRotationTimeFlag(parser) flags.AddSkipInitialVersionCreationFlag(parser) labels_util.AddCreateLabelsFlags(parser) parser.add_argument( '--purpose', choices=sorted(maps.PURPOSE_MAP.keys()), required=True, help='The "purpose" of the key.') parser.display_info.AddCacheUpdater(flags.KeyRingCompleter) flags.AddProtectionLevelFlag(parser) flags.AddDefaultAlgorithmFlag(parser) flags.AddImportOnlyFlag(parser) flags.AddDestroyScheduledDurationFlag(parser) flags.AddCryptoKeyBackendFlag(parser) flags.AddAllowedAccessReasonsFlag(parser) def _CreateRequest(self, args): messages = cloudkms_base.GetMessagesModule() purpose = maps.PURPOSE_MAP[args.purpose] valid_algorithms = maps.VALID_ALGORITHMS_MAP[purpose] # Check default algorithm has been specified for non-symmetric-encryption # keys. For backward compatibility, the algorithm is # google-symmetric-encryption by default if the purpose is encryption. if not args.default_algorithm: if args.purpose != 'encryption': raise kms_exceptions.ArgumentError( '--default-algorithm needs to be specified when creating a key with' ' --purpose={}. The valid algorithms are: {}'.format( args.purpose, ', '.join(valid_algorithms))) args.default_algorithm = 'google-symmetric-encryption' # Check default algorithm and purpose are compatible. if args.default_algorithm not in valid_algorithms: raise kms_exceptions.ArgumentError( 'Default algorithm and purpose are incompatible. Here are the valid ' 'algorithms for --purpose={}: {}'.format(args.purpose, ', '.join(valid_algorithms))) crypto_key_ref = args.CONCEPTS.key.Parse() parent_ref = crypto_key_ref.Parent() req = messages.CloudkmsProjectsLocationsKeyRingsCryptoKeysCreateRequest( parent=parent_ref.RelativeName(), cryptoKeyId=crypto_key_ref.Name(), cryptoKey=messages.CryptoKey( purpose=purpose, versionTemplate=messages.CryptoKeyVersionTemplate( protectionLevel=maps.PROTECTION_LEVEL_MAPPER.GetEnumForChoice( args.protection_level), algorithm=maps.ALGORITHM_MAPPER.GetEnumForChoice( args.default_algorithm)), labels=labels_util.ParseCreateArgs(args, messages.CryptoKey.LabelsValue), importOnly=args.import_only, cryptoKeyBackend=args.crypto_key_backend), skipInitialVersionCreation=args.skip_initial_version_creation) flags.SetNextRotationTime(args, req.cryptoKey) flags.SetRotationPeriod(args, req.cryptoKey) flags.SetDestroyScheduledDuration(args, req.cryptoKey) flags.SetKeyAccessJustificationsPolicy(args, req.cryptoKey) return req def Run(self, args): client = cloudkms_base.GetClientInstance() return client.projects_locations_keyRings_cryptoKeys.Create( self._CreateRequest(args))