github.com/aldelo/common@v1.5.1/wrapper/kms/kms.go (about) 1 package kms 2 3 /* 4 * Copyright 2020-2023 Aldelo, LP 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 // ================================================================================================================= 20 // AWS CREDENTIAL: 21 // use $> aws configure (to set aws access key and secret to target machine) 22 // Store AWS Access ID and Secret Key into Default Profile Using '$ aws configure' cli 23 // 24 // To Install & Setup AWS CLI on Host: 25 // 1) https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html 26 // On Ubuntu, if host does not have zip and unzip: 27 // $> sudo apt install zip 28 // $> sudo apt install unzip 29 // On Ubuntu, to install AWS CLI v2: 30 // $> curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" 31 // $> unzip awscliv2.zip 32 // $> sudo ./aws/install 33 // 2) $> aws configure set region awsRegionName --profile default 34 // 3) $> aws configure 35 // follow prompts to enter Access ID and Secret Key 36 // 37 // AWS Region Name Reference: 38 // us-west-2, us-east-1, ap-northeast-1, etc 39 // See: https://docs.aws.amazon.com/general/latest/gr/rande.html 40 // ================================================================================================================= 41 42 import ( 43 "context" 44 "errors" 45 util "github.com/aldelo/common" 46 "github.com/aldelo/common/crypto" 47 awshttp2 "github.com/aldelo/common/wrapper/aws" 48 "github.com/aldelo/common/wrapper/aws/awsregion" 49 "github.com/aldelo/common/wrapper/xray" 50 "github.com/aws/aws-sdk-go/aws" 51 "github.com/aws/aws-sdk-go/aws/session" 52 "github.com/aws/aws-sdk-go/service/kms" 53 awsxray "github.com/aws/aws-xray-sdk-go/xray" 54 "net/http" 55 ) 56 57 // ================================================================================================================ 58 // STRUCTS 59 // ================================================================================================================ 60 61 // KMS struct encapsulates the AWS KMS access functionality 62 type KMS struct { 63 // define the AWS region that KMS is located at 64 AwsRegion awsregion.AWSRegion 65 66 // custom http2 client options 67 HttpOptions *awshttp2.HttpClientSettings 68 69 // define kms key name 70 AesKmsKeyName string 71 RsaKmsKeyName string 72 SignatureKmsKeyName string 73 74 // store aws session object 75 sess *session.Session 76 77 // store kms client object 78 kmsClient *kms.KMS 79 80 _parentSegment *xray.XRayParentSegment 81 } 82 83 // ================================================================================================================ 84 // STRUCTS FUNCTIONS 85 // ================================================================================================================ 86 87 // ---------------------------------------------------------------------------------------------------------------- 88 // utility functions 89 // ---------------------------------------------------------------------------------------------------------------- 90 91 // Connect will establish a connection to the KMS service 92 func (k *KMS) Connect(parentSegment ...*xray.XRayParentSegment) (err error) { 93 if xray.XRayServiceOn() { 94 if len(parentSegment) > 0 { 95 k._parentSegment = parentSegment[0] 96 } 97 98 seg := xray.NewSegment("KMS-Connect", k._parentSegment) 99 defer seg.Close() 100 defer func() { 101 _ = seg.Seg.AddMetadata("KDS-AWS-Region", k.AwsRegion) 102 103 if err != nil { 104 _ = seg.Seg.AddError(err) 105 } 106 }() 107 108 err = k.connectInternal() 109 110 if err == nil { 111 awsxray.AWS(k.kmsClient.Client) 112 } 113 114 return err 115 } else { 116 return k.connectInternal() 117 } 118 } 119 120 // Connect will establish a connection to the KMS service 121 func (k *KMS) connectInternal() error { 122 // clean up prior session reference 123 k.sess = nil 124 125 if !k.AwsRegion.Valid() || k.AwsRegion == awsregion.UNKNOWN { 126 return errors.New("Connect To KMS Failed: (AWS Session Error) " + "Region is Required") 127 } 128 129 // create custom http2 client if needed 130 var httpCli *http.Client 131 var httpErr error 132 133 if k.HttpOptions == nil { 134 k.HttpOptions = new(awshttp2.HttpClientSettings) 135 } 136 137 // use custom http2 client 138 h2 := &awshttp2.AwsHttp2Client{ 139 Options: k.HttpOptions, 140 } 141 142 if httpCli, httpErr = h2.NewHttp2Client(); httpErr != nil { 143 return errors.New("Connect to KMS Failed: (AWS Session Error) " + "Create Custom Http2 Client Errored = " + httpErr.Error()) 144 } 145 146 // establish aws session connection and keep session object in struct 147 if sess, err := session.NewSession( 148 &aws.Config{ 149 Region: aws.String(k.AwsRegion.Key()), 150 HTTPClient: httpCli, 151 }); err != nil { 152 // aws session error 153 return errors.New("Connect To KMS Failed: (AWS Session Error) " + err.Error()) 154 } else { 155 // aws session obtained 156 k.sess = sess 157 158 // create cached objects for shared use 159 k.kmsClient = kms.New(k.sess) 160 161 if k.kmsClient == nil { 162 return errors.New("Connect To KMS Client Failed: (New KMS Client Connection) " + "Connection Object Nil") 163 } 164 165 // session stored to struct 166 return nil 167 } 168 } 169 170 // Disconnect will disjoin from aws session by clearing it 171 func (k *KMS) Disconnect() { 172 k.kmsClient = nil 173 k.sess = nil 174 } 175 176 // UpdateParentSegment updates this struct's xray parent segment, if no parent segment, set nil 177 func (k *KMS) UpdateParentSegment(parentSegment *xray.XRayParentSegment) { 178 k._parentSegment = parentSegment 179 } 180 181 // ---------------------------------------------------------------------------------------------------------------- 182 // kms-cmk encrypt/decrypt via aes 256 functions 183 // ---------------------------------------------------------------------------------------------------------------- 184 185 // EncryptViaCmkAes256 will use kms cmk to encrypt plainText using aes 256 symmetric kms cmk key, and return cipherText string, 186 // the cipherText can only be decrypted with aes 256 symmetric kms cmk key 187 func (k *KMS) EncryptViaCmkAes256(plainText string) (cipherText string, err error) { 188 var segCtx context.Context 189 segCtx = nil 190 191 seg := xray.NewSegmentNullable("KMS-EncryptViaCmkAes256", k._parentSegment) 192 193 if seg != nil { 194 segCtx = seg.Ctx 195 196 defer seg.Close() 197 defer func() { 198 _ = seg.Seg.AddMetadata("KMS-EncryptViaCmkAes256-AES-KMS-KeyName", k.AesKmsKeyName) 199 _ = seg.Seg.AddMetadata("KMS-EncryptViaCmkAes256-PlainText-Length", len(plainText)) 200 _ = seg.Seg.AddMetadata("KMS-EncryptViaCmkAes256-Result-CipherText-Length", len(cipherText)) 201 202 if err != nil { 203 _ = seg.Seg.AddError(err) 204 } 205 }() 206 } 207 208 // validate 209 if k.kmsClient == nil { 210 err = errors.New("EncryptViaCmkAes256 with KMS CMK Failed: " + "KMS Client is Required") 211 return "", err 212 } 213 214 if len(k.AesKmsKeyName) <= 0 { 215 err = errors.New("EncryptViaCmkAes256 with KMS CMK Failed: " + "AES KMS Key Name is Required") 216 return "", err 217 } 218 219 if len(plainText) <= 0 { 220 err = errors.New("EncryptViaCmkAes256 with KMS CMK Failed: " + "PlainText is Required") 221 return "", err 222 } 223 224 // prepare key info 225 keyId := "alias/" + k.AesKmsKeyName 226 227 // encrypt symmetric using kms cmk 228 var encryptedOutput *kms.EncryptOutput 229 var e error 230 231 if segCtx == nil { 232 encryptedOutput, e = k.kmsClient.Encrypt(&kms.EncryptInput{ 233 EncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 234 KeyId: aws.String(keyId), 235 Plaintext: []byte(plainText), 236 }) 237 } else { 238 encryptedOutput, e = k.kmsClient.EncryptWithContext(segCtx, 239 &kms.EncryptInput{ 240 EncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 241 KeyId: aws.String(keyId), 242 Plaintext: []byte(plainText), 243 }) 244 } 245 246 if e != nil { 247 err = errors.New("EncryptViaCmkAes256 with KMS CMK Failed: (Symmetric Encrypt) " + e.Error()) 248 return "", err 249 } 250 251 // return encrypted cipher text blob 252 cipherText = util.ByteToHex(encryptedOutput.CiphertextBlob) 253 return cipherText, nil 254 } 255 256 // ReEncryptViaCmkAes256 will re-encrypt sourceCipherText using the new targetKmsKeyName via kms, (must be targeting aes 256 key) 257 // the re-encrypted cipherText is then returned 258 func (k *KMS) ReEncryptViaCmkAes256(sourceCipherText string, targetKmsKeyName string) (targetCipherText string, err error) { 259 var segCtx context.Context 260 segCtx = nil 261 262 seg := xray.NewSegmentNullable("KMS-ReEncryptViaCmkAes256", k._parentSegment) 263 264 if seg != nil { 265 segCtx = seg.Ctx 266 267 defer seg.Close() 268 defer func() { 269 _ = seg.Seg.AddMetadata("KMS-ReEncryptViaCmkAes256-Source-AES-KMS-KeyName", k.AesKmsKeyName) 270 _ = seg.Seg.AddMetadata("KMS-ReEncryptViaCmkAes256-Target-AES-KMS-KeyName", targetKmsKeyName) 271 _ = seg.Seg.AddMetadata("KMS-ReEncryptViaCmkAes256-SourceCipherText-Length", len(sourceCipherText)) 272 _ = seg.Seg.AddMetadata("KMS-ReEncryptViaCmkAes256-Result-Target-CipherText-Length", len(targetCipherText)) 273 274 if err != nil { 275 _ = seg.Seg.AddError(err) 276 } 277 }() 278 } 279 280 // validate 281 if k.kmsClient == nil { 282 err = errors.New("ReEncryptViaCmkAes256 with KMS CMK Failed: " + "KMS Client is Required") 283 return "", err 284 } 285 286 if len(k.AesKmsKeyName) <= 0 { 287 err = errors.New("ReEncryptViaCmkAes256 with KMS CMK Failed: " + "AES KMS Key Name is Required") 288 return "", err 289 } 290 291 if len(sourceCipherText) <= 0 { 292 err = errors.New("ReEncryptViaCmkAes256 with KMS CMK Failed: " + "Source CipherText is Required") 293 return "", err 294 } 295 296 if len(targetKmsKeyName) <= 0 { 297 err = errors.New("ReEncryptViaCmkAes256 with KMS CMK Failed: " + "Target KMS Key Name is Required") 298 return "", err 299 } 300 301 // prepare key info 302 keyId := "alias/" + k.AesKmsKeyName 303 304 // convert hex to bytes 305 cipherBytes, ce := util.HexToByte(sourceCipherText) 306 307 if ce != nil { 308 err = errors.New("ReEncryptViaCmkAes256 with KMS CMK Failed: (Unmarshal Source CipherText Hex To Byte) " + ce.Error()) 309 return "", err 310 } 311 312 // re-encrypt symmetric kms cmk 313 var reEncryptOutput *kms.ReEncryptOutput 314 var e error 315 316 if segCtx == nil { 317 reEncryptOutput, e = k.kmsClient.ReEncrypt(&kms.ReEncryptInput{ 318 SourceEncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 319 SourceKeyId: aws.String(keyId), 320 DestinationEncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 321 DestinationKeyId: aws.String("alias/" + targetKmsKeyName), 322 CiphertextBlob: cipherBytes, 323 }) 324 } else { 325 reEncryptOutput, e = k.kmsClient.ReEncryptWithContext(segCtx, 326 &kms.ReEncryptInput{ 327 SourceEncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 328 SourceKeyId: aws.String(keyId), 329 DestinationEncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 330 DestinationKeyId: aws.String("alias/" + targetKmsKeyName), 331 CiphertextBlob: cipherBytes, 332 }) 333 } 334 335 if e != nil { 336 err = errors.New("ReEncryptViaCmkAes256 with KMS CMK Failed: (Symmetric ReEncrypt) " + e.Error()) 337 return "", err 338 } 339 340 // return encrypted cipher text blob 341 targetCipherText = util.ByteToHex(reEncryptOutput.CiphertextBlob) 342 return targetCipherText, nil 343 } 344 345 // DecryptViaCmkAes256 will use kms cmk to decrypt cipherText using symmetric aes 256 kms cmk key, and return plainText string, 346 // the cipherText can only be decrypted with the symmetric aes 256 kms cmk key 347 func (k *KMS) DecryptViaCmkAes256(cipherText string) (plainText string, err error) { 348 var segCtx context.Context 349 segCtx = nil 350 351 seg := xray.NewSegmentNullable("KMS-DecryptViaCmkAes256", k._parentSegment) 352 353 if seg != nil { 354 segCtx = seg.Ctx 355 356 defer seg.Close() 357 defer func() { 358 _ = seg.Seg.AddMetadata("KMS-DecryptViaCmkAes256-AES-KMS-KeyName", k.AesKmsKeyName) 359 _ = seg.Seg.AddMetadata("KMS-DecryptViaCmkAes256-CipherText-Length", len(cipherText)) 360 _ = seg.Seg.AddMetadata("KMS-DecryptViaCmkAes256-Result-PlainText-Length", len(plainText)) 361 362 if err != nil { 363 _ = seg.Seg.AddError(err) 364 } 365 }() 366 } 367 368 // validate 369 if k.kmsClient == nil { 370 err = errors.New("DecryptViaCmkAes256 with KMS CMK Failed: " + "KMS Client is Required") 371 return "", err 372 } 373 374 if len(k.AesKmsKeyName) <= 0 { 375 err = errors.New("DecryptViaCmkAes256 with KMS CMK Failed: " + "AES KMS Key Name is Required") 376 return "", err 377 } 378 379 if len(cipherText) <= 0 { 380 err = errors.New("DecryptViaCmkAes256 with KMS CMK Failed: " + "Cipher Text is Required") 381 return "", err 382 } 383 384 // prepare key info 385 keyId := "alias/" + k.AesKmsKeyName 386 cipherBytes, ce := util.HexToByte(cipherText) 387 388 if ce != nil { 389 err = errors.New("DecryptViaCmkAes256 with KMS CMK Failed: (Unmarshal CipherText Hex To Byte) " + ce.Error()) 390 return "", err 391 } 392 393 // decrypt symmetric using kms cmk 394 var decryptedOutput *kms.DecryptOutput 395 var e error 396 397 if segCtx == nil { 398 decryptedOutput, e = k.kmsClient.Decrypt(&kms.DecryptInput{ 399 EncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 400 KeyId: aws.String(keyId), 401 CiphertextBlob: cipherBytes, 402 }) 403 } else { 404 decryptedOutput, e = k.kmsClient.DecryptWithContext(segCtx, 405 &kms.DecryptInput{ 406 EncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 407 KeyId: aws.String(keyId), 408 CiphertextBlob: cipherBytes, 409 }) 410 } 411 412 if e != nil { 413 err = errors.New("DecryptViaCmkAes256 with KMS CMK Failed: (Symmetric Decrypt) " + e.Error()) 414 return "", err 415 } 416 417 // return decrypted cipher text blob 418 plainText = string(decryptedOutput.Plaintext) 419 return plainText, nil 420 } 421 422 // ---------------------------------------------------------------------------------------------------------------- 423 // kms-cmk encrypt/decrypt via rsa 2048 public/private key functions 424 // ---------------------------------------------------------------------------------------------------------------- 425 426 // EncryptViaCmkRsa2048 will use kms cmk to encrypt plainText with asymmetric rsa 2048 kms cmk public key, and return cipherText string, 427 // the cipherText can only be decrypted with the paired asymmetric rsa 2048 kms cmk private key 428 // 429 // *** To Encrypt using Public Key Outside of KMS *** 430 // 1. Copy Public Key from AWS KMS for the given RSA CMK 431 // 2. Using External RSA Public Key Crypto Encrypt Function with the given Public Key to Encrypt 432 func (k *KMS) EncryptViaCmkRsa2048(plainText string) (cipherText string, err error) { 433 var segCtx context.Context 434 segCtx = nil 435 436 seg := xray.NewSegmentNullable("KMS-EncryptViaCmkRsa2048", k._parentSegment) 437 438 if seg != nil { 439 segCtx = seg.Ctx 440 441 defer seg.Close() 442 defer func() { 443 _ = seg.Seg.AddMetadata("KMS-EncryptViaCmkRsa2048-RSA-KMS-KeyName", k.RsaKmsKeyName) 444 _ = seg.Seg.AddMetadata("KMS-EncryptViaCmkRsa2048-PlainText-Length", len(plainText)) 445 _ = seg.Seg.AddMetadata("KMS-EncryptViaCmkRsa2048-Result-CipherText-Length", len(cipherText)) 446 447 if err != nil { 448 _ = seg.Seg.AddError(err) 449 } 450 }() 451 } 452 453 // validate 454 if k.kmsClient == nil { 455 err = errors.New("EncryptViaCmkRsa2048 with KMS CMK Failed: " + "KMS Client is Required") 456 return "", err 457 } 458 459 if len(k.RsaKmsKeyName) <= 0 { 460 err = errors.New("EncryptViaCmkRsa2048 with KMS CMK Failed: " + "RSA KMS Key Name is Required") 461 return "", err 462 } 463 464 if len(plainText) <= 0 { 465 err = errors.New("EncryptViaCmkRsa2048 with KMS CMK Failed: " + "PlainText is Required") 466 return "", err 467 } 468 469 if len(plainText) > 214 { 470 err = errors.New("EncryptViaCmkRsa2048 with KMS CMK Failed: " + "PlainText Cannot Exceed 214 Bytes") 471 return "", err 472 } 473 474 // prepare key info 475 keyId := "alias/" + k.RsaKmsKeyName 476 477 // encrypt asymmetric using kms cmk 478 var encryptedOutput *kms.EncryptOutput 479 var e error 480 481 if segCtx == nil { 482 encryptedOutput, e = k.kmsClient.Encrypt(&kms.EncryptInput{ 483 EncryptionAlgorithm: aws.String("RSAES_OAEP_SHA_256"), 484 KeyId: aws.String(keyId), 485 Plaintext: []byte(plainText), 486 }) 487 } else { 488 encryptedOutput, e = k.kmsClient.EncryptWithContext(segCtx, 489 &kms.EncryptInput{ 490 EncryptionAlgorithm: aws.String("RSAES_OAEP_SHA_256"), 491 KeyId: aws.String(keyId), 492 Plaintext: []byte(plainText), 493 }) 494 } 495 496 if e != nil { 497 err = errors.New("EncryptViaCmkRsa2048 with KMS CMK Failed: (Asymmetric Encrypt) " + e.Error()) 498 return "", err 499 } 500 501 // return encrypted cipher text blob 502 cipherText = util.ByteToHex(encryptedOutput.CiphertextBlob) 503 return cipherText, nil 504 } 505 506 // ReEncryptViaCmkRsa2048 will re-encrypt sourceCipherText using the new targetKmsKeyName via kms, (must be targeting rsa 2048 key) 507 // the re-encrypted cipherText is then returned 508 func (k *KMS) ReEncryptViaCmkRsa2048(sourceCipherText string, targetKmsKeyName string) (targetCipherText string, err error) { 509 var segCtx context.Context 510 segCtx = nil 511 512 seg := xray.NewSegmentNullable("KMS-ReEncryptViaCmkRsa2048", k._parentSegment) 513 514 if seg != nil { 515 segCtx = seg.Ctx 516 517 defer seg.Close() 518 defer func() { 519 _ = seg.Seg.AddMetadata("KMS-ReEncryptViaCmkRsa2048-Source-RSA-KMS-KeyName", k.AesKmsKeyName) 520 _ = seg.Seg.AddMetadata("KMS-ReEncryptViaCmkRsa2048-Target-RSA-KMS-KeyName", targetKmsKeyName) 521 _ = seg.Seg.AddMetadata("KMS-ReEncryptViaCmkRsa2048-Source-CipherText-Length", len(sourceCipherText)) 522 _ = seg.Seg.AddMetadata("KMS-ReEncryptViaCmkRsa2048-Result-Target-CipherText-Length", len(targetCipherText)) 523 524 if err != nil { 525 _ = seg.Seg.AddError(err) 526 } 527 }() 528 } 529 530 // validate 531 if k.kmsClient == nil { 532 err = errors.New("ReEncryptViaCmkRsa2048 with KMS CMK Failed: " + "KMS Client is Required") 533 return "", err 534 } 535 536 if len(k.RsaKmsKeyName) <= 0 { 537 err = errors.New("ReEncryptViaCmkRsa2048 with KMS CMK Failed: " + "RSA KMS Key Name is Required") 538 return "", err 539 } 540 541 if len(sourceCipherText) <= 0 { 542 err = errors.New("ReEncryptViaCmkRsa2048 with KMS CMK Failed: " + "Source CipherText is Required") 543 return "", err 544 } 545 546 if len(targetKmsKeyName) <= 0 { 547 err = errors.New("ReEncryptViaCmkRsa2048 with KMS CMK Failed: " + "Target KMS Key Name is Required") 548 return "", err 549 } 550 551 // prepare key info 552 keyId := "alias/" + k.RsaKmsKeyName 553 554 // convert hex to bytes 555 cipherBytes, ce := util.HexToByte(sourceCipherText) 556 557 if ce != nil { 558 err = errors.New("ReEncryptViaCmkRsa2048 with KMS CMK Failed: (Unmarshal Source CipherText Hex To Byte) " + ce.Error()) 559 return "", err 560 } 561 562 // re-encrypt asymmetric kms cmk 563 var reEncryptOutput *kms.ReEncryptOutput 564 var e error 565 566 if segCtx == nil { 567 reEncryptOutput, e = k.kmsClient.ReEncrypt(&kms.ReEncryptInput{ 568 SourceEncryptionAlgorithm: aws.String("RSAES_OAEP_SHA_256"), 569 SourceKeyId: aws.String(keyId), 570 DestinationEncryptionAlgorithm: aws.String("RSAES_OAEP_SHA_256"), 571 DestinationKeyId: aws.String("alias/" + targetKmsKeyName), 572 CiphertextBlob: cipherBytes, 573 }) 574 } else { 575 reEncryptOutput, e = k.kmsClient.ReEncryptWithContext(segCtx, 576 &kms.ReEncryptInput{ 577 SourceEncryptionAlgorithm: aws.String("RSAES_OAEP_SHA_256"), 578 SourceKeyId: aws.String(keyId), 579 DestinationEncryptionAlgorithm: aws.String("RSAES_OAEP_SHA_256"), 580 DestinationKeyId: aws.String("alias/" + targetKmsKeyName), 581 CiphertextBlob: cipherBytes, 582 }) 583 } 584 585 if e != nil { 586 err = errors.New("ReEncryptViaCmkRsa2048 with KMS CMK Failed: (Asymmetric ReEncrypt) " + e.Error()) 587 return "", err 588 } 589 590 // return encrypted cipher text blob 591 targetCipherText = util.ByteToHex(reEncryptOutput.CiphertextBlob) 592 return targetCipherText, nil 593 } 594 595 // DecryptViaCmkRsa2048 will use kms cmk to decrypt cipherText using asymmetric rsa 2048 kms cmk private key, and return plainText string, 596 // the cipherText can only be decrypted with the asymmetric rsa 2048 kms cmk private key 597 func (k *KMS) DecryptViaCmkRsa2048(cipherText string) (plainText string, err error) { 598 var segCtx context.Context 599 segCtx = nil 600 601 seg := xray.NewSegmentNullable("KMS-DecryptViaCmkRsa2048", k._parentSegment) 602 603 if seg != nil { 604 segCtx = seg.Ctx 605 606 defer seg.Close() 607 defer func() { 608 _ = seg.Seg.AddMetadata("KMS-DecryptViaCmkRsa2048-RSA-KMS-KeyName", k.RsaKmsKeyName) 609 _ = seg.Seg.AddMetadata("KMS-DecryptViaCmkRsa2048-CipherText-Length", len(cipherText)) 610 _ = seg.Seg.AddMetadata("KMS-DecryptViaCmkRsa2048-Result-PlainText-Length", len(plainText)) 611 612 if err != nil { 613 _ = seg.Seg.AddError(err) 614 } 615 }() 616 } 617 618 // validate 619 if k.kmsClient == nil { 620 err = errors.New("DecryptViaCmkRsa2048 with KMS CMK Failed: " + "KMS Client is Required") 621 return "", err 622 } 623 624 if len(k.RsaKmsKeyName) <= 0 { 625 err = errors.New("DecryptViaCmkRsa2048 with KMS CMK Failed: " + "RSA KMS Key Name is Required") 626 return "", err 627 } 628 629 if len(cipherText) <= 0 { 630 err = errors.New("DecryptViaCmkRsa2048 with KMS CMK Failed: " + "Cipher Text is Required") 631 return "", err 632 } 633 634 // prepare key info 635 keyId := "alias/" + k.RsaKmsKeyName 636 cipherBytes, ce := util.HexToByte(cipherText) 637 638 if ce != nil { 639 err = errors.New("DecryptViaCmkRsa2048 with KMS CMK Failed: (Unmarshal CipherText Hex To Byte) " + ce.Error()) 640 return "", err 641 } 642 643 // decrypt symmetric using kms cmk 644 var decryptedOutput *kms.DecryptOutput 645 var e error 646 647 if segCtx == nil { 648 decryptedOutput, e = k.kmsClient.Decrypt(&kms.DecryptInput{ 649 EncryptionAlgorithm: aws.String("RSAES_OAEP_SHA_256"), 650 KeyId: aws.String(keyId), 651 CiphertextBlob: cipherBytes, 652 }) 653 } else { 654 decryptedOutput, e = k.kmsClient.DecryptWithContext(segCtx, 655 &kms.DecryptInput{ 656 EncryptionAlgorithm: aws.String("RSAES_OAEP_SHA_256"), 657 KeyId: aws.String(keyId), 658 CiphertextBlob: cipherBytes, 659 }) 660 } 661 662 if e != nil { 663 err = errors.New("DecryptViaCmkRsa2048 with KMS CMK Failed: (Asymmetric Decrypt) " + e.Error()) 664 return "", err 665 } 666 667 // return decrypted cipher text blob 668 plainText = string(decryptedOutput.Plaintext) 669 return plainText, nil 670 } 671 672 // ---------------------------------------------------------------------------------------------------------------- 673 // kms-cmk sign/verify via rsa 2048 private/public key functions 674 // ---------------------------------------------------------------------------------------------------------------- 675 676 // SignViaCmkRsa2048 will sign dataToSign using KMS CMK RSA Sign/Verify Key (Private Key on KMS will be used to securely sign) 677 func (k *KMS) SignViaCmkRsa2048(dataToSign string) (signature string, err error) { 678 var segCtx context.Context 679 segCtx = nil 680 681 seg := xray.NewSegmentNullable("KMS-SignViaCmkRsa2048", k._parentSegment) 682 683 if seg != nil { 684 segCtx = seg.Ctx 685 686 defer seg.Close() 687 defer func() { 688 _ = seg.Seg.AddMetadata("KMS-SignViaCmkRsa2048-Signature-KMS-KeyName", k.SignatureKmsKeyName) 689 _ = seg.Seg.AddMetadata("KMS-SignViaCmkRsa2048-DataToSign-Length", len(dataToSign)) 690 _ = seg.Seg.AddMetadata("KMS-SignViaCmkRsa2048-Result-Signature", signature) 691 692 if err != nil { 693 _ = seg.Seg.AddError(err) 694 } 695 }() 696 } 697 698 // validate 699 if k.kmsClient == nil { 700 err = errors.New("SignViaCmkRsa2048 with KMS Failed: " + "KMS Client is Required") 701 return "", err 702 } 703 704 if len(k.SignatureKmsKeyName) <= 0 { 705 err = errors.New("SignViaCmkRsa2048 with KMS Failed: " + "Signature KMS Key Name is Required") 706 return "", err 707 } 708 709 if len(dataToSign) <= 0 { 710 err = errors.New("SignViaCmkRsa2048 with KMS Failed: " + "Data To Sign is Required") 711 return "", err 712 } 713 714 // prepare key info 715 keyId := "alias/" + k.SignatureKmsKeyName 716 717 // perform sign action using kms rsa sign/verify cmk 718 var signOutput *kms.SignOutput 719 var e error 720 721 if segCtx == nil { 722 signOutput, e = k.kmsClient.Sign(&kms.SignInput{ 723 KeyId: aws.String(keyId), 724 SigningAlgorithm: aws.String("RSASSA_PKCS1_V1_5_SHA_256"), 725 MessageType: aws.String("RAW"), 726 Message: []byte(dataToSign), 727 }) 728 } else { 729 signOutput, e = k.kmsClient.SignWithContext(segCtx, 730 &kms.SignInput{ 731 KeyId: aws.String(keyId), 732 SigningAlgorithm: aws.String("RSASSA_PKCS1_V1_5_SHA_256"), 733 MessageType: aws.String("RAW"), 734 Message: []byte(dataToSign), 735 }) 736 } 737 738 if e != nil { 739 err = errors.New("SignViaCmkRsa2048 with KMS Failed: (Sign Action) " + e.Error()) 740 return "", err 741 } 742 743 // return signature 744 signature = util.ByteToHex(signOutput.Signature) 745 return signature, nil 746 } 747 748 // VerifyViaCmkRsa2048 will verify dataToVerify with signature using KMS CMK RSA Sign/Verify Key (Public Key on KMS will be used securely to verify) 749 // 750 // signatureToVerify = prior signed signature in hex to verify against the dataToVerify parameter 751 // 752 // *** To Verify using Public Key Outside of KMS *** 753 // 1. Copy Public Key from AWS KMS for the given RSA CMK 754 // 2. Using External RSA Public Key Crypto Verify Function with the given Public Key to Verify 755 func (k *KMS) VerifyViaCmkRsa2048(dataToVerify string, signatureToVerify string) (signatureValid bool, err error) { 756 var segCtx context.Context 757 segCtx = nil 758 759 seg := xray.NewSegmentNullable("KMS-VerifyViaCmkRsa2048", k._parentSegment) 760 761 if seg != nil { 762 segCtx = seg.Ctx 763 764 defer seg.Close() 765 defer func() { 766 _ = seg.Seg.AddMetadata("KMS-VerifyViaCmkRsa2048-Signature-KMS-KeyName", k.SignatureKmsKeyName) 767 _ = seg.Seg.AddMetadata("KMS-VerifyViaCmkRsa2048-DataToVerify-Length", len(dataToVerify)) 768 _ = seg.Seg.AddMetadata("KMS-VerifyViaCmkRsa2048-Signature-To-Verify", signatureToVerify) 769 _ = seg.Seg.AddMetadata("KMS-VerifyViaCmkRsa2048-Result-SignatureValid", signatureValid) 770 771 if err != nil { 772 _ = seg.Seg.AddError(err) 773 } 774 }() 775 } 776 777 // validate 778 if k.kmsClient == nil { 779 err = errors.New("VerifyViaCmkRsa2048 with KMS Failed: " + "KMS Client is Required") 780 return false, err 781 } 782 783 if len(k.SignatureKmsKeyName) <= 0 { 784 err = errors.New("VerifyViaCmkRsa2048 with KMS Failed: " + "Signature KMS Key Name is Required") 785 return false, err 786 } 787 788 if len(dataToVerify) <= 0 { 789 err = errors.New("VerifyViaCmkRsa2048 with KMS Failed: " + "Data To Verify is Required") 790 return false, err 791 } 792 793 if len(signatureToVerify) <= 0 { 794 err = errors.New("VerifyViaCmkRsa2048 with KMS Failed: " + "Signature To Verify is Required") 795 return false, err 796 } 797 798 // prepare key info 799 keyId := "alias/" + k.SignatureKmsKeyName 800 signatureBytes, ce := util.HexToByte(signatureToVerify) 801 802 if ce != nil { 803 err = errors.New("VerifyViaCmkRsa2048 with KMS Failed: (Marshal SignatureToVerify Hex To Byte) " + ce.Error()) 804 return false, err 805 } 806 807 // perform verify action using kms rsa sign/verify cmk 808 var verifyOutput *kms.VerifyOutput 809 var e error 810 811 if segCtx == nil { 812 verifyOutput, e = k.kmsClient.Verify(&kms.VerifyInput{ 813 KeyId: aws.String(keyId), 814 SigningAlgorithm: aws.String("RSASSA_PKCS1_V1_5_SHA_256"), 815 MessageType: aws.String("RAW"), 816 Message: []byte(dataToVerify), 817 Signature: signatureBytes, 818 }) 819 } else { 820 verifyOutput, e = k.kmsClient.VerifyWithContext(segCtx, 821 &kms.VerifyInput{ 822 KeyId: aws.String(keyId), 823 SigningAlgorithm: aws.String("RSASSA_PKCS1_V1_5_SHA_256"), 824 MessageType: aws.String("RAW"), 825 Message: []byte(dataToVerify), 826 Signature: signatureBytes, 827 }) 828 } 829 830 if e != nil { 831 err = errors.New("VerifyViaCmkRsa2048 with KMS Failed: (Verify Action) " + e.Error()) 832 return false, err 833 } 834 835 // return verify result 836 signatureValid = *verifyOutput.SignatureValid 837 return signatureValid, nil 838 } 839 840 // ---------------------------------------------------------------------------------------------------------------- 841 // kms data-key encrypt/decrypt aes 256 functions 842 // ---------------------------------------------------------------------------------------------------------------- 843 844 // GenerateDataKeyAes256 will return an encrypted data key generated by kms cmk, 845 // this data key is encrypted, and able to decrypt only via kms cmk (therefore it is safe to store in memory or at rest) 846 // 847 // cipherKey = encrypted data key in hex (must use KMS CMK to decrypt such key) 848 func (k *KMS) GenerateDataKeyAes256() (cipherKey string, err error) { 849 var segCtx context.Context 850 segCtx = nil 851 852 seg := xray.NewSegmentNullable("KMS-GenerateDataKeyAes256", k._parentSegment) 853 854 if seg != nil { 855 segCtx = seg.Ctx 856 857 defer seg.Close() 858 defer func() { 859 _ = seg.Seg.AddMetadata("KMS-GenerateDataKeyAes256-AES-KMS-KeyName", k.AesKmsKeyName) 860 _ = seg.Seg.AddMetadata("KMS-GenerateDataKeyAes256-Result-CipherKey-Length", len(cipherKey)) 861 862 if err != nil { 863 _ = seg.Seg.AddError(err) 864 } 865 }() 866 } 867 868 // validate 869 if k.kmsClient == nil { 870 err = errors.New("GenerateDataKeyAes256 with KMS Failed: " + "KMS Client is Required") 871 return "", err 872 } 873 874 if len(k.AesKmsKeyName) <= 0 { 875 err = errors.New("GenerateDataKeyAes256 with KMS Failed: " + "AES KMS Key Name is Required") 876 return "", err 877 } 878 879 // prepare key info 880 keyId := "alias/" + k.AesKmsKeyName 881 keySpec := "AES_256" // always use AES 256 882 dataKeyInput := kms.GenerateDataKeyWithoutPlaintextInput{ 883 KeyId: aws.String(keyId), 884 KeySpec: aws.String(keySpec), 885 } 886 887 // generate data key 888 var dataKeyOutput *kms.GenerateDataKeyWithoutPlaintextOutput 889 var e error 890 891 if segCtx == nil { 892 dataKeyOutput, e = k.kmsClient.GenerateDataKeyWithoutPlaintext(&dataKeyInput) 893 } else { 894 dataKeyOutput, e = k.kmsClient.GenerateDataKeyWithoutPlaintextWithContext(segCtx, &dataKeyInput) 895 } 896 897 if e != nil { 898 err = errors.New("GenerateDataKeyAes256 with KMS Failed: (Gen Data Key) " + e.Error()) 899 return "", err 900 } 901 902 // return encrypted key via cipherKey 903 cipherKey = util.ByteToHex(dataKeyOutput.CiphertextBlob) 904 return cipherKey, nil 905 } 906 907 // EncryptWithDataKeyAes256 will encrypt plainText using cipherKey that was generated via GenerateDataKeyAes256() 908 // 909 // cipherKey = encrypted data key in hex (must use KMS CMK to decrypt such key) 910 func (k *KMS) EncryptWithDataKeyAes256(plainText string, cipherKey string) (cipherText string, err error) { 911 var segCtx context.Context 912 segCtx = nil 913 914 seg := xray.NewSegmentNullable("KMS-EncryptWithDataKeyAes256", k._parentSegment) 915 916 if seg != nil { 917 segCtx = seg.Ctx 918 919 defer seg.Close() 920 defer func() { 921 _ = seg.Seg.AddMetadata("KMS-EncryptWithDataKeyAes256-AES-KMS-KeyName", k.AesKmsKeyName) 922 _ = seg.Seg.AddMetadata("KMS-EncryptWithDataKeyAes256-PlainText-Length", len(plainText)) 923 _ = seg.Seg.AddMetadata("KMS-EncryptWithDataKeyAes256-CipherKey-Length", len(cipherKey)) 924 _ = seg.Seg.AddMetadata("KMS-EncryptWithDataKeyAes256-Result-CipherText-Length", len(cipherText)) 925 926 if err != nil { 927 _ = seg.Seg.AddError(err) 928 } 929 }() 930 } 931 932 // validate 933 if k.kmsClient == nil { 934 err = errors.New("EncryptWithDataKeyAes256 with KMS Failed: " + "KMS Client is Required") 935 return "", err 936 } 937 938 if len(k.AesKmsKeyName) <= 0 { 939 err = errors.New("EncryptWithDataKeyAes256 with KMS Failed: " + "AES KMS Key Name is Required") 940 return "", err 941 } 942 943 if len(plainText) <= 0 { 944 err = errors.New("EncryptWithDataKeyAes256 with KMS Failed: " + "PlainText is Required") 945 return "", err 946 } 947 948 if len(cipherKey) <= 0 { 949 err = errors.New("EncryptWithDataKeyAes256 with KMS Failed: " + "CipherKey is Required") 950 return "", err 951 } 952 953 // prepare key info 954 keyId := "alias/" + k.AesKmsKeyName 955 cipherBytes, ce := util.HexToByte(cipherKey) 956 957 if ce != nil { 958 err = errors.New("EncryptWithDataKeyAes256 with KMS Failed: (Unmarshal CipherKey Hex To Byte) " + ce.Error()) 959 return "", err 960 } 961 962 // decrypt cipherKey using kms cmk 963 var dataKeyOutput *kms.DecryptOutput 964 var e error 965 966 if segCtx == nil { 967 dataKeyOutput, e = k.kmsClient.Decrypt(&kms.DecryptInput{ 968 EncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 969 KeyId: aws.String(keyId), 970 CiphertextBlob: cipherBytes, 971 }) 972 } else { 973 dataKeyOutput, e = k.kmsClient.DecryptWithContext(segCtx, 974 &kms.DecryptInput{ 975 EncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 976 KeyId: aws.String(keyId), 977 CiphertextBlob: cipherBytes, 978 }) 979 } 980 981 if e != nil { 982 err = errors.New("EncryptWithDataKeyAes256 with KMS Failed: (Decrypt Data Key) " + e.Error()) 983 return "", err 984 } 985 986 // perform encryption action using decrypted plaintext data key 987 buf, e := crypto.AesGcmEncrypt(plainText, string(dataKeyOutput.Plaintext)) 988 989 // clean up data key from memory immediately 990 dataKeyOutput.SetPlaintext([]byte{}) 991 dataKeyOutput = nil 992 993 // evaluate encrypted result 994 if e != nil { 995 err = errors.New("EncryptWithDataKeyAes256 with KMS Failed: (Encrypt Data) " + e.Error()) 996 return "", err 997 } else { 998 cipherText = buf 999 } 1000 1001 // return encrypted data 1002 return cipherText, nil 1003 } 1004 1005 // DecryptWithDataKeyAes256 will decrypt cipherText using cipherKey that was generated via GenerateDataKeyAes256() 1006 // 1007 // cipherKey = encrypted data key in hex (must use KMS CMK to decrypt such key) 1008 func (k *KMS) DecryptWithDataKeyAes256(cipherText string, cipherKey string) (plainText string, err error) { 1009 var segCtx context.Context 1010 segCtx = nil 1011 1012 seg := xray.NewSegmentNullable("KMS-DecryptWithDataKeyAes256", k._parentSegment) 1013 1014 if seg != nil { 1015 segCtx = seg.Ctx 1016 1017 defer seg.Close() 1018 defer func() { 1019 _ = seg.Seg.AddMetadata("KMS-DecryptWithDataKeyAes256-AES-KMS-KeyName", k.AesKmsKeyName) 1020 _ = seg.Seg.AddMetadata("KMS-DecryptWithDataKeyAes256-CipherText-Length", len(cipherText)) 1021 _ = seg.Seg.AddMetadata("KMS-DecryptWithDataKeyAes256-CipherKey-Length", len(cipherKey)) 1022 _ = seg.Seg.AddMetadata("KMS-DecryptWithDataKeyAes256-Result-PlainText-Length", len(plainText)) 1023 1024 if err != nil { 1025 _ = seg.Seg.AddError(err) 1026 } 1027 }() 1028 } 1029 1030 // validate 1031 if k.kmsClient == nil { 1032 err = errors.New("DecryptWithDataKeyAes256 with KMS Failed: " + "KMS Client is Required") 1033 return "", err 1034 } 1035 1036 if len(k.AesKmsKeyName) <= 0 { 1037 err = errors.New("DecryptWithDataKeyAes256 with KMS Failed: " + "AES KMS Key Name is Required") 1038 return "", err 1039 } 1040 1041 if len(cipherText) <= 0 { 1042 err = errors.New("DecryptWithDataKeyAes256 with KMS Failed: " + "CipherText is Required") 1043 return "", err 1044 } 1045 1046 if len(cipherKey) <= 0 { 1047 err = errors.New("DecryptWithDataKeyAes256 with KMS Failed: " + "CipherKey is Required") 1048 return "", err 1049 } 1050 1051 // prepare key info 1052 keyId := "alias/" + k.AesKmsKeyName 1053 cipherBytes, ce := util.HexToByte(cipherKey) 1054 1055 if ce != nil { 1056 err = errors.New("DecryptWithDataKeyAes256 with KMS Failed: (Unmarshal CipherKey Hex To Byte) " + ce.Error()) 1057 return "", err 1058 } 1059 1060 // decrypt cipherKey using kms cmk 1061 var dataKeyOutput *kms.DecryptOutput 1062 var e error 1063 1064 if segCtx == nil { 1065 dataKeyOutput, e = k.kmsClient.Decrypt(&kms.DecryptInput{ 1066 EncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 1067 KeyId: aws.String(keyId), 1068 CiphertextBlob: cipherBytes, 1069 }) 1070 } else { 1071 dataKeyOutput, e = k.kmsClient.DecryptWithContext(segCtx, 1072 &kms.DecryptInput{ 1073 EncryptionAlgorithm: aws.String("SYMMETRIC_DEFAULT"), 1074 KeyId: aws.String(keyId), 1075 CiphertextBlob: cipherBytes, 1076 }) 1077 } 1078 1079 if e != nil { 1080 err = errors.New("DecryptWithDataKeyAes256 with KMS Failed: (Decrypt Data Key) " + e.Error()) 1081 return "", err 1082 } 1083 1084 // perform decryption action using decrypted plaintext data key 1085 buf, e := crypto.AesGcmDecrypt(cipherText, string(dataKeyOutput.Plaintext)) 1086 1087 // clean up data key from memory immediately 1088 dataKeyOutput.SetPlaintext([]byte{}) 1089 dataKeyOutput = nil 1090 1091 // evaluate decrypted result 1092 if e != nil { 1093 err = errors.New("DecryptWithDataKeyAes256 with KMS Failed: (Decrypt Data) " + e.Error()) 1094 return "", err 1095 } else { 1096 plainText = buf 1097 } 1098 1099 // return decrypted data 1100 return plainText, nil 1101 }