/* * Copyright 2014 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. */ package com.amazonaws.services.dynamodbv2.datamodeling.encryption.providers; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStore.PasswordProtection; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.SecretKeyEntry; import java.security.PrivateKey; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.EncryptionContext; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.DecryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.encryption.materials.EncryptionMaterials; import com.amazonaws.services.dynamodbv2.datamodeling.internal.Utils; import com.amazonaws.util.Base64; public class KeyStoreMaterialsProviderTest { private static final String certPem = "MIIDbTCCAlWgAwIBAgIJANdRvzVsW1CIMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV" + "BAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMQwwCgYDVQQKDANBV1MxGzAZBgNV" + "BAMMEktleVN0b3JlIFRlc3QgQ2VydDAeFw0xMzA1MDgyMzMyMjBaFw0xMzA2MDcy" + "MzMyMjBaME0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMQwwCgYD" + "VQQKDANBV1MxGzAZBgNVBAMMEktleVN0b3JlIFRlc3QgQ2VydDCCASIwDQYJKoZI" + "hvcNAQEBBQADggEPADCCAQoCggEBAJ8+umOX8x/Ma4OZishtYpcA676bwK5KScf3" + "w+YGM37L12KTdnOyieiGtRW8p0fS0YvnhmVTvaky09I33bH+qy9gliuNL2QkyMxp" + "uu1IwkTKKuB67CaKT6osYJLFxV/OwHcaZnTszzDgbAVg/Z+8IZxhPgxMzMa+7nDn" + "hEm9Jd+EONq3PnRagnFeLNbMIePprdJzXHyNNiZKRRGQ/Mo9rr7mqMLSKnFNsmzB" + "OIfeZM8nXeg+cvlmtXl72obwnGGw2ksJfaxTPm4eEhzRoAgkbjPPLHbwiJlc+GwF" + "i8kh0Y3vQTj/gOFE4nzipkm7ux1lsGHNRVpVDWpjNd8Fl9JFELkCAwEAAaNQME4w" + "HQYDVR0OBBYEFM0oGUuFAWlLXZaMXoJgGZxWqfOxMB8GA1UdIwQYMBaAFM0oGUuF" + "AWlLXZaMXoJgGZxWqfOxMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB" + "AAXCsXeC8ZRxovP0Wc6C5qv3d7dtgJJVzHwoIRt2YR3yScBa1XI40GKT80jP3MYH" + "8xMu3mBQtcYrgRKZBy4GpHAyxoFTnPcuzq5Fg7dw7fx4E4OKIbWOahdxwtbVxQfZ" + "UHnGG88Z0bq2twj7dALGyJhUDdiccckJGmJPOFMzjqsvoAu0n/p7eS6y5WZ5ewqw" + "p7VwYOP3N9wVV7Podmkh1os+eCcp9GoFf0MHBMFXi2Ps2azKx8wHRIA5D1MZv/Va" + "4L4/oTBKCjORpFlP7EhMksHBYnjqXLDP6awPMAgQNYB5J9zX6GfJsAgly3t4Rjr5" + "cLuNYBmRuByFGo+SOdrj6D8="; private static final String keyPem = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCfPrpjl/MfzGuD" + "mYrIbWKXAOu+m8CuSknH98PmBjN+y9dik3ZzsonohrUVvKdH0tGL54ZlU72pMtPS" + "N92x/qsvYJYrjS9kJMjMabrtSMJEyirgeuwmik+qLGCSxcVfzsB3GmZ07M8w4GwF" + "YP2fvCGcYT4MTMzGvu5w54RJvSXfhDjatz50WoJxXizWzCHj6a3Sc1x8jTYmSkUR" + "kPzKPa6+5qjC0ipxTbJswTiH3mTPJ13oPnL5ZrV5e9qG8JxhsNpLCX2sUz5uHhIc" + "0aAIJG4zzyx28IiZXPhsBYvJIdGN70E4/4DhROJ84qZJu7sdZbBhzUVaVQ1qYzXf" + "BZfSRRC5AgMBAAECggEBAJMwx9eGe5LIwBfDtCPN93LbxwtHq7FtuQS8XrYexTpN" + "76eN5c7LF+11lauh1HzuwAEw32iJHqVl9aQ5PxFm85O3ExbuSP+ngHJwx/bLacVr" + "mHYlKGH3Net1WU5Qvz7vO7bbEBjDSj9DMJVIMSWUHv0MZO25jw2lLX/ufrgpvPf7" + "KXSgXg/8uV7PbnTbBDNlg02u8eOc+IbH4O8XDKAhD+YQ8AE3pxtopJbb912U/cJs" + "Y0hQ01zbkWYH7wL9BeQmR7+TEjjtr/IInNjnXmaOmSX867/rTSTuozaVrl1Ce7r8" + "EmUDg9ZLZeKfoNYovMy08wnxWVX2J+WnNDjNiSOm+IECgYEA0v3jtGrOnKbd0d9E" + "dbyIuhjgnwp+UsgALIiBeJYjhFS9NcWgs+02q/0ztqOK7g088KBBQOmiA+frLIVb" + "uNCt/3jF6kJvHYkHMZ0eBEstxjVSM2UcxzJ6ceHZ68pmrru74382TewVosxccNy0" + "glsUWNN0t5KQDcetaycRYg50MmcCgYEAwTb8klpNyQE8AWxVQlbOIEV24iarXxex" + "7HynIg9lSeTzquZOXjp0m5omQ04psil2gZ08xjiudG+Dm7QKgYQcxQYUtZPQe15K" + "m+2hQM0jA7tRfM1NAZHoTmUlYhzRNX6GWAqQXOgjOqBocT4ySBXRaSQq9zuZu36s" + "fI17knap798CgYArDa2yOf0xEAfBdJqmn7MSrlLfgSenwrHuZGhu78wNi7EUUOBq" + "9qOqUr+DrDmEO+VMgJbwJPxvaZqeehPuUX6/26gfFjFQSI7UO+hNHf4YLPc6D47g" + "wtcjd9+c8q8jRqGfWWz+V4dOsf7G9PJMi0NKoNN3RgvpE+66J72vUZ26TwKBgEUq" + "DdfGA7pEetp3kT2iHT9oHlpuRUJRFRv2s015/WQqVR+EOeF5Q2zADZpiTIK+XPGg" + "+7Rpbem4UYBXPruGM1ZECv3E4AiJhGO0+Nhdln8reswWIc7CEEqf4nXwouNnW2gA" + "wBTB9Hp0GW8QOKedR80/aTH/X9TCT7R2YRnY6JQ5AoGBAKjgPySgrNDhlJkW7jXR" + "WiGpjGSAFPT9NMTvEHDo7oLTQ8AcYzcGQ7ISMRdVXR6GJOlFVsH4NLwuHGtcMTPK" + "zoHbPHJyOn1SgC5tARD/1vm5CsG2hATRpWRQCTJFg5VRJ4R7Pz+HuxY4SoABcPQd" + "K+MP8GlGqTldC6NaB1s7KuAX"; private static SecretKey encryptionKey; private static SecretKey macKey; private static KeyStore keyStore; private static final String password = "Password"; private static final PasswordProtection passwordProtection = new PasswordProtection(password.toCharArray()); private Map<String, String> description; private EncryptionContext ctx; private static PrivateKey privateKey; private static Certificate certificate; @BeforeClass public static void setUpBeforeClass() throws Exception { KeyGenerator macGen = KeyGenerator.getInstance("HmacSHA256"); macGen.init(256, Utils.getRng()); macKey = macGen.generateKey(); KeyGenerator aesGen = KeyGenerator.getInstance("AES"); aesGen.init(128, Utils.getRng()); encryptionKey = aesGen.generateKey(); keyStore = KeyStore.getInstance("jceks"); keyStore.load(null, password.toCharArray()); KeyFactory kf = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec rsaSpec = new PKCS8EncodedKeySpec(Base64.decode(keyPem)); privateKey = kf.generatePrivate(rsaSpec); CertificateFactory cf = CertificateFactory.getInstance("X509"); certificate = cf.generateCertificate(new ByteArrayInputStream(Base64.decode(certPem))); keyStore.setEntry("enc", new SecretKeyEntry(encryptionKey), passwordProtection); keyStore.setEntry("sig", new SecretKeyEntry(macKey), passwordProtection); keyStore.setEntry("enc-a", new PrivateKeyEntry(privateKey, new Certificate[] {certificate}), passwordProtection); keyStore.setEntry("sig-a", new PrivateKeyEntry(privateKey, new Certificate[] {certificate}), passwordProtection); keyStore.setCertificateEntry("trustedCert", certificate); } @Before public void setUp() { description = new HashMap<String, String>(); description.put("TestKey", "test value"); description = Collections.unmodifiableMap(description); ctx = new EncryptionContext.Builder().build(); } @Test @SuppressWarnings("unchecked") public void simpleSymMac() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, Collections.EMPTY_MAP); EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); assertEquals(macKey, encryptionMaterials.getSigningKey()); assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); assertEquals(macKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); } @Test @SuppressWarnings("unchecked") public void simpleSymSig() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig-a", passwordProtection, passwordProtection, Collections.EMPTY_MAP); EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); assertEquals(privateKey, encryptionMaterials.getSigningKey()); assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); assertEquals(certificate.getPublicKey(), prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); } @Test public void equalSymDescMac() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); assertEquals(macKey, encryptionMaterials.getSigningKey()); assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getDecryptionKey()); assertEquals(macKey, prov.getDecryptionMaterials(ctx(encryptionMaterials)).getVerificationKey()); } @Test public void superSetSymDescMac() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); assertEquals(macKey, encryptionMaterials.getSigningKey()); Map<String, String> tmpDesc = new HashMap<String, String>(encryptionMaterials.getMaterialDescription()); tmpDesc.put("randomValue", "random"); assertEquals(encryptionKey, prov.getDecryptionMaterials(ctx(tmpDesc)).getDecryptionKey()); assertEquals(macKey, prov.getDecryptionMaterials(ctx(tmpDesc)).getVerificationKey()); } @Test @SuppressWarnings("unchecked") public void subSetSymDescMac() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); assertEquals(macKey, encryptionMaterials.getSigningKey()); assertNull(prov.getDecryptionMaterials(ctx(Collections.EMPTY_MAP))); } @Test public void noMatchSymDescMac() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); EncryptionMaterials encryptionMaterials = prov.getEncryptionMaterials(ctx); assertEquals(encryptionKey, encryptionMaterials.getEncryptionKey()); assertEquals(macKey, encryptionMaterials.getSigningKey()); Map<String, String> tmpDesc = new HashMap<String, String>(); tmpDesc.put("randomValue", "random"); assertNull(prov.getDecryptionMaterials(ctx(tmpDesc))); } @Test public void testRefresh() throws Exception { // Mostly make sure we don't throw an exception KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc", "sig", passwordProtection, passwordProtection, description); prov.refresh(); } @Test public void asymSimpleMac() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "sig", passwordProtection, passwordProtection, description); EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); SecretKey encryptionKey = eMat.getEncryptionKey(); assertNotNull(encryptionKey); assertEquals(macKey, eMat.getSigningKey()); DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); assertEquals(encryptionKey, dMat.getDecryptionKey()); assertEquals(macKey, dMat.getVerificationKey()); } @Test public void asymSimpleSig() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "sig-a", passwordProtection, passwordProtection, description); EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); SecretKey encryptionKey = eMat.getEncryptionKey(); assertNotNull(encryptionKey); assertEquals(privateKey, eMat.getSigningKey()); DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); assertEquals(encryptionKey, dMat.getDecryptionKey()); assertEquals(certificate.getPublicKey(), dMat.getVerificationKey()); } @Test public void asymSigVerifyOnly() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "enc-a", "trustedCert", passwordProtection, null, description); EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); SecretKey encryptionKey = eMat.getEncryptionKey(); assertNotNull(encryptionKey); assertNull(eMat.getSigningKey()); DecryptionMaterials dMat = prov.getDecryptionMaterials(ctx(eMat)); assertEquals(encryptionKey, dMat.getDecryptionKey()); assertEquals(certificate.getPublicKey(), dMat.getVerificationKey()); } @Test public void asymSigEncryptOnly() throws Exception { KeyStoreMaterialsProvider prov = new KeyStoreMaterialsProvider(keyStore, "trustedCert", "sig-a", null, passwordProtection, description); EncryptionMaterials eMat = prov.getEncryptionMaterials(ctx); SecretKey encryptionKey = eMat.getEncryptionKey(); assertNotNull(encryptionKey); assertEquals(privateKey, eMat.getSigningKey()); try { prov.getDecryptionMaterials(ctx(eMat)); fail("Expected exception"); } catch (IllegalStateException ex) { assertEquals("No private decryption key provided.", ex.getMessage()); } } private static EncryptionContext ctx(EncryptionMaterials mat) { return ctx(mat.getMaterialDescription()); } private static EncryptionContext ctx(Map<String, String> desc) { return new EncryptionContext.Builder() .withMaterialDescription(desc).build(); } }