add new API encryptVaultKey(vaultKey, userKey)

and `decodeECPublicKey(byte[])`
This commit is contained in:
Sebastian Stenzel 2024-01-20 13:12:59 +01:00
parent e9ee17493b
commit 2e443c72a9
No known key found for this signature in database
2 changed files with 65 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -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());
}
}