diff options
Diffstat (limited to 'vespa-athenz')
3 files changed, 137 insertions, 1 deletions
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java new file mode 100644 index 00000000000..b3b5df0e68b --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSigner.java @@ -0,0 +1,85 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.identityprovider.client; + +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; +import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; +import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; +import com.yahoo.vespa.athenz.tls.SignatureAlgorithm; + +import java.nio.ByteBuffer; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.time.Instant; +import java.util.Base64; +import java.util.Set; +import java.util.TreeSet; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Generates and validates the signature for a {@link SignedIdentityDocument} + * + * @author bjorncs + */ +public class IdentityDocumentSigner { + + public String generateSignature(VespaUniqueInstanceId providerUniqueId, + AthenzService providerService, + String configServerHostname, + String instanceHostname, + Instant createdAt, + Set<String> ipAddresses, + IdentityType identityType, + PrivateKey privateKey) { + try { + Signature signer = createSigner(); + signer.initSign(privateKey); + writeToSigner(signer, providerUniqueId, providerService, configServerHostname, instanceHostname, createdAt, ipAddresses, identityType); + byte[] signature = signer.sign(); + return Base64.getEncoder().encodeToString(signature); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + public boolean hasValidSignature(SignedIdentityDocument doc, PublicKey publicKey) { + try { + Signature signer = createSigner(); + signer.initVerify(publicKey); + writeToSigner(signer, doc.providerUniqueId(), doc.providerService(), doc.configServerHostname(), doc.instanceHostname(), doc.createdAt(), doc.ipAddresses(), doc.identityType()); + return signer.verify(Base64.getDecoder().decode(doc.signature())); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + private static Signature createSigner() throws NoSuchAlgorithmException { + return Signature.getInstance(SignatureAlgorithm.SHA512_WITH_RSA.getAlgorithmName()); + } + + private static void writeToSigner(Signature signer, + VespaUniqueInstanceId providerUniqueId, + AthenzService providerService, + String configServerHostname, + String instanceHostname, + Instant createdAt, + Set<String> ipAddresses, + IdentityType identityType) throws SignatureException { + signer.update(providerUniqueId.asDottedString().getBytes(UTF_8)); + signer.update(providerService.getFullName().getBytes(UTF_8)); + signer.update(configServerHostname.getBytes(UTF_8)); + signer.update(instanceHostname.getBytes(UTF_8)); + ByteBuffer timestampAsBuffer = ByteBuffer.allocate(Long.BYTES); + timestampAsBuffer.putLong(createdAt.toEpochMilli()); + signer.update(timestampAsBuffer.array()); + for (String ipAddress : new TreeSet<>(ipAddresses)) { + signer.update(ipAddress.getBytes(UTF_8)); + } + signer.update(identityType.id().getBytes(UTF_8)); + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java index 3d85a12f714..2f3e2721751 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/tls/SignatureAlgorithm.java @@ -5,7 +5,8 @@ package com.yahoo.vespa.athenz.tls; * @author bjorncs */ public enum SignatureAlgorithm { - SHA256_WITH_RSA("SHA256withRSA"); + SHA256_WITH_RSA("SHA256withRSA"), + SHA512_WITH_RSA("SHA512withRSA"); private final String algorithmName; diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java new file mode 100644 index 00000000000..9cc500ee241 --- /dev/null +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/IdentityDocumentSignerTest.java @@ -0,0 +1,50 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.identityprovider.client; + +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.identityprovider.api.IdentityType; +import com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument; +import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; +import com.yahoo.vespa.athenz.tls.KeyAlgorithm; +import com.yahoo.vespa.athenz.tls.KeyUtils; +import org.junit.Test; + +import java.net.URI; +import java.security.KeyPair; +import java.time.Instant; +import java.util.Arrays; +import java.util.HashSet; + +import static com.yahoo.vespa.athenz.identityprovider.api.IdentityType.TENANT; +import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_DOCUMENT_VERSION; +import static com.yahoo.vespa.athenz.identityprovider.api.SignedIdentityDocument.DEFAULT_KEY_VERSION; +import static org.junit.Assert.*; + +/** + * @author bjorncs + */ +public class IdentityDocumentSignerTest { + + @Test + public void generates_and_validates_signature() { + IdentityDocumentSigner signer = new IdentityDocumentSigner(); + IdentityType identityType = TENANT; + VespaUniqueInstanceId id = + new VespaUniqueInstanceId(1, "cluster-id", "instance", "application", "tenant", "region", "environment", identityType); + AthenzService providerService = new AthenzService("vespa", "service"); + KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.RSA); + String configserverHostname = "configserverhostname"; + String instanceHostname = "instancehostname"; + Instant createdAt = Instant.EPOCH; + HashSet<String> ipAddresses = new HashSet<>(Arrays.asList("1.2.3.4", "::1")); + String signature = + signer.generateSignature(id, providerService, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType, keyPair.getPrivate()); + + SignedIdentityDocument signedIdentityDocument = new SignedIdentityDocument( + null, signature, DEFAULT_KEY_VERSION, id, "dns-suffix", providerService, URI.create("https://zts"), + DEFAULT_DOCUMENT_VERSION, configserverHostname, instanceHostname, createdAt, ipAddresses, identityType); + + assertTrue(signer.hasValidSignature(signedIdentityDocument, keyPair.getPublic())); + } + +}
\ No newline at end of file |