Alon Bar-Lev has uploaded a new change for review.

Change subject: uutil: crypto: EnvelopeEncryptDecrypt initial implementation
......................................................................

uutil: crypto: EnvelopeEncryptDecrypt initial implementation

Change-Id: I3b7444ba279bbfaadd162dee4a552b434249aaac
Signed-off-by: Alon Bar-Lev <alo...@redhat.com>
---
A 
backend/manager/modules/uutils/src/main/java/org/ovirt/engine/core/uutils/crypto/EnvelopeEncryptDecrypt.java
A 
backend/manager/modules/uutils/src/test/java/org/ovirt/engine/core/uutils/crypto/EnvelopeEncryptDecryptTest.java
A backend/manager/modules/uutils/src/test/resources/key2.p12
3 files changed, 226 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/30/42330/1

diff --git 
a/backend/manager/modules/uutils/src/main/java/org/ovirt/engine/core/uutils/crypto/EnvelopeEncryptDecrypt.java
 
b/backend/manager/modules/uutils/src/main/java/org/ovirt/engine/core/uutils/crypto/EnvelopeEncryptDecrypt.java
new file mode 100644
index 0000000..a0df435
--- /dev/null
+++ 
b/backend/manager/modules/uutils/src/main/java/org/ovirt/engine/core/uutils/crypto/EnvelopeEncryptDecrypt.java
@@ -0,0 +1,141 @@
+package org.ovirt.engine.core.uutils.crypto;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyException;
+import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.apache.commons.codec.binary.Base64;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.codehaus.jackson.map.type.TypeFactory;
+
+public class EnvelopeEncryptDecrypt {
+
+    private static final String PUBKEY_DIGEST_ALGO = "SHA-1";
+    private static final String PKEY_MODE_PADDING = "ECB/PKCS1Padding";
+
+    private static final String CONTENT_KEY = "content";
+    private static final String RANDOM_KEY = "random";
+
+    private static final String CIPHER_ALGO_KEY = "cipherAlgo";
+    private static final String ENCRYPTED_CONTENT_KEY = "encryptedContent";
+    private static final String IV_KEY = "iv";
+    private static final String WRAPPED_KEY_KEY = "wrappedKey";
+    private static final String WRAP_ALGO_KEY = "wrapAlgo";
+    private static final String WRAP_KEY_DIGEST_ALGO_KEY = "wrapKeyDigestAlgo";
+    private static final String WRAP_KEY_DIGEST_KEY = "wrapKeyDigest";
+
+    private static final Random random;
+    static {
+        try {
+            random= SecureRandom.getInstance("SHA1PRNG");
+        } catch (NoSuchAlgorithmException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Encrypt a content using envelope.
+     * @param algo Cipher algorithm to use.
+     * @param bits Size of cipher key.
+     * @param cert Certificate to encrypt to (wrap key using public key).
+     * @param blockSize Adjust the size of content to blockSize.
+     * @param content Content to encrypt.
+     * @return Base64 value of envelope.
+     *
+     * The blockSize is used in order to hide actual content size.
+     */
+    public static String Encrypt(
+        String algo,
+        int bits,
+        Certificate cert,
+        int blockSize,
+        byte[] content
+    ) throws GeneralSecurityException, IOException {
+        final String wrapAlgo = cert.getPublicKey().getAlgorithm() + "/" + 
PKEY_MODE_PADDING;
+        final Base64 base64 = new Base64(0);
+        final Map<String, String> map = new HashMap<String, String>();
+        final Map<String, String> env = new HashMap<String, String>();
+
+        env.put(CONTENT_KEY, base64.encodeToString(content));
+        byte[] r = new byte[((content.length / blockSize) + 1) * blockSize - 
content.length];
+        random.nextBytes(r);
+        env.put(RANDOM_KEY, base64.encodeToString(r));
+
+        KeyGenerator gen = KeyGenerator.getInstance(algo.split("/", 2)[0]);
+        gen.init(bits);
+        Key key = gen.generateKey();
+        Cipher cipher = Cipher.getInstance(algo);
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+        Cipher wrap = Cipher.getInstance(wrapAlgo);
+        wrap.init(Cipher.WRAP_MODE, cert);
+
+        map.put(WRAP_ALGO_KEY, wrapAlgo);
+        map.put(CIPHER_ALGO_KEY, algo);
+        map.put(ENCRYPTED_CONTENT_KEY, 
base64.encodeToString(cipher.doFinal(new 
ObjectMapper().writeValueAsString(env).getBytes(Charset.forName("UTF-8")))));
+        map.put(IV_KEY, base64.encodeToString(cipher.getIV()));
+        map.put(WRAPPED_KEY_KEY, base64.encodeToString(wrap.wrap(key)));
+        map.put(WRAP_KEY_DIGEST_ALGO_KEY, PUBKEY_DIGEST_ALGO);
+        map.put(WRAP_KEY_DIGEST_KEY, 
base64.encodeToString(MessageDigest.getInstance(PUBKEY_DIGEST_ALGO).digest(cert.getPublicKey().getEncoded())));
+        return base64.encodeToString(new 
ObjectMapper().writeValueAsString(map).getBytes(Charset.forName("UTF-8")));
+    }
+
+    /**
+     * Decrypt a content using envelope.
+     * @param pkeyEntry A private key entry (key and certificate) to use for 
decryption.
+     * @param blob value of envelope.
+     * @return content.
+     */
+    public static byte[] Decrypt(
+        KeyStore.PrivateKeyEntry pkeyEntry,
+        String blob
+    ) throws GeneralSecurityException, IOException {
+        final Map<String, String> map = new ObjectMapper().readValue(
+            Base64.decodeBase64(blob),
+            TypeFactory.defaultInstance().constructMapType(HashMap.class, 
String.class, String.class)
+        );
+
+        if (pkeyEntry.getCertificate() != null) {
+            if (
+                !MessageDigest.isEqual(
+                    Base64.decodeBase64(map.get(WRAP_KEY_DIGEST_KEY)),
+                    
MessageDigest.getInstance(map.get(WRAP_KEY_DIGEST_ALGO_KEY)).digest(pkeyEntry.getCertificate().getPublicKey().getEncoded())
+                )
+            ) {
+                throw new KeyException("Private key entry mismatch");
+            }
+        }
+
+        Cipher wrap = Cipher.getInstance(map.get(WRAP_ALGO_KEY));
+        wrap.init(Cipher.UNWRAP_MODE, pkeyEntry.getPrivateKey());
+        Cipher cipher = Cipher.getInstance(map.get(CIPHER_ALGO_KEY));
+        cipher.init(
+            Cipher.DECRYPT_MODE,
+            wrap.unwrap(
+                Base64.decodeBase64(map.get(WRAPPED_KEY_KEY)),
+                cipher.getAlgorithm().split("/", 2)[0],
+                Cipher.SECRET_KEY
+            ),
+            new IvParameterSpec(Base64.decodeBase64(map.get(IV_KEY)))
+        );
+
+        final Map<String, String> env = new ObjectMapper().readValue(
+            
cipher.doFinal(Base64.decodeBase64(map.get(ENCRYPTED_CONTENT_KEY))),
+            TypeFactory.defaultInstance().constructMapType(HashMap.class, 
String.class, String.class)
+        );
+        return Base64.decodeBase64(env.get(CONTENT_KEY));
+    }
+
+}
diff --git 
a/backend/manager/modules/uutils/src/test/java/org/ovirt/engine/core/uutils/crypto/EnvelopeEncryptDecryptTest.java
 
b/backend/manager/modules/uutils/src/test/java/org/ovirt/engine/core/uutils/crypto/EnvelopeEncryptDecryptTest.java
new file mode 100644
index 0000000..e65a5d9
--- /dev/null
+++ 
b/backend/manager/modules/uutils/src/test/java/org/ovirt/engine/core/uutils/crypto/EnvelopeEncryptDecryptTest.java
@@ -0,0 +1,85 @@
+package org.ovirt.engine.core.uutils.crypto;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.security.KeyException;
+import java.security.KeyStore;
+
+import org.junit.Test;
+
+public class EnvelopeEncryptDecryptTest {
+
+    private static KeyStore getKeyStore(String storeType, String store, String 
password) throws Exception {
+        KeyStore ks = KeyStore.getInstance(storeType);
+        try (InputStream is = ClassLoader.getSystemResourceAsStream(store)) {
+            ks.load(is, password.toCharArray());
+        }
+        return ks;
+    }
+
+    private static KeyStore.PrivateKeyEntry getPrivateKeyEntry(KeyStore ks, 
String alias, String password) throws Exception {
+        return (KeyStore.PrivateKeyEntry)ks.getEntry(alias, new 
KeyStore.PasswordProtection(password.toCharArray()));
+    }
+
+    @Test
+    public void test1() throws Exception {
+        KeyStore.PrivateKeyEntry entry = 
getPrivateKeyEntry(getKeyStore("PKCS12", "key.p12", "NoSoup4U"), "1", 
"NoSoup4U");
+
+        byte[] test = "testing 1 2 3 4".getBytes(Charset.forName("UTF-8"));
+
+        assertArrayEquals(
+            test,
+            EnvelopeEncryptDecrypt.Decrypt(
+                entry,
+                EnvelopeEncryptDecrypt.Encrypt(
+                    "AES/OFB/PKCS5Padding",
+                    256,
+                    entry.getCertificate(),
+                    100,
+                    test
+                )
+            )
+        );
+    }
+
+    @Test
+    public void test2() throws Exception {
+        KeyStore.PrivateKeyEntry entry = 
getPrivateKeyEntry(getKeyStore("PKCS12", "key.p12", "NoSoup4U"), "1", 
"NoSoup4U");
+
+        byte[] test = "testing 1 2 3 4".getBytes(Charset.forName("UTF-8"));
+        String blob = 
"eyJjaXBoZXJBbGdvIjoiQUVTL09GQi9QS0NTNVBhZGRpbmciLCJpdiI6Im01aDgxYUxmdkNycXZtS3R3L290SFE9PSIsIndyYXBLZXlEaWdlc3QiOiJXNkdCbHFrRUlwbUx2aE94VFlybHpwV1JrQjg9Iiwid3JhcHBlZEtleSI6ImE1TXhCWFFBY25kZi95U2J4YWRMdWwySWxHNFpkTWgzOHlUdE9HLzI2MDBHR204ZHRudklpSSsxakE3c0k4cDI1VVAwbWFBL29KdG9xTUVHd0Fhc3VNdkxhVEFPbHJId1RwM2ROaWlWSUFlNmREY21WVTYrd0JLNEljMjBNdjluKzQrRVExUTZ6OXBWSldXQjEzNXk4TEM3NlUzK2czVnBkVm5SWFNGTllpUjR3L2xGMFllVVFxR1J1RnlVRzJLUEV1RzFZTjl5amZjeDB1RWxKb1VYRGNoUDlOY3VqaE9pUHcvTUJ0YzRzSjNjTVZobFh1ZXFtTG85ZE1KWngzS2wzWkNFQTlrMS9Md2wvRzZwNXlsc2gxeWwxSmhwL2M3RmRDRjRJcWt6cFZVejZsZXd4cktnZFhlSG1oOE1CWk5BdkJIK2plYmZrRitlcHZ0c0t0SWJBZz09IiwiZW5jcnlwdGVkQ29udGVudCI6IlhJajluaTFQL1VVZUptOUhYc0orYWIvSkxGS1p1ZnRlM0c1TkFIME0xbkFUR3VtMEwweEhNKzhSYUY0aHZ6dFJ1S3FZb0cxYytmR3dVTG9rRW1pejczZzRmQzZhUTBWL3BManNray81eW43OEZQYVpOQU9oM1ByNmFOemNGbkR0NVBnOVJiK3h5TzIzM0ZlTGw4bWMvOFVRSUEzUURQT1pvbmlxWVdEOXM5VEhYbG56Z1dUSE5GY2g3U0VSZHZqOUtRdHcvUkxPMS9sTVMybmtONUJUZHozYkx2ZXY5YjNWMWdrY!
 
1ZKRDRpams9Iiwid3JhcEtleURpZ2VzdEFsZ28iOiJTSEEtMSIsIndyYXBBbGdvIjoiUlNBL0VDQi9QS0NTMVBhZGRpbmcifQ==";
+
+        assertArrayEquals(
+            test,
+            EnvelopeEncryptDecrypt.Decrypt(
+                entry,
+                blob
+            )
+        );
+    }
+
+    @Test(expected=KeyException.class)
+    public void testInvalidKey() throws Exception {
+        KeyStore.PrivateKeyEntry entry1 = 
getPrivateKeyEntry(getKeyStore("PKCS12", "key.p12", "NoSoup4U"), "1", 
"NoSoup4U");
+        KeyStore.PrivateKeyEntry entry2 = 
getPrivateKeyEntry(getKeyStore("PKCS12", "key2.p12", "mypass"), "1", "mypass");
+
+        byte[] test = "testing 1 2 3 4".getBytes(Charset.forName("UTF-8"));
+
+        assertArrayEquals(
+            test,
+            EnvelopeEncryptDecrypt.Decrypt(
+                entry2,
+                EnvelopeEncryptDecrypt.Encrypt(
+                    "AES/OFB/PKCS5Padding",
+                    256,
+                    entry1.getCertificate(),
+                    100,
+                    test
+                )
+            )
+        );
+    }
+
+}
diff --git a/backend/manager/modules/uutils/src/test/resources/key2.p12 
b/backend/manager/modules/uutils/src/test/resources/key2.p12
new file mode 100644
index 0000000..14db512
--- /dev/null
+++ b/backend/manager/modules/uutils/src/test/resources/key2.p12
Binary files differ


-- 
To view, visit https://gerrit.ovirt.org/42330
To unsubscribe, visit https://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I3b7444ba279bbfaadd162dee4a552b434249aaac
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Alon Bar-Lev <alo...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to