Преглед изворни кода

add new API `encryptVaultKey(vaultKey, userKey)`

and `decodeECPublicKey(byte[])`
Sebastian Stenzel пре 1 година
родитељ
комит
2e443c72a9

+ 37 - 0
src/main/java/org/cryptomator/ui/keyloading/hub/JWEHelper.java

@@ -24,6 +24,7 @@ import java.security.interfaces.ECPrivateKey;
 import java.security.interfaces.ECPublicKey;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.Map;
@@ -92,6 +93,42 @@ class JWEHelper {
 			throw new KeyDecodeFailedException(e);
 		}
 	}
+
+	/**
+	 * Attempts to decode a DER-encoded EC public key.
+	 *
+	 * @param encoded DER-encoded EC public key
+	 * @return the decoded key
+	 * @throws KeyDecodeFailedException On malformed input
+	 */
+	public static ECPublicKey decodeECPublicKey(byte[] encoded) throws KeyDecodeFailedException {
+		try {
+			KeyFactory factory = KeyFactory.getInstance(EC_ALG);
+			var publicKey = factory.generatePublic(new X509EncodedKeySpec(encoded));
+			if (publicKey instanceof ECPublicKey ecPublicKey) {
+				return ecPublicKey;
+			} else {
+				throw new IllegalStateException(EC_ALG + " key factory not generating ECPublicKeys");
+			}
+		} catch (NoSuchAlgorithmException e) {
+			throw new IllegalStateException(EC_ALG + " not supported");
+		} catch (InvalidKeySpecException e) {
+			throw new KeyDecodeFailedException(e);
+		}
+	}
+
+	public static JWEObject encryptVaultKey(Masterkey vaultKey, ECPublicKey userKey) {
+		try {
+			var encodedVaultKey = Base64.getEncoder().encodeToString(vaultKey.getEncoded());
+			var keyGen = new ECKeyGenerator(Curve.P_384);
+			var ephemeralKeyPair = keyGen.generate();
+			var header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A256GCM).ephemeralPublicKey(ephemeralKeyPair.toPublicJWK()).build();
+			var payload = new Payload(Map.of(JWE_PAYLOAD_KEY_FIELD, encodedVaultKey));
+			var jwe = new JWEObject(header, payload);
+			jwe.encrypt(new ECDHEncrypter(userKey));
+			return jwe;
+		} catch (JOSEException e) {
+			throw new RuntimeException(e);
 		}
 	}
 

+ 28 - 0
src/test/java/org/cryptomator/ui/keyloading/hub/JWEHelperTest.java

@@ -1,6 +1,7 @@
 package org.cryptomator.ui.keyloading.hub;
 
 import com.nimbusds.jose.JWEObject;
+import org.cryptomator.cryptolib.api.Masterkey;
 import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
 import org.cryptomator.cryptolib.common.P384KeyPair;
 import org.junit.jupiter.api.Assertions;
@@ -140,4 +141,31 @@ public class JWEHelperTest {
 		Assertions.assertThrows(MasterkeyLoadingFailedException.class, () -> JWEHelper.decryptVaultKey(jwe, privateKey));
 	}
 
+	@Test
+	@DisplayName("decrypt(encrypt(vaultKey, userPublicKey), userPrivateKey) == vaultKey")
+	public void testEncryptAndDecryptVaultKey() {
+		var keyBytes = new byte[64];
+		Arrays.fill(keyBytes, 0, 32, (byte) 0x55);
+		Arrays.fill(keyBytes, 32, 64, (byte) 0x77);
+		var vaultKey = new Masterkey(keyBytes);
+		var userKey = P384KeyPair.generate();
+
+		var encrypted = JWEHelper.encryptVaultKey(vaultKey, userKey.getPublic());
+		var decrypted = JWEHelper.decryptVaultKey(encrypted, userKey.getPrivate());
+
+		Assertions.assertArrayEquals(keyBytes, decrypted.getEncoded());
+	}
+
+	@Test
+	@DisplayName("decrypt(encrypt(userKey, devicePublicKey), devicePrivateKey) == userKey")
+	public void testEncryptAndDecryptUserKey() {
+		var userKey = P384KeyPair.generate();
+		var deviceKey = P384KeyPair.generate();
+
+		var encrypted = JWEHelper.encryptUserKey(userKey.getPrivate(), deviceKey.getPublic());
+		var decrypted = JWEHelper.decryptUserKey(encrypted, deviceKey.getPrivate());
+
+		Assertions.assertArrayEquals(userKey.getPrivate().getEncoded(), decrypted.getEncoded());
+	}
+
 }