You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2019/08/26 08:38:55 UTC

[sling-org-apache-sling-committer-cli] 01/01: experiment with bouncycastle for release signature check

This is an automated email from the ASF dual-hosted git repository.

radu pushed a commit to branch bouncycastle-pgp
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-committer-cli.git

commit 1c6ea560ddd03947c0fd0b4e14104a1db13b55d6
Author: Radu Cotescu <ra...@apache.org>
AuthorDate: Mon Aug 26 10:38:11 2019 +0200

    experiment with bouncycastle for release signature check
---
 pom.xml                                            |  12 +++
 .../sling/cli/impl/pgp/PGPSignaturesValidator.java | 104 +++++++++++++++++++++
 .../cli/impl/pgp/SignatureVerificationResult.java  |  42 +++++++++
 3 files changed, 158 insertions(+)

diff --git a/pom.xml b/pom.xml
index 4a00263..5fc6e33 100644
--- a/pom.xml
+++ b/pom.xml
@@ -251,6 +251,18 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpg-jdk15on</artifactId>
+            <version>1.62</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.62</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
         </dependency>
diff --git a/src/main/java/org/apache/sling/cli/impl/pgp/PGPSignaturesValidator.java b/src/main/java/org/apache/sling/cli/impl/pgp/PGPSignaturesValidator.java
new file mode 100644
index 0000000..43db756
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/pgp/PGPSignaturesValidator.java
@@ -0,0 +1,104 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF 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 org.apache.sling.cli.impl.pgp;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.bouncycastle.bcpg.ArmoredInputStream;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPObjectFactory;
+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.operator.bc.BcKeyFingerprintCalculator;
+import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.osgi.service.component.annotations.Component;
+
+@Component(service = PGPSignaturesValidator.class)
+public class PGPSignaturesValidator {
+
+    private static final String KEYS_FILE = "/tmp/sling-keys.asc";
+
+    public SignatureVerificationResult verify(File artifact, File signatureFile) {
+        PGPPublicKeyRingCollection keyRing = readKeyRing();
+        try (
+                InputStream file = new FileInputStream(artifact);
+                InputStream signature = new FileInputStream(signatureFile)
+        ) {
+            InputStream sigInputStream = PGPUtil.getDecoderStream(signature);
+            PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(sigInputStream, new BcKeyFingerprintCalculator());
+            PGPSignatureList sigList = (PGPSignatureList) pgpObjectFactory.nextObject();
+            PGPSignature pgpSignature = sigList.get(0);
+            PGPPublicKey key = keyRing.getPublicKey(pgpSignature.getKeyID());
+            if (key == null) {
+                throw new IllegalStateException(String.format("Signature %s was not generated with any of the known keys.",
+                        signatureFile.getName()));
+            }
+            pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), key);
+            try (InputStream inArtifact = new BufferedInputStream(file)) {
+                int t;
+                while ((t = inArtifact.read()) >= 0) {
+                    pgpSignature.update((byte) t);
+                }
+            }
+            return new SignatureVerificationResult(pgpSignature.verify(), key);
+        } catch (PGPException | IOException e) {
+            throw new IllegalStateException(String.format("Unable to verify signature %s.", signatureFile.getName()), e);
+        }
+    }
+
+    private PGPPublicKeyRingCollection readKeyRing() {
+        File keysFile = new File(KEYS_FILE);
+        if (!keysFile.exists()) {
+            throw new IllegalStateException(String.format("Sling keys file does not exist at %s.", KEYS_FILE));
+        }
+        try (InputStream in = new FileInputStream(keysFile)) {
+            InputStream bouncyIn = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);
+            if (bouncyIn instanceof ArmoredInputStream) {
+                ArmoredInputStream as = (ArmoredInputStream) bouncyIn;
+                List<PGPPublicKeyRing> keyRings = new ArrayList<>();
+                while (!as.isEndOfStream()) {
+                    PGPPublicKeyRingCollection collection = new PGPPublicKeyRingCollection(as, new JcaKeyFingerprintCalculator());
+                    Iterator<PGPPublicKeyRing> readKeyRings = collection.getKeyRings();
+                    while (readKeyRings.hasNext()) {
+                        PGPPublicKeyRing keyRing = readKeyRings.next();
+                        keyRings.add(keyRing);
+                    }
+                }
+                if (!keyRings.isEmpty()) {
+                    return new PGPPublicKeyRingCollection(keyRings);
+                }
+            }
+            throw new IllegalStateException(String.format("Sling keys file from %s does not contain any keys.", KEYS_FILE));
+        } catch (IOException | PGPException e) {
+            throw new IllegalStateException(String.format("Cannot read Sling keys file at %s.", KEYS_FILE), e);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/cli/impl/pgp/SignatureVerificationResult.java b/src/main/java/org/apache/sling/cli/impl/pgp/SignatureVerificationResult.java
new file mode 100644
index 0000000..d724596
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/pgp/SignatureVerificationResult.java
@@ -0,0 +1,42 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements.  See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership.  The ASF 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 org.apache.sling.cli.impl.pgp;
+
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.jetbrains.annotations.NotNull;
+
+public class SignatureVerificationResult {
+
+    private boolean valid;
+    private PGPPublicKey key;
+
+    public SignatureVerificationResult(boolean valid, @NotNull PGPPublicKey key) {
+        this.key = key;
+        this.valid = valid;
+    }
+
+    public boolean isValid() {
+        return valid;
+    }
+
+    @NotNull
+    public PGPPublicKey getKey() {
+        return key;
+    }
+}