Skip to content

Commit

Permalink
aws parameter store implementation for PrivateKeyStore interface (#2631)
Browse files Browse the repository at this point in the history
bcprov-ext-jdk18on is folded into bcprov-jdk18on from 1.78.1

Signed-off-by: Abhijeet V <31417623+abvaidya@users.noreply.github.com>
  • Loading branch information
abvaidya authored May 30, 2024
1 parent 89dede1 commit 95c568e
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 10 deletions.
5 changes: 0 additions & 5 deletions containers/jetty/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,6 @@
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk18on</artifactId>
<version>1.78</version>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
Expand Down
16 changes: 16 additions & 0 deletions libs/java/auth_core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@
<code.coverage.min>0.9007</code.coverage.min>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>${aws2.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
Expand Down Expand Up @@ -79,6 +91,10 @@
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>ssm</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright The Athenz Authors
*
* 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 com.yahoo.athenz.auth.impl;

import com.yahoo.athenz.auth.PrivateKeyStore;
import com.yahoo.athenz.auth.ServerPrivateKey;
import com.yahoo.athenz.auth.util.Crypto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.ssm.SsmClient;

import java.lang.invoke.MethodHandles;
import java.security.PrivateKey;

public class AWSParameterStorePrivateKeyStore implements PrivateKeyStore {

private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

private static final String ZMS_SERVICE = "zms";
private static final String ZTS_SERVICE = "zts";
private static final String MSD_SERVICE = "msd";

private static final String ATHENZ_PROP_ZMS_KEY_NAME = "athenz.aws.zms.key_name";
private static final String ATHENZ_PROP_ZMS_KEY_ID_NAME = "athenz.aws.zms.key_id_name";
private static final String ATHENZ_PROP_ZTS_KEY_NAME = "athenz.aws.zts.key_name";
private static final String ATHENZ_PROP_ZTS_KEY_ID_NAME = "athenz.aws.zts.key_id_name";
private static final String ATHENZ_PROP_MSD_KEY_NAME = "athenz.aws.msd.key_name";
private static final String ATHENZ_PROP_MSD_KEY_ID_NAME = "athenz.aws.msd.key_id_name";

private static final String ATHENZ_DEFAULT_KEY_NAME = "service_private_key";
private static final String ATHENZ_DEFAULT_KEY_ID_NAME = "service_private_key_id";

private final SsmClient ssmClient;

AWSParameterStorePrivateKeyStore(SsmClient ssmClient) {
this.ssmClient = ssmClient;
}

@Override
public char[] getSecret(String appName, String keyName) {
return getSsmParameter(keyName).toCharArray();
}

@Override
public ServerPrivateKey getPrivateKey(String service, String serverHostName, String serverRegion, String algorithm) {
if (serverRegion == null || serverRegion.isEmpty()) {
LOG.error("server region not specified");
return null;
}
String keyName;
String keyIdName;
final String objectSuffix = "." + algorithm.toLowerCase();
if (ZMS_SERVICE.equals(service)) {
keyName = System.getProperty(ATHENZ_PROP_ZMS_KEY_NAME, ATHENZ_DEFAULT_KEY_NAME) + objectSuffix;
keyIdName = System.getProperty(ATHENZ_PROP_ZMS_KEY_ID_NAME, ATHENZ_DEFAULT_KEY_ID_NAME) + objectSuffix;
} else if (ZTS_SERVICE.equals(service)) {
keyName = System.getProperty(ATHENZ_PROP_ZTS_KEY_NAME, ATHENZ_DEFAULT_KEY_NAME) + objectSuffix;
keyIdName = System.getProperty(ATHENZ_PROP_ZTS_KEY_ID_NAME, ATHENZ_DEFAULT_KEY_ID_NAME) + objectSuffix;
} else if (MSD_SERVICE.equals(service)) {
keyName = System.getProperty(ATHENZ_PROP_MSD_KEY_NAME, ATHENZ_DEFAULT_KEY_NAME) + objectSuffix;
keyIdName = System.getProperty(ATHENZ_PROP_MSD_KEY_ID_NAME, ATHENZ_DEFAULT_KEY_ID_NAME) + objectSuffix;
} else {
LOG.error("Unknown service specified: {}", service);
return null;
}

PrivateKey pkey = null;
try {
pkey = Crypto.loadPrivateKey(getSsmParameter(keyName));
} catch (Exception ex) {
LOG.error("unable to load private key", ex);
}
return pkey == null ? null : new ServerPrivateKey(pkey, getSsmParameter(keyIdName));
}

private String getSsmParameter(final String keyName) {
return ssmClient.getParameter(r -> r.name(keyName).withDecryption(true)).parameter().value();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright The Athenz Authors
*
* 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 com.yahoo.athenz.auth.impl;

import com.yahoo.athenz.auth.PrivateKeyStore;
import com.yahoo.athenz.auth.PrivateKeyStoreFactory;
import software.amazon.awssdk.services.ssm.SsmClient;

public class AWSParameterStorePrivateKeyStoreFactory implements PrivateKeyStoreFactory {

@Override
public PrivateKeyStore create() {
return new AWSParameterStorePrivateKeyStore(SsmClient.create());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright The Athenz Authors
*
* 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 com.yahoo.athenz.auth.impl;

import com.yahoo.athenz.auth.PrivateKeyStore;
import org.testng.annotations.Test;

import static org.testng.Assert.assertTrue;

public class AWSParameterStorePrivateKeyStoreFactoryTest {

@Test
public void createAWSParameterStorePrivateKeyStore() {
System.setProperty("aws.region", "us-west-2");
PrivateKeyStore privateKeyStore = new AWSParameterStorePrivateKeyStoreFactory().create();
assertTrue(privateKeyStore instanceof AWSParameterStorePrivateKeyStore);
System.clearProperty("aws.region");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright The Athenz Authors
*
* 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 com.yahoo.athenz.auth.impl;
import com.yahoo.athenz.auth.PrivateKeyStore;
import org.mockito.Mockito;
import org.testng.annotations.Test;
import software.amazon.awssdk.services.ssm.SsmClient;
import software.amazon.awssdk.services.ssm.model.GetParameterResponse;
import software.amazon.awssdk.services.ssm.model.Parameter;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.testng.Assert.*;

public class AWSParameterStorePrivateKeyStoreTest {

private AWSParameterStorePrivateKeyStoreFactory getFactory(final SsmClient ssmClient) {
return new AWSParameterStorePrivateKeyStoreFactory() {
@Override
public PrivateKeyStore create() {
return new AWSParameterStorePrivateKeyStore(ssmClient);
}
};
}

@Test
public void testGetSecret() {
SsmClient ssmClient = Mockito.mock(SsmClient.class);
when(ssmClient.getParameter(any(Consumer.class)))
.thenReturn(GetParameterResponse.builder().parameter(Parameter.builder().value("secret").build()).build());
AWSParameterStorePrivateKeyStore store = (AWSParameterStorePrivateKeyStore)getFactory(ssmClient).create();
assertEquals(store.getSecret("app1", "key1"), "secret".toCharArray());
}

@Test
public void testGetPrivateKey() throws IOException {
SsmClient ssmClient = Mockito.mock(SsmClient.class);
Path path = Paths.get("src/test/resources/unit_test_ec_private.key");
try (FileInputStream fis = new FileInputStream(path.toFile())) {
String secret = new String(fis.readAllBytes());
when(ssmClient.getParameter(any(Consumer.class)))
.thenReturn(GetParameterResponse.builder().parameter(Parameter.builder().value(secret).build()).build());
AWSParameterStorePrivateKeyStore store = (AWSParameterStorePrivateKeyStore)getFactory(ssmClient).create();
assertNotNull(store.getPrivateKey("zms", "host1", "region1", "EC"));
}
}

@Test
public void testGetPrivateKeyInvalidInputs() {
SsmClient ssmClient = Mockito.mock(SsmClient.class);
AWSParameterStorePrivateKeyStore store = (AWSParameterStorePrivateKeyStore)getFactory(ssmClient).create();
assertNull(store.getPrivateKey("unknown", "host1", "region1", "EC"));
assertNull(store.getPrivateKey("zms", "host1", "region1", "unknown"));
assertNull(store.getPrivateKey("zms", "host1", null, "RSA"));
}
}
5 changes: 0 additions & 5 deletions libs/java/server_common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,6 @@
<artifactId>bcutil-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk18on</artifactId>
<version>1.78</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
Expand Down

0 comments on commit 95c568e

Please sign in to comment.