All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.qcloud.cos.internal.crypto.ContentCryptoMaterial Maven / Gradle / Ivy

/*
 * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. 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.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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.
 
 * According to cos feature, we modify some class,comment, field name, etc.
 */


package com.qcloud.cos.internal.crypto;

import static com.qcloud.cos.internal.crypto.KMSSecuredCEK.isKMSKeyWrapped;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.security.Key;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qcloud.cos.Headers;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.internal.CosServiceRequest;
import com.qcloud.cos.model.COSObject;
import com.qcloud.cos.model.MaterialsDescriptionProvider;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.utils.Base64;
import com.qcloud.cos.utils.Jackson;
import com.qcloud.cos.utils.StringUtils;
import com.tencentcloudapi.kms.v20190118.models.DecryptRequest;
import com.tencentcloudapi.kms.v20190118.models.DecryptResponse;
import com.tencentcloudapi.kms.v20190118.models.EncryptRequest;
import com.tencentcloudapi.kms.v20190118.models.EncryptResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Cryptographic material used for client-side content encrypt/decryption in COS. This includes the
 * randomly generated one-time secured CEK (content-encryption-key) and the respective key wrapping
 * algorithm, if any, and the cryptographic scheme in use.
 */
final class ContentCryptoMaterial {
    // null if cek is not secured via key wrapping
    private final String keyWrappingAlgorithm;
    private final CipherLite cipherLite;

    private final Map kekMaterialsDescription;
    private final byte[] encryptedCEK;

    private final byte[] securedIV;

    private static final Logger log = LoggerFactory.getLogger(ContentCryptoMaterial.class);

    ContentCryptoMaterial(Map kekMaterialsDescription, byte[] encryptedCEK,
            String keyWrappingAlgorithm, CipherLite cipherLite, byte[] securedIV) {
        this.cipherLite = cipherLite;
        this.keyWrappingAlgorithm = keyWrappingAlgorithm;
        this.encryptedCEK = encryptedCEK.clone();
        this.kekMaterialsDescription = kekMaterialsDescription;
        this.securedIV = securedIV;
    }

    /**
     * Returns the key wrapping algorithm, or null if the content key is not secured via a key
     * wrapping algorithm.
     * 

* Note if the returned value is {@value KMSSecuredCEK#KEY_PROTECTION_MECHANISM}, it means the * key is protected via KMS rather than a direct key-wrapping algorithm. */ String getKeyWrappingAlgorithm() { return keyWrappingAlgorithm; } /** * Returns true if KMS is used for the content key protection; false otherwise. */ private boolean usesKMSKey() { return isKMSKeyWrapped(keyWrappingAlgorithm); } /** * Returns the content crypto scheme. */ ContentCryptoScheme getContentCryptoScheme() { return cipherLite.getContentCryptoScheme(); } /** * Returns the given metadata updated with this content crypto material. */ ObjectMetadata toObjectMetadata(ObjectMetadata metadata, CryptoMode mode) { return toObjectMetadata(metadata); } /** * Returns the metadata in the latest format. */ private ObjectMetadata toObjectMetadata(ObjectMetadata metadata) { // If we generated a symmetric key to encrypt the data, store it in the // object metadata. byte[] encryptedCEK = getEncryptedCEK(); metadata.addUserMetadata(Headers.ENCRYPTION_KEY, Base64.encodeAsString(encryptedCEK)); // Put the cipher initialization vector (IV) into the object metadata metadata.addUserMetadata(Headers.ENCRYPTION_START, Base64.encodeAsString(securedIV)); // Put the materials description into the object metadata as JSON String matdesc = kekMaterialDescAsJson(); if (matdesc != null) { metadata.addUserMetadata(Headers.ENCRYPTION_MATDESC, kekMaterialDescAsJson()); } // The CRYPTO_CEK_ALGORITHM, CRYPTO_TAG_LENGTH and // CRYPTO_KEYWRAP_ALGORITHM were not available in the Encryption Only // (EO) implementation ContentCryptoScheme scheme = getContentCryptoScheme(); metadata.addUserMetadata(Headers.ENCRYPTION_CEK_ALG, scheme.getCipherAlgorithm()); int tagLen = scheme.getTagLengthInBits(); if (tagLen > 0) metadata.addUserMetadata(Headers.CRYPTO_TAG_LENGTH, String.valueOf(tagLen)); String keyWrapAlgo = getKeyWrappingAlgorithm(); if (keyWrapAlgo != null) metadata.addUserMetadata(Headers.ENCRYPTION_WRAP_ALG, keyWrapAlgo); return metadata; } /** * Returns the json string in the latest format. */ String toJsonString() { Map map = new HashMap(); byte[] encryptedCEK = getEncryptedCEK(); map.put(Headers.CRYPTO_KEY_V2, Base64.encodeAsString(encryptedCEK)); byte[] iv = cipherLite.getIV(); map.put(Headers.CRYPTO_IV, Base64.encodeAsString(iv)); map.put(Headers.MATERIALS_DESCRIPTION, kekMaterialDescAsJson()); ContentCryptoScheme scheme = getContentCryptoScheme(); map.put(Headers.CRYPTO_CEK_ALGORITHM, scheme.getCipherAlgorithm()); int tagLen = scheme.getTagLengthInBits(); if (tagLen > 0) map.put(Headers.CRYPTO_TAG_LENGTH, String.valueOf(tagLen)); String keyWrapAlgo = getKeyWrappingAlgorithm(); if (keyWrapAlgo != null) map.put(Headers.CRYPTO_KEYWRAP_ALGORITHM, keyWrapAlgo); return Jackson.toJsonString(map); } /** * Returns the key-encrypting-key material description as a non-null json string; */ private String kekMaterialDescAsJson() { Map kekMaterialDesc = getKEKMaterialsDescription(); if (kekMaterialDesc == null || kekMaterialDesc.isEmpty()) return null; return Jackson.toJsonString(kekMaterialDesc); } /** * Returns the corresponding kek material description from the given json; or null if the input * is null. */ @SuppressWarnings("unchecked") private static Map matdescFromJson(String json) { Map map = Jackson.fromJsonString(json, Map.class); return map == null ? null : Collections.unmodifiableMap(map); } /** * Returns the content encrypting key unwrapped or decrypted. Note if KMS is used for key * protection, a remote call will be made to KMS to decrypt the ciphertext blob. * * @param cekSecured the content encrypting key in wrapped or encrypted form; must not be null * @param keyWrapAlgo key wrapping algorithm; or null if direct encryption instead of key * wrapping is used * @param materials the client key encrypting key material for the content encrypting key * @param securityProvider security provider or null if the default security provider of the JCE * is used */ private static SecretKey cek(byte[] cekSecured, String keyWrapAlgo, EncryptionMaterials materials, Provider securityProvider, ContentCryptoScheme contentCryptoScheme, QCLOUDKMS kms) { if (isKMSKeyWrapped(keyWrapAlgo)) return cekByKMS(cekSecured, keyWrapAlgo, materials, contentCryptoScheme, kms); Key kek; if (materials.getKeyPair() != null) { // Do envelope decryption with private key from key pair kek = materials.getKeyPair().getPrivate(); if (kek == null) { throw new CosClientException("Key encrypting key not available"); } } else { // Do envelope decryption with symmetric key kek = materials.getSymmetricKey(); if (kek == null) { throw new CosClientException("Key encrypting key not available"); } } try { if (keyWrapAlgo != null) { // Key wrapping specified Cipher cipher = securityProvider == null ? Cipher.getInstance(keyWrapAlgo) : Cipher.getInstance(keyWrapAlgo, securityProvider); cipher.init(Cipher.UNWRAP_MODE, kek); return (SecretKey) cipher.unwrap(cekSecured, keyWrapAlgo, Cipher.SECRET_KEY); } // fall back to the Encryption Only (EO) key decrypting method Cipher cipher; if (securityProvider != null) { cipher = Cipher.getInstance(kek.getAlgorithm(), securityProvider); } else { cipher = Cipher.getInstance(kek.getAlgorithm()); } cipher.init(Cipher.DECRYPT_MODE, kek); byte[] decryptedSymmetricKeyBytes = cipher.doFinal(cekSecured); return new SecretKeySpec(decryptedSymmetricKeyBytes, JceEncryptionConstants.SYMMETRIC_KEY_ALGORITHM); } catch (Exception e) { throw new CosClientException("Unable to decrypt symmetric key from object metadata", e); } } /** * Decrypts the secured CEK via KMS; involves network calls. * * @return the CEK (in plaintext). */ private static SecretKey cekByKMS(byte[] cekSecured, String keyWrapAlgo, EncryptionMaterials materials, ContentCryptoScheme contentCryptoScheme, QCLOUDKMS kms) { DecryptRequest decryptReq = new DecryptRequest(); Map materialDesc = materials.getMaterialsDescription(); try { ObjectMapper mapper = new ObjectMapper(); decryptReq.setEncryptionContext(mapper.writeValueAsString(materialDesc)); } catch (JsonProcessingException e) { throw new CosClientException("decrypt request set encryption context got json processing exception", e); } decryptReq.setCiphertextBlob(new String(cekSecured)); DecryptResponse decryptRes = kms.decrypt(decryptReq); byte[] key = Base64.decode(decryptRes.getPlaintext()); return new SecretKeySpec(key, contentCryptoScheme.getKeyGeneratorAlgorithm()); } /** * @return a non-null content crypto material. */ static ContentCryptoMaterial fromObjectMetadata(ObjectMetadata metadata, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, boolean keyWrapExpected, QCLOUDKMS kms) { return fromObjectMetadata0(metadata, kekMaterialAccessor, securityProvider, null, keyWrapExpected, kms); } /** * Factory method to return the content crypto material from the COS object meta data, using the * specified key encrypting key material accessor and an optional security provider. * * @return a non-null content crypto material. */ static ContentCryptoMaterial fromObjectMetadata(ObjectMetadata metadata, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, long[] range, boolean keyWrapExpected, QCLOUDKMS kms) { return fromObjectMetadata0(metadata, kekMaterialAccessor, securityProvider, range, keyWrapExpected, kms); } /** * @return a non-null content crypto material. */ private static ContentCryptoMaterial fromObjectMetadata0(ObjectMetadata metadata, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, long[] range, boolean keyWrapExpected, QCLOUDKMS kms) { // CEK and IV Map userMeta = metadata.getUserMetadata(); // new version has different header if (userMeta.get(Headers.ENCRYPTION_KEY) != null) { return fromObjectMetadata1(userMeta, kekMaterialAccessor, securityProvider, range, keyWrapExpected, kms); } String b64key = userMeta.get(Headers.CRYPTO_KEY_V2); if (b64key == null) { b64key = userMeta.get(Headers.CRYPTO_KEY); if (b64key == null) throw new CosClientException("Content encrypting key not found."); } byte[] cekWrapped = Base64.decode(b64key); //byte[] iv = userMeta.get(Headers.CRYPTO_IV).getBytes(); byte[] iv = Base64.decode(userMeta.get(Headers.CRYPTO_IV)); if (cekWrapped == null || iv == null) { throw new CosClientException("Content encrypting key or IV not found."); } // Material description String matdescStr = userMeta.get(Headers.MATERIALS_DESCRIPTION); final String keyWrapAlgo = userMeta.get(Headers.CRYPTO_KEYWRAP_ALGORITHM); final boolean isKMS = isKMSKeyWrapped(keyWrapAlgo); final Map core = matdescFromJson(matdescStr); final EncryptionMaterials materials; if (isKMS) { materials = new KMSEncryptionMaterials( core.get(KMSEncryptionMaterials.CUSTOMER_MASTER_KEY_ID)); materials.addDescriptions(core); } else { materials = kekMaterialAccessor == null ? null : kekMaterialAccessor.getEncryptionMaterials(core); if (materials == null) { throw new CosClientException("Unable to retrieve the client encryption materials"); } } // CEK algorithm String cekAlgo = userMeta.get(Headers.CRYPTO_CEK_ALGORITHM); boolean isRangeGet = range != null; // The content crypto scheme may vary depending on whether // it is a range get operation ContentCryptoScheme contentCryptoScheme = ContentCryptoScheme.fromCEKAlgo(cekAlgo, isRangeGet); if (isRangeGet) { // Adjust the IV as needed iv = contentCryptoScheme.adjustIV(iv, range[0]); } else { // Validate the tag length supported int tagLenExpected = contentCryptoScheme.getTagLengthInBits(); if (tagLenExpected > 0) { String s = userMeta.get(Headers.CRYPTO_TAG_LENGTH); int tagLenActual = Integer.parseInt(s); if (tagLenExpected != tagLenActual) { throw new CosClientException("Unsupported tag length: " + tagLenActual + ", expected: " + tagLenExpected); } } } // Unwrap or decrypt the CEK if (keyWrapExpected && keyWrapAlgo == null) throw newKeyWrapException(); SecretKey cek = cek(cekWrapped, keyWrapAlgo, materials, securityProvider, contentCryptoScheme, kms); return new ContentCryptoMaterial(core, cekWrapped, keyWrapAlgo, contentCryptoScheme .createCipherLite(cek, iv, Cipher.DECRYPT_MODE, securityProvider), null); } private static ContentCryptoMaterial fromObjectMetadata1(Map userMeta, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, long[] range, boolean keyWrapExpected, QCLOUDKMS kms) { String b64SecuredKey = userMeta.get(Headers.ENCRYPTION_KEY); if (b64SecuredKey == null) { throw new CosClientException("Content encrypting key not found."); } byte[] securedKey = Base64.decode(b64SecuredKey); String b64SecuredIV = userMeta.get(Headers.ENCRYPTION_START); if (b64SecuredIV == null) { throw new CosClientException("Content encrypting key or IV not found."); } byte[] securedIV = Base64.decode(b64SecuredIV); final String keyWrapAlgo = userMeta.get(Headers.ENCRYPTION_WRAP_ALG); final String matdescStr = userMeta.get(Headers.ENCRYPTION_MATDESC); final boolean isKMS = isKMSKeyWrapped(keyWrapAlgo); final Map metadesc = matdescFromJson(matdescStr); final EncryptionMaterials materials; if (isKMS) { if (kekMaterialAccessor instanceof KMSEncryptionMaterialsProvider) { KMSEncryptionMaterialsProvider kmsMaterialsProvider = (KMSEncryptionMaterialsProvider) kekMaterialAccessor; materials = kmsMaterialsProvider.getEncryptionMaterials(); } else { throw new CosClientException("Must use KMSEncryptionMaterials"); } if (metadesc != null) { materials.addDescriptions(metadesc); } } else { materials = kekMaterialAccessor == null ? null : kekMaterialAccessor.getEncryptionMaterials(metadesc); if (materials == null) { throw new CosClientException("Unable to retrieve the client encryption materials"); } } // CEK algorithm String cekAlgo = userMeta.get(Headers.ENCRYPTION_CEK_ALG); boolean isRangeGet = range != null; // The content crypto scheme may vary depending on whether // it is a range get operation ContentCryptoScheme contentCryptoScheme = ContentCryptoScheme.fromCEKAlgo(cekAlgo, isRangeGet); byte[] iv = decryptIV(securedIV, keyWrapAlgo, materials, securityProvider, contentCryptoScheme, kms); if (isRangeGet) { // Adjust the IV as needed iv = contentCryptoScheme.adjustIV(iv, range[0]); } // Unwrap or decrypt the CEK if (keyWrapExpected && keyWrapAlgo == null) throw newKeyWrapException(); SecretKey cek = cek(securedKey, keyWrapAlgo, materials, securityProvider, contentCryptoScheme, kms); return new ContentCryptoMaterial(metadesc, securedKey, keyWrapAlgo, contentCryptoScheme .createCipherLite(cek, iv, Cipher.DECRYPT_MODE, securityProvider), null); } private static CosClientException newKeyWrapException() { return new CosClientException("Missing key-wrap for the content-encrypting-key"); } /** * @return a non-null content crypto material. */ static ContentCryptoMaterial fromInstructionFile(Map instFile, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, boolean keyWrapExpected, QCLOUDKMS kms) { return fromInstructionFile0(instFile, kekMaterialAccessor, securityProvider, null, keyWrapExpected, kms); } /** * Factory method to return the content crypto material from the cos instruction file, using the * specified key encrypting key material accessor and an optional security provider. * * @return a non-null content crypto material. */ static ContentCryptoMaterial fromInstructionFile(Map instFile, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, long[] range, boolean keyWrapExpected, QCLOUDKMS kms) { return fromInstructionFile0(instFile, kekMaterialAccessor, securityProvider, range, keyWrapExpected, kms); } /** * @return a non-null content crypto material. */ private static ContentCryptoMaterial fromInstructionFile0(Map instFile, EncryptionMaterialsAccessor kekMaterialAccessor, Provider securityProvider, long[] range, boolean keyWrapExpected, QCLOUDKMS kms) { // CEK and IV String b64key = instFile.get(Headers.CRYPTO_KEY_V2); if (b64key == null) { b64key = instFile.get(Headers.CRYPTO_KEY); if (b64key == null) throw new CosClientException("Content encrypting key not found."); } byte[] cekWrapped = Base64.decode(b64key); byte[] iv = Base64.decode(instFile.get(Headers.CRYPTO_IV)); if (cekWrapped == null || iv == null) { throw new CosClientException( "Necessary encryption info not found in the instruction file " + instFile); } final String keyWrapAlgo = instFile.get(Headers.CRYPTO_KEYWRAP_ALGORITHM); final boolean isKMS = isKMSKeyWrapped(keyWrapAlgo); // Material description String matdescStr = instFile.get(Headers.MATERIALS_DESCRIPTION); final Map core = matdescFromJson(matdescStr); EncryptionMaterials materials; if (isKMS) { materials = new KMSEncryptionMaterials( core.get(KMSEncryptionMaterials.CUSTOMER_MASTER_KEY_ID)); materials.addDescriptions(core); } else { materials = kekMaterialAccessor == null ? null : kekMaterialAccessor.getEncryptionMaterials(core); if (materials == null) { throw new CosClientException( "Unable to retrieve the encryption materials that originally " + "encrypted object corresponding to instruction file " + instFile); } } // CEK algorithm final String cekAlgo = instFile.get(Headers.CRYPTO_CEK_ALGORITHM); final boolean isRangeGet = range != null; // The content crypto scheme may vary depending on whether // it is a range get operation ContentCryptoScheme contentCryptoScheme = ContentCryptoScheme.fromCEKAlgo(cekAlgo, isRangeGet); if (isRangeGet) { // Adjust the IV as needed iv = contentCryptoScheme.adjustIV(iv, range[0]); } else { // Validate the tag length supported int tagLenExpected = contentCryptoScheme.getTagLengthInBits(); if (tagLenExpected > 0) { String s = instFile.get(Headers.CRYPTO_TAG_LENGTH); int tagLenActual = Integer.parseInt(s); if (tagLenExpected != tagLenActual) { throw new CosClientException("Unsupported tag length: " + tagLenActual + ", expected: " + tagLenExpected); } } } // Unwrap or decrypt the CEK if (keyWrapExpected && keyWrapAlgo == null) throw newKeyWrapException(); SecretKey cek = cek(cekWrapped, keyWrapAlgo, materials, securityProvider, contentCryptoScheme, kms); return new ContentCryptoMaterial(core, cekWrapped, keyWrapAlgo, contentCryptoScheme .createCipherLite(cek, iv, Cipher.DECRYPT_MODE, securityProvider), null); } /** * Parses instruction data retrieved from COS and returns a JSON string representing the * instruction. Made for testing purposes. */ static String parseInstructionFile(COSObject instructionFile) { try { return convertStreamToString(instructionFile.getObjectContent()); } catch (Exception e) { throw new CosClientException("Error parsing JSON instruction file", e); } } /** * Converts the contents of an input stream to a String */ private static String convertStreamToString(InputStream inputStream) throws IOException { if (inputStream == null) { return ""; } else { StringBuilder stringBuilder = new StringBuilder(); String line; try { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StringUtils.UTF8)); while ((line = reader.readLine()) != null) { stringBuilder.append(line); } } finally { inputStream.close(); } return stringBuilder.toString(); } } /** * Return the cipher lite used for content encryption/decryption purposes. */ CipherLite getCipherLite() { return cipherLite; } /** * Returns the description of the kek materials that were used to encrypt the cek. */ Map getKEKMaterialsDescription() { return this.kekMaterialsDescription; } /** * Returns an array of bytes representing the encrypted envelope symmetric key. * * @return an array of bytes representing the encrypted envelope symmetric key. */ byte[] getEncryptedCEK() { return this.encryptedCEK.clone(); } /** * Recreates a new content crypto material from the current material given a new KEK * material-descriptions. The purpose is to re-encrypt the CEK under a different KEK. * * Note network calls are involved if the CEK has been or is to be protected by KMS. * * @param newKEKMatDesc material descriptions for the new KEK; never null * @param accessor used to retrieve the KEK given the corresponding material description * @param targetScheme the target crypto scheme to be used for key wrapping, etc. * @param p optional security provider; null means to use the default. * @throws SecurityException if the old and new material description are the same; or if the old * and new KEK are the same */ ContentCryptoMaterial recreate(Map newKEKMatDesc, EncryptionMaterialsAccessor accessor, COSCryptoScheme targetScheme, Provider p, QCLOUDKMS kms, CosServiceRequest req) { if (!usesKMSKey() && newKEKMatDesc.equals(kekMaterialsDescription)) { throw new SecurityException( "Material description of the new KEK must differ from the current one"); } final EncryptionMaterials origKEK; if (usesKMSKey()) { origKEK = new KMSEncryptionMaterials( kekMaterialsDescription.get(KMSEncryptionMaterials.CUSTOMER_MASTER_KEY_ID)); } else { origKEK = accessor.getEncryptionMaterials(kekMaterialsDescription); if (origKEK == null) { throw new CosClientException("Unable to retrieve the origin encryption materials"); } } EncryptionMaterials newKEK = accessor.getEncryptionMaterials(newKEKMatDesc); if (newKEK == null) { throw new CosClientException("No material available with the description " + newKEKMatDesc + " from the encryption material provider"); } SecretKey cek = cek(encryptedCEK, keyWrappingAlgorithm, origKEK, p, getContentCryptoScheme(), kms); ContentCryptoMaterial output = create(cek, cipherLite.getIV(), newKEK, getContentCryptoScheme(), // must use same // content crypto // scheme targetScheme, p, kms, req); if (Arrays.equals(output.encryptedCEK, encryptedCEK)) { throw new SecurityException("The new KEK must differ from the original"); } return output; } /** * Recreates a new content crypto material from the current material given a new KEK encryption * materials. The purpose is to re-encrypt the CEK under the new KEK. * * Note network calls are involved if the CEK has been or is to be protected by KMS. * * @param newKEK encryption materials for the new KEK; must not be null * @param accessor used to retrieve the original KEK given the corresponding material * description * @param targetScheme the target crypto scheme to use for recreating the content crypto * material * @param p optional security provider; null means to use the default. * @throws SecurityException if the old and new material description are the same; or if the old * and new KEK are the same */ ContentCryptoMaterial recreate(EncryptionMaterials newKEK, EncryptionMaterialsAccessor accessor, COSCryptoScheme targetScheme, Provider p, QCLOUDKMS kms, CosServiceRequest req) { if (!usesKMSKey() && newKEK.getMaterialsDescription().equals(kekMaterialsDescription)) { throw new SecurityException( "Material description of the new KEK must differ from the current one"); } final EncryptionMaterials origKEK; if (usesKMSKey()) { origKEK = new KMSEncryptionMaterials( kekMaterialsDescription.get(KMSEncryptionMaterials.CUSTOMER_MASTER_KEY_ID)); } else { origKEK = accessor.getEncryptionMaterials(kekMaterialsDescription); if (origKEK == null) { throw new CosClientException("Unable to retrieve the origin encryption materials"); } } SecretKey cek = cek(encryptedCEK, keyWrappingAlgorithm, origKEK, p, getContentCryptoScheme(), kms); ContentCryptoMaterial output = create(cek, cipherLite.getIV(), newKEK, getContentCryptoScheme(), // must use same // content crypto // scheme targetScheme, // target scheme used to recreate the content crypto material p, kms, req); if (Arrays.equals(output.encryptedCEK, encryptedCEK)) { throw new SecurityException("The new KEK must differ from the original"); } return output; } /** * Returns a new instance of ContentCryptoMaterial for the input parameters using * the specified content crypto scheme, and the key wrapping and secure randomness specified of * the specified cos crypto scheme. * * Note network calls are involved if the CEK is to be protected by KMS. * * @param cek content encrypting key; must not be null. * @param iv initialization vector; must not be null. * @param contentCryptoScheme content crypto scheme to be used * @param targetScheme the target cos crypto scheme to be used for recreating the content crypto * material by providing the key wrapping scheme and mechanism for secure randomness * @param provider optional security provider */ static ContentCryptoMaterial create(SecretKey cek, byte[] iv, EncryptionMaterials kekMaterials, ContentCryptoScheme contentCryptoScheme, COSCryptoScheme targetScheme, Provider provider, QCLOUDKMS kms, CosServiceRequest req) { return doCreate(cek, iv, kekMaterials, contentCryptoScheme, targetScheme, provider, kms, req); } /** * Returns a new instance of ContentCryptoMaterial for the input parameters using * the specified cos crypto scheme. Note network calls are involved if the CEK is to be * protected by KMS. * * @param cek content encrypting key * @param iv initialization vector * @param kekMaterials kek encryption material used to secure the CEK; can be KMS enabled. * @param scheme cos crypto scheme to be used for the content crypto material by providing the * content crypto scheme, key wrapping scheme and mechanism for secure randomness * @param provider optional security provider * @param kms reference to the KMS client * @param req originating service request */ static ContentCryptoMaterial create(SecretKey cek, byte[] iv, EncryptionMaterials kekMaterials, COSCryptoScheme scheme, Provider provider, QCLOUDKMS kms, CosServiceRequest req) { return doCreate(cek, iv, kekMaterials, scheme.getContentCryptoScheme(), scheme, provider, kms, req); } /** * Returns a new instance of ContentCryptoMaterial for the given input parameters * by using the specified content crypto scheme, and COS crypto scheme. * * Note network calls are involved if the CEK is to be protected by KMS. * * @param cek content encrypting key * @param iv initialization vector * @param kekMaterials kek encryption material used to secure the CEK; can be KMS enabled. * @param contentCryptoScheme content crypto scheme to be used, which can differ from the one of * targetCOSCryptoScheme * @param targetCOSCryptoScheme the target cos crypto scheme to be used for providing the key * wrapping scheme and mechanism for secure randomness * @param provider security provider * @param kms reference to the KMS client * @param req the originating COS service request */ private static ContentCryptoMaterial doCreate(SecretKey cek, byte[] iv, EncryptionMaterials kekMaterials, ContentCryptoScheme contentCryptoScheme, COSCryptoScheme targetCOSCryptoScheme, Provider provider, QCLOUDKMS kms, CosServiceRequest req) { // Secure the envelope symmetric key either by encryption, key wrapping // or KMS. SecuredCEK cekSecured = secureCEK(cek, kekMaterials, targetCOSCryptoScheme.getKeyWrapScheme(), targetCOSCryptoScheme.getSecureRandom(), provider, kms, req); byte[] securedIV = encryptIV(iv, kekMaterials, targetCOSCryptoScheme.getKeyWrapScheme(), targetCOSCryptoScheme.getSecureRandom(), provider, kms, req); return wrap(cek, iv, contentCryptoScheme, provider, cekSecured, securedIV); } /** * Returns a new instance of ContentCryptoMaterial by wrapping the input * parameters, including the already secured CEK. No network calls are involved. */ public static ContentCryptoMaterial wrap(SecretKey cek, byte[] iv, ContentCryptoScheme contentCryptoScheme, Provider provider, SecuredCEK cekSecured, byte[] securedIV) { return new ContentCryptoMaterial(cekSecured.getMaterialDescription(), cekSecured.getEncrypted(), cekSecured.getKeyWrapAlgorithm(), contentCryptoScheme.createCipherLite(cek, iv, Cipher.ENCRYPT_MODE, provider), securedIV); } /** * Secure the given CEK. Note network calls are involved if the CEK is to be protected by KMS. * * @param cek content encrypting key to be secured * @param materials used to provide the key-encryption-key (KEK); or if it is KMS-enabled, the * customer master key id and material description. * @param contentCryptoScheme the content crypto scheme * @param p optional security provider; can be null if the default is used. * @return a secured CEK in the form of ciphertext or ciphertext blob. */ private static SecuredCEK secureCEK(SecretKey cek, EncryptionMaterials materials, COSKeyWrapScheme kwScheme, SecureRandom srand, Provider p, QCLOUDKMS kms, CosServiceRequest req) { final Map matdesc; if (materials.isKMSEnabled()) { matdesc = mergeMaterialDescriptions(materials, req); EncryptRequest encryptRequest = new EncryptRequest(); try { ObjectMapper mapper = new ObjectMapper(); encryptRequest.setEncryptionContext(mapper.writeValueAsString(matdesc)); } catch (JsonProcessingException e) { throw new CosClientException("encrypt request set encryption context got json processing exception", e); } encryptRequest.setKeyId(materials.getCustomerMasterKeyId()); encryptRequest.setPlaintext(cek.getEncoded().toString()); EncryptResponse encryptResponse = kms.encrypt(encryptRequest); byte[] keyBlob = encryptResponse.getCiphertextBlob().getBytes(); return new KMSSecuredCEK(keyBlob, matdesc); } else { matdesc = materials.getMaterialsDescription(); } Key kek; if (materials.getKeyPair() != null) { // Do envelope encryption with public key from key pair kek = materials.getKeyPair().getPublic(); } else { // Do envelope encryption with symmetric key kek = materials.getSymmetricKey(); } String keyWrapAlgo = kwScheme.getKeyWrapAlgorithm(kek); try { Cipher cipher = p == null ? Cipher.getInstance(keyWrapAlgo) : Cipher.getInstance(keyWrapAlgo, p); cipher.init(Cipher.WRAP_MODE, kek, srand); return new SecuredCEK(cipher.wrap(cek), keyWrapAlgo, matdesc); } catch (Exception e) { throw new CosClientException("Unable to encrypt symmetric key", e); } } public static byte[] encryptIV(byte[] iv, EncryptionMaterials materials, COSKeyWrapScheme kwScheme, SecureRandom srand, Provider p, QCLOUDKMS kms, CosServiceRequest req) { if (materials.isKMSEnabled()) { Map matdesc = mergeMaterialDescriptions(materials, req); EncryptRequest encryptRequest = new EncryptRequest(); try { ObjectMapper mapper = new ObjectMapper(); encryptRequest.setEncryptionContext(mapper.writeValueAsString(matdesc)); } catch (JsonProcessingException e) { throw new CosClientException("encrypt request set encryption context got json processing exception", e); } encryptRequest.setKeyId(materials.getCustomerMasterKeyId()); encryptRequest.setPlaintext(Base64.encodeAsString(iv)); EncryptResponse encryptResponse = kms.encrypt(encryptRequest); String cipherIV = encryptResponse.getCiphertextBlob(); return cipherIV.getBytes(Charset.forName("UTF-8")); } Key kek; if (materials.getKeyPair() != null) { // Do envelope encryption with public key from key pair kek = materials.getKeyPair().getPublic(); } else { // Do envelope encryption with symmetric key kek = materials.getSymmetricKey(); } String keyWrapAlgo = kwScheme.getKeyWrapAlgorithm(kek); try { Cipher cipher = p == null ? Cipher.getInstance(keyWrapAlgo) : Cipher.getInstance(keyWrapAlgo, p); cipher.init(Cipher.ENCRYPT_MODE, kek, srand); return cipher.doFinal(iv); } catch (Exception e) { throw new CosClientException("Unable to encrypt IV", e); } } public static byte[] decryptIV(byte[] iv, String keyWrapAlgo, EncryptionMaterials materials, Provider securityProvider, ContentCryptoScheme contentCryptoScheme, QCLOUDKMS kms) { if (materials.isKMSEnabled()) { DecryptRequest decryptReq = new DecryptRequest(); Map materialDesc = materials.getMaterialsDescription(); try { ObjectMapper mapper = new ObjectMapper(); decryptReq.setEncryptionContext(mapper.writeValueAsString(materialDesc)); } catch (JsonProcessingException e) { throw new CosClientException("decrypt request set encryption context got json processing exception", e); } decryptReq.setCiphertextBlob(new String(iv, Charset.forName("UTF-8"))); DecryptResponse decryptRes = kms.decrypt(decryptReq); return Base64.decode(decryptRes.getPlaintext()); } Key kek; if (materials.getKeyPair() != null) { // Do envelope decryption with private key from key pair kek = materials.getKeyPair().getPrivate(); if (kek == null) { throw new CosClientException("Key encrypting key not available"); } } else { // Do envelope decryption with symmetric key kek = materials.getSymmetricKey(); if (kek == null) { throw new CosClientException("Key encrypting key not available"); } } try { if (keyWrapAlgo != null) { // Key wrapping specified Cipher cipher = securityProvider == null ? Cipher.getInstance(keyWrapAlgo) : Cipher.getInstance(keyWrapAlgo, securityProvider); cipher.init(Cipher.DECRYPT_MODE, kek); return cipher.doFinal(iv); } // fall back to the Encryption Only (EO) key decrypting method Cipher cipher; if (securityProvider != null) { cipher = Cipher.getInstance(kek.getAlgorithm(), securityProvider); } else { cipher = Cipher.getInstance(kek.getAlgorithm()); } cipher.init(Cipher.DECRYPT_MODE, kek); return cipher.doFinal(iv); } catch (Exception e) { throw new CosClientException("Unable to decrypt symmetric key from object metadata", e); } } static Map mergeMaterialDescriptions(EncryptionMaterials materials, CosServiceRequest req) { Map matdesc = materials.getMaterialsDescription(); if (req instanceof MaterialsDescriptionProvider) { MaterialsDescriptionProvider mdp = (MaterialsDescriptionProvider) req; Map matdesc_req = mdp.getMaterialsDescription(); if (matdesc_req != null) { matdesc = new TreeMap(matdesc); matdesc.putAll(matdesc_req); // request takes precedence } } return matdesc; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy