mirror of
https://github.com/google/nomulus.git
synced 2025-05-20 19:29:35 +02:00
Expose the functionality to decrypt given data using keyring
This allows us to provide the keyring a blob of encrypted data and a key name, and have it decrypt it for us. Also fixed javadoc length in Keyring.java. It seems like it was using a 80-character length limit. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=222995542
This commit is contained in:
parent
0ed0bcc99f
commit
4416601a1d
6 changed files with 70 additions and 30 deletions
|
@ -155,6 +155,11 @@ public final class InMemoryKeyring implements Keyring {
|
||||||
"In-memory keyring does not support the retrieval of encrypted data.");
|
"In-memory keyring does not support the retrieval of encrypted data.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getDecryptedData(String keyName, String encryptedData) {
|
||||||
|
throw new RuntimeException("In-memory keyring does not support decrypting of supplied data.");
|
||||||
|
}
|
||||||
|
|
||||||
/** Does nothing. */
|
/** Does nothing. */
|
||||||
@Override
|
@Override
|
||||||
public void close() {}
|
public void close() {}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Qualifier;
|
import javax.inject.Qualifier;
|
||||||
|
@ -134,4 +135,10 @@ public final class KeyModule {
|
||||||
static Function<String, String> provideEncryptedDataRetriever(Keyring keyring) {
|
static Function<String, String> provideEncryptedDataRetriever(Keyring keyring) {
|
||||||
return keyring::getEncryptedData;
|
return keyring::getEncryptedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("keyringDecrypter")
|
||||||
|
static BiFunction<String, String, byte[]> provideKeyringDecrypter(Keyring keyring) {
|
||||||
|
return keyring::getDecryptedData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@ import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
/**
|
/**
|
||||||
* Nomulus keyring interface.
|
* Nomulus keyring interface.
|
||||||
*
|
*
|
||||||
* <p>Separate methods are defined for each specific situation in which the
|
* <p>Separate methods are defined for each specific situation in which the registry server needs a
|
||||||
* registry server needs a secret value, like a PGP key or password.
|
* secret value, like a PGP key or password.
|
||||||
*/
|
*/
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
public interface Keyring extends AutoCloseable {
|
public interface Keyring extends AutoCloseable {
|
||||||
|
@ -31,11 +31,10 @@ public interface Keyring extends AutoCloseable {
|
||||||
/**
|
/**
|
||||||
* Returns the key which should be used to sign RDE deposits being uploaded to a third-party.
|
* Returns the key which should be used to sign RDE deposits being uploaded to a third-party.
|
||||||
*
|
*
|
||||||
* <p>When we give all our data to the escrow provider, they'll need
|
* <p>When we give all our data to the escrow provider, they'll need a signature to ensure the
|
||||||
* a signature to ensure the data is authentic.
|
* data is authentic.
|
||||||
*
|
*
|
||||||
* <p>This keypair should only be known to the domain registry shared
|
* <p>This keypair should only be known to the domain registry shared registry system.
|
||||||
* registry system.
|
|
||||||
*
|
*
|
||||||
* @see google.registry.rde.RdeUploadAction
|
* @see google.registry.rde.RdeUploadAction
|
||||||
*/
|
*/
|
||||||
|
@ -44,12 +43,10 @@ public interface Keyring extends AutoCloseable {
|
||||||
/**
|
/**
|
||||||
* Returns public key for encrypting escrow deposits being staged to cloud storage.
|
* Returns public key for encrypting escrow deposits being staged to cloud storage.
|
||||||
*
|
*
|
||||||
* <p>This adds an additional layer of security so cloud storage administrators
|
* <p>This adds an additional layer of security so cloud storage administrators won't be tempted
|
||||||
* won't be tempted to go poking around the App Engine Cloud Console and see a
|
* to go poking around the App Engine Cloud Console and see a dump of the entire database.
|
||||||
* dump of the entire database.
|
|
||||||
*
|
*
|
||||||
* <p>This keypair should only be known to the domain registry shared
|
* <p>This keypair should only be known to the domain registry shared registry system.
|
||||||
* registry system.
|
|
||||||
*
|
*
|
||||||
* @see #getRdeStagingDecryptionKey()
|
* @see #getRdeStagingDecryptionKey()
|
||||||
*/
|
*/
|
||||||
|
@ -58,10 +55,9 @@ public interface Keyring extends AutoCloseable {
|
||||||
/**
|
/**
|
||||||
* Returns private key for decrypting escrow deposits retrieved from cloud storage.
|
* Returns private key for decrypting escrow deposits retrieved from cloud storage.
|
||||||
*
|
*
|
||||||
* <p>This method may impose restrictions on who can call it. For example, we'd want
|
* <p>This method may impose restrictions on who can call it. For example, we'd want to check that
|
||||||
* to check that the caller isn't an HTTP request attacking a vulnerability in the
|
* the caller isn't an HTTP request attacking a vulnerability in the admin console. The request
|
||||||
* admin console. The request should originate from a backend task queue servlet
|
* should originate from a backend task queue servlet invocation of the RDE upload thing.
|
||||||
* invocation of the RDE upload thing.
|
|
||||||
*
|
*
|
||||||
* @see #getRdeStagingEncryptionKey()
|
* @see #getRdeStagingEncryptionKey()
|
||||||
* @see google.registry.rde.RdeUploadAction
|
* @see google.registry.rde.RdeUploadAction
|
||||||
|
@ -92,9 +88,9 @@ public interface Keyring extends AutoCloseable {
|
||||||
/**
|
/**
|
||||||
* Returns public key for SSH client connections made by RDE.
|
* Returns public key for SSH client connections made by RDE.
|
||||||
*
|
*
|
||||||
* <p>This is a string containing what would otherwise be the contents of an
|
* <p>This is a string containing what would otherwise be the contents of an {@code
|
||||||
* {@code ~/.ssh/id_rsa.pub} file. It's usually a single line with the name of
|
* ~/.ssh/id_rsa.pub} file. It's usually a single line with the name of the algorithm, the base64
|
||||||
* the algorithm, the base64 key, and the email address of the owner.
|
* key, and the email address of the owner.
|
||||||
*
|
*
|
||||||
* @see google.registry.rde.RdeUploadAction
|
* @see google.registry.rde.RdeUploadAction
|
||||||
*/
|
*/
|
||||||
|
@ -103,13 +99,12 @@ public interface Keyring extends AutoCloseable {
|
||||||
/**
|
/**
|
||||||
* Returns private key for SSH client connections made by RDE.
|
* Returns private key for SSH client connections made by RDE.
|
||||||
*
|
*
|
||||||
* <p>This is a string containing what would otherwise be the contents of an
|
* <p>This is a string containing what would otherwise be the contents of an {@code ~/.ssh/id_rsa}
|
||||||
* {@code ~/.ssh/id_rsa} file. It's ASCII-armored text.
|
* file. It's ASCII-armored text.
|
||||||
*
|
*
|
||||||
* <p>This method may impose restrictions on who can call it. For example, we'd want
|
* <p>This method may impose restrictions on who can call it. For example, we'd want to check that
|
||||||
* to check that the caller isn't an HTTP request attacking a vulnerability in the
|
* the caller isn't an HTTP request attacking a vulnerability in the admin console. The request
|
||||||
* admin console. The request should originate from a backend task queue servlet
|
* should originate from a backend task queue servlet invocation of the RDE upload thing.
|
||||||
* invocation of the RDE upload thing.
|
|
||||||
*
|
*
|
||||||
* @see google.registry.rde.RdeUploadAction
|
* @see google.registry.rde.RdeUploadAction
|
||||||
*/
|
*/
|
||||||
|
@ -162,6 +157,9 @@ public interface Keyring extends AutoCloseable {
|
||||||
*/
|
*/
|
||||||
String getEncryptedData(String keyName);
|
String getEncryptedData(String keyName);
|
||||||
|
|
||||||
|
/** Decrypts the given encrypted data using the key name. */
|
||||||
|
byte[] getDecryptedData(String keyName, String encryptedData);
|
||||||
|
|
||||||
// Don't throw so try-with-resources works better.
|
// Don't throw so try-with-resources works better.
|
||||||
@Override
|
@Override
|
||||||
void close();
|
void close();
|
||||||
|
|
|
@ -205,13 +205,27 @@ public class KmsKeyring implements Keyring {
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getDecryptedData(String keyName) {
|
private byte[] getDecryptedData(String keyName) {
|
||||||
KmsSecret secret = getSecret(keyName);
|
String encryptedData = getEncryptedData(keyName);
|
||||||
|
return getDecryptedData(keyName, encryptedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getDecryptedData(KmsSecret secret) {
|
||||||
String encryptedData = getEncryptedData(secret);
|
String encryptedData = getEncryptedData(secret);
|
||||||
|
return getDecryptedData(secret, encryptedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getDecryptedData(KmsSecret secret, String encryptedData) {
|
||||||
try {
|
try {
|
||||||
return kmsConnection.decrypt(secret.getName(), encryptedData);
|
return kmsConnection.decrypt(secret.getName(), encryptedData);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new KeyringException(
|
throw new KeyringException(
|
||||||
String.format("CloudKMS decrypt operation failed for secret %s", keyName), e);
|
String.format("CloudKMS decrypt operation failed for secret %s", secret.getName()), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getDecryptedData(String keyName, String encryptedData) {
|
||||||
|
KmsSecret secret = getSecret(keyName);
|
||||||
|
return getDecryptedData(secret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ package google.registry.keyring.kms;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static google.registry.testing.DatastoreHelper.persistResources;
|
import static google.registry.testing.DatastoreHelper.persistResources;
|
||||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
|
@ -26,7 +26,6 @@ import google.registry.model.server.KmsSecretRevision;
|
||||||
import google.registry.model.server.KmsSecretRevision.Builder;
|
import google.registry.model.server.KmsSecretRevision.Builder;
|
||||||
import google.registry.testing.AppEngineRule;
|
import google.registry.testing.AppEngineRule;
|
||||||
import google.registry.testing.BouncyCastleProviderRule;
|
import google.registry.testing.BouncyCastleProviderRule;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import org.bouncycastle.openpgp.PGPKeyPair;
|
import org.bouncycastle.openpgp.PGPKeyPair;
|
||||||
import org.bouncycastle.openpgp.PGPPrivateKey;
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
@ -179,14 +178,26 @@ public class KmsKeyringTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_getEncryptedJsonCredential() throws UnsupportedEncodingException {
|
public void test_getEncryptedJsonCredential() {
|
||||||
saveCleartextSecret("json-credential-string");
|
saveCleartextSecret("json-credential-string");
|
||||||
|
|
||||||
String encryptedJsonCredential = keyring.getEncryptedData("json-credential-string");
|
String encryptedJsonCredential = keyring.getEncryptedData("json-credential-string");
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
new String(
|
new String(
|
||||||
Arrays.reverse(BaseEncoding.base64().decode(encryptedJsonCredential)), US_ASCII))
|
Arrays.reverse(BaseEncoding.base64().decode(encryptedJsonCredential)), UTF_8))
|
||||||
|
.isEqualTo("json-credential-stringmoo");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_decryptJsonCredential() {
|
||||||
|
saveCleartextSecret("json-credential-string");
|
||||||
|
|
||||||
|
String encryptedJsonCredential = keyring.getEncryptedData("json-credential-string");
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
new String(
|
||||||
|
keyring.getDecryptedData("json-credential-string", encryptedJsonCredential), UTF_8))
|
||||||
.isEqualTo("json-credential-stringmoo");
|
.isEqualTo("json-credential-stringmoo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,6 +156,11 @@ public final class FakeKeyringModule {
|
||||||
"Fake keyring does not support the retrieval of encrypted data.");
|
"Fake keyring does not support the retrieval of encrypted data.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getDecryptedData(String keyName, String encryptedData) {
|
||||||
|
throw new RuntimeException("Fake keyring does not support decrypting of supplied data.");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {}
|
public void close() {}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue