diff --git a/java/google/registry/keyring/api/InMemoryKeyring.java b/java/google/registry/keyring/api/InMemoryKeyring.java index d2bcf8775..30509ab88 100644 --- a/java/google/registry/keyring/api/InMemoryKeyring.java +++ b/java/google/registry/keyring/api/InMemoryKeyring.java @@ -149,6 +149,12 @@ public final class InMemoryKeyring implements Keyring { return jsonCredential; } + @Override + public String getEncryptedData(String keyName) { + throw new RuntimeException( + "In-memory keyring does not support the retrieval of encrypted data."); + } + /** Does nothing. */ @Override public void close() {} diff --git a/java/google/registry/keyring/api/KeyModule.java b/java/google/registry/keyring/api/KeyModule.java index d4282e28d..81ee1bd93 100644 --- a/java/google/registry/keyring/api/KeyModule.java +++ b/java/google/registry/keyring/api/KeyModule.java @@ -20,6 +20,8 @@ import dagger.Module; import dagger.Provides; import java.lang.annotation.Documented; import java.util.Optional; +import java.util.function.Function; +import javax.inject.Named; import javax.inject.Qualifier; import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPPrivateKey; @@ -126,4 +128,10 @@ public final class KeyModule { static String provideJsonCredential(Keyring keyring) { return keyring.getJsonCredential(); } + + @Provides + @Named("encryptedDataRetriever") + static Function provideEncryptedDataRetriever(Keyring keyring) { + return keyring::getEncryptedData; + } } diff --git a/java/google/registry/keyring/api/Keyring.java b/java/google/registry/keyring/api/Keyring.java index e600ae529..74ee8bead 100644 --- a/java/google/registry/keyring/api/Keyring.java +++ b/java/google/registry/keyring/api/Keyring.java @@ -156,6 +156,12 @@ public interface Keyring extends AutoCloseable { */ String getJsonCredential(); + /** + * Returns the encrypted data for the given key name. Only use this method when decryption is not + * required. + */ + String getEncryptedData(String keyName); + // Don't throw so try-with-resources works better. @Override void close(); diff --git a/java/google/registry/keyring/kms/KmsKeyring.java b/java/google/registry/keyring/kms/KmsKeyring.java index 262145f91..75a2f97db 100644 --- a/java/google/registry/keyring/kms/KmsKeyring.java +++ b/java/google/registry/keyring/kms/KmsKeyring.java @@ -155,10 +155,27 @@ public class KmsKeyring implements Keyring { return getString(StringKeyLabel.JSON_CREDENTIAL_STRING); } + @Override + public String getEncryptedData(String keyName) { + KmsSecret secret = getSecret(keyName); + return ofy().load().key(secret.getLatestRevision()).now().getEncryptedValue(); + } + + private String getEncryptedData(KmsSecret secret) { + return ofy().load().key(secret.getLatestRevision()).now().getEncryptedValue(); + } + /** No persistent resources are maintained for this Keyring implementation. */ @Override public void close() {} + private KmsSecret getSecret(String keyName) { + KmsSecret secret = + ofy().load().key(Key.create(getCrossTldKey(), KmsSecret.class, keyName)).now(); + checkState(secret != null, "Requested secret '%s' does not exist.", keyName); + return secret; + } + private String getString(StringKeyLabel keyLabel) { return KeySerializer.deserializeString(getDecryptedData(keyLabel.getLabel())); } @@ -185,11 +202,8 @@ public class KmsKeyring implements Keyring { } private byte[] getDecryptedData(String keyName) { - KmsSecret secret = - ofy().load().key(Key.create(getCrossTldKey(), KmsSecret.class, keyName)).now(); - checkState(secret != null, "Requested secret '%s' does not exist.", keyName); - String encryptedData = ofy().load().key(secret.getLatestRevision()).now().getEncryptedValue(); - + KmsSecret secret = getSecret(keyName); + String encryptedData = getEncryptedData(secret); try { return kmsConnection.decrypt(secret.getName(), encryptedData); } catch (Exception e) { diff --git a/javatests/google/registry/keyring/kms/KmsKeyringTest.java b/javatests/google/registry/keyring/kms/KmsKeyringTest.java index 9bf52aca5..153bfaed9 100644 --- a/javatests/google/registry/keyring/kms/KmsKeyringTest.java +++ b/javatests/google/registry/keyring/kms/KmsKeyringTest.java @@ -16,17 +16,21 @@ 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.US_ASCII; import com.google.common.collect.ImmutableList; +import com.google.common.io.BaseEncoding; 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.UnsupportedEncodingException; import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.util.Arrays; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -174,6 +178,18 @@ public class KmsKeyringTest { assertThat(jsonCredential).isEqualTo("json-credential-stringmoo"); } + @Test + public void test_getEncryptedJsonCredential() throws UnsupportedEncodingException { + saveCleartextSecret("json-credential-string"); + + String encryptedJsonCredential = keyring.getEncryptedData("json-credential-string"); + + assertThat( + new String( + Arrays.reverse(BaseEncoding.base64().decode(encryptedJsonCredential)), US_ASCII)) + .isEqualTo("json-credential-stringmoo"); + } + private static void persistSecret(String secretName, byte[] secretValue) { KmsConnection kmsConnection = new FakeKmsConnection(); diff --git a/javatests/google/registry/testing/FakeKeyringModule.java b/javatests/google/registry/testing/FakeKeyringModule.java index f0328e6b6..afb6887d4 100644 --- a/javatests/google/registry/testing/FakeKeyringModule.java +++ b/javatests/google/registry/testing/FakeKeyringModule.java @@ -150,6 +150,12 @@ public final class FakeKeyringModule { return rdeReceiverKey; } + @Override + public String getEncryptedData(String keyName) { + throw new RuntimeException( + "Fake keyring does not support the retrieval of encrypted data."); + } + @Override public void close() {} };