[java-identity-provider] 05/07: IDP-1595 Start to add the per plugin truststore
Rod Widdowson
rdw at steadingsoftware.com
Tue Jun 9 09:52:31 UTC 2020
This is an automated email from the git hooks/post-receive script.
rdw pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=2a421608bb2958d623da28cfe2b882c2538f0f41
commit 2a421608bb2958d623da28cfe2b882c2538f0f41
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Tue May 19 11:12:54 2020 +0100
IDP-1595 Start to add the per plugin truststore
https://issues.shibboleth.net/jira/browse/IDP-1595
---
idp-installer/pom.xml | 21 +-
.../idp/installer/plugin/impl/TrustStore.java | 218 +++++++++++++++++++++
.../idp/installer/plugin/impl/TrustStoreTest.java | 100 ++++++++++
.../net/shibboleth/idp/installer/plugin/keys.txt | 189 ++++++++++++++++++
.../shibboleth/idp/installer/plugin/shib.ico.asc | 16 ++
5 files changed, 539 insertions(+), 5 deletions(-)
diff --git a/idp-installer/pom.xml b/idp-installer/pom.xml
index b7ef11caf..9e0564cd7 100644
--- a/idp-installer/pom.xml
+++ b/idp-installer/pom.xml
@@ -78,11 +78,27 @@
<artifactId>spring-core</artifactId>
</dependency>
+ <dependency>
+ <groupId>net.shibboleth.ext</groupId>
+ <artifactId>spring-extensions</artifactId>
+ </dependency>
+
<dependency>
<groupId>net.shibboleth.ext</groupId>
<artifactId>spring-extensions</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpg-jdk15on</artifactId>
+ <version>${bouncycastle.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+
<!-- Provided Dependencies -->
<!-- Runtime Dependencies -->
@@ -99,11 +115,6 @@
<artifactId>opensaml-saml-impl</artifactId>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk15on</artifactId>
- <scope>test</scope>
- </dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/TrustStore.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/TrustStore.java
new file mode 100644
index 000000000..4ca82cbfc
--- /dev/null
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/plugin/impl/TrustStore.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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 net.shibboleth.idp.installer.plugin.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Collections;
+
+import javax.annotation.Nonnull;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureList;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.utilities.java.support.component.AbstractInitializableComponent;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+/**
+ * Code to handle (load, update, check) the trust store for an individual plugin.
+ * a thin shim on BC.
+ */
+public final class TrustStore extends AbstractInitializableComponent {
+
+ /** logger. */
+ @NonnullAfterInit private final Logger log = LoggerFactory.getLogger(TrustStore.class);
+
+ /** Where the IdP is installed. */
+ @NonnullAfterInit private String idpHome;
+
+ /** The plugin this is the trust store for. */
+ @NonnullAfterInit private String pluginId;
+
+ /** The key store. */
+ @NonnullAfterInit private Path store;
+
+ /** The key store backup. */
+ @NonnullAfterInit private Path backup;
+
+ /** KeyRing. */
+ @NonnullAfterInit private PGPPublicKeyRingCollection keyRings;
+
+ /** Set the pluginId.
+ * @param what The id to set.
+ */
+ public void setPluginId(final String what) {
+ pluginId = what;
+ }
+
+ /** Set the IdPHome.
+ * @param what The idpHome to set.
+ */
+ public void setIdpHome(final String what) {
+ idpHome = what;
+ }
+
+ /** Load the store from its designated location.
+ *
+ * @throws IOException from {@link Files#newInputStream(Path, java.nio.file.OpenOption...)} and from
+ * {@link PGPPublicKeyRingCollection#PGPPublicKeyRingCollection(InputStream,
+ * org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator)}
+ */
+ protected void loadStore() throws IOException {
+ try (final InputStream in = Files.newInputStream(store);
+ final InputStream in2 = PGPUtil.getDecoderStream(in)) {
+ keyRings = new PGPPublicKeyRingCollection(in2, new JcaKeyFingerprintCalculator());
+ } catch (final PGPException e) {
+ throw new IOException("Bad keystore", e);
+ }
+ }
+
+
+ /** Create an empty store and save to new location.
+ *
+ * @throws IOException from {@link #saveStore()} and in the unlikely event that
+ * {@link PGPPublicKeyRingCollection#PGPPublicKeyRingCollection(java.util.Collection)}
+ * has problems.
+ */
+ protected void createNewStore() throws IOException {
+ try {
+ keyRings = new PGPPublicKeyRingCollection(Collections.emptyList());
+ } catch (final PGPException e) {
+ throw new IOException("Bad keystore", e);
+ }
+ saveStore();
+ }
+
+ /** Save the store to its designated location.
+ *
+ * @throws IOException from {@link Files#newOutputStream(Path, java.nio.file.OpenOption...)} and
+ * from {@link PGPPublicKeyRingCollection#encode(OutputStream)}
+ */
+ public void saveStore() throws IOException {
+ if (Files.exists(store)) {
+ Files.copy(store, backup, StandardCopyOption.REPLACE_EXISTING);
+ }
+ try (final OutputStream out = Files.newOutputStream(store)) {
+ keyRings.encode(out);
+ }
+ }
+
+ /** Provide an opaque signature object from an input stream.
+ * @param stream what to read.
+ * @return the Signature.
+ * @throws IOException if there is a problem reading the file of it it doesn't represent a signature
+ */
+ public static Signature signatureOf(final InputStream stream) throws IOException {
+ return new Signature(stream);
+ }
+
+ /** Does the key that made this signature exist in our keyrings?
+ * @param signature what to ask about
+ * @return whether it is there
+ */
+ public boolean contains(final Signature signature) {
+
+ final PGPSignature sig = signature.getSignature();
+
+ for (final PGPPublicKeyRing keyRing : keyRings) {
+ for (final PGPPublicKey key : keyRing) {
+ if (sig.getKeyID() == key.getKeyID()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (idpHome == null) {
+ throw new ComponentInitializationException("IdP home not set up");
+ }
+
+ if (pluginId == null) {
+ throw new ComponentInitializationException("Plugin IN not set up");
+ }
+
+ final Path home = Path.of(idpHome);
+ if (!Files.exists(home)) {
+ throw new ComponentInitializationException("IdP home '" + idpHome + "' does not exist");
+ }
+
+ try {
+ final Path parent = home.resolve("credentials").resolve(pluginId);
+ if (!Files.exists(parent)) {
+ log.info("Plugin {}: Trust store folder does not exist, creating", pluginId);
+ Files.createDirectories(parent);
+ }
+ store = parent.resolve("truststore.asc");
+ backup = parent.resolve("truststore.asc.backup");
+ if (!Files.exists(store)) {
+ log.info("Plugin {}: Trust store does not exist, creating", pluginId);
+ createNewStore();
+ } else {
+ log.debug("Plugin {}: Trust store exists, loading", pluginId);
+ loadStore();
+ }
+ } catch (final IOException e) {
+ throw new ComponentInitializationException(e);
+ }
+ }
+
+ /**
+ * An opaque handle around a {@link PGPSignature}.
+ */
+ public static final class Signature {
+
+ /** What we are hiding. */
+ @Nonnull private PGPSignature signature;
+
+ protected Signature(final @Nonnull InputStream input) throws IOException {
+ try (final InputStream sigStream = PGPUtil.getDecoderStream(input)) {
+ final JcaPGPObjectFactory factory = new JcaPGPObjectFactory(sigStream);
+ final Object first = factory.nextObject();
+ if (first instanceof PGPSignatureList) {
+ final PGPSignatureList list = (PGPSignatureList) first;
+ signature = list.get(0);
+ } else {
+ throw new IOException("Provided file was not a signature");
+ }
+ }
+ }
+
+ protected PGPSignature getSignature() {
+ return signature;
+ }
+ }
+}
diff --git a/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/TrustStoreTest.java b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/TrustStoreTest.java
new file mode 100644
index 000000000..88252ce7e
--- /dev/null
+++ b/idp-installer/src/test/java/net/shibboleth/idp/installer/plugin/impl/TrustStoreTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You 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 net.shibboleth.idp.installer.plugin.impl;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import net.shibboleth.idp.installer.plugin.impl.TrustStore.Signature;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+ at SuppressWarnings("javadoc")
+public class TrustStoreTest {
+
+ private final static String pluginId = "net.shibboleth.plugin.test";
+
+ private Path dir;
+
+
+ @BeforeClass public void setup() throws IOException {
+ dir = Files.createTempDirectory("TrustStoreTest");
+ }
+
+ @AfterClass public void teardown() throws IOException {
+
+ Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+ /** {@inheritDoc} */
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ Files.delete(file);
+ return FileVisitResult.CONTINUE;
+ }
+
+ /** {@inheritDoc} */
+ public FileVisitResult postVisitDirectory(Path directory, IOException exc) throws IOException {
+ Files.delete(directory);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ }
+
+ @AfterMethod public void deleteStore() throws IOException {
+ Files.deleteIfExists(dir.resolve("credentials").resolve(pluginId).resolve("truststore.asc"));
+ }
+
+ @Test public void signatureAbsentTest() throws ComponentInitializationException, IOException {
+ final TrustStore ts = new TrustStore();
+ ts.setIdpHome(dir.toString());
+ ts.setPluginId(pluginId);
+ ts.initialize();
+ try (InputStream sigStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico.asc")) {
+ final Signature signature = TrustStore.signatureOf(sigStream);
+ assertFalse(ts.contains(signature));
+ }
+ }
+
+ @Test public void signaturePresentTest() throws ComponentInitializationException, IOException {
+ try (final InputStream trustStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/keys.txt");
+ final OutputStream outStream = Files.newOutputStream(dir.resolve("credentials").resolve(pluginId).resolve("truststore.asc"))) {
+ trustStream.transferTo(outStream);
+ }
+
+ final TrustStore ts = new TrustStore();
+ ts.setIdpHome(dir.toString());
+ ts.setPluginId(pluginId);
+ ts.initialize();
+ try( final InputStream sigStream = TrustStoreTest.class.getResourceAsStream("/net/shibboleth/idp/installer/plugin/shib.ico.asc")) {
+ final Signature signature = TrustStore.signatureOf(sigStream);
+ assertTrue(ts.contains(signature));
+ }
+ }
+
+}
diff --git a/idp-installer/src/test/resources/net/shibboleth/idp/installer/plugin/keys.txt b/idp-installer/src/test/resources/net/shibboleth/idp/installer/plugin/keys.txt
new file mode 100644
index 000000000..0ccc32281
--- /dev/null
+++ b/idp-installer/src/test/resources/net/shibboleth/idp/installer/plugin/keys.txt
@@ -0,0 +1,189 @@
+
+pub 4096R/2A4B3FF0 2019-09-18
+uid Rod Widdowson <rdw at steadingsoftware.com>
+sub 4096R/441D628D 2019-09-18
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+HEdQFQ==
+=/gwj
+-----END PGP PUBLIC KEY BLOCK-----
+
+OBS security:shibboleth RPM repository key
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+mQGiBEholYgRBACW/kTYZi5mAEMP8j1qui2bRnWmYblFbiZvb5JMJYUWL/jyMjHj
+LLJvVrnOre/AxexH9KmaJwBuNoa/X9l/tQGIb49QRhR346QQUbQcwlYpckg5ccqN
+qlAURHEdjCxRxMhzPs+C/F6Nqa/fHpAectW0JNRqAVVd9CWjCG3l6I2CywCgk9UI
+SUAvaB5bVMxEVrFAKrVh4MUEAISeUwOaTIZftIamjo0VrnYemHS4SmGqMALEtHeG
+/o7ecMhLb/MvreEVISrE1hbfmnObYoiVXWJrorOEZDh1hOVdGRkHJOYHvQSRp9uC
+/uy4Mmog4R7ba5Ct5wpw0RCav2HMqwOJEyCX6jnip5P5LMmaFdGg/RQZM/e9mbyF
+VeL0A/0WK//+VWrQya1rQIG+v4IZ+rWvrkqXKtrzELkWd/3vWNMXi4BfeBM/itLQ
+yYOdhnx55FHxBzv+dK+UErrdTM8Ingljy7oztV/G6+K15CcGvEy9ITb62v2bDdS2
+uYWEFOQJK+I6aU4paNysvtKsOlTt9FhyfJJW9G3kJDUBPM9HbLRIc2VjdXJpdHk6
+c2hpYmJvbGV0aCBPQlMgUHJvamVjdCA8c2VjdXJpdHk6c2hpYmJvbGV0aEBidWls
+ZC5vcGVuc3VzZS5vcmc+iGYEExECACYFAlgaKTsCGwMFCRPQQ7MGCwkIBwMCBBUC
+CAMEFgIDAQIeAQIXgAAKCRBzyTdFfQobPWC4AJ96FdlQrvlAHzQjqT994h6lZxfZ
+pgCfSBPRhrdNGh72+d288vgZ7ptV4/SIRgQTEQIABgUCSGiViAAKCRA7MBG3a51l
+Iy3vAKCUPurlZup+vzQtpij3FMo0JAVW9ACgjdkt/hWt0WsjfHb+/cPwXtgx9X8=
+=6fV2
+-----END PGP PUBLIC KEY BLOCK-----
+
+Henri Mikkonen PGP key
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+A+i1fd0Gifis1HhBeA==
+=ObHY
+-----END PGP PUBLIC KEY BLOCK-----
+
+philsmart GPG key 4096-bit RSA key, ID C21771DD, created 2020-02-25
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v2.0.22 (GNU/Linux)
+
+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
+3aNjQSVwGC4Owz97QpUq9StZb1qhvIw=
+=1e4x
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/idp-installer/src/test/resources/net/shibboleth/idp/installer/plugin/shib.ico.asc b/idp-installer/src/test/resources/net/shibboleth/idp/installer/plugin/shib.ico.asc
new file mode 100644
index 000000000..a4f481adf
--- /dev/null
+++ b/idp-installer/src/test/resources/net/shibboleth/idp/installer/plugin/shib.ico.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCAAdFiEESvTYPu3fQ9o8BssxAUg/JipLP/AFAl7CnsoACgkQAUg/JipL
+P/Ap8hAAjxJL7rOnflKnduC6pm3Yj81sQ2nlGtemAgNBU0AJvPwN4huj5doejDG+
+IpNR48eqQlJPXEplg5FHa53PvJMuptD2Ii+jvzEJjZvaZgt3NwzRPO90Eay+ceWs
+ojHDUFHROJdxwCudmV3SLQzARB8TN1ZZdo49zjUFd9mPlbB953frvQtvLlefGGyU
+sKtISfdbtjZ5xbpxDgLcu/4t83RPRSDhMfcG+2wMBATAv+Pt5LshKvAf7QAcLc0g
+/T50i82ppxsuU+Jbsc7tRkYJ7yHWfkhfZKGztsLLCfcYNR0O5Q9bI1CeSqyOd9gj
+V4NZsuK8mEF7GhdTqGMOEVs191dXkN7PasNeG8eZzgdIDQgUdR5y8oXJyCVU+nMl
++Q4XUwou+tuZY1SCU+v8rBjbVM/6GwDlGcWbpv2lQXIqd1hLjJ6dZNgNg3A0VhPN
+Sd17t5yBcraqVkSsxGMGOq6iIY9QH2f5MeHzyBkuqLQr0TefLpGhaHSpcg0MPtec
+Xt5wcNIdyCjtgPl9Nu0zRiukYCBJN+2No6hcfKDgjPuqPCoRphBskIYQ+10kmcF6
+kM0+jdX5pYfaaQe7rIpqRXH43jpzkfGVF4TZB5sr5kdc/1hQJ/oc7/ufdS8taNtj
+gFHdJkGXAMdJiojoJEun5tBiOE4Svmy/II6PEapyZ5D7MeSVITA=
+=0lzv
+-----END PGP SIGNATURE-----
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list