From 045d7020da4384b460ffb1c4b8edea924b7ba118 Mon Sep 17 00:00:00 2001 From: Morten Tokle Date: Tue, 24 Oct 2017 13:08:24 +0200 Subject: separate interface from implementation --- .../jdisc/athenz/AthenzIdentityProvider.java | 144 +------------------- .../athenz/impl/AthenzIdentityProviderImpl.java | 145 +++++++++++++++++++++ .../container/jdisc/athenz/impl/package-info.java | 7 + .../jdisc/athenz/AthenzIdentityProviderTest.java | 3 +- 4 files changed, 157 insertions(+), 142 deletions(-) create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzIdentityProviderImpl.java create mode 100644 container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/package-info.java (limited to 'container-disc') diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java index f7717f93db0..55485f2a3ea 100644 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java @@ -1,149 +1,11 @@ package com.yahoo.container.jdisc.athenz; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.annotations.Beta; -import com.google.inject.Inject; -import com.yahoo.cloud.config.ConfigserverConfig; -import com.yahoo.component.AbstractComponent; -import com.yahoo.container.core.identity.IdentityConfig; -import com.yahoo.container.jdisc.athenz.impl.AthenzService; -import com.yahoo.container.jdisc.athenz.impl.InstanceIdentity; -import com.yahoo.container.jdisc.athenz.impl.InstanceRegisterInformation; -import com.yahoo.container.jdisc.athenz.impl.ServiceProviderApi; -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.ExtensionsGenerator; -import org.bouncycastle.asn1.x509.GeneralName; -import org.bouncycastle.asn1.x509.GeneralNames; -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; -import org.bouncycastle.pkcs.PKCS10CertificationRequest; -import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; -import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; -import org.bouncycastle.util.io.pem.PemObject; - -import javax.security.auth.x500.X500Principal; -import java.io.IOException; -import java.io.StringWriter; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; - /** * @author mortent */ -@Beta -// TODO Separate out into interface and "hidden" implementation -public final class AthenzIdentityProvider extends AbstractComponent { - - private InstanceIdentity instanceIdentity; - - private final String dnsSuffix; - private final String providerUniqueId; - - @Inject - public AthenzIdentityProvider(IdentityConfig config, ConfigserverConfig configserverConfig) throws IOException { - this(config, new ServiceProviderApi(configserverConfig.loadBalancerAddress()), new AthenzService()); - } - - // Test only - public AthenzIdentityProvider(IdentityConfig config, ServiceProviderApi serviceProviderApi, AthenzService athenzService) throws IOException { - KeyPair keyPair = createKeyPair(); - String signedIdentityDocument = serviceProviderApi.getSignedIdentityDocument(); - String athenzUrl = getZtsEndpoint(signedIdentityDocument); - dnsSuffix = getDnsSuffix(signedIdentityDocument); - providerUniqueId = getProviderUniqueId(signedIdentityDocument); - String providerServiceName = getProviderServiceName(signedIdentityDocument); - - InstanceRegisterInformation instanceRegisterInformation = new InstanceRegisterInformation( - providerServiceName, - config.domain(), - config.serviceName(), - signedIdentityDocument, - createCSR(keyPair, config), - true - ); - instanceIdentity = athenzService.sendInstanceRegisterRequest(instanceRegisterInformation, athenzUrl); - } - - private static String getProviderUniqueId(String signedIdentityDocument) throws IOException { - return getJsonNode(signedIdentityDocument, "provider-unique-id"); - } - - private static String getDnsSuffix(String signedIdentityDocument) throws IOException { - return getJsonNode(signedIdentityDocument, "dns-suffix"); - } - - private static String getProviderServiceName(String signedIdentityDocument) throws IOException { - return getJsonNode(signedIdentityDocument, "provider-service"); - } +public interface AthenzIdentityProvider { - private static String getZtsEndpoint(String signedIdentityDocument) throws IOException { - return getJsonNode(signedIdentityDocument, "zts-endpoint"); - } + String getNToken(); - private static String getJsonNode(String jsonString, String path) throws IOException { - ObjectMapper mapper = new ObjectMapper(); - JsonNode jsonNode = mapper.readTree(jsonString); - return jsonNode.get(path).asText(); - } - - private static KeyPair createKeyPair() { - try { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); - return kpg.generateKeyPair(); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } - } - - private String createCSR(KeyPair keyPair, IdentityConfig identityConfig) throws IOException { - - try { - // Add SAN dnsname .. - // and SAN dnsname .instanceid.athenz. - GeneralNames subjectAltNames = new GeneralNames(new GeneralName[]{ - new GeneralName(GeneralName.dNSName, String.format("%s.%s.%s", - identityConfig.serviceName(), - identityConfig.domain().replace(".", "-"), - dnsSuffix)), - new GeneralName(GeneralName.dNSName, String.format("%s.instanceid.athenz.%s", - providerUniqueId, - dnsSuffix)) - }); - - ExtensionsGenerator extGen = new ExtensionsGenerator(); - extGen.addExtension(Extension.subjectAlternativeName, false, subjectAltNames); - - X500Principal subject = new X500Principal( - String.format("CN=%s.%s", identityConfig.domain(), identityConfig.serviceName())); - - PKCS10CertificationRequestBuilder requestBuilder = - new JcaPKCS10CertificationRequestBuilder(subject, keyPair.getPublic()); - requestBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate()); - PKCS10CertificationRequest csr = requestBuilder.build( - new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate())); - - PemObject pemObject = new PemObject("CERTIFICATE REQUEST", csr.getEncoded()); - try (StringWriter stringWriter = new StringWriter()) { - try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) { - pemWriter.writeObject(pemObject); - return stringWriter.toString(); - } - } - } catch (OperatorCreationException e) { - throw new RuntimeException(e); - } - } - - public String getNToken() { - return instanceIdentity.getServiceToken(); - } - - public String getX509Cert() { - return instanceIdentity.getX509Certificate(); - } + String getX509Cert(); } - diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzIdentityProviderImpl.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzIdentityProviderImpl.java new file mode 100644 index 00000000000..d269747c418 --- /dev/null +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/AthenzIdentityProviderImpl.java @@ -0,0 +1,145 @@ +package com.yahoo.container.jdisc.athenz.impl; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Inject; +import com.yahoo.cloud.config.ConfigserverConfig; +import com.yahoo.component.AbstractComponent; +import com.yahoo.container.core.identity.IdentityConfig; +import com.yahoo.container.jdisc.athenz.AthenzIdentityProvider; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.ExtensionsGenerator; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; +import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; +import org.bouncycastle.util.io.pem.PemObject; + +import javax.security.auth.x500.X500Principal; +import java.io.IOException; +import java.io.StringWriter; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; + +/** + * @author mortent + */ +public final class AthenzIdentityProviderImpl extends AbstractComponent implements AthenzIdentityProvider { + + private InstanceIdentity instanceIdentity; + + private final String dnsSuffix; + private final String providerUniqueId; + + @Inject + public AthenzIdentityProviderImpl(IdentityConfig config, ConfigserverConfig configserverConfig) throws IOException { + this(config, new ServiceProviderApi(configserverConfig.loadBalancerAddress()), new AthenzService()); + } + + // Test only + public AthenzIdentityProviderImpl(IdentityConfig config, ServiceProviderApi serviceProviderApi, AthenzService athenzService) throws IOException { + KeyPair keyPair = createKeyPair(); + String signedIdentityDocument = serviceProviderApi.getSignedIdentityDocument(); + String athenzUrl = getZtsEndpoint(signedIdentityDocument); + dnsSuffix = getDnsSuffix(signedIdentityDocument); + providerUniqueId = getProviderUniqueId(signedIdentityDocument); + String providerServiceName = getProviderServiceName(signedIdentityDocument); + + InstanceRegisterInformation instanceRegisterInformation = new InstanceRegisterInformation( + providerServiceName, + config.domain(), + config.serviceName(), + signedIdentityDocument, + createCSR(keyPair, config), + true + ); + instanceIdentity = athenzService.sendInstanceRegisterRequest(instanceRegisterInformation, athenzUrl); + } + + private static String getProviderUniqueId(String signedIdentityDocument) throws IOException { + return getJsonNode(signedIdentityDocument, "provider-unique-id"); + } + + private static String getDnsSuffix(String signedIdentityDocument) throws IOException { + return getJsonNode(signedIdentityDocument, "dns-suffix"); + } + + private static String getProviderServiceName(String signedIdentityDocument) throws IOException { + return getJsonNode(signedIdentityDocument, "provider-service"); + } + + private static String getZtsEndpoint(String signedIdentityDocument) throws IOException { + return getJsonNode(signedIdentityDocument, "zts-endpoint"); + } + + private static String getJsonNode(String jsonString, String path) throws IOException { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.readTree(jsonString); + return jsonNode.get(path).asText(); + } + + private static KeyPair createKeyPair() { + try { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + return kpg.generateKeyPair(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + private String createCSR(KeyPair keyPair, IdentityConfig identityConfig) throws IOException { + + try { + // Add SAN dnsname .. + // and SAN dnsname .instanceid.athenz. + GeneralNames subjectAltNames = new GeneralNames(new GeneralName[]{ + new GeneralName(GeneralName.dNSName, String.format("%s.%s.%s", + identityConfig.serviceName(), + identityConfig.domain().replace(".", "-"), + dnsSuffix)), + new GeneralName(GeneralName.dNSName, String.format("%s.instanceid.athenz.%s", + providerUniqueId, + dnsSuffix)) + }); + + ExtensionsGenerator extGen = new ExtensionsGenerator(); + extGen.addExtension(Extension.subjectAlternativeName, false, subjectAltNames); + + X500Principal subject = new X500Principal( + String.format("CN=%s.%s", identityConfig.domain(), identityConfig.serviceName())); + + PKCS10CertificationRequestBuilder requestBuilder = + new JcaPKCS10CertificationRequestBuilder(subject, keyPair.getPublic()); + requestBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate()); + PKCS10CertificationRequest csr = requestBuilder.build( + new JcaContentSignerBuilder("SHA256withRSA").build(keyPair.getPrivate())); + + PemObject pemObject = new PemObject("CERTIFICATE REQUEST", csr.getEncoded()); + try (StringWriter stringWriter = new StringWriter()) { + try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) { + pemWriter.writeObject(pemObject); + return stringWriter.toString(); + } + } + } catch (OperatorCreationException e) { + throw new RuntimeException(e); + } + } + + @Override + public String getNToken() { + return instanceIdentity.getServiceToken(); + } + + @Override + public String getX509Cert() { + return instanceIdentity.getX509Certificate(); + } +} + diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/package-info.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/package-info.java new file mode 100644 index 00000000000..b239d8480ec --- /dev/null +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/impl/package-info.java @@ -0,0 +1,7 @@ +/** + * @author mortent + */ +@ExportPackage +package com.yahoo.container.jdisc.athenz.impl; + +import com.yahoo.osgi.annotation.ExportPackage; \ No newline at end of file diff --git a/container-disc/src/test/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProviderTest.java b/container-disc/src/test/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProviderTest.java index 2651cfd3a63..4577402b8c1 100644 --- a/container-disc/src/test/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProviderTest.java +++ b/container-disc/src/test/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProviderTest.java @@ -1,6 +1,7 @@ package com.yahoo.container.jdisc.athenz; import com.yahoo.container.core.identity.IdentityConfig; +import com.yahoo.container.jdisc.athenz.impl.AthenzIdentityProviderImpl; import com.yahoo.container.jdisc.athenz.impl.AthenzService; import com.yahoo.container.jdisc.athenz.impl.InstanceIdentity; import com.yahoo.container.jdisc.athenz.impl.ServiceProviderApi; @@ -29,7 +30,7 @@ public class AthenzIdentityProviderTest { when(athenzService.sendInstanceRegisterRequest(any(), anyString())).thenReturn( new InstanceIdentity(null, null, null, null, null, null, null, null, "TOKEN")); - AthenzIdentityProvider identityProvider = new AthenzIdentityProvider(config, serviceProviderApi, athenzService); + AthenzIdentityProvider identityProvider = new AthenzIdentityProviderImpl(config, serviceProviderApi, athenzService); Assert.assertEquals("TOKEN", identityProvider.getNToken()); } -- cgit v1.2.3