|
| 1 | +package keyring |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "fmt" |
| 6 | + "os" |
| 7 | + |
| 8 | + "github.com/aws/aws-sdk-go-v2/aws" |
| 9 | + "github.com/aws/aws-sdk-go-v2/config" |
| 10 | + "github.com/aws/aws-sdk-go-v2/service/dynamodb" |
| 11 | + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" |
| 12 | + "github.com/aws/aws-sdk-go-v2/service/kms" |
| 13 | + |
| 14 | + mpl "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygenerated" |
| 15 | + mpltypes "github.com/aws/aws-cryptographic-material-providers-library/releases/go/mpl/awscryptographymaterialproviderssmithygeneratedtypes" |
| 16 | + "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes" |
| 17 | + "github.com/aws/aws-database-encryption-sdk-dynamodb/awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes" |
| 18 | + "github.com/aws/aws-database-encryption-sdk-dynamodb/dbesdkmiddleware" |
| 19 | + "github.com/aws/aws-database-encryption-sdk-dynamodb/releases/go/dbesdk/examples/utils" |
| 20 | + kmstypes "github.com/aws/aws-sdk-go-v2/service/kms/types" |
| 21 | +) |
| 22 | + |
| 23 | +func PutItemGetItem(kmsRsaKeyID string, kmsRSAPublicKeyFileName string) { |
| 24 | + ddbTableName := "DynamoDbEncryptionInterceptorTestTableCS" |
| 25 | + // 1. Create the aws kms client |
| 26 | + cfg, err := config.LoadDefaultConfig(context.TODO()) |
| 27 | + if err != nil { |
| 28 | + panic(err) |
| 29 | + } |
| 30 | + kmsClient := kms.NewFromConfig(cfg, func(o *kms.Options) { |
| 31 | + o.Region = "us-west-2" |
| 32 | + }) |
| 33 | + // 2. Initialize the mpl client |
| 34 | + matProv, err := mpl.NewClient( |
| 35 | + mpltypes.MaterialProvidersConfig{}, |
| 36 | + ) |
| 37 | + if err != nil { |
| 38 | + panic(err) |
| 39 | + } |
| 40 | + // 3. Load UTF-8 encoded public key PEM file. |
| 41 | + // You may have an RSA public key file already defined. |
| 42 | + // If not, this method will call the KMS RSA key, retrieve its public key, |
| 43 | + // and store it in a PEM file for example use. |
| 44 | + if !utils.FileExists(kmsRSAPublicKeyFileName) { |
| 45 | + err = writePublicKeyPemForRsaKey(kmsRsaKeyID, kmsRSAPublicKeyFileName) |
| 46 | + if err != nil { |
| 47 | + panic(err) |
| 48 | + } |
| 49 | + } |
| 50 | + kmsRSAPublicKey, err := os.ReadFile(kmsRSAPublicKeyFileName) |
| 51 | + if err != nil { |
| 52 | + panic(err) |
| 53 | + } |
| 54 | + |
| 55 | + // 4. Create the keyring |
| 56 | + awsKmsRSAKeyringInput := mpltypes.CreateAwsKmsRsaKeyringInput{ |
| 57 | + KmsClient: kmsClient, |
| 58 | + KmsKeyId: kmsRsaKeyID, |
| 59 | + PublicKey: kmsRSAPublicKey, |
| 60 | + EncryptionAlgorithm: kmstypes.EncryptionAlgorithmSpecRsaesOaepSha256, |
| 61 | + } |
| 62 | + keyring, err := matProv.CreateAwsKmsRsaKeyring(context.Background(), awsKmsRSAKeyringInput) |
| 63 | + if err != nil { |
| 64 | + panic(err) |
| 65 | + } |
| 66 | + |
| 67 | + // 2. Configure attribute actions for encryption/signing |
| 68 | + attributeActions := map[string]awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes.CryptoAction{ |
| 69 | + "partition_key": awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes.CryptoActionSignOnly, // Partition key must be SIGN_ONLY |
| 70 | + "sort_key": awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes.CryptoActionSignOnly, // Sort key must be SIGN_ONLY |
| 71 | + "sensitive_data": awscryptographydbencryptionsdkstructuredencryptionsmithygeneratedtypes.CryptoActionEncryptAndSign, |
| 72 | + } |
| 73 | + |
| 74 | + // 3. Configure table encryption |
| 75 | + allowedUnsignedAttributePrefix := ":" |
| 76 | + sortKeyName := "sort_key" |
| 77 | + algorithmSuiteID := mpltypes.DBEAlgorithmSuiteIdAlgAes256GcmHkdfSha512CommitKeySymsigHmacSha384 |
| 78 | + tableConfig := awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes.DynamoDbTableEncryptionConfig{ |
| 79 | + LogicalTableName: ddbTableName, |
| 80 | + PartitionKeyName: "partition_key", |
| 81 | + SortKeyName: &sortKeyName, |
| 82 | + AttributeActionsOnEncrypt: attributeActions, |
| 83 | + Keyring: keyring, |
| 84 | + AllowedUnsignedAttributePrefix: &allowedUnsignedAttributePrefix, |
| 85 | + AlgorithmSuiteId: &algorithmSuiteID, |
| 86 | + } |
| 87 | + |
| 88 | + tableConfigsMap := make(map[string]awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes.DynamoDbTableEncryptionConfig) |
| 89 | + tableConfigsMap[ddbTableName] = tableConfig |
| 90 | + listOfTableConfigs := awscryptographydbencryptionsdkdynamodbsmithygeneratedtypes.DynamoDbTablesEncryptionConfig{ |
| 91 | + TableEncryptionConfigs: tableConfigsMap, |
| 92 | + } |
| 93 | + // 4. Create encrypted DynamoDB client |
| 94 | + dbEsdkMiddleware, err := dbesdkmiddleware.NewDBEsdkMiddleware(listOfTableConfigs) |
| 95 | + if err != nil { |
| 96 | + panic(err) |
| 97 | + } |
| 98 | + ddb := dynamodb.NewFromConfig(cfg, dbEsdkMiddleware.CreateMiddleware()) |
| 99 | + |
| 100 | + // 5. Put an encrypted item |
| 101 | + item := map[string]types.AttributeValue{ |
| 102 | + "partition_key": &types.AttributeValueMemberS{Value: "awsKmsRsaKeyringItem"}, |
| 103 | + "sort_key": &types.AttributeValueMemberN{Value: "0"}, |
| 104 | + "sensitive_data": &types.AttributeValueMemberS{Value: "encrypt and sign me!"}, |
| 105 | + } |
| 106 | + |
| 107 | + putInput := &dynamodb.PutItemInput{ |
| 108 | + TableName: aws.String(ddbTableName), |
| 109 | + Item: item, |
| 110 | + } |
| 111 | + |
| 112 | + _, err = ddb.PutItem(context.TODO(), putInput) |
| 113 | + if err != nil { |
| 114 | + panic(err) |
| 115 | + } |
| 116 | + |
| 117 | + // 6. Get and decrypt the item |
| 118 | + key := map[string]types.AttributeValue{ |
| 119 | + "partition_key": &types.AttributeValueMemberS{Value: "awsKmsRsaKeyringItem"}, |
| 120 | + "sort_key": &types.AttributeValueMemberN{Value: "0"}, |
| 121 | + } |
| 122 | + |
| 123 | + getInput := &dynamodb.GetItemInput{ |
| 124 | + TableName: aws.String(ddbTableName), |
| 125 | + Key: key, |
| 126 | + ConsistentRead: aws.Bool(true), |
| 127 | + } |
| 128 | + |
| 129 | + result, err := ddb.GetItem(context.TODO(), getInput) |
| 130 | + if err != nil { |
| 131 | + panic(err) |
| 132 | + } |
| 133 | + fmt.Println(result.Item["partition_key"].(*types.AttributeValueMemberS).Value) |
| 134 | + // Verify the decrypted item |
| 135 | + if result.Item["sensitive_data"].(*types.AttributeValueMemberS).Value != "encrypt and sign me!" { |
| 136 | + panic("unexpected value for attribute1") |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +func writePublicKeyPemForRsaKey(rsaKeyArn, filename string) error { |
| 141 | + cfg, err := config.LoadDefaultConfig(context.TODO()) |
| 142 | + if err != nil { |
| 143 | + return err |
| 144 | + } |
| 145 | + |
| 146 | + kmsClient := kms.NewFromConfig(cfg) |
| 147 | + resp, err := kmsClient.GetPublicKey(context.TODO(), &kms.GetPublicKeyInput{ |
| 148 | + KeyId: aws.String(rsaKeyArn), |
| 149 | + }) |
| 150 | + if err != nil { |
| 151 | + return err |
| 152 | + } |
| 153 | + return utils.WritePublicKey(resp.PublicKey, filename) |
| 154 | +} |
0 commit comments