|
| 1 | +/* |
| 2 | + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except |
| 5 | + * in compliance with the License. A copy of the License is located at |
| 6 | + * |
| 7 | + * http://aws.amazon.com/apache2.0 |
| 8 | + * |
| 9 | + * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, |
| 10 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
| 11 | + * specific language governing permissions and limitations under the License. |
| 12 | + */ |
| 13 | + |
| 14 | +package com.amazonaws.encryptionsdk.internal; |
| 15 | + |
| 16 | +import com.amazonaws.encryptionsdk.EncryptedDataKey; |
| 17 | +import com.amazonaws.encryptionsdk.exception.AwsCryptoException; |
| 18 | +import com.amazonaws.encryptionsdk.model.KeyBlob; |
| 19 | +import org.apache.commons.lang3.ArrayUtils; |
| 20 | + |
| 21 | +import javax.crypto.Cipher; |
| 22 | +import javax.crypto.SecretKey; |
| 23 | +import java.nio.charset.Charset; |
| 24 | +import java.nio.charset.StandardCharsets; |
| 25 | +import java.security.GeneralSecurityException; |
| 26 | +import java.security.Key; |
| 27 | +import java.security.PrivateKey; |
| 28 | +import java.security.PublicKey; |
| 29 | +import java.util.Map; |
| 30 | + |
| 31 | +/** |
| 32 | + * Abstract class for encrypting and decrypting JCE data keys. |
| 33 | + */ |
| 34 | +public abstract class JceKeyCipher { |
| 35 | + |
| 36 | + private final Key wrappingKey; |
| 37 | + private final Key unwrappingKey; |
| 38 | + private static final Charset KEY_NAME_ENCODING = StandardCharsets.UTF_8; |
| 39 | + |
| 40 | + /** |
| 41 | + * Returns a new instance of a JceKeyCipher based on the |
| 42 | + * Advanced Encryption Standard in Galois/Counter Mode. |
| 43 | + * |
| 44 | + * @param secretKey The secret key to use for encrypt/decrypt operations. |
| 45 | + * @return The JceKeyCipher. |
| 46 | + */ |
| 47 | + public static JceKeyCipher aesGcm(SecretKey secretKey) { |
| 48 | + return new AesGcmJceKeyCipher(secretKey); |
| 49 | + } |
| 50 | + |
| 51 | + /** |
| 52 | + * Returns a new instance of a JceKeyCipher based on RSA. |
| 53 | + * |
| 54 | + * @param wrappingKey The public key to use for encrypting the key. |
| 55 | + * @param unwrappingKey The private key to use for decrypting the key. |
| 56 | + * @param transformation The transformation. |
| 57 | + * @return The JceKeyCipher. |
| 58 | + */ |
| 59 | + public static JceKeyCipher rsa(PublicKey wrappingKey, PrivateKey unwrappingKey, String transformation) { |
| 60 | + return new RsaJceKeyCipher(wrappingKey, unwrappingKey, transformation); |
| 61 | + } |
| 62 | + |
| 63 | + JceKeyCipher(Key wrappingKey, Key unwrappingKey) { |
| 64 | + this.wrappingKey = wrappingKey; |
| 65 | + this.unwrappingKey = unwrappingKey; |
| 66 | + } |
| 67 | + |
| 68 | + abstract WrappingData buildWrappingCipher(Key key, Map<String, String> encryptionContext) throws GeneralSecurityException; |
| 69 | + |
| 70 | + abstract Cipher buildUnwrappingCipher(Key key, byte[] extraInfo, int offset, |
| 71 | + Map<String, String> encryptionContext) throws GeneralSecurityException; |
| 72 | + |
| 73 | + |
| 74 | + /** |
| 75 | + * Encrypts the given key, incorporating the given keyName and encryptionContext. |
| 76 | + * @param key The key to encrypt. |
| 77 | + * @param keyName A UTF-8 encoded representing a name for the key. |
| 78 | + * @param keyNamespace A UTF-8 encoded value that namespaces the key. |
| 79 | + * @param encryptionContext A key-value mapping of arbitrary, non-secret, UTF-8 encoded strings used |
| 80 | + * during encryption and decryption to provide additional authenticated data (AAD). |
| 81 | + * @return The encrypted data key. |
| 82 | + */ |
| 83 | + public EncryptedDataKey encryptKey(final byte[] key, final String keyName, final String keyNamespace, |
| 84 | + final Map<String, String> encryptionContext) { |
| 85 | + |
| 86 | + final byte[] keyNameBytes = keyName.getBytes(KEY_NAME_ENCODING); |
| 87 | + |
| 88 | + try { |
| 89 | + final JceKeyCipher.WrappingData wData = buildWrappingCipher(wrappingKey, encryptionContext); |
| 90 | + final Cipher cipher = wData.cipher; |
| 91 | + final byte[] encryptedKey = cipher.doFinal(key); |
| 92 | + |
| 93 | + final byte[] provInfo; |
| 94 | + if (wData.extraInfo.length == 0) { |
| 95 | + provInfo = keyNameBytes; |
| 96 | + } else { |
| 97 | + provInfo = new byte[keyNameBytes.length + wData.extraInfo.length]; |
| 98 | + System.arraycopy(keyNameBytes, 0, provInfo, 0, keyNameBytes.length); |
| 99 | + System.arraycopy(wData.extraInfo, 0, provInfo, keyNameBytes.length, wData.extraInfo.length); |
| 100 | + } |
| 101 | + |
| 102 | + return new KeyBlob(keyNamespace, provInfo, encryptedKey); |
| 103 | + } catch (final GeneralSecurityException gsex) { |
| 104 | + throw new AwsCryptoException(gsex); |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + /** |
| 109 | + * Decrypts the given encrypted data key. |
| 110 | + * |
| 111 | + * @param edk The encrypted data key. |
| 112 | + * @param keyName A UTF-8 encoded String representing a name for the key. |
| 113 | + * @param encryptionContext A key-value mapping of arbitrary, non-secret, UTF-8 encoded strings used |
| 114 | + * during encryption and decryption to provide additional authenticated data (AAD). |
| 115 | + * @return The decrypted key. |
| 116 | + * @throws GeneralSecurityException If a problem occurred decrypting the key. |
| 117 | + */ |
| 118 | + public byte[] decryptKey(final EncryptedDataKey edk, final String keyName, |
| 119 | + final Map<String, String> encryptionContext) throws GeneralSecurityException { |
| 120 | + final byte[] keyNameBytes = keyName.getBytes(KEY_NAME_ENCODING); |
| 121 | + |
| 122 | + final Cipher cipher = buildUnwrappingCipher(unwrappingKey, edk.getProviderInformation(), |
| 123 | + keyNameBytes.length, encryptionContext); |
| 124 | + return cipher.doFinal(edk.getEncryptedDataKey()); |
| 125 | + } |
| 126 | + |
| 127 | + static class WrappingData { |
| 128 | + public final Cipher cipher; |
| 129 | + public final byte[] extraInfo; |
| 130 | + |
| 131 | + WrappingData(final Cipher cipher, final byte[] extraInfo) { |
| 132 | + this.cipher = cipher; |
| 133 | + this.extraInfo = extraInfo != null ? extraInfo : ArrayUtils.EMPTY_BYTE_ARRAY; |
| 134 | + } |
| 135 | + } |
| 136 | +} |
0 commit comments