Skip to content

Commit a1f2ccd

Browse files
authored
Add Azure Signer to Multiplatform signing (Consensys#173)
1 parent bb4d6ee commit a1f2ccd

File tree

24 files changed

+477
-135
lines changed

24 files changed

+477
-135
lines changed

acceptance-tests/src/test/java/tech/pegasys/ethsigner/tests/dsl/signer/TransactionSignerParamsSupplier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public Collection<String> get() {
5757
params.add(String.valueOf(hashicorpVaultPort));
5858
} else if (azureKeyVault != null) {
5959
params.add("azure-signer");
60-
params.add("--keyvault-name");
60+
params.add("--key-vault-name");
6161
params.add(azureKeyVault);
6262
params.add("--key-name");
6363
params.add("TestKey");

ethsigner/signer/azure/src/main/java/tech/pegasys/ethsigner/signer/azure/AzureConfig.java

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,30 @@
1212
*/
1313
package tech.pegasys.ethsigner.signer.azure;
1414

15+
import static com.google.common.base.Preconditions.checkNotNull;
16+
1517
public class AzureConfig {
16-
private final String keyvaultName;
18+
private final String keyVaultName;
1719
private final String keyName;
1820
private final String keyVersion;
1921
private final String clientId;
2022
private final String clientSecret;
2123

2224
public AzureConfig(
23-
String keyvaultName,
24-
String keyName,
25-
String keyVersion,
26-
String clientId,
27-
String clientSecret) {
28-
this.keyvaultName = keyvaultName;
25+
final String keyVaultName,
26+
final String keyName,
27+
final String keyVersion,
28+
final String clientId,
29+
final String clientSecret) {
30+
this.keyVaultName = keyVaultName;
2931
this.keyName = keyName;
3032
this.keyVersion = keyVersion;
3133
this.clientId = clientId;
3234
this.clientSecret = clientSecret;
3335
}
3436

35-
public String getKeyvaultName() {
36-
return keyvaultName;
37+
public String getKeyVaultName() {
38+
return keyVaultName;
3739
}
3840

3941
public String getKeyName() {
@@ -51,4 +53,48 @@ public String getClientId() {
5153
public String getClientSecret() {
5254
return clientSecret;
5355
}
56+
57+
public static class AzureConfigBuilder {
58+
59+
private String keyVaultName;
60+
private String keyName;
61+
private String keyVersion;
62+
private String clientId;
63+
private String clientSecret;
64+
65+
public AzureConfigBuilder withKeyVaultName(final String keyVaultName) {
66+
this.keyVaultName = keyVaultName;
67+
return this;
68+
}
69+
70+
public AzureConfigBuilder withKeyName(final String keyName) {
71+
this.keyName = keyName;
72+
return this;
73+
}
74+
75+
public AzureConfigBuilder withKeyVersion(final String keyVersion) {
76+
this.keyVersion = keyVersion;
77+
return this;
78+
}
79+
80+
public AzureConfigBuilder withClientId(final String clientId) {
81+
this.clientId = clientId;
82+
return this;
83+
}
84+
85+
public AzureConfigBuilder withClientSecret(String clientSecret) {
86+
this.clientSecret = clientSecret;
87+
return this;
88+
}
89+
90+
public AzureConfig build() {
91+
checkNotNull(keyVaultName, "Key Vault Name was not set.");
92+
checkNotNull(keyName, "Key Name was not set.");
93+
checkNotNull(keyVersion, "Key Version was not set.");
94+
checkNotNull(clientId, "Client Id was not set.");
95+
checkNotNull(clientSecret, "Client Secret was not set.");
96+
97+
return new AzureConfig(keyVaultName, keyName, keyVersion, clientId, clientSecret);
98+
}
99+
}
54100
}

ethsigner/signer/azure/src/main/java/tech/pegasys/ethsigner/signer/azure/AzureKeyVaultTransactionSigner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public Signature sign(final byte[] data) {
5555

5656
if (signature.length != 64) {
5757
throw new RuntimeException(
58-
"Invalid signature from the keyvault signing service, must be 64 bytes long");
58+
"Invalid signature from the key vault signing service, must be 64 bytes long");
5959
}
6060

6161
// reference: blog by Tomislav Markovski

ethsigner/signer/azure/src/main/java/tech/pegasys/ethsigner/signer/azure/AzureKeyVaultTransactionSignerFactory.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ public TransactionSigner createSigner(final AzureConfig config) {
5151
checkNotNull(config, "Config must be specified");
5252
final KeyVaultClientCustom client =
5353
vaultAuthenticator.getAuthenticatedClient(config.getClientId(), config.getClientSecret());
54-
final String baseUrl = constructAzureKeyVaultUrl(config.getKeyvaultName());
54+
final String baseUrl = constructAzureKeyVaultUrl(config.getKeyVaultName());
5555

5656
final JsonWebKey key;
57-
final KeyIdentifier kid;
57+
final KeyIdentifier keyIdentifier;
5858
try {
59-
kid = new KeyIdentifier(baseUrl, config.getKeyName(), config.getKeyVersion());
60-
key = client.getKey(kid.toString()).key();
59+
keyIdentifier = new KeyIdentifier(baseUrl, config.getKeyName(), config.getKeyVersion());
60+
key = client.getKey(keyIdentifier.toString()).key();
6161
} catch (final KeyVaultErrorException ex) {
6262
if (ex.response().raw().code() == 401) {
6363
LOG.debug(INACCESSIBLE_KEY_ERROR);
@@ -82,7 +82,7 @@ public TransactionSigner createSigner(final AzureConfig config) {
8282

8383
final byte[] rawPublicKey = Bytes.concat(key.x(), key.y());
8484
final BigInteger publicKey = new BigInteger(1, rawPublicKey);
85-
return new AzureKeyVaultTransactionSigner(client, kid.toString(), publicKey);
85+
return new AzureKeyVaultTransactionSigner(client, keyIdentifier.toString(), publicKey);
8686
}
8787

8888
public static String constructAzureKeyVaultUrl(final String keyVaultName) {

ethsigner/signer/azure/src/main/java/tech/pegasys/ethsigner/signer/azure/AzureSubCommand.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
public class AzureSubCommand extends SignerSubCommand {
3535

3636
@Option(
37-
names = {"--keyvault-name"},
37+
names = {"--keyvault-name, --key-vault-name"},
3838
description = "Name of the vault to access - used as the sub-domain to vault.azure.net",
3939
required = true,
4040
arity = "1")
41-
private String keyvaultName;
41+
private String keyVaultName;
4242

4343
@Option(
4444
names = {"--key-name"},
@@ -54,7 +54,7 @@ public class AzureSubCommand extends SignerSubCommand {
5454

5555
@Option(
5656
names = {"--client-id"},
57-
description = "The ID used to authenticate with Azure keyvault",
57+
description = "The ID used to authenticate with Azure key vault",
5858
required = true)
5959
private String clientId;
6060

@@ -77,7 +77,7 @@ private TransactionSigner createSigner() throws TransactionSignerInitializationE
7777
}
7878

7979
final AzureConfig config =
80-
new AzureConfig(keyvaultName, keyName, keyVersion, clientId, clientSecret);
80+
new AzureConfig(keyVaultName, keyName, keyVersion, clientId, clientSecret);
8181

8282
final AzureKeyVaultTransactionSignerFactory factory =
8383
new AzureKeyVaultTransactionSignerFactory(new AzureKeyVaultAuthenticator());

ethsigner/signer/azure/src/test/java/tech/pegasys/ethsigner/signer/azure/AzureKeyVaultAuthenticatorTest.java

Lines changed: 6 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import tech.pegasys.ethsigner.TransactionSignerInitializationException;
2020
import tech.pegasys.ethsigner.core.signing.Signature;
2121
import tech.pegasys.ethsigner.core.signing.TransactionSigner;
22+
import tech.pegasys.ethsigner.signer.azure.AzureConfig.AzureConfigBuilder;
2223

2324
import java.math.BigInteger;
2425

@@ -117,7 +118,7 @@ public void accessingNonExistentKeyVaultThrowsExceptionWithMessage() {
117118
final AzureConfigBuilder configBuilder = createValidConfigBuilder();
118119

119120
final String invalidVaultName = "invalidKeyVault";
120-
configBuilder.withKeyvaultName(invalidVaultName);
121+
configBuilder.withKeyVaultName(invalidVaultName);
121122

122123
final String expectedMessage =
123124
String.format(
@@ -166,50 +167,12 @@ public void nullClientAndOrSecretAreHandledCleanly() {
166167
.isInstanceOf(IllegalArgumentException.class);
167168
}
168169

169-
private static class AzureConfigBuilder {
170-
171-
private String keyvaultName;
172-
private String keyName;
173-
private String keyVersion;
174-
private String clientId;
175-
private String clientSecret;
176-
177-
public AzureConfigBuilder withKeyvaultName(String keyvaultName) {
178-
this.keyvaultName = keyvaultName;
179-
return this;
180-
}
181-
182-
public AzureConfigBuilder withKeyName(String keyName) {
183-
this.keyName = keyName;
184-
return this;
185-
}
186-
187-
public AzureConfigBuilder withKeyVersion(String keyVersion) {
188-
this.keyVersion = keyVersion;
189-
return this;
190-
}
191-
192-
public AzureConfigBuilder withClientId(String clientId) {
193-
this.clientId = clientId;
194-
return this;
195-
}
196-
197-
public AzureConfigBuilder withClientSecret(String clientSecret) {
198-
this.clientSecret = clientSecret;
199-
return this;
200-
}
201-
202-
public AzureConfig build() {
203-
return new AzureConfig(keyvaultName, keyName, keyVersion, clientId, clientSecret);
204-
}
205-
}
206-
207170
private AzureConfigBuilder createValidConfigBuilder() {
208171
return new AzureConfigBuilder()
209-
.withClientId(clientId)
210-
.withClientSecret(clientSecret)
211-
.withKeyVersion(validKeyVersion)
172+
.withKeyVaultName("ethsignertestkey")
212173
.withKeyName("TestKey")
213-
.withKeyvaultName("ethsignertestkey");
174+
.withKeyVersion(validKeyVersion)
175+
.withClientId(clientId)
176+
.withClientSecret(clientSecret);
214177
}
215178
}

ethsigner/signer/multifile-based/src/main/java/tech/pegasys/ethsigner/signer/multifilebased/KeyPasswordLoader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Optional<KeyPasswordFile> loadKeyAndPasswordForAddress(final String address) {
4949
.collect(Collectors.toList());
5050

5151
if (matchingKeys.size() > 1) {
52-
LOG.error("Found multiple key/password matches for address {}", address);
52+
LOG.error("Found multiple key/password matches for address " + address);
5353
return Optional.empty();
5454
} else if (matchingKeys.isEmpty()) {
5555
return Optional.empty();

ethsigner/signer/multiplatform/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dependencies {
2828
implementation project(':ethsigner:commandline')
2929
implementation project(':ethsigner:signing-api')
3030
implementation project(':ethsigner:signer:file-based')
31+
implementation project(':ethsigner:signer:azure')
3132

3233
implementation 'info.picocli:picocli'
3334
implementation 'com.google.guava:guava'

ethsigner/signer/multiplatform/src/main/java/tech/pegasys/ethsigner/signer/multiplatform/MultiPlatformSubCommand.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import tech.pegasys.ethsigner.SignerSubCommand;
1616
import tech.pegasys.ethsigner.TransactionSignerInitializationException;
1717
import tech.pegasys.ethsigner.core.signing.TransactionSignerProvider;
18+
import tech.pegasys.ethsigner.signer.azure.AzureKeyVaultAuthenticator;
19+
import tech.pegasys.ethsigner.signer.azure.AzureKeyVaultTransactionSignerFactory;
1820

1921
import java.nio.file.Path;
2022

@@ -54,7 +56,12 @@ public TransactionSignerProvider createSignerFactory()
5456
throws TransactionSignerInitializationException {
5557
final SigningMetadataTomlConfigLoader signingMetadataTomlConfigLoader =
5658
new SigningMetadataTomlConfigLoader(directoryPath);
57-
return new MultiPlatformTransactionSignerProvider(signingMetadataTomlConfigLoader);
59+
60+
final AzureKeyVaultTransactionSignerFactory azureFactory =
61+
new AzureKeyVaultTransactionSignerFactory(new AzureKeyVaultAuthenticator());
62+
63+
return new MultiPlatformTransactionSignerProvider(
64+
signingMetadataTomlConfigLoader, azureFactory);
5865
}
5966

6067
@Override

ethsigner/signer/multiplatform/src/main/java/tech/pegasys/ethsigner/signer/multiplatform/MultiPlatformTransactionSignerProvider.java

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
import tech.pegasys.ethsigner.TransactionSignerInitializationException;
1616
import tech.pegasys.ethsigner.core.signing.TransactionSigner;
1717
import tech.pegasys.ethsigner.core.signing.TransactionSignerProvider;
18+
import tech.pegasys.ethsigner.signer.azure.AzureKeyVaultTransactionSignerFactory;
1819
import tech.pegasys.ethsigner.signer.filebased.FileBasedSignerFactory;
20+
import tech.pegasys.ethsigner.signer.multiplatform.metadata.AzureSigningMetadataFile;
21+
import tech.pegasys.ethsigner.signer.multiplatform.metadata.FileBasedSigningMetadataFile;
1922

2023
import java.util.Objects;
2124
import java.util.Optional;
@@ -25,41 +28,69 @@
2528
import org.apache.logging.log4j.LogManager;
2629
import org.apache.logging.log4j.Logger;
2730

28-
public class MultiPlatformTransactionSignerProvider implements TransactionSignerProvider {
31+
public class MultiPlatformTransactionSignerProvider
32+
implements TransactionSignerProvider, MultiSignerFactory {
2933

3034
private static final Logger LOG = LogManager.getLogger();
3135

3236
private final SigningMetadataTomlConfigLoader signingMetadataTomlConfigLoader;
37+
private final AzureKeyVaultTransactionSignerFactory azureFactory;
3338

3439
MultiPlatformTransactionSignerProvider(
35-
final SigningMetadataTomlConfigLoader signingMetadataTomlConfigLoader) {
40+
final SigningMetadataTomlConfigLoader signingMetadataTomlConfigLoader,
41+
final AzureKeyVaultTransactionSignerFactory azureFactory) {
3642
this.signingMetadataTomlConfigLoader = signingMetadataTomlConfigLoader;
43+
this.azureFactory = azureFactory;
3744
}
3845

3946
@Override
4047
public Optional<TransactionSigner> getSigner(final String address) {
41-
return signingMetadataTomlConfigLoader.loadMetadataForAddress(address).map(this::createSigner);
48+
return signingMetadataTomlConfigLoader
49+
.loadMetadataForAddress(address)
50+
.map(metadataFile -> metadataFile.createSigner(this));
4251
}
4352

4453
@Override
4554
public Set<String> availableAddresses() {
4655
return signingMetadataTomlConfigLoader.loadAvailableSigningMetadataTomlConfigs().stream()
47-
.map(this::createSigner)
56+
.map(metadataFile -> metadataFile.createSigner(this))
4857
.filter(Objects::nonNull)
4958
.map(TransactionSigner::getAddress)
5059
.collect(Collectors.toSet());
5160
}
5261

53-
private TransactionSigner createSigner(final FileBasedSigningMetadataFile signingMetadataFile) {
62+
@Override
63+
public TransactionSigner createSigner(final AzureSigningMetadataFile metadataFile) {
64+
final TransactionSigner signer;
65+
try {
66+
signer = azureFactory.createSigner(metadataFile.getConfig());
67+
} catch (final TransactionSignerInitializationException e) {
68+
LOG.error("Failed to construct Azure signer from " + metadataFile.getBaseFilename());
69+
return null;
70+
}
71+
72+
final String signerAddress = signer.getAddress();
73+
if (!signerAddress.equals(metadataFile.getBaseFilename())) {
74+
LOG.error(
75+
String.format(
76+
"Azure signer's Ethereum Address (%s) does not align with metadata filename (%s)",
77+
signerAddress, metadataFile.getBaseFilename()));
78+
return null;
79+
}
80+
LOG.info("Loaded signer for address {}", signerAddress);
81+
return signer;
82+
}
83+
84+
@Override
85+
public TransactionSigner createSigner(final FileBasedSigningMetadataFile metadataFile) {
5486
try {
5587
final TransactionSigner signer =
5688
FileBasedSignerFactory.createSigner(
57-
signingMetadataFile.getKeyPath(), signingMetadataFile.getPasswordPath());
58-
LOG.debug("Loaded signer with key '{}'", signingMetadataFile.getKeyPath().getFileName());
89+
metadataFile.getKeyPath(), metadataFile.getPasswordPath());
90+
LOG.debug("Loaded signer with key '{}'", metadataFile.getKeyPath().getFileName());
5991
return signer;
6092
} catch (final TransactionSignerInitializationException e) {
61-
LOG.warn(
62-
"Unable to load signer with key '{}'", signingMetadataFile.getKeyPath().getFileName(), e);
93+
LOG.error("Unable to load signer with key " + metadataFile.getKeyPath().getFileName(), e);
6394
return null;
6495
}
6596
}

0 commit comments

Comments
 (0)