diff --git a/java/google/registry/keyring/api/KeySerializer.java b/java/google/registry/keyring/api/KeySerializer.java new file mode 100644 index 000000000..2b37a31e7 --- /dev/null +++ b/java/google/registry/keyring/api/KeySerializer.java @@ -0,0 +1,134 @@ +// Copyright 2017 The Nomulus Authors. 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. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License 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 google.registry.keyring.api; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing; +import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; + +/** + * Collection of tools to serialize / deserialize PGP types. + * + *
There is no way to serialize / deserialize a PGPPrivateKey on its own, you have to pair it + * with the public key. + */ +public final class KeySerializer { + + + private KeySerializer() {} + + /** + * Serialize a PGPPublicKey + * + *
The reason we're not using {@link PGPPublicKey#getEncoded()} is to use {@link + * ArmoredOutputStream}. + */ + public static byte[] serializePublicKey(PGPPublicKey publicKey) throws IOException { + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) { + // NOTE: We have to close the ArmoredOutputStream before calling the underlying OutputStream's + // "toByteArray". Failing to do so would result in a truncated serialization as we took the + // byte array before the ArmoredOutputStream wrote all the data. + // + // Even "flushing" the ArmoredOutputStream isn't enough - as there are parts that are only + // written by the ArmoredOutputStream when it is closed: the "-----END PGP PRIVATE KEY + // BLOCK-----" (or similar) footer. + try (ArmoredOutputStream out = new ArmoredOutputStream(byteStream)) { + publicKey.encode(out); + } + return byteStream.toByteArray(); + } + } + + /** Deserialize a PGPPublicKey */ + public static PGPPublicKey deserializePublicKey(byte[] serialized) throws IOException { + return + new BcPGPPublicKeyRing( + PGPUtil.getDecoderStream( + new ByteArrayInputStream(serialized))).getPublicKey(); + } + + /** Serializes a string */ + public static byte[] serializeString(String key) { + return key.getBytes(UTF_8); + } + + /** Deserializes a string */ + public static String deserializeString(byte[] serialized) { + return new String(serialized, UTF_8); + } + + /** + * Serialize a PGPKeyPair + * + *
Use this to serialize a PGPPrivateKey as well (pairing it with the corresponding + * PGPPublicKey), as private keys can't be serialized on their own. + */ + public static byte[] serializeKeyPair(PGPKeyPair keyPair) throws IOException, PGPException { + try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) { + // NOTE: We have to close the ArmoredOutputStream before calling the underlying OutputStream's + // "toByteArray". Failing to do so would result in a truncated serialization as we took the + // byte array before the ArmoredOutputStream wrote all the data. + // + // Even "flushing" the ArmoredOutputStream isn't enough - as there are parts that are only + // written by the ArmoredOutputStream when it is closed: the "-----END PGP PRIVATE KEY + // BLOCK-----" (or similar) footer. + try (ArmoredOutputStream out = new ArmoredOutputStream(byteStream)) { + new PGPSecretKey( + keyPair.getPrivateKey(), + keyPair.getPublicKey(), + new JcaPGPDigestCalculatorProviderBuilder() + .setProvider("BC") + .build() + .get(HashAlgorithmTags.SHA256), + true, + null).encode(out); + } + return byteStream.toByteArray(); + } + } + + /** Deserialize a PGPKeyPair */ + public static PGPKeyPair deserializeKeyPair(byte[] serialized) + throws IOException, PGPException { + PGPSecretKey secretKey = + new BcPGPSecretKeyRing( + PGPUtil.getDecoderStream( + new ByteArrayInputStream(serialized))).getSecretKey(); + return new PGPKeyPair( + secretKey.getPublicKey(), + secretKey.extractPrivateKey(createSecretKeyDecryptor())); + } + + private static PBESecretKeyDecryptor createSecretKeyDecryptor() { + // There shouldn't be a passphrase on the key + return new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()) + .build(new char[0]); + } +} diff --git a/java/google/registry/keyring/kms/KmsKeyring.java b/java/google/registry/keyring/kms/KmsKeyring.java index 7de1e09d2..8ed5a023c 100644 --- a/java/google/registry/keyring/kms/KmsKeyring.java +++ b/java/google/registry/keyring/kms/KmsKeyring.java @@ -14,31 +14,23 @@ package google.registry.keyring.kms; +import static com.google.common.base.CaseFormat.LOWER_HYPHEN; +import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE; import static com.google.common.base.Preconditions.checkState; -import static google.registry.keyring.api.PgpHelper.KeyRequirement.ENCRYPT; import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; import static google.registry.model.ofy.ObjectifyService.ofy; -import static java.nio.charset.StandardCharsets.UTF_8; import com.googlecode.objectify.Key; +import google.registry.keyring.api.KeySerializer; import google.registry.keyring.api.Keyring; import google.registry.keyring.api.KeyringException; -import google.registry.keyring.api.PgpHelper; import google.registry.model.server.KmsSecret; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import javax.inject.Inject; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPUtil; -import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing; -import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing; -import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; -import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; /** * A {@link Keyring} implementation which stores encrypted secrets in Datastore and decrypts them @@ -49,22 +41,42 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; */ public class KmsKeyring implements Keyring { - static final String BRAINTREE_PRIVATE_KEY_NAME = "braintree-private-key"; - static final String BRDA_RECEIVER_PUBLIC_NAME = "brda-receiver-public"; - static final String BRDA_SIGNING_PRIVATE_NAME = "brda-signing-private"; - static final String BRDA_SIGNING_PUBLIC_NAME = "brda-signing-public"; - static final String ICANN_REPORTING_PASSWORD_NAME = "icann-reporting-password"; - static final String JSON_CREDENTIAL_NAME = "json-credential"; - static final String MARKSDB_DNL_LOGIN_NAME = "marksdb-dnl-login"; - static final String MARKSDB_LORDN_PASSWORD_NAME = "marksdb-lordn-password"; - static final String MARKSDB_SMDRL_LOGIN_NAME = "marksdb-smdrl-login"; - static final String RDE_RECEIVER_PUBLIC_NAME = "rde-receiver-public"; - static final String RDE_SIGNING_PRIVATE_NAME = "rde-signing-private"; - static final String RDE_SIGNING_PUBLIC_NAME = "rde-signing-public"; - static final String RDE_SSH_CLIENT_PRIVATE_NAME = "rde-ssh-client-private"; - static final String RDE_SSH_CLIENT_PUBLIC_NAME = "rde-ssh-client-public"; - static final String RDE_STAGING_PRIVATE_NAME = "rde-staging-private"; - static final String RDE_STAGING_PUBLIC_NAME = "rde-staging-public"; + static enum PrivateKeyLabel { + BRDA_SIGNING_PRIVATE, + RDE_SIGNING_PRIVATE, + RDE_STAGING_PRIVATE; + + String getLabel() { + return UPPER_UNDERSCORE.to(LOWER_HYPHEN, name()); + } + } + + static enum PublicKeyLabel { + BRDA_RECEIVER_PUBLIC, + BRDA_SIGNING_PUBLIC, + RDE_RECEIVER_PUBLIC, + RDE_SIGNING_PUBLIC, + RDE_STAGING_PUBLIC; + + String getLabel() { + return UPPER_UNDERSCORE.to(LOWER_HYPHEN, name()); + } + } + + static enum StringKeyLabel { + BRAINTREE_PRIVATE_KEY_STRING, + ICANN_REPORTING_PASSWORD_STRING, + JSON_CREDENTIAL_STRING, + MARKSDB_DNL_LOGIN_STRING, + MARKSDB_LORDN_PASSWORD_STRING, + MARKSDB_SMDRL_LOGIN_STRING, + RDE_SSH_CLIENT_PRIVATE_STRING, + RDE_SSH_CLIENT_PUBLIC_STRING; + + String getLabel() { + return UPPER_UNDERSCORE.to(LOWER_HYPHEN, name()); + } + } private final KmsConnection kmsConnection; @@ -75,118 +87,101 @@ public class KmsKeyring implements Keyring { @Override public PGPKeyPair getRdeSigningKey() { - return getKeyPair(RDE_SIGNING_PUBLIC_NAME, RDE_SIGNING_PRIVATE_NAME); + return getKeyPair(PrivateKeyLabel.RDE_SIGNING_PRIVATE); } @Override public PGPPublicKey getRdeStagingEncryptionKey() { - return getPublicKeyForEncrypting(RDE_STAGING_PUBLIC_NAME); + return getPublicKey(PublicKeyLabel.RDE_STAGING_PUBLIC); } @Override public PGPPrivateKey getRdeStagingDecryptionKey() { - return getPrivateKey(RDE_STAGING_PRIVATE_NAME); + return getPrivateKey(PrivateKeyLabel.RDE_STAGING_PRIVATE); } @Override public PGPPublicKey getRdeReceiverKey() { - return getPublicKeyForEncrypting(RDE_RECEIVER_PUBLIC_NAME); + return getPublicKey(PublicKeyLabel.RDE_RECEIVER_PUBLIC); } @Override public PGPKeyPair getBrdaSigningKey() { - return getKeyPair(BRDA_SIGNING_PUBLIC_NAME, BRDA_SIGNING_PRIVATE_NAME); + return getKeyPair(PrivateKeyLabel.BRDA_SIGNING_PRIVATE); } @Override public PGPPublicKey getBrdaReceiverKey() { - return getPublicKeyForEncrypting(BRDA_RECEIVER_PUBLIC_NAME); + return getPublicKey(PublicKeyLabel.BRDA_RECEIVER_PUBLIC); } @Override public String getRdeSshClientPublicKey() { - return new String(getDecryptedData((RDE_SSH_CLIENT_PUBLIC_NAME)), UTF_8); + return getString(StringKeyLabel.RDE_SSH_CLIENT_PUBLIC_STRING); } @Override public String getRdeSshClientPrivateKey() { - return new String(getDecryptedData(RDE_SSH_CLIENT_PRIVATE_NAME), UTF_8); + return getString(StringKeyLabel.RDE_SSH_CLIENT_PRIVATE_STRING); } @Override public String getIcannReportingPassword() { - return new String(getDecryptedData(ICANN_REPORTING_PASSWORD_NAME), UTF_8); + return getString(StringKeyLabel.ICANN_REPORTING_PASSWORD_STRING); } @Override public String getMarksdbDnlLogin() { - return new String(getDecryptedData(MARKSDB_DNL_LOGIN_NAME), UTF_8); + return getString(StringKeyLabel.MARKSDB_DNL_LOGIN_STRING); } @Override public String getMarksdbLordnPassword() { - return new String(getDecryptedData(MARKSDB_LORDN_PASSWORD_NAME), UTF_8); + return getString(StringKeyLabel.MARKSDB_LORDN_PASSWORD_STRING); } @Override public String getMarksdbSmdrlLogin() { - return new String(getDecryptedData(MARKSDB_SMDRL_LOGIN_NAME), UTF_8); + return getString(StringKeyLabel.MARKSDB_SMDRL_LOGIN_STRING); } @Override public String getJsonCredential() { - return new String(getDecryptedData(JSON_CREDENTIAL_NAME), UTF_8); + return getString(StringKeyLabel.JSON_CREDENTIAL_STRING); } @Override public String getBraintreePrivateKey() { - return new String(getDecryptedData(BRAINTREE_PRIVATE_KEY_NAME), UTF_8); + return getString(StringKeyLabel.BRAINTREE_PRIVATE_KEY_STRING); } /** No persistent resources are maintained for this Keyring implementation. */ @Override public void close() {} - private PGPKeyPair getKeyPair(String publicKeyName, String privateKeyName) { - try { - PGPPublicKey publicKey = - new BcPGPPublicKeyRing(getPgpInputStream(publicKeyName)).getPublicKey(); - return new PGPKeyPair(publicKey, getPrivateKey(privateKeyName)); - } catch (IOException e) { - throw new KeyringException( - String.format( - "Could not parse public key %s and private key %s", publicKeyName, privateKeyName), - e); - } + private String getString(StringKeyLabel keyLabel) { + return KeySerializer.deserializeString(getDecryptedData(keyLabel.getLabel())); } - private PGPPublicKey getPublicKeyForEncrypting(String publicKeyName) { + private PGPKeyPair getKeyPair(PrivateKeyLabel keyLabel) { try { - return PgpHelper.lookupPublicSubkey( - new BcPGPPublicKeyRing(getPgpInputStream(publicKeyName)), ENCRYPT) - .get(); - } catch (IOException e) { - throw new KeyringException(String.format("Could not parse public key %s", publicKeyName), e); - } - } - - private PGPPrivateKey getPrivateKey(String privateKeyName) { - try { - PGPSecretKeyRing privateKeyRing = new BcPGPSecretKeyRing(getPgpInputStream(privateKeyName)); - // There shouldn't be a passphrase on the key - return privateKeyRing - .getSecretKey() - .extractPrivateKey( - new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()) - .build(new char[0])); + return KeySerializer.deserializeKeyPair(getDecryptedData(keyLabel.getLabel())); } catch (IOException | PGPException e) { throw new KeyringException( - String.format("Could not parse private key %s", privateKeyName), e); + String.format("Could not parse private keyLabel %s", keyLabel), e); } } - private InputStream getPgpInputStream(String privateKeyName) throws IOException { - return PGPUtil.getDecoderStream(new ByteArrayInputStream(getDecryptedData(privateKeyName))); + private PGPPublicKey getPublicKey(PublicKeyLabel keyLabel) { + try { + return KeySerializer.deserializePublicKey(getDecryptedData(keyLabel.getLabel())); + } catch (IOException e) { + throw new KeyringException(String.format("Could not parse public keyLabel %s", keyLabel), e); + } + } + + private PGPPrivateKey getPrivateKey(PrivateKeyLabel keyLabel) { + return getKeyPair(keyLabel).getPrivateKey(); } private byte[] getDecryptedData(String keyName) { diff --git a/java/google/registry/keyring/kms/KmsUpdater.java b/java/google/registry/keyring/kms/KmsUpdater.java index af80653e9..cf9dc1598 100644 --- a/java/google/registry/keyring/kms/KmsUpdater.java +++ b/java/google/registry/keyring/kms/KmsUpdater.java @@ -16,37 +16,44 @@ package google.registry.keyring.kms; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; -import static google.registry.keyring.kms.KmsKeyring.BRAINTREE_PRIVATE_KEY_NAME; -import static google.registry.keyring.kms.KmsKeyring.BRDA_RECEIVER_PUBLIC_NAME; -import static google.registry.keyring.kms.KmsKeyring.BRDA_SIGNING_PRIVATE_NAME; -import static google.registry.keyring.kms.KmsKeyring.BRDA_SIGNING_PUBLIC_NAME; -import static google.registry.keyring.kms.KmsKeyring.ICANN_REPORTING_PASSWORD_NAME; -import static google.registry.keyring.kms.KmsKeyring.JSON_CREDENTIAL_NAME; -import static google.registry.keyring.kms.KmsKeyring.MARKSDB_DNL_LOGIN_NAME; -import static google.registry.keyring.kms.KmsKeyring.MARKSDB_LORDN_PASSWORD_NAME; -import static google.registry.keyring.kms.KmsKeyring.MARKSDB_SMDRL_LOGIN_NAME; -import static google.registry.keyring.kms.KmsKeyring.RDE_RECEIVER_PUBLIC_NAME; -import static google.registry.keyring.kms.KmsKeyring.RDE_SIGNING_PRIVATE_NAME; -import static google.registry.keyring.kms.KmsKeyring.RDE_SIGNING_PUBLIC_NAME; -import static google.registry.keyring.kms.KmsKeyring.RDE_SSH_CLIENT_PRIVATE_NAME; -import static google.registry.keyring.kms.KmsKeyring.RDE_SSH_CLIENT_PUBLIC_NAME; -import static google.registry.keyring.kms.KmsKeyring.RDE_STAGING_PRIVATE_NAME; -import static google.registry.keyring.kms.KmsKeyring.RDE_STAGING_PUBLIC_NAME; +import static google.registry.keyring.kms.KmsKeyring.PrivateKeyLabel.BRDA_SIGNING_PRIVATE; +import static google.registry.keyring.kms.KmsKeyring.PrivateKeyLabel.RDE_SIGNING_PRIVATE; +import static google.registry.keyring.kms.KmsKeyring.PrivateKeyLabel.RDE_STAGING_PRIVATE; +import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.BRDA_RECEIVER_PUBLIC; +import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.BRDA_SIGNING_PUBLIC; +import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_RECEIVER_PUBLIC; +import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_SIGNING_PUBLIC; +import static google.registry.keyring.kms.KmsKeyring.PublicKeyLabel.RDE_STAGING_PUBLIC; +import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.BRAINTREE_PRIVATE_KEY_STRING; +import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.ICANN_REPORTING_PASSWORD_STRING; +import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.JSON_CREDENTIAL_STRING; +import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.MARKSDB_DNL_LOGIN_STRING; +import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.MARKSDB_LORDN_PASSWORD_STRING; +import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.MARKSDB_SMDRL_LOGIN_STRING; +import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.RDE_SSH_CLIENT_PRIVATE_STRING; +import static google.registry.keyring.kms.KmsKeyring.StringKeyLabel.RDE_SSH_CLIENT_PUBLIC_STRING; import static google.registry.model.ofy.ObjectifyService.ofy; import static google.registry.util.PreconditionsUtils.checkArgumentNotNull; -import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableMap; import com.googlecode.objectify.VoidWork; +import google.registry.keyring.api.KeySerializer; +import google.registry.keyring.kms.KmsKeyring.PrivateKeyLabel; +import google.registry.keyring.kms.KmsKeyring.PublicKeyLabel; +import google.registry.keyring.kms.KmsKeyring.StringKeyLabel; import google.registry.model.server.KmsSecret; import google.registry.model.server.KmsSecretRevision; + import java.io.IOException; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; + import javax.inject.Inject; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing; /** * The {@link KmsUpdater} accumulates updates to a {@link KmsKeyring} and persists them to KMS and @@ -65,77 +72,56 @@ public final class KmsUpdater { this.secretValues = new LinkedHashMap<>(); } - public KmsUpdater setRdeSigningKey(BcPGPSecretKeyRing secretKeyRing) throws IOException { - checkArgumentNotNull(secretKeyRing); - setSecret(RDE_SIGNING_PRIVATE_NAME, checkArgumentNotNull(secretKeyRing).getEncoded()); - setSecret(RDE_SIGNING_PUBLIC_NAME, secretKeyRing.getPublicKey().getEncoded()); - return this; + public KmsUpdater setRdeSigningKey(PGPKeyPair keyPair) throws IOException, PGPException { + return setKeyPair(keyPair, RDE_SIGNING_PRIVATE, RDE_SIGNING_PUBLIC); } - public KmsUpdater setRdeStagingKey(BcPGPSecretKeyRing secretKeyRing) throws IOException { - checkArgumentNotNull(secretKeyRing); - - setSecret(RDE_STAGING_PRIVATE_NAME, secretKeyRing.getEncoded()); - setSecret(RDE_STAGING_PUBLIC_NAME, secretKeyRing.getPublicKey().getEncoded()); - return this; + public KmsUpdater setRdeStagingKey(PGPKeyPair keyPair) throws IOException, PGPException { + return setKeyPair(keyPair, RDE_STAGING_PRIVATE, RDE_STAGING_PUBLIC); } - public KmsUpdater setRdeReceiverPublicKey(PGPPublicKey rdeReceiverPublicKey) throws IOException { - setSecret(RDE_RECEIVER_PUBLIC_NAME, checkArgumentNotNull(rdeReceiverPublicKey).getEncoded()); - return this; + public KmsUpdater setRdeReceiverPublicKey(PGPPublicKey publicKey) throws IOException { + return setPublicKey(publicKey, RDE_RECEIVER_PUBLIC); } - public KmsUpdater setBrdaSigningKey(BcPGPSecretKeyRing secretKeyRing) throws IOException { - checkArgumentNotNull(secretKeyRing); - setSecret(BRDA_SIGNING_PRIVATE_NAME, secretKeyRing.getEncoded()); - setSecret(BRDA_SIGNING_PUBLIC_NAME, secretKeyRing.getPublicKey().getEncoded()); - return this; + public KmsUpdater setBrdaSigningKey(PGPKeyPair keyPair) throws IOException, PGPException { + return setKeyPair(keyPair, BRDA_SIGNING_PRIVATE, BRDA_SIGNING_PUBLIC); } public KmsUpdater setBrdaReceiverPublicKey(PGPPublicKey publicKey) throws IOException { - setSecret(BRDA_RECEIVER_PUBLIC_NAME, checkArgumentNotNull(publicKey).getEncoded()); - return this; + return setPublicKey(publicKey, BRDA_RECEIVER_PUBLIC); } public KmsUpdater setRdeSshClientPublicKey(String asciiPublicKey) { - setSecret(RDE_SSH_CLIENT_PUBLIC_NAME, checkArgumentNotNull(asciiPublicKey).getBytes(UTF_8)); - return this; + return setString(asciiPublicKey, RDE_SSH_CLIENT_PUBLIC_STRING); } public KmsUpdater setRdeSshClientPrivateKey(String asciiPrivateKey) { - setSecret(RDE_SSH_CLIENT_PRIVATE_NAME, checkArgumentNotNull(asciiPrivateKey).getBytes(UTF_8)); - return this; + return setString(asciiPrivateKey, RDE_SSH_CLIENT_PRIVATE_STRING); } public KmsUpdater setIcannReportingPassword(String password) { - setSecret(ICANN_REPORTING_PASSWORD_NAME, checkArgumentNotNull(password).getBytes(UTF_8)); - return this; + return setString(password, ICANN_REPORTING_PASSWORD_STRING); } public KmsUpdater setMarksdbDnlLogin(String login) { - setSecret(MARKSDB_DNL_LOGIN_NAME, checkArgumentNotNull(login).getBytes(UTF_8)); - return this; + return setString(login, MARKSDB_DNL_LOGIN_STRING); } public KmsUpdater setMarksdbLordnPassword(String password) { - setSecret(MARKSDB_LORDN_PASSWORD_NAME, checkArgumentNotNull(password).getBytes(UTF_8)); - return this; + return setString(password, MARKSDB_LORDN_PASSWORD_STRING); } public KmsUpdater setMarksdbSmdrlLogin(String login) { - setSecret(MARKSDB_SMDRL_LOGIN_NAME, checkArgumentNotNull(login).getBytes(UTF_8)); - return this; + return setString(login, MARKSDB_SMDRL_LOGIN_STRING); } public KmsUpdater setJsonCredential(String credential) { - setSecret(JSON_CREDENTIAL_NAME, checkArgumentNotNull(credential).getBytes(UTF_8)); - return this; + return setString(credential, JSON_CREDENTIAL_STRING); } public KmsUpdater setBraintreePrivateKey(String braintreePrivateKey) { - setSecret( - BRAINTREE_PRIVATE_KEY_NAME, checkArgumentNotNull(braintreePrivateKey).getBytes(UTF_8)); - return this; + return setString(braintreePrivateKey, BRAINTREE_PRIVATE_KEY_STRING); } /** @@ -167,6 +153,31 @@ public final class KmsUpdater { return encryptedValues.build(); } + private KmsUpdater setString(String key, StringKeyLabel stringKeyLabel) { + checkArgumentNotNull(key); + + setSecret(stringKeyLabel.getLabel(), KeySerializer.serializeString(key)); + return this; + } + + private KmsUpdater setPublicKey(PGPPublicKey publicKey, PublicKeyLabel publicKeyLabel) + throws IOException { + checkArgumentNotNull(publicKey); + + setSecret(publicKeyLabel.getLabel(), KeySerializer.serializePublicKey(publicKey)); + return this; + } + + private KmsUpdater setKeyPair( + PGPKeyPair keyPair, PrivateKeyLabel privateKeyLabel, PublicKeyLabel publicKeyLabel) + throws IOException, PGPException { + checkArgumentNotNull(keyPair); + + setSecret(privateKeyLabel.getLabel(), KeySerializer.serializeKeyPair(keyPair)); + setSecret(publicKeyLabel.getLabel(), KeySerializer.serializePublicKey(keyPair.getPublicKey())); + return this; + } + /** * Persists encrypted secrets to Datastore as {@link KmsSecretRevision} entities and makes them * primary. {@link KmsSecret} entities point to the latest {@link KmsSecretRevision}. diff --git a/javatests/google/registry/keyring/kms/BUILD b/javatests/google/registry/keyring/kms/BUILD index 89a9c4189..01d0baef2 100644 --- a/javatests/google/registry/keyring/kms/BUILD +++ b/javatests/google/registry/keyring/kms/BUILD @@ -15,6 +15,7 @@ java_library( "pgp-public-keyring.asc", ], deps = [ + "//java/google/registry/keyring/api", "//java/google/registry/keyring/kms", "//java/google/registry/model", "//javatests/google/registry/testing", diff --git a/javatests/google/registry/keyring/kms/KmsKeyringTest.java b/javatests/google/registry/keyring/kms/KmsKeyringTest.java index 77a260c93..4a8025b55 100644 --- a/javatests/google/registry/keyring/kms/KmsKeyringTest.java +++ b/javatests/google/registry/keyring/kms/KmsKeyringTest.java @@ -16,13 +16,13 @@ package google.registry.keyring.kms; import static com.google.common.truth.Truth.assertThat; import static google.registry.testing.DatastoreHelper.persistResources; -import static java.nio.charset.StandardCharsets.UTF_8; - import com.google.common.collect.ImmutableList; +import google.registry.keyring.api.KeySerializer; import google.registry.model.server.KmsSecret; import google.registry.model.server.KmsSecretRevision; import google.registry.model.server.KmsSecretRevision.Builder; import google.registry.testing.AppEngineRule; +import google.registry.testing.BouncyCastleProviderRule; import java.io.IOException; import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPPrivateKey; @@ -36,6 +36,8 @@ import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class KmsKeyringTest { + @Rule public final BouncyCastleProviderRule bouncy = new BouncyCastleProviderRule(); + @Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); private KmsKeyring keyring; @@ -51,8 +53,8 @@ public class KmsKeyringTest { PGPKeyPair rdeSigningKey = keyring.getRdeSigningKey(); - assertThat(rdeSigningKey.getKeyID()) - .isEqualTo(KmsTestHelper.getPublicKeyring().getPublicKey().getKeyID()); + assertThat(KeySerializer.serializeKeyPair(rdeSigningKey)) + .isEqualTo(KeySerializer.serializeKeyPair(KmsTestHelper.getKeyPair())); } @Test @@ -62,17 +64,20 @@ public class KmsKeyringTest { PGPPublicKey rdeStagingEncryptionKey = keyring.getRdeStagingEncryptionKey(); assertThat(rdeStagingEncryptionKey.getFingerprint()) - .isEqualTo(KmsTestHelper.getPublicKeyring().getPublicKey().getFingerprint()); + .isEqualTo(KmsTestHelper.getPublicKey().getFingerprint()); } @Test public void test_getRdeStagingDecryptionKey() throws Exception { savePrivateKeySecret("rde-staging-private"); + savePublicKeySecret("rde-staging-public"); PGPPrivateKey rdeStagingDecryptionKey = keyring.getRdeStagingDecryptionKey(); + PGPPublicKey rdeStagingEncryptionKey = keyring.getRdeStagingEncryptionKey(); + PGPKeyPair keyPair = new PGPKeyPair(rdeStagingEncryptionKey, rdeStagingDecryptionKey); - assertThat(rdeStagingDecryptionKey.getKeyID()) - .isEqualTo(KmsTestHelper.getPrivateKeyring().getSecretKey().getKeyID()); + assertThat(KeySerializer.serializeKeyPair(keyPair)) + .isEqualTo(KeySerializer.serializeKeyPair(KmsTestHelper.getKeyPair())); } @Test @@ -82,7 +87,7 @@ public class KmsKeyringTest { PGPPublicKey rdeReceiverKey = keyring.getRdeReceiverKey(); assertThat(rdeReceiverKey.getFingerprint()) - .isEqualTo(KmsTestHelper.getPublicKeyring().getPublicKey().getFingerprint()); + .isEqualTo(KmsTestHelper.getPublicKey().getFingerprint()); } @Test @@ -91,8 +96,8 @@ public class KmsKeyringTest { PGPKeyPair brdaSigningKey = keyring.getBrdaSigningKey(); - assertThat(brdaSigningKey.getKeyID()) - .isEqualTo(KmsTestHelper.getPrivateKeyring().getPublicKey().getKeyID()); + assertThat(KeySerializer.serializeKeyPair(brdaSigningKey)) + .isEqualTo(KeySerializer.serializeKeyPair(KmsTestHelper.getKeyPair())); } @Test @@ -102,80 +107,80 @@ public class KmsKeyringTest { PGPPublicKey brdaReceiverKey = keyring.getBrdaReceiverKey(); assertThat(brdaReceiverKey.getFingerprint()) - .isEqualTo(KmsTestHelper.getPublicKeyring().getPublicKey().getFingerprint()); + .isEqualTo(KmsTestHelper.getPublicKey().getFingerprint()); } @Test public void test_getRdeSshClientPublicKey() throws Exception { - saveCleartextSecret("rde-ssh-client-public"); + saveCleartextSecret("rde-ssh-client-public-string"); String rdeSshClientPublicKey = keyring.getRdeSshClientPublicKey(); - assertThat(rdeSshClientPublicKey).isEqualTo("rde-ssh-client-publicmoo"); + assertThat(rdeSshClientPublicKey).isEqualTo("rde-ssh-client-public-stringmoo"); } @Test public void test_getRdeSshClientPrivateKey() throws Exception { - saveCleartextSecret("rde-ssh-client-private"); + saveCleartextSecret("rde-ssh-client-private-string"); String rdeSshClientPrivateKey = keyring.getRdeSshClientPrivateKey(); - assertThat(rdeSshClientPrivateKey).isEqualTo("rde-ssh-client-privatemoo"); + assertThat(rdeSshClientPrivateKey).isEqualTo("rde-ssh-client-private-stringmoo"); } @Test public void test_getIcannReportingPassword() throws Exception { - saveCleartextSecret("icann-reporting-password"); + saveCleartextSecret("icann-reporting-password-string"); String icannReportingPassword = keyring.getIcannReportingPassword(); - assertThat(icannReportingPassword).isEqualTo("icann-reporting-passwordmoo"); + assertThat(icannReportingPassword).isEqualTo("icann-reporting-password-stringmoo"); } @Test public void test_getMarksdbDnlLogin() throws Exception { - saveCleartextSecret("marksdb-dnl-login"); + saveCleartextSecret("marksdb-dnl-login-string"); String marksdbDnlLogin = keyring.getMarksdbDnlLogin(); - assertThat(marksdbDnlLogin).isEqualTo("marksdb-dnl-loginmoo"); + assertThat(marksdbDnlLogin).isEqualTo("marksdb-dnl-login-stringmoo"); } @Test public void test_getMarksdbLordnPassword() throws Exception { - saveCleartextSecret("marksdb-lordn-password"); + saveCleartextSecret("marksdb-lordn-password-string"); String marksdbLordnPassword = keyring.getMarksdbLordnPassword(); - assertThat(marksdbLordnPassword).isEqualTo("marksdb-lordn-passwordmoo"); + assertThat(marksdbLordnPassword).isEqualTo("marksdb-lordn-password-stringmoo"); } @Test public void test_getMarksdbSmdrlLogin() throws Exception { - saveCleartextSecret("marksdb-smdrl-login"); + saveCleartextSecret("marksdb-smdrl-login-string"); String marksdbSmdrlLogin = keyring.getMarksdbSmdrlLogin(); - assertThat(marksdbSmdrlLogin).isEqualTo("marksdb-smdrl-loginmoo"); + assertThat(marksdbSmdrlLogin).isEqualTo("marksdb-smdrl-login-stringmoo"); } @Test public void test_getJsonCredential() throws Exception { - saveCleartextSecret("json-credential"); + saveCleartextSecret("json-credential-string"); String jsonCredential = keyring.getJsonCredential(); - assertThat(jsonCredential).isEqualTo("json-credentialmoo"); + assertThat(jsonCredential).isEqualTo("json-credential-stringmoo"); } @Test public void test_getBraintreePrivateKey() throws Exception { - saveCleartextSecret("braintree-private-key"); + saveCleartextSecret("braintree-private-key-string"); String braintreePrivateKey = keyring.getBraintreePrivateKey(); - assertThat(braintreePrivateKey).isEqualTo("braintree-private-keymoo"); + assertThat(braintreePrivateKey).isEqualTo("braintree-private-key-stringmoo"); } private static void persistSecret(String secretName, byte[] secretValue) throws IOException { @@ -192,15 +197,15 @@ public class KmsKeyringTest { } private static void saveCleartextSecret(String secretName) throws Exception { - persistSecret(secretName, (secretName + "moo").getBytes(UTF_8)); + persistSecret(secretName, KeySerializer.serializeString(secretName + "moo")); } private static void savePublicKeySecret(String publicKeyName) throws Exception { - persistSecret(publicKeyName, KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded()); + persistSecret(publicKeyName, KeySerializer.serializePublicKey(KmsTestHelper.getPublicKey())); } private static void savePrivateKeySecret(String privateKeyName) throws Exception { - persistSecret(privateKeyName, KmsTestHelper.getPrivateKeyring().getEncoded()); + persistSecret(privateKeyName, KeySerializer.serializeKeyPair(KmsTestHelper.getKeyPair())); } private static void saveKeyPairSecret(String publicKeyName, String privateKeyName) diff --git a/javatests/google/registry/keyring/kms/KmsTestHelper.java b/javatests/google/registry/keyring/kms/KmsTestHelper.java index ed4918770..8ed95946e 100644 --- a/javatests/google/registry/keyring/kms/KmsTestHelper.java +++ b/javatests/google/registry/keyring/kms/KmsTestHelper.java @@ -18,9 +18,13 @@ import static com.google.common.io.Resources.getResource; import com.google.common.io.ByteSource; import com.google.common.io.Resources; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPUtil; -import org.bouncycastle.openpgp.bc.BcPGPPublicKeyRing; import org.bouncycastle.openpgp.bc.BcPGPSecretKeyRing; +import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; /** Stores dummy values for test use in {@link KmsUpdaterTest} and {@link KmsKeyringTest}. */ final class KmsTestHelper { @@ -28,20 +32,25 @@ final class KmsTestHelper { static final String DUMMY_CRYPTO_KEY_VERSION = "cheeseburger"; static final String DUMMY_ENCRYPTED_VALUE = "meow"; - /** The contents of a dummy PGP public key stored in a file. */ - private static final ByteSource PGP_PUBLIC_KEYRING = - Resources.asByteSource(getResource(KmsTestHelper.class, "pgp-public-keyring.asc")); - /** The contents of a dummy PGP private key stored in a file. */ private static final ByteSource PGP_PRIVATE_KEYRING = Resources.asByteSource(getResource(KmsTestHelper.class, "pgp-private-keyring-registry.asc")); - static BcPGPPublicKeyRing getPublicKeyring() throws Exception { - return new BcPGPPublicKeyRing(PGPUtil.getDecoderStream(PGP_PUBLIC_KEYRING.openStream())); + private static BcPGPSecretKeyRing getPrivateKeyring() throws Exception { + return new BcPGPSecretKeyRing(PGPUtil.getDecoderStream(PGP_PRIVATE_KEYRING.openStream())); } - static BcPGPSecretKeyRing getPrivateKeyring() throws Exception { - return new BcPGPSecretKeyRing(PGPUtil.getDecoderStream(PGP_PRIVATE_KEYRING.openStream())); + static PGPPublicKey getPublicKey() throws Exception { + return getPrivateKeyring().getPublicKey(); + } + + static PGPKeyPair getKeyPair() throws Exception { + PGPSecretKey secretKey = getPrivateKeyring().getSecretKey(); + return new PGPKeyPair( + secretKey.getPublicKey(), + secretKey.extractPrivateKey( + new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()) + .build(new char[0]))); } private KmsTestHelper() {} diff --git a/javatests/google/registry/keyring/kms/KmsUpdaterTest.java b/javatests/google/registry/keyring/kms/KmsUpdaterTest.java index 2226f8f5b..0592b591b 100644 --- a/javatests/google/registry/keyring/kms/KmsUpdaterTest.java +++ b/javatests/google/registry/keyring/kms/KmsUpdaterTest.java @@ -17,13 +17,15 @@ package google.registry.keyring.kms; import static com.google.common.truth.Truth.assertThat; import static google.registry.model.common.EntityGroupRoot.getCrossTldKey; import static google.registry.model.ofy.ObjectifyService.ofy; -import static java.nio.charset.StandardCharsets.UTF_8; - import com.googlecode.objectify.Key; +import google.registry.keyring.api.KeySerializer; import google.registry.model.server.KmsSecret; import google.registry.model.server.KmsSecretRevision; import google.registry.testing.AppEngineRule; +import google.registry.testing.BouncyCastleProviderRule; import java.io.IOException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPPublicKey; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -35,6 +37,8 @@ public class KmsUpdaterTest { @Rule public final AppEngineRule appEngine = AppEngineRule.builder().withDatastore().build(); + @Rule public final BouncyCastleProviderRule bouncy = new BouncyCastleProviderRule(); + private KmsUpdater updater; @Before @@ -51,11 +55,15 @@ public class KmsUpdaterTest { .update(); verifySecretAndSecretRevisionWritten( - "braintree-private-key", "braintree-private-key/foo", getCiphertext("value1")); + "braintree-private-key-string", + "braintree-private-key-string/foo", + getCiphertext("value1")); verifySecretAndSecretRevisionWritten( - "icann-reporting-password", "icann-reporting-password/foo", getCiphertext("value2")); + "icann-reporting-password-string", + "icann-reporting-password-string/foo", + getCiphertext("value2")); verifySecretAndSecretRevisionWritten( - "json-credential", "json-credential/foo", getCiphertext("value3")); + "json-credential-string", "json-credential-string/foo", getCiphertext("value3")); } @Test @@ -63,31 +71,33 @@ public class KmsUpdaterTest { updater.setBraintreePrivateKey("value1").update(); verifySecretAndSecretRevisionWritten( - "braintree-private-key", "braintree-private-key/foo", getCiphertext("value1")); + "braintree-private-key-string", + "braintree-private-key-string/foo", + getCiphertext("value1")); } @Test public void test_setBrdaReceiverKey() throws Exception { - updater.setBrdaReceiverPublicKey(KmsTestHelper.getPublicKeyring().getPublicKey()).update(); + updater.setBrdaReceiverPublicKey(KmsTestHelper.getPublicKey()).update(); verifySecretAndSecretRevisionWritten( "brda-receiver-public", "brda-receiver-public/foo", - getCiphertext(KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded())); + getCiphertext(KmsTestHelper.getPublicKey())); } @Test public void test_setBrdaSigningKey() throws Exception { - updater.setBrdaSigningKey(KmsTestHelper.getPrivateKeyring()).update(); + updater.setBrdaSigningKey(KmsTestHelper.getKeyPair()).update(); verifySecretAndSecretRevisionWritten( "brda-signing-private", "brda-signing-private/foo", - getCiphertext(KmsTestHelper.getPrivateKeyring().getEncoded())); + getCiphertext(KmsTestHelper.getKeyPair())); verifySecretAndSecretRevisionWritten( "brda-signing-public", "brda-signing-public/foo", - getCiphertext(KmsTestHelper.getPrivateKeyring().getPublicKey().getEncoded())); + getCiphertext(KmsTestHelper.getPublicKey())); } @Test @@ -95,7 +105,9 @@ public class KmsUpdaterTest { updater.setIcannReportingPassword("value1").update(); verifySecretAndSecretRevisionWritten( - "icann-reporting-password", "icann-reporting-password/foo", getCiphertext("value1")); + "icann-reporting-password-string", + "icann-reporting-password-string/foo", + getCiphertext("value1")); } @Test @@ -103,7 +115,7 @@ public class KmsUpdaterTest { updater.setJsonCredential("value1").update(); verifySecretAndSecretRevisionWritten( - "json-credential", "json-credential/foo", getCiphertext("value1")); + "json-credential-string", "json-credential-string/foo", getCiphertext("value1")); } @Test @@ -111,7 +123,7 @@ public class KmsUpdaterTest { updater.setMarksdbDnlLogin("value1").update(); verifySecretAndSecretRevisionWritten( - "marksdb-dnl-login", "marksdb-dnl-login/foo", getCiphertext("value1")); + "marksdb-dnl-login-string", "marksdb-dnl-login-string/foo", getCiphertext("value1")); } @Test @@ -119,7 +131,9 @@ public class KmsUpdaterTest { updater.setMarksdbLordnPassword("value1").update(); verifySecretAndSecretRevisionWritten( - "marksdb-lordn-password", "marksdb-lordn-password/foo", getCiphertext("value1")); + "marksdb-lordn-password-string", + "marksdb-lordn-password-string/foo", + getCiphertext("value1")); } @Test @@ -127,31 +141,32 @@ public class KmsUpdaterTest { updater.setMarksdbSmdrlLogin("value1").update(); verifySecretAndSecretRevisionWritten( - "marksdb-smdrl-login", "marksdb-smdrl-login/foo", getCiphertext("value1")); + "marksdb-smdrl-login-string", "marksdb-smdrl-login-string/foo", getCiphertext("value1")); } @Test public void test_setRdeReceiverKey() throws Exception { - updater.setRdeReceiverPublicKey(KmsTestHelper.getPublicKeyring().getPublicKey()).update(); + updater.setRdeReceiverPublicKey(KmsTestHelper.getPublicKey()).update(); verifySecretAndSecretRevisionWritten( "rde-receiver-public", "rde-receiver-public/foo", - getCiphertext(KmsTestHelper.getPublicKeyring().getPublicKey().getEncoded())); + getCiphertext( + KeySerializer.serializePublicKey(KmsTestHelper.getPublicKey()))); } @Test public void test_setRdeSigningKey() throws Exception { - updater.setRdeSigningKey(KmsTestHelper.getPrivateKeyring()).update(); + updater.setRdeSigningKey(KmsTestHelper.getKeyPair()).update(); verifySecretAndSecretRevisionWritten( "rde-signing-private", "rde-signing-private/foo", - getCiphertext(KmsTestHelper.getPrivateKeyring().getEncoded())); + getCiphertext(KmsTestHelper.getKeyPair())); verifySecretAndSecretRevisionWritten( "rde-signing-public", "rde-signing-public/foo", - getCiphertext(KmsTestHelper.getPrivateKeyring().getPublicKey().getEncoded())); + getCiphertext(KmsTestHelper.getPublicKey())); } @Test @@ -159,7 +174,9 @@ public class KmsUpdaterTest { updater.setRdeSshClientPrivateKey("value1").update(); verifySecretAndSecretRevisionWritten( - "rde-ssh-client-private", "rde-ssh-client-private/foo", getCiphertext("value1")); + "rde-ssh-client-private-string", + "rde-ssh-client-private-string/foo", + getCiphertext("value1")); } @Test @@ -167,21 +184,23 @@ public class KmsUpdaterTest { updater.setRdeSshClientPublicKey("value1").update(); verifySecretAndSecretRevisionWritten( - "rde-ssh-client-public", "rde-ssh-client-public/foo", getCiphertext("value1")); + "rde-ssh-client-public-string", + "rde-ssh-client-public-string/foo", + getCiphertext("value1")); } @Test public void test_setRdeStagingKey() throws Exception { - updater.setRdeStagingKey(KmsTestHelper.getPrivateKeyring()).update(); + updater.setRdeStagingKey(KmsTestHelper.getKeyPair()).update(); verifySecretAndSecretRevisionWritten( "rde-staging-private", "rde-staging-private/foo", - getCiphertext(KmsTestHelper.getPrivateKeyring().getEncoded())); + getCiphertext(KmsTestHelper.getKeyPair())); verifySecretAndSecretRevisionWritten( "rde-staging-public", "rde-staging-public/foo", - getCiphertext(KmsTestHelper.getPrivateKeyring().getPublicKey().getEncoded())); + getCiphertext(KmsTestHelper.getPublicKey())); } @@ -200,6 +219,14 @@ public class KmsUpdaterTest { } private static String getCiphertext(String plaintext) throws IOException { - return getCiphertext(plaintext.getBytes(UTF_8)); + return getCiphertext(KeySerializer.serializeString(plaintext)); + } + + private static String getCiphertext(PGPPublicKey publicKey) throws IOException { + return getCiphertext(KeySerializer.serializePublicKey(publicKey)); + } + + private static String getCiphertext(PGPKeyPair keyPair) throws Exception { + return getCiphertext(KeySerializer.serializeKeyPair(keyPair)); } }