From 28cd212a7a8c91e7e9636b668e0f62544ee6d4a9 Mon Sep 17 00:00:00 2001 From: Slawomir Jaranowski Date: Sat, 24 Oct 2020 21:27:43 +0200 Subject: [PATCH] PGPShowMojo - Show information about artifact signature. connected: #169, #136 --- lombok.config | 3 + pom.xml | 11 + src/it/parentForTest/pom-test.xml | 5 + src/it/showSignature/invoker.properties | 16 ++ src/it/showSignature/pom-test.xml | 41 +++ src/it/showSignature/postbuild.groovy | 30 +++ .../simplify4u/plugins/AbstractPGPMojo.java | 56 ++--- .../simplify4u/plugins/ArtifactResolver.java | 14 +- .../simplify4u/plugins/PGPMojoException.java | 75 ++++++ .../org/simplify4u/plugins/PGPShowMojo.java | 206 +++++++++++++++ .../org/simplify4u/plugins/PGPVerifyMojo.java | 52 +++- .../plugins/keyserver/PGPKeysCache.java | 4 +- .../simplify4u/plugins/pgp/ArtifactInfo.java | 39 +++ .../org/simplify4u/plugins/pgp/KeyInfo.java | 41 +++ .../plugins/pgp/PGPSignatureInfo.java | 35 +++ .../simplify4u/plugins/pgp/SignatureInfo.java | 32 +++ .../plugins/pgp/SignatureStatus.java | 25 ++ .../plugins/utils/PGPSignatureUtils.java | 194 +++++++++++++- .../plugins/PGPMojoExceptionTest.java | 86 +++++++ .../simplify4u/plugins/PGPShowMojoTest.java | 237 ++++++++++++++++++ .../plugins/TestArtifactBuilder.java | 19 +- .../plugins/utils/PGPSignatureUtilsTest.java | 182 ++++++++++++-- src/test/resources/F8484389379ACEAC.asc | 35 +++ src/test/resources/helloworld-1.0.jar | 1 + src/test/resources/helloworld-1.0.jar.asc | 16 +- src/test/resources/wrong.asc | 1 + 26 files changed, 1372 insertions(+), 84 deletions(-) create mode 100644 lombok.config create mode 100644 src/it/showSignature/invoker.properties create mode 100644 src/it/showSignature/pom-test.xml create mode 100644 src/it/showSignature/postbuild.groovy create mode 100644 src/main/java/org/simplify4u/plugins/PGPMojoException.java create mode 100644 src/main/java/org/simplify4u/plugins/PGPShowMojo.java create mode 100644 src/main/java/org/simplify4u/plugins/pgp/ArtifactInfo.java create mode 100644 src/main/java/org/simplify4u/plugins/pgp/KeyInfo.java create mode 100644 src/main/java/org/simplify4u/plugins/pgp/PGPSignatureInfo.java create mode 100644 src/main/java/org/simplify4u/plugins/pgp/SignatureInfo.java create mode 100644 src/main/java/org/simplify4u/plugins/pgp/SignatureStatus.java create mode 100644 src/test/java/org/simplify4u/plugins/PGPMojoExceptionTest.java create mode 100644 src/test/java/org/simplify4u/plugins/PGPShowMojoTest.java create mode 100644 src/test/resources/F8484389379ACEAC.asc create mode 100644 src/test/resources/helloworld-1.0.jar create mode 100644 src/test/resources/wrong.asc diff --git a/lombok.config b/lombok.config new file mode 100644 index 00000000..2210d289 --- /dev/null +++ b/lombok.config @@ -0,0 +1,3 @@ +lombok.log.fieldName=LOGGER +lombok.addLombokGeneratedAnnotation=true +lombok.anyConstructor.addConstructorProperties=true diff --git a/pom.xml b/pom.xml index 90e403ee..991da3b3 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,12 @@ slf4j-api ${slf4j.version} + + org.projectlombok + lombok + 1.18.12 + provided + @@ -270,6 +276,11 @@ + + org.projectlombok + lombok + provided + org.slf4j slf4j-api diff --git a/src/it/parentForTest/pom-test.xml b/src/it/parentForTest/pom-test.xml index 8ce9b3ee..21b81da1 100644 --- a/src/it/parentForTest/pom-test.xml +++ b/src/it/parentForTest/pom-test.xml @@ -65,6 +65,11 @@ maven-jar-plugin @maven-jar-plugin.version@ + + org.apache.maven.plugins + maven-release-plugin + @maven-release-plugin.version@ + org.apache.maven.plugins maven-resources-plugin diff --git a/src/it/showSignature/invoker.properties b/src/it/showSignature/invoker.properties new file mode 100644 index 00000000..75a86157 --- /dev/null +++ b/src/it/showSignature/invoker.properties @@ -0,0 +1,16 @@ +# +# Copyright 2020 Slawomir Jaranowski +# +# 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. +# +invoker.goals = pgpverify:show -Dartifact=junit:junit:4.12 -DshowPom diff --git a/src/it/showSignature/pom-test.xml b/src/it/showSignature/pom-test.xml new file mode 100644 index 00000000..aa7373d8 --- /dev/null +++ b/src/it/showSignature/pom-test.xml @@ -0,0 +1,41 @@ + + + + 4.0.0 + + + test + it-test-parent + 0.0.1-SNAPSHOT + + + + test + 0.0.1-SNAPSHOT + pom + + + + + org.simplify4u.plugins + pgpverify-maven-plugin + @project.version@ + + + + diff --git a/src/it/showSignature/postbuild.groovy b/src/it/showSignature/postbuild.groovy new file mode 100644 index 00000000..ca9290a7 --- /dev/null +++ b/src/it/showSignature/postbuild.groovy @@ -0,0 +1,30 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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. + */ + +def buildLog = new File( basedir, 'build.log' ).text + +assert buildLog.contains('type: jar') +assert buildLog.contains('type: pom') +assert buildLog.contains('version: 4.12') + +assert buildLog.contains('PGP signature:') +assert buildLog.contains('keyId: 0xEFE8086F9E93774E') + +assert buildLog.contains('PGP key:') +assert buildLog.contains('fingerprint: 0xD4C89EA4AAF455FD88B22087EFE8086F9E93774E') +assert buildLog.contains('master key: 0x58E79B6ABC762159DC0B1591164BD2247B936711') + +assert buildLog.contains('[INFO] BUILD SUCCESS') diff --git a/src/main/java/org/simplify4u/plugins/AbstractPGPMojo.java b/src/main/java/org/simplify4u/plugins/AbstractPGPMojo.java index 2819ea1e..4898cbbe 100644 --- a/src/main/java/org/simplify4u/plugins/AbstractPGPMojo.java +++ b/src/main/java/org/simplify4u/plugins/AbstractPGPMojo.java @@ -22,15 +22,15 @@ import javax.inject.Inject; import io.vavr.control.Try; +import lombok.Setter; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.Parameter; -import org.codehaus.plexus.resource.loader.ResourceNotFoundException; import org.simplify4u.plugins.keyserver.PGPKeysCache; -import org.simplify4u.plugins.keysmap.KeysMap; +import org.simplify4u.plugins.utils.PGPSignatureUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,36 +41,14 @@ public abstract class AbstractPGPMojo extends AbstractMojo { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractPGPMojo.class); - @Inject - protected MavenSession session; - @Inject - protected PGPKeysCache pgpKeysCache; + protected final ArtifactResolver artifactResolver; - @Inject - protected KeysMap keysMap; + protected final PGPKeysCache pgpKeysCache; - /** - *

- * Specifies the location of a file that contains the map of dependencies to PGP key. - *

- * - *

- * This can be path to local file, path to file on plugin classpath or url address. - *

- * - *

- * Format description. - *

- * - *

- * You can use ready keys map: https://github.com/s4u/pgp-keys-map - *

- * - * @since 1.1.0 - */ - @Parameter(property = "pgpverify.keysMapLocation", defaultValue = "") - private String keysMapLocation; + protected final PGPSignatureUtils pgpSignatureUtils; + + protected final MavenSession session; /** * The directory for storing cached PGP public keys. @@ -108,6 +86,7 @@ public abstract class AbstractPGPMojo extends AbstractMojo { * @since 1.3.0 */ @Parameter(property = "pgpverify.skip", defaultValue = "false") + @Setter private boolean skip; /** @@ -128,6 +107,15 @@ public abstract class AbstractPGPMojo extends AbstractMojo { @Parameter(property = "pgpverify.quiet", defaultValue = "false") private boolean quiet; + @Inject + AbstractPGPMojo(ArtifactResolver artifactResolver, PGPKeysCache pgpKeysCache, + PGPSignatureUtils pgpSignatureUtils, MavenSession session) { + this.artifactResolver = artifactResolver; + this.pgpKeysCache = pgpKeysCache; + this.pgpSignatureUtils = pgpSignatureUtils; + this.session = session; + } + @Override public final Log getLog() { throw new UnsupportedOperationException("SLF4J should be used directly"); @@ -144,10 +132,6 @@ private void initPgpKeysCache() throws IOException { pgpKeysCache.init(pgpKeysCachePath, pgpKeyServer, pgpKeyServerLoadBalance, proxyName); } - private void initKeysMap() throws ResourceNotFoundException, IOException { - keysMap.load(keysMapLocation); - } - @Override public final void execute() throws MojoExecutionException, MojoFailureException { @@ -156,10 +140,8 @@ public final void execute() throws MojoExecutionException, MojoFailureException return; } - Try.run(() -> { - initPgpKeysCache(); - initKeysMap(); - }).getOrElseThrow(e -> new MojoFailureException(e.getMessage(), e)); + Try.run(this::initPgpKeysCache) + .getOrElseThrow(e -> new MojoFailureException(e.getMessage(), e)); executeConfiguredMojo(); } diff --git a/src/main/java/org/simplify4u/plugins/ArtifactResolver.java b/src/main/java/org/simplify4u/plugins/ArtifactResolver.java index f64f9c89..0c43a2c6 100644 --- a/src/main/java/org/simplify4u/plugins/ArtifactResolver.java +++ b/src/main/java/org/simplify4u/plugins/ArtifactResolver.java @@ -57,7 +57,7 @@ * Artifact resolver for project dependencies, build plug-ins, and build plug-in dependencies. */ @Named -final class ArtifactResolver { +public class ArtifactResolver { private static final Logger LOG = LoggerFactory.getLogger(ArtifactResolver.class); @@ -333,8 +333,12 @@ private Set resolveTransitively(Artifact artifact, SkipFilter dependen throws MojoExecutionException { final ArtifactFilter requestFilter = a -> !dependencyFilter.shouldSkipArtifact(a); final ArtifactResolutionRequest request = new ArtifactResolutionRequest() - .setArtifact(artifact).setLocalRepository(localRepository).setRemoteRepositories(remoteRepositories) - .setResolutionFilter(requestFilter).setCollectionFilter(requestFilter).setResolveTransitively(true); + .setArtifact(artifact) + .setLocalRepository(localRepository) + .setRemoteRepositories(remoteRepositories) + .setResolutionFilter(requestFilter) + .setCollectionFilter(requestFilter) + .setResolveTransitively(true); final ArtifactResolutionResult resolution = repositorySystem.resolve(request); if (!resolution.isSuccess()) { if (resolution.hasMissingArtifacts()) { @@ -356,7 +360,7 @@ private Set resolveTransitively(Artifact artifact, SkipFilter dependen } } - private Artifact resolvePom(Artifact artifact) { + public Artifact resolvePom(Artifact artifact) { final Artifact pomArtifact = repositorySystem.createProjectArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()); final ArtifactResolutionResult result = request(pomArtifact, remoteRepositories); @@ -367,7 +371,7 @@ private Artifact resolvePom(Artifact artifact) { return pomArtifact; } - private Artifact resolveArtifact(Artifact artifact) { + public Artifact resolveArtifact(Artifact artifact) { final ArtifactResolutionResult result = request(artifact, remoteRepositories); if (!result.isSuccess()) { result.getExceptions().forEach(e -> LOG.warn("Failed to resolve {}: {}", artifact.getId(), e.getMessage())); diff --git a/src/main/java/org/simplify4u/plugins/PGPMojoException.java b/src/main/java/org/simplify4u/plugins/PGPMojoException.java new file mode 100644 index 00000000..2e1b3330 --- /dev/null +++ b/src/main/java/org/simplify4u/plugins/PGPMojoException.java @@ -0,0 +1,75 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins; + + +import java.util.Arrays; + +/** + * Common runtime exception for Maven mojo. + * + * @author Slawomir Jaaranowski + */ +public class PGPMojoException extends RuntimeException { + + private static class Formatter { + + String message; + Throwable cause; + + public Formatter(String messageToFormat, Object[] args) { + Object[] argsToFormat; + Object last = args.length > 0 ? args[args.length - 1] : null; + if (last instanceof Throwable) { + argsToFormat = Arrays.copyOf(args, args.length - 1); + cause = (Throwable) last; + } else { + argsToFormat = args; + } + message = String.format(messageToFormat, argsToFormat); + } + } + + private static final long serialVersionUID = -2691735554566086599L; + + public PGPMojoException() { + super(); + } + + public PGPMojoException(Throwable cause) { + super(cause); + } + + public PGPMojoException(String message) { + super(message); + } + + /** + * Message can contains {@link String#format(String, Object...)} placeholders. + *

+ * If last argument is Throwable, then will be used as a cause of exception. + * + * @param message message of exception + * @param args additional args for message formatting + */ + public PGPMojoException(String message, Object... args) { + this(new Formatter(message, args)); + } + + private PGPMojoException(Formatter format) { + super(format.message, format.cause); + } +} diff --git a/src/main/java/org/simplify4u/plugins/PGPShowMojo.java b/src/main/java/org/simplify4u/plugins/PGPShowMojo.java new file mode 100644 index 00000000..2b25143c --- /dev/null +++ b/src/main/java/org/simplify4u/plugins/PGPShowMojo.java @@ -0,0 +1,206 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins; + +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import javax.inject.Inject; + +import static org.simplify4u.plugins.ArtifactResolver.SignatureRequirement.NONE; + +import io.vavr.control.Try; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.repository.RepositorySystem; +import org.apache.maven.shared.utils.logging.MessageBuilder; +import org.apache.maven.shared.utils.logging.MessageUtils; +import org.bouncycastle.openpgp.PGPUtil; +import org.simplify4u.plugins.keyserver.PGPKeysCache; +import org.simplify4u.plugins.pgp.ArtifactInfo; +import org.simplify4u.plugins.pgp.KeyInfo; +import org.simplify4u.plugins.pgp.PGPSignatureInfo; +import org.simplify4u.plugins.pgp.SignatureInfo; +import org.simplify4u.plugins.pgp.SignatureStatus; +import org.simplify4u.plugins.utils.PGPSignatureUtils; + +/** + * Show information about artifact signature. + * + * @author Slawomir Jaranowski + * @since 1.10.0 + */ +@Slf4j +@Mojo(name = PGPShowMojo.MOJO_NAME, requiresDirectInvocation = true, requiresOnline = true, requiresProject = false) +public class PGPShowMojo extends AbstractPGPMojo { + + public static final String MOJO_NAME = "show"; + + private final RepositorySystem repositorySystem; + + /** + * Show signature for pom files also. + * + * @since 1.10.0 + */ + @Setter + @Parameter(property = "showPom", defaultValue = "false") + protected boolean showPom; + + /** + * A artifact name to show pgp signature in format groupId:artifactId:version[:packaging[:classifier]]. + * + * @since 1.10.0 + */ + @Parameter(property = "artifact", required = true) + @Setter + private String artifact; + + private boolean hasError; + + @Inject + PGPShowMojo(ArtifactResolver artifactResolver, PGPKeysCache pgpKeysCache, PGPSignatureUtils pgpSignatureUtils, + MavenSession session, RepositorySystem repositorySystem) { + super(artifactResolver, pgpKeysCache, pgpSignatureUtils, session); + this.repositorySystem = repositorySystem; + } + + @Override + protected String getMojoName() { + return MOJO_NAME; + } + + @Override + protected void executeConfiguredMojo() throws MojoExecutionException { + + + Set artifactsToCheck = new HashSet<>(); + Artifact artifactToCheck = prepareArtifactToCheck(); + + artifactsToCheck.add(artifactResolver.resolveArtifact(artifactToCheck)); + + if (showPom && artifactToCheck.isResolved()) { + artifactsToCheck.add(artifactResolver.resolvePom(artifactToCheck)); + } + + Map artifactMap = artifactResolver.resolveSignatures(artifactsToCheck, NONE); + + artifactMap.forEach(this::processArtifact); + + if (hasError) { + throw new PGPMojoException("Some of artifact can't be checked"); + } + } + + private void processArtifact(Artifact artifact, Artifact artifactAsc) { + + PGPSignatureInfo signatureInfo = pgpSignatureUtils.getSignatureInfo(artifact, artifactAsc, pgpKeysCache); + + MessageBuilder messageBuilder = MessageUtils.buffer(); + messageBuilder.newline(); + messageBuilder.newline(); + + ArtifactInfo artifactInfo = signatureInfo.getArtifact(); + + messageBuilder.a("Artifact:").newline(); + messageBuilder.a("\tgroupId: ").strong(artifactInfo.getGroupId()).newline(); + messageBuilder.a("\tartifactId: ").strong(artifactInfo.getArtifactId()).newline(); + messageBuilder.a("\ttype: ").strong(artifactInfo.getType()).newline(); + Optional.ofNullable(artifactInfo.getClassifier()).ifPresent( + classifier -> messageBuilder.a("\tclassifier: ").strong(classifier).newline()); + messageBuilder.a("\tversion: ").strong(artifactInfo.getVersion()).newline(); + if (signatureInfo.getStatus() == SignatureStatus.ARTIFACT_NOT_RESOLVED) { + messageBuilder.a("\t").error("artifact was not resolved - try mvn -U ...").newline(); + } + + messageBuilder.newline(); + + SignatureInfo signature = signatureInfo.getSignature(); + if (signature != null) { + messageBuilder.a("PGP signature:").newline(); + messageBuilder.a("\tversion: ").strong(signature.getVersion()).newline(); + messageBuilder.a("\talgorithm: ") + .strong(Try.of(() -> + PGPUtil.getSignatureName(signature.getKeyAlgorithm(), signature.getHashAlgorithm())).get()) + .newline(); + messageBuilder.a("\tkeyId: ").strong(signature.getKeyId()).newline(); + messageBuilder.a("\tcreate date: ").strong(signature.getDate()).newline(); + messageBuilder.a("\tstatus: "); + if (signatureInfo.getStatus() == SignatureStatus.SIGNATURE_VALID) { + messageBuilder.success("valid"); + } else { + messageBuilder.error("invalid"); + } + messageBuilder.newline(); + } else if (signatureInfo.getStatus() == SignatureStatus.SIGNATURE_NOT_RESOLVED) { + messageBuilder.a("\t") + .error("PGP signature was not resolved - try mvn -U ...").newline(); + } + + messageBuilder.newline(); + + KeyInfo key = signatureInfo.getKey(); + if (key != null) { + messageBuilder.a("PGP key:").newline(); + messageBuilder.a("\tversion: ").strong(key.getVersion()).newline(); + messageBuilder.a("\talgorithm: ") + .strong(pgpSignatureUtils.keyAlgorithmName(key.getAlgorithm())).newline(); + messageBuilder.a("\tbits: ").strong(key.getBits()).newline(); + messageBuilder.a("\tfingerprint: ").strong(key.getFingerprint()).newline(); + Optional.ofNullable(key.getMaster()).ifPresent(masterKey -> + messageBuilder.a("\tmaster key: ").strong(masterKey).newline() + ); + messageBuilder.a("\tcreate date: ").strong(key.getDate()).newline(); + messageBuilder.a("\tuids: ").strong(key.getUids()).newline(); + } + + messageBuilder.newline(); + + Optional.ofNullable(signatureInfo.getErrorMessage()).ifPresent(errorMessage -> + messageBuilder.error(errorMessage).newline()); + + LOGGER.info(messageBuilder.toString()); + + hasError |= (signatureInfo.getStatus() != SignatureStatus.SIGNATURE_VALID + && signatureInfo.getStatus() != SignatureStatus.SIGNATURE_INVALID); + } + + private Artifact prepareArtifactToCheck() { + + String[] aItems = Optional.ofNullable(artifact).map(String::trim) + .map(s -> s.split(":")) + .filter(a -> a.length >= 3 && a.length <= 5) + .orElseThrow(() -> + new PGPMojoException("The parameters 'artifact' is miss or in invalid format" + + " - groupId:artifactId:version[:packaging[:classifier]]")); + + return repositorySystem.createArtifactWithClassifier(aItems[0], aItems[1], aItems[2], + getItem(aItems, 3, "jar"), getItem(aItems, 4, null)); + } + + private static String getItem(String[] items, int index, String defaultValue) { + if (items.length > index) { + return items[index]; + } + return defaultValue; + } +} diff --git a/src/main/java/org/simplify4u/plugins/PGPVerifyMojo.java b/src/main/java/org/simplify4u/plugins/PGPVerifyMojo.java index 96102f2e..f824064f 100644 --- a/src/main/java/org/simplify4u/plugins/PGPVerifyMojo.java +++ b/src/main/java/org/simplify4u/plugins/PGPVerifyMojo.java @@ -28,8 +28,10 @@ import java.util.Set; import javax.inject.Inject; +import io.vavr.control.Try; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; +import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; @@ -44,6 +46,8 @@ import org.simplify4u.plugins.ArtifactResolver.Configuration; import org.simplify4u.plugins.ArtifactResolver.SignatureRequirement; import org.simplify4u.plugins.keyserver.PGPKeyNotFound; +import org.simplify4u.plugins.keyserver.PGPKeysCache; +import org.simplify4u.plugins.keysmap.KeysMap; import org.simplify4u.plugins.skipfilters.CompositeSkipper; import org.simplify4u.plugins.skipfilters.ProvidedDependencySkipper; import org.simplify4u.plugins.skipfilters.ReactorDependencySkipper; @@ -73,8 +77,7 @@ public class PGPVerifyMojo extends AbstractPGPMojo { private static final String PGP_VERIFICATION_RESULT_FORMAT = "{} PGP Signature {}\n {} UserIds: {}"; - @Inject - private ArtifactResolver artifactResolver; + protected final KeysMap keysMap; /** * Scope used to build dependency list. @@ -215,6 +218,35 @@ public class PGPVerifyMojo extends AbstractPGPMojo { @Parameter(property = "pgpverify.disableChecksum", defaultValue = "false") private boolean disableChecksum; + /** + *

+ * Specifies the location of a file that contains the map of dependencies to PGP key. + *

+ * + *

+ * This can be path to local file, path to file on plugin classpath or url address. + *

+ * + *

+ * Format description. + *

+ * + *

+ * You can use ready keys map: https://github.com/s4u/pgp-keys-map + *

+ * + * @since 1.1.0 + */ + @Parameter(property = "pgpverify.keysMapLocation", defaultValue = "") + private String keysMapLocation; + + @Inject + PGPVerifyMojo(ArtifactResolver artifactResolver, PGPKeysCache pgpKeysCache, PGPSignatureUtils pgpSignatureUtils, + MavenSession session, KeysMap keysMap) { + super(artifactResolver, pgpKeysCache, pgpSignatureUtils, session); + this.keysMap = keysMap; + } + @Override protected String getMojoName() { return MOJO_NAME; @@ -223,6 +255,8 @@ protected String getMojoName() { @Override public void executeConfiguredMojo() throws MojoExecutionException, MojoFailureException { + initKeysMap(); + checkDeprecated(); final File mavenBuildDir = new File(session.getCurrentProject().getBuild().getDirectory()); @@ -277,6 +311,12 @@ private void checkDeprecated() { } } + private void initKeysMap() throws MojoExecutionException { + + Try.run(() -> keysMap.load(keysMapLocation)) + .getOrElseThrow(e -> new MojoExecutionException(e.getMessage(), e)); + } + private SignatureRequirement determineSignaturePolicy() { if (failNoSignature) { return SignatureRequirement.REQUIRED; @@ -349,11 +389,11 @@ private boolean verifyPGPSignature(Artifact artifact, Artifact ascArtifact) thro try { final PGPSignature pgpSignature; try (FileInputStream input = new FileInputStream(signatureFile)) { - pgpSignature = PGPSignatureUtils.loadSignature(input); + pgpSignature = pgpSignatureUtils.loadSignature(input); } verifyWeakSignature(pgpSignature); - sigKeyID = PGPSignatureUtils.retrieveKeyId(pgpSignature); + sigKeyID = pgpSignatureUtils.retrieveKeyId(pgpSignature); PGPPublicKeyRing publicKeyRing = pgpKeysCache.getKeyRing(sigKeyID); PGPPublicKey publicKey = sigKeyID.getKeyFromRing(publicKeyRing); @@ -368,7 +408,7 @@ private boolean verifyPGPSignature(Artifact artifact, Artifact ascArtifact) thro } pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); - PGPSignatureUtils.readFileContentInto(pgpSignature, artifactFile); + pgpSignatureUtils.readFileContentInto(pgpSignature, artifactFile); LOGGER.debug("signature.KeyAlgorithm: {} signature.hashAlgorithm: {}", pgpSignature.getKeyAlgorithm(), pgpSignature.getHashAlgorithm()); @@ -401,7 +441,7 @@ private boolean verifyPGPSignature(Artifact artifact, Artifact ascArtifact) thro } private void verifyWeakSignature(PGPSignature pgpSignature) throws MojoFailureException { - final String weakHashAlgorithm = PGPSignatureUtils.checkWeakHashAlgorithm(pgpSignature); + final String weakHashAlgorithm = pgpSignatureUtils.checkWeakHashAlgorithm(pgpSignature); if (weakHashAlgorithm == null) { return; } diff --git a/src/main/java/org/simplify4u/plugins/keyserver/PGPKeysCache.java b/src/main/java/org/simplify4u/plugins/keyserver/PGPKeysCache.java index 989d7600..6c7c67cd 100644 --- a/src/main/java/org/simplify4u/plugins/keyserver/PGPKeysCache.java +++ b/src/main/java/org/simplify4u/plugins/keyserver/PGPKeysCache.java @@ -52,10 +52,12 @@ import org.slf4j.LoggerFactory; /** + * Manage PGP keys local cache. + * * @author Slawomir Jaranowski. */ @Named -public final class PGPKeysCache { +public class PGPKeysCache { private static final Logger LOGGER = LoggerFactory.getLogger(PGPKeysCache.class); private static final String NL = System.lineSeparator(); diff --git a/src/main/java/org/simplify4u/plugins/pgp/ArtifactInfo.java b/src/main/java/org/simplify4u/plugins/pgp/ArtifactInfo.java new file mode 100644 index 00000000..aa909d52 --- /dev/null +++ b/src/main/java/org/simplify4u/plugins/pgp/ArtifactInfo.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins.pgp; + +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +@Value +@Builder +public class ArtifactInfo { + + @NonNull + String groupId; + + @NonNull + String artifactId; + + @NonNull + String type; + + @NonNull + String version; + + String classifier; +} diff --git a/src/main/java/org/simplify4u/plugins/pgp/KeyInfo.java b/src/main/java/org/simplify4u/plugins/pgp/KeyInfo.java new file mode 100644 index 00000000..a50ad4c2 --- /dev/null +++ b/src/main/java/org/simplify4u/plugins/pgp/KeyInfo.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins.pgp; + +import java.util.Collection; +import java.util.Date; + +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +@Value +@Builder +public class KeyInfo { + + @NonNull + String fingerprint; + + String master; + Collection uids; + + int version; + int algorithm; + + int bits; + + Date date; +} diff --git a/src/main/java/org/simplify4u/plugins/pgp/PGPSignatureInfo.java b/src/main/java/org/simplify4u/plugins/pgp/PGPSignatureInfo.java new file mode 100644 index 00000000..561ae5c7 --- /dev/null +++ b/src/main/java/org/simplify4u/plugins/pgp/PGPSignatureInfo.java @@ -0,0 +1,35 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins.pgp; + +import lombok.Builder; +import lombok.NonNull; +import lombok.Value; + +@Value +@Builder +public class PGPSignatureInfo { + + @NonNull + ArtifactInfo artifact; + + KeyInfo key; + SignatureInfo signature; + + @NonNull + SignatureStatus status; + String errorMessage; +} diff --git a/src/main/java/org/simplify4u/plugins/pgp/SignatureInfo.java b/src/main/java/org/simplify4u/plugins/pgp/SignatureInfo.java new file mode 100644 index 00000000..d7d27423 --- /dev/null +++ b/src/main/java/org/simplify4u/plugins/pgp/SignatureInfo.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins.pgp; + +import java.util.Date; + +import lombok.Builder; +import lombok.Value; + +@Value +@Builder +public class SignatureInfo { + + int hashAlgorithm; + int keyAlgorithm; + String keyId; + Date date; + int version; +} diff --git a/src/main/java/org/simplify4u/plugins/pgp/SignatureStatus.java b/src/main/java/org/simplify4u/plugins/pgp/SignatureStatus.java new file mode 100644 index 00000000..af58e5dc --- /dev/null +++ b/src/main/java/org/simplify4u/plugins/pgp/SignatureStatus.java @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins.pgp; + +public enum SignatureStatus { + ARTIFACT_NOT_RESOLVED, + SIGNATURE_NOT_RESOLVED, + ERROR, + SIGNATURE_ERROR, + SIGNATURE_VALID, + SIGNATURE_INVALID +} diff --git a/src/main/java/org/simplify4u/plugins/utils/PGPSignatureUtils.java b/src/main/java/org/simplify4u/plugins/utils/PGPSignatureUtils.java index 976f4b3b..a651db15 100644 --- a/src/main/java/org/simplify4u/plugins/utils/PGPSignatureUtils.java +++ b/src/main/java/org/simplify4u/plugins/utils/PGPSignatureUtils.java @@ -25,27 +25,37 @@ import java.io.InputStream; import java.math.BigInteger; import java.util.Optional; +import javax.inject.Named; +import io.vavr.control.Try; +import org.apache.maven.artifact.Artifact; import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.bcpg.sig.IssuerFingerprint; import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPLiteralData; import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureList; import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.simplify4u.plugins.keyserver.PGPKeysCache; +import org.simplify4u.plugins.pgp.ArtifactInfo; +import org.simplify4u.plugins.pgp.KeyInfo; +import org.simplify4u.plugins.pgp.PGPSignatureInfo; +import org.simplify4u.plugins.pgp.SignatureInfo; +import org.simplify4u.plugins.pgp.SignatureStatus; /** * Utilities for PGP Signature class. */ -public final class PGPSignatureUtils { - - private PGPSignatureUtils() { - // No need to instantiate utility class. - } +@Named +public class PGPSignatureUtils { /** * Check PGP signature for bad algorithms. @@ -54,7 +64,7 @@ private PGPSignatureUtils() { * * @return Returns null if no bad algorithms used, or algorithm name if used. */ - public static String checkWeakHashAlgorithm(PGPSignature signature) { + public String checkWeakHashAlgorithm(PGPSignature signature) { switch (signature.getHashAlgorithm()) { case HashAlgorithmTags.MD5: return "MD5"; @@ -91,10 +101,9 @@ public static String checkWeakHashAlgorithm(PGPSignature signature) { * * @return Returns the (first) read PGP signature. * - * @throws IOException In case of bad content. * @throws PGPSignatureException In case of failure loading signature. */ - public static PGPSignature loadSignature(InputStream input) throws IOException, PGPSignatureException { + public PGPSignature loadSignature(InputStream input) throws PGPSignatureException { try { InputStream sigInputStream = PGPUtil.getDecoderStream(input); @@ -115,18 +124,35 @@ public static PGPSignature loadSignature(InputStream input) throws IOException, if (nextObject instanceof PGPLiteralData) { InputStream dataStream = ((PGPLiteralData) nextObject).getDataStream(); - while (dataStream.read() > 0) { + byte[] buf = new byte[8192]; + while (dataStream.read(buf) > 0) { // we must read whole packet in order to proper input stream shift } } } - } catch (PGPException e) { + } catch (IOException | PGPException e) { throw new PGPSignatureException(e.getMessage(), e); } throw new PGPSignatureException("PGP signature not found."); } + /** + * Load PGPSignature from file. + * + * @param file the file having PGPSignature content + * + * @return Returns the (first) read PGP signature. + * + * @throws PGPSignatureException In case of failure loading signature. + * @throws IOException In case of IO failures. + */ + public PGPSignature loadSignature(File file) throws IOException, PGPSignatureException { + try (InputStream in = new FileInputStream(file)) { + return loadSignature(in); + } + } + /** * Read the content of a file into the PGP signature instance (for verification). * @@ -135,7 +161,7 @@ public static PGPSignature loadSignature(InputStream input) throws IOException, * * @throws IOException In case of failure to open the file or failure while reading its content. */ - public static void readFileContentInto(final PGPSignature signature, final File file) throws IOException { + public void readFileContentInto(final PGPSignature signature, final File file) throws IOException { try (InputStream inArtifact = new BufferedInputStream(new FileInputStream(file))) { byte[] buf = new byte[8192]; int t; @@ -154,7 +180,7 @@ public static void readFileContentInto(final PGPSignature signature, final File * * @throws PGPSignatureException In case of problem with signature data */ - public static PGPKeyId retrieveKeyId(PGPSignature signature) throws PGPSignatureException { + public PGPKeyId retrieveKeyId(PGPSignature signature) throws PGPSignatureException { Optional hashedSubPackets = Optional .ofNullable(signature.getHashedSubPackets()); @@ -216,4 +242,148 @@ public static PGPKeyId retrieveKeyId(PGPSignature signature) throws PGPSignature return pgpKeyId; } + + /** + * Create {@link PGPSignatureInfo} contains data about artifact, signature, public key used to verify and + * verification status. + * + * @param artifact The artifact to check signature + * @param artifactAsc The artifact contains signature + * @param cache PGP cache for access public key + * + * @return PGPSignatureInfo with verification result + */ + public PGPSignatureInfo getSignatureInfo(Artifact artifact, Artifact artifactAsc, PGPKeysCache cache) { + + PGPSignatureInfo.PGPSignatureInfoBuilder signatureInfoBuilder = PGPSignatureInfo.builder(); + + signatureInfoBuilder.artifact(ArtifactInfo.builder() + .groupId(artifact.getGroupId()) + .artifactId(artifact.getArtifactId()) + .type(artifact.getType()) + .classifier(artifact.getClassifier()) + .version(artifact.getVersion()) + .build()); + + if (!artifact.isResolved()) { + return signatureInfoBuilder.status(SignatureStatus.ARTIFACT_NOT_RESOLVED).build(); + } + + if (artifactAsc == null || !artifactAsc.isResolved()) { + return signatureInfoBuilder.status(SignatureStatus.SIGNATURE_NOT_RESOLVED).build(); + } + + PGPSignature signature = Try.of(() -> loadSignature(artifactAsc.getFile())) + .onFailure(e -> + signatureInfoBuilder.errorMessage(e.getMessage()).status(SignatureStatus.SIGNATURE_ERROR)) + .getOrNull(); + + if (signature == null) { + return signatureInfoBuilder.build(); + } + + PGPKeyId keyId = Try.of(() -> retrieveKeyId(signature)) + .onFailure(e -> + signatureInfoBuilder.errorMessage(e.getMessage()).status(SignatureStatus.SIGNATURE_ERROR)) + .getOrNull(); + + if (keyId == null) { + return signatureInfoBuilder.build(); + } + + signatureInfoBuilder.signature( + SignatureInfo.builder() + .hashAlgorithm(signature.getHashAlgorithm()) + .keyAlgorithm(signature.getKeyAlgorithm()) + .date(signature.getCreationTime()) + .keyId(keyId.toString()) + .version(signature.getVersion()) + .build()); + + PGPPublicKeyRing publicKeys = Try.of(() -> cache.getKeyRing(keyId)) + .onFailure(e -> signatureInfoBuilder.errorMessage(e.getMessage()).status(SignatureStatus.ERROR)) + .getOrNull(); + + if (publicKeys == null) { + return signatureInfoBuilder.build(); + } + + PGPPublicKey publicKey = keyId.getKeyFromRing(publicKeys); + + signatureInfoBuilder.key(KeyInfo.builder() + .fingerprint(PublicKeyUtils.fingerprint(publicKey)) + .master(PublicKeyUtils.getMasterKey(publicKey, publicKeys) + .map(PublicKeyUtils::fingerprint) + .orElse(null)) + .uids(PublicKeyUtils.getUserIDs(publicKey, publicKeys)) + .version(publicKey.getVersion()) + .algorithm(publicKey.getAlgorithm()) + .bits(publicKey.getBitStrength()) + .date(publicKey.getCreationTime()) + .build()); + + Boolean verifyStatus = Try.of(() -> { + signature.init(new BcPGPContentVerifierBuilderProvider(), publicKey); + readFileContentInto(signature, artifact.getFile()); + return signature.verify(); + }).onFailure(e -> signatureInfoBuilder.errorMessage(e.getMessage()).status(SignatureStatus.ERROR)) + .getOrNull(); + + if (verifyStatus == null) { + return signatureInfoBuilder.build(); + } + + return signatureInfoBuilder + .status(Boolean.TRUE.equals(verifyStatus) + ? SignatureStatus.SIGNATURE_VALID : SignatureStatus.SIGNATURE_INVALID) + .build(); + } + + /** + * Map Public-Key algorithms id to name + * + * @param keyAlgorithm key algorithm id + * + * @return key algorithm name + * + * @throws UnsupportedOperationException if algorithm is is not known + */ + public String keyAlgorithmName(int keyAlgorithm) { + switch (keyAlgorithm) { + case PublicKeyAlgorithmTags.RSA_GENERAL: + return "RSA (Encrypt or Sign)"; + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + return "RSA Encrypt-Only"; + case PublicKeyAlgorithmTags.RSA_SIGN: + return "RSA Sign-Only"; + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + return "Elgamal (Encrypt-Only)"; + case PublicKeyAlgorithmTags.DSA: + return "DSA (Digital Signature Algorithm)"; + case PublicKeyAlgorithmTags.ECDH: + return "Elliptic Curve"; + case PublicKeyAlgorithmTags.ECDSA: + return "Elliptic Curve Digital Signature"; + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + return "Elgamal (Encrypt or Sign)"; + case PublicKeyAlgorithmTags.DIFFIE_HELLMAN: + return "Diffie-Hellman"; + case PublicKeyAlgorithmTags.EDDSA: + return "EdDSA"; + case PublicKeyAlgorithmTags.EXPERIMENTAL_1: + case PublicKeyAlgorithmTags.EXPERIMENTAL_2: + case PublicKeyAlgorithmTags.EXPERIMENTAL_3: + case PublicKeyAlgorithmTags.EXPERIMENTAL_4: + case PublicKeyAlgorithmTags.EXPERIMENTAL_5: + case PublicKeyAlgorithmTags.EXPERIMENTAL_6: + case PublicKeyAlgorithmTags.EXPERIMENTAL_7: + case PublicKeyAlgorithmTags.EXPERIMENTAL_8: + case PublicKeyAlgorithmTags.EXPERIMENTAL_9: + case PublicKeyAlgorithmTags.EXPERIMENTAL_10: + case PublicKeyAlgorithmTags.EXPERIMENTAL_11: + return "Experimental - " + keyAlgorithm; + default: + throw new UnsupportedOperationException("Unknown key algorithm value encountered: " + keyAlgorithm); + } + } } diff --git a/src/test/java/org/simplify4u/plugins/PGPMojoExceptionTest.java b/src/test/java/org/simplify4u/plugins/PGPMojoExceptionTest.java new file mode 100644 index 00000000..1309bbf2 --- /dev/null +++ b/src/test/java/org/simplify4u/plugins/PGPMojoExceptionTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.testng.annotations.Test; + +public class PGPMojoExceptionTest { + + @Test + void emptyMessage() { + + PGPMojoException exception = new PGPMojoException(); + + assertThat(exception) + .hasMessage(null) + .hasRootCause(null); + } + + @Test + void simpleMessage() { + + PGPMojoException exception = new PGPMojoException("exception message"); + + assertThat(exception) + .hasMessage("exception message") + .hasRootCause(null); + } + + @Test + void simpleMessageWithException() { + + Exception cause = new Exception(); + PGPMojoException exception = new PGPMojoException("exception message", cause); + + assertThat(exception) + .hasMessage("exception message") + .hasRootCause(cause); + } + + @Test + void paramsInMessage() { + + PGPMojoException exception = new PGPMojoException("exception message: %d and %s", 1, "second"); + + assertThat(exception) + .hasMessage("exception message: 1 and second") + .hasRootCause(null); + } + + @Test + void paramsInMessageWithException() { + + Exception cause = new Exception(); + PGPMojoException exception = new PGPMojoException("exception message: %d and %s", 1, "second", cause); + + assertThat(exception) + .hasMessage("exception message: 1 and second") + .hasRootCause(cause); + } + + @Test + void onlyException() { + Exception cause = new Exception(); + PGPMojoException exception = new PGPMojoException(cause); + + assertThat(exception) + .hasMessage("java.lang.Exception") + .hasRootCause(cause); + } + +} diff --git a/src/test/java/org/simplify4u/plugins/PGPShowMojoTest.java b/src/test/java/org/simplify4u/plugins/PGPShowMojoTest.java new file mode 100644 index 00000000..8cd7e535 --- /dev/null +++ b/src/test/java/org/simplify4u/plugins/PGPShowMojoTest.java @@ -0,0 +1,237 @@ +/* + * Copyright 2020 Slawomir Jaranowski + * + * 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 org.simplify4u.plugins; + +import java.io.IOException; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; +import static org.simplify4u.plugins.ArtifactResolver.SignatureRequirement.NONE; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.repository.RepositorySystem; +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; +import org.bouncycastle.openpgp.PGPException; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.simplify4u.plugins.keyserver.PGPKeysCache; +import org.simplify4u.plugins.pgp.ArtifactInfo; +import org.simplify4u.plugins.pgp.KeyInfo; +import org.simplify4u.plugins.pgp.PGPSignatureInfo; +import org.simplify4u.plugins.pgp.SignatureInfo; +import org.simplify4u.plugins.pgp.SignatureStatus; +import org.simplify4u.plugins.utils.PGPSignatureUtils; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +@Listeners(MockitoTestNGListener.class) +public class PGPShowMojoTest { + + @Mock + private ArtifactResolver artifactResolver; + + @Mock + private PGPKeysCache pgpKeysCache; + + @Mock + private PGPSignatureUtils pgpSignatureUtils; + + @Mock + private RepositorySystem repositorySystem; + + @InjectMocks + private PGPShowMojo mojo; + + + @BeforeMethod + void setup() { + mojo = new PGPShowMojo(artifactResolver, pgpKeysCache, pgpSignatureUtils, null, repositorySystem); + } + + @DataProvider + public static Object[] invalidArtifactNames() { + return new Object[]{null, "test", "test:test", "test:test:1.0:type:class:class"}; + } + + @Test(dataProvider = "invalidArtifactNames") + void shouldThrowExceptionForInvalidArtifact(String artifact) { + + //given + mojo.setArtifact(artifact); + + // when, then + assertThatCode(() -> mojo.execute()) + .isExactlyInstanceOf(PGPMojoException.class) + .hasRootCause(null) + .hasMessage("The parameters 'artifact' is miss or in invalid format" + + " - groupId:artifactId:version[:packaging[:classifier]]"); + } + + @Test + void shouldSkipExecute() throws MojoFailureException, MojoExecutionException { + + //given + mojo.setSkip(true); + + // when + mojo.execute(); + + // then + verifyNoMoreInteractions(artifactResolver, pgpKeysCache, pgpSignatureUtils, repositorySystem); + } + + @Test + void shouldProcessArtifact() throws MojoFailureException, MojoExecutionException, IOException { + + Artifact artifact = TestArtifactBuilder.testArtifact().build(); + Artifact artifactAsc = TestArtifactBuilder.testArtifact().packaging("jar.asc").build(); + + //given + mojo.setArtifact("groupId:artifactId:1.0.0:war"); + + when(repositorySystem.createArtifactWithClassifier(anyString(), anyString(), anyString(), anyString(), isNull())).thenReturn(artifact); + when(artifactResolver.resolveSignatures(anyCollection(), eq(NONE))).thenReturn(Collections.singletonMap(artifact, artifactAsc)); + + + PGPSignatureInfo pgpSignatureInfo = aPGPSignatureInfoBuilder() + .status(SignatureStatus.SIGNATURE_VALID) + .build(); + + //when(pgpSignatureUtils.keyAlgorithmName(anyInt())).thenCallRealMethod(); + when(pgpSignatureUtils.getSignatureInfo(any(), any(), any())).thenReturn(pgpSignatureInfo); + + // when + mojo.execute(); + + // then + verify(repositorySystem).createArtifactWithClassifier("groupId", "artifactId", "1.0.0", "war", null); + verify(artifactResolver).resolveArtifact(artifact); + verify(artifactResolver).resolveSignatures(anyCollection(), eq(NONE)); + + verify(pgpSignatureUtils).getSignatureInfo(artifact, artifactAsc, pgpKeysCache); + verify(pgpSignatureUtils).keyAlgorithmName(anyInt()); + + verify(pgpKeysCache).init(null, null, false, null); + + verifyNoMoreInteractions(artifactResolver, pgpKeysCache, pgpSignatureUtils, repositorySystem); + } + + @Test + void shouldProcessArtifactWithPom() throws MojoFailureException, MojoExecutionException, IOException, PGPException { + + Artifact artifact = TestArtifactBuilder.testArtifact().build(); + + //given + mojo.setArtifact("groupId:artifactId:1.0.0:war"); + mojo.setShowPom(true); + + when(repositorySystem.createArtifactWithClassifier(anyString(), anyString(), anyString(), anyString(), isNull())) + .thenReturn(artifact); + + // when + mojo.execute(); + + // then + verify(repositorySystem).createArtifactWithClassifier("groupId", "artifactId", "1.0.0", "war", null); + verify(artifactResolver).resolveArtifact(artifact); + verify(artifactResolver).resolvePom(artifact); + verify(artifactResolver).resolveSignatures(anyCollection(), eq(NONE)); + + + verify(pgpKeysCache).init(null, null, false, null); + + verifyNoMoreInteractions(artifactResolver, pgpKeysCache, pgpSignatureUtils, repositorySystem); + } + + @Test + void shouldFailForNotResolvedArtifact() throws MojoFailureException, MojoExecutionException, IOException, PGPException { + + Artifact artifact = TestArtifactBuilder.testArtifact().notResolved().build(); + Artifact artifactAsc = TestArtifactBuilder.testArtifact().packaging("jar.asc").build(); + + //given + mojo.setArtifact("groupId:artifactId:1.0.0:war"); + + when(repositorySystem.createArtifactWithClassifier(anyString(), anyString(), anyString(), anyString(), isNull())) + .thenReturn(artifact); + + when(artifactResolver.resolveSignatures(anyCollection(), eq(NONE))) + .thenReturn(Collections.singletonMap(artifact, artifactAsc)); + + PGPSignatureInfo pgpSignatureInfo = aPGPSignatureInfoBuilder() + .status(SignatureStatus.ARTIFACT_NOT_RESOLVED) + .build(); + + when(pgpSignatureUtils.getSignatureInfo(any(), any(), any())).thenReturn(pgpSignatureInfo); + + // when + assertThatCode(() -> mojo.execute()) + .isExactlyInstanceOf(PGPMojoException.class) + .hasMessage("Some of artifact can't be checked"); + + // then + verify(repositorySystem).createArtifactWithClassifier("groupId", "artifactId", "1.0.0", "war", null); + verify(artifactResolver).resolveArtifact(artifact); + verify(artifactResolver).resolveSignatures(anyCollection(), eq(NONE)); + + verify(pgpSignatureUtils).keyAlgorithmName(anyInt()); + + verify(pgpKeysCache).init(null, null, false, null); + + verifyNoMoreInteractions(artifactResolver, pgpKeysCache, pgpSignatureUtils, repositorySystem); + } + + private PGPSignatureInfo.PGPSignatureInfoBuilder aPGPSignatureInfoBuilder() { + return PGPSignatureInfo.builder() + .artifact(ArtifactInfo.builder() + .groupId("groupId") + .artifactId("artifactId") + .type("jar") + .classifier("classifier") + .version("1.0") + .build()) + .signature(SignatureInfo.builder() + .version(4) + .keyId("0x1234") + .hashAlgorithm(HashAlgorithmTags.MD5) + .keyAlgorithm(PublicKeyAlgorithmTags.RSA_GENERAL) + .build()) + .key(KeyInfo.builder() + .version(4) + .fingerprint("0x1234") + .master("0x4321") + .algorithm(PublicKeyAlgorithmTags.RSA_GENERAL) + .uids(Collections.singleton("Test uid ")) + .bits(2048) + .build()); + } + +} diff --git a/src/test/java/org/simplify4u/plugins/TestArtifactBuilder.java b/src/test/java/org/simplify4u/plugins/TestArtifactBuilder.java index 21153229..cfe2279b 100644 --- a/src/test/java/org/simplify4u/plugins/TestArtifactBuilder.java +++ b/src/test/java/org/simplify4u/plugins/TestArtifactBuilder.java @@ -15,6 +15,8 @@ */ package org.simplify4u.plugins; +import java.io.File; + import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DefaultArtifact; @@ -27,6 +29,8 @@ public class TestArtifactBuilder { private String artifactId = "test"; private String packaging = "jar"; private String version = "1.1.1" ; + private boolean resolved = true; + private File file; public static TestArtifactBuilder testArtifact() { return new TestArtifactBuilder(); @@ -52,7 +56,20 @@ public TestArtifactBuilder version(String version) { return this; } + public TestArtifactBuilder notResolved() { + this.resolved = false; + return this; + } + + public TestArtifactBuilder file(File file) { + this.file = file; + return this; + } + public Artifact build() { - return new DefaultArtifact(groupId, artifactId, version, "", packaging, "", null); + DefaultArtifact artifact = new DefaultArtifact(groupId, artifactId, version, "", packaging, "", null); + artifact.setResolved(resolved); + artifact.setFile(file); + return artifact; } } diff --git a/src/test/java/org/simplify4u/plugins/utils/PGPSignatureUtilsTest.java b/src/test/java/org/simplify4u/plugins/utils/PGPSignatureUtilsTest.java index d2895b6c..73e96bea 100644 --- a/src/test/java/org/simplify4u/plugins/utils/PGPSignatureUtilsTest.java +++ b/src/test/java/org/simplify4u/plugins/utils/PGPSignatureUtilsTest.java @@ -1,24 +1,47 @@ package org.simplify4u.plugins.utils; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.Objects; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import io.vavr.control.Try; +import org.apache.maven.artifact.Artifact; +import org.assertj.core.api.Condition; import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSignature; +import org.mockito.Mockito; +import org.simplify4u.plugins.TestArtifactBuilder; +import org.simplify4u.plugins.keyserver.PGPKeysCache; +import org.simplify4u.plugins.pgp.ArtifactInfo; +import org.simplify4u.plugins.pgp.KeyInfo; +import org.simplify4u.plugins.pgp.PGPSignatureInfo; +import org.simplify4u.plugins.pgp.SignatureInfo; +import org.simplify4u.plugins.pgp.SignatureStatus; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class PGPSignatureUtilsTest { + private final PGPSignatureUtils pgpSignatureUtils = new PGPSignatureUtils(); + @Test public void testLoadSignatureNull() { - assertThatCode(() -> PGPSignatureUtils.loadSignature(null)) + assertThatCode(() -> pgpSignatureUtils.loadSignature((InputStream) null)) .isExactlyInstanceOf(NullPointerException.class); } @@ -26,17 +49,27 @@ public void testLoadSignatureNull() { public void testLoadSignatureNoContent() throws IOException { try (InputStream input = getClass().getResourceAsStream("/empty.asc")) { - assertThatCode(() -> PGPSignatureUtils.loadSignature(input)) + assertThatCode(() -> pgpSignatureUtils.loadSignature(input)) .isExactlyInstanceOf(PGPSignatureException.class) .hasMessage("PGP signature not found."); } } + @Test + public void loadSignatureInvalidContent() throws IOException { + + try (InputStream input = getClass().getResourceAsStream("/wrong.asc")) { + assertThatCode(() -> pgpSignatureUtils.loadSignature(input)) + .isExactlyInstanceOf(PGPSignatureException.class) + .hasRootCauseExactlyInstanceOf(IOException.class); + } + } + @Test public void testLoadSignatureContentNotSignature() throws IOException { try (InputStream input = getClass().getResourceAsStream("/3D8B00E198E21827.asc")) { - assertThatCode(() -> PGPSignatureUtils.loadSignature(input)) + assertThatCode(() -> pgpSignatureUtils.loadSignature(input)) .isExactlyInstanceOf(PGPSignatureException.class) .hasMessage("PGP signature not found."); } @@ -46,15 +79,15 @@ public void testLoadSignatureContentNotSignature() throws IOException { public void testLoadSignatureContentIsSignature() throws IOException, PGPSignatureException { try (InputStream input = getClass().getResourceAsStream("/helloworld-1.0.jar.asc")) { - PGPSignature signature = PGPSignatureUtils.loadSignature(input); - assertThat(signature.getKeyID()).isEqualTo(0x9F1A263E15FD0AC9L); + PGPSignature signature = pgpSignatureUtils.loadSignature(input); + assertThat(signature.getKeyID()).isEqualTo(0xF8484389379ACEACL); } } @Test public void testCheckWeakHashAlgorithmNull() { - assertThatCode(() -> PGPSignatureUtils.checkWeakHashAlgorithm(null)) + assertThatCode(() -> pgpSignatureUtils.checkWeakHashAlgorithm(null)) .isExactlyInstanceOf(NullPointerException.class); } @@ -64,7 +97,7 @@ public void testCheckWeakHashAlgorithmAllAlgorithms(int algorithm, boolean stron PGPSignature sig = mock(PGPSignature.class); when(sig.getHashAlgorithm()).thenReturn(algorithm); - assertThat(PGPSignatureUtils.checkWeakHashAlgorithm(sig) == null).isEqualTo(strong); + assertThat(pgpSignatureUtils.checkWeakHashAlgorithm(sig) == null).isEqualTo(strong); } @DataProvider(name = "provider-signature-hash-algorithms") @@ -90,7 +123,7 @@ public void testCheckWeakHashAlgorithmsUnknownAlgorithm() { PGPSignature sig = mock(PGPSignature.class); when(sig.getHashAlgorithm()).thenReturn(999); - assertThatCode(() -> PGPSignatureUtils.checkWeakHashAlgorithm(sig)) + assertThatCode(() -> pgpSignatureUtils.checkWeakHashAlgorithm(sig)) .isExactlyInstanceOf(UnsupportedOperationException.class); } @@ -98,7 +131,7 @@ public void testCheckWeakHashAlgorithmsUnknownAlgorithm() { public void loadSignatureFromPGPMessage() throws IOException, PGPSignatureException { try (InputStream input = getClass().getResourceAsStream("/fop-0.95.pom.asc")) { - PGPSignature signature = PGPSignatureUtils.loadSignature(input); + PGPSignature signature = pgpSignatureUtils.loadSignature(input); assertThat(signature.getKeyID()).isEqualTo(0x8E1E35C66754351BL); } } @@ -108,11 +141,11 @@ public void signatureKeyIdFromSubpackage() throws IOException, PGPSignatureExcep PGPSignature signature; try (InputStream input = getClass().getResourceAsStream("/helloworld-1.0.jar.asc")) { - signature = PGPSignatureUtils.loadSignature(input); + signature = pgpSignatureUtils.loadSignature(input); } - PGPKeyId pgpKeyId = PGPSignatureUtils.retrieveKeyId(signature); - assertThat(pgpKeyId).asString().isEqualTo("0x466583F9480EBE2462C46B309F1A263E15FD0AC9"); + PGPKeyId pgpKeyId = pgpSignatureUtils.retrieveKeyId(signature); + assertThat(pgpKeyId).asString().isEqualTo("0x6636274B2E8BEA9D15A61143F8484389379ACEAC"); } @Test @@ -120,11 +153,132 @@ public void signatureKeyIdFromSubpackageIssuerKeyInHashed() throws IOException, PGPSignature signature; try (InputStream input = getClass().getResourceAsStream("/ant-launcher-1.9.4.jar.asc")) { - signature = PGPSignatureUtils.loadSignature(input); + signature = pgpSignatureUtils.loadSignature(input); } - PGPKeyId pgpKeyId = PGPSignatureUtils.retrieveKeyId(signature); + PGPKeyId pgpKeyId = pgpSignatureUtils.retrieveKeyId(signature); assertThat(pgpKeyId).asString().isEqualTo("0x5EFAD9FE82A7FBCD"); } + @Test + void getSignatureThrowNullPointer() { + + assertThatCode(() -> pgpSignatureUtils.getSignatureInfo(null, null, null)) + .isExactlyInstanceOf(NullPointerException.class); + } + + Condition compareWitArtifact(Artifact artifact) { + return new Condition<>(artifactInfo -> + Objects.equals(artifactInfo.getArtifactId(), artifact.getArtifactId()) && + Objects.equals(artifactInfo.getGroupId(), artifact.getGroupId()) && + Objects.equals(artifactInfo.getType(), artifact.getType()) && + Objects.equals(artifactInfo.getClassifier(), artifact.getClassifier()) && + Objects.equals(artifactInfo.getVersion(), artifact.getVersion()), + "the same as %s", artifact); + } + + @Test + void getSignatureArtifactNotResolved() { + + Artifact artifact = TestArtifactBuilder.testArtifact().notResolved().build(); + Artifact artifactAsc = TestArtifactBuilder.testArtifact().notResolved().build(); + + PGPSignatureInfo signatureInfo = pgpSignatureUtils.getSignatureInfo(artifact, artifactAsc, null); + + assertThat(signatureInfo.getStatus()).isEqualTo(SignatureStatus.ARTIFACT_NOT_RESOLVED); + assertThat(signatureInfo.getArtifact()).is(compareWitArtifact(artifact)); + assertThat(signatureInfo.getSignature()).isNull(); + assertThat(signatureInfo.getKey()).isNull(); + assertThat(signatureInfo.getErrorMessage()).isNull(); + } + + @Test + void getSignatureAscNotResolved() { + + Artifact artifact = TestArtifactBuilder.testArtifact().build(); + Artifact artifactAsc = TestArtifactBuilder.testArtifact().notResolved().build(); + + PGPSignatureInfo signatureInfo = pgpSignatureUtils.getSignatureInfo(artifact, artifactAsc, null); + + assertThat(signatureInfo.getStatus()).isEqualTo(SignatureStatus.SIGNATURE_NOT_RESOLVED); + assertThat(signatureInfo.getArtifact()).is(compareWitArtifact(artifact)); + assertThat(signatureInfo.getSignature()).isNull(); + } + + @Test + void getSignatureInvalidAsc() { + + Artifact artifact = TestArtifactBuilder.testArtifact().build(); + Artifact artifactAsc = TestArtifactBuilder.testArtifact() + .file(new File(getClass().getResource("/empty.asc").getFile())) + .build(); + + PGPSignatureInfo signatureInfo = pgpSignatureUtils.getSignatureInfo(artifact, artifactAsc, null); + + assertThat(signatureInfo.getStatus()).isEqualTo(SignatureStatus.SIGNATURE_ERROR); + assertThat(signatureInfo.getArtifact()).is(compareWitArtifact(artifact)); + assertThat(signatureInfo.getSignature()).isNull(); + } + + @Test + void getSignaturePositiveFlow() throws IOException, PGPException { + + Artifact artifact = TestArtifactBuilder.testArtifact() + .file(new File(getClass().getResource("/helloworld-1.0.jar").getFile())) + .build(); + + Artifact artifactAsc = TestArtifactBuilder.testArtifact() + .file(new File(getClass().getResource("/helloworld-1.0.jar.asc").getFile())) + .build(); + + PGPPublicKeyRing pgpPublicKeys; + try (InputStream inputStream = getClass().getResourceAsStream("/F8484389379ACEAC.asc")) { + pgpPublicKeys = PublicKeyUtils.loadPublicKeyRing(inputStream, PGPKeyId.from(0xF8484389379ACEACL)) + .orElse(null); + } + + PGPKeysCache keysCache = Mockito.mock(PGPKeysCache.class); + when(keysCache.getKeyRing(any())).thenReturn(pgpPublicKeys); + + PGPSignatureInfo signatureInfo = pgpSignatureUtils.getSignatureInfo(artifact, artifactAsc, keysCache); + + assertThat(signatureInfo.getStatus()).isEqualTo(SignatureStatus.SIGNATURE_VALID); + assertThat(signatureInfo.getArtifact()).is(compareWitArtifact(artifact)); + + assertThat(signatureInfo.getSignature()).isEqualTo(SignatureInfo.builder() + .keyAlgorithm(1) + .hashAlgorithm(8) + .keyId("0x6636274B2E8BEA9D15A61143F8484389379ACEAC") + .date(Date.from(ZonedDateTime.parse("2020-10-24T06:55:35Z").toInstant())) + .version(4) + .build()); + + assertThat(signatureInfo.getKey()).isEqualTo(KeyInfo.builder() + .fingerprint("0x6636274B2E8BEA9D15A61143F8484389379ACEAC") + .uids(Collections.singleton("Slawomir Jaranowski ")) + .version(4) + .algorithm(1) + .bits(2048) + .date(Date.from(ZonedDateTime.parse("2013-02-19T22:28:49Z").toInstant())) + .build()); + } + + @DataProvider + public static Object[] keyAlgorithms() { + return Arrays.stream(PublicKeyAlgorithmTags.class.getDeclaredFields()) + .map(filed -> Try.of(()->filed.getInt(null)).get()) + .toArray(); + } + + @Test(dataProvider = "keyAlgorithms") + void keyAlgorithmNameShouldBeResolved(int keyAlgorithm) { + assertThat(pgpSignatureUtils.keyAlgorithmName(keyAlgorithm)).isNotBlank(); + } + + @Test + void unKnownKeyAlgorithmThrowExceptio() { + assertThatCode(() -> pgpSignatureUtils.keyAlgorithmName(9999998)) + .isExactlyInstanceOf(UnsupportedOperationException.class) + .hasMessage("Unknown key algorithm value encountered: 9999998"); + } } diff --git a/src/test/resources/F8484389379ACEAC.asc b/src/test/resources/F8484389379ACEAC.asc new file mode 100644 index 00000000..c31b8920 --- /dev/null +++ b/src/test/resources/F8484389379ACEAC.asc @@ -0,0 +1,35 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFEj/KEBCADXJeKfpoqI9eUuHrkSMa0V+FLpfH677/oDgrU5liTeoCDSwSH5 +usX0L+geEHYU6Gn221brU6nsrZR+WCE42+NWiiavwozNny0aeZU0wMReowh0IwjJ +QN55SbTJYVuEmxMNMZp/Qun01bIzLlEMmElfC847K9qqylFHSnjOa1cHTIgt7Idy +Izua+myc2dYxF81CkLZESpUIvZ6iZSF5yiAvOATrckFYhdMpvVPBKedu5QhGKgS3 +J9ddoJbvDr0pvlfSSbQhp8WKOIF9iEooAkQvnqdxkl7oAVEyFct43SrRbwO0BZOI +HAiEtliQfpSBghihQIx+9Z3d26pRhC7qCsrBABEBAAG0LFNsYXdvbWlyIEphcmFu +b3dza2kgPHMuamFyYW5vd3NraUBnbWFpbC5jb20+iQE4BBMBAgAiAhsDBgsJCAcD +AgYVCAIJCgsEFgIDAQIeAQIXgAUCWHJXMQAKCRD4SEOJN5rOrNJbCACVzQ1cX49l +A5mcWZg8YxjNXH11srviFP3PVxOhYTXxTDxMUZAp+K9GNzk6TCNqs6ki2nY3M1dO +FDCAZCbnX4pOv2LG4Me9fOKLYxAgc4ckODlr4QbmkdDjVde1uibc0wnNtFElWZUy +tPNEC/cK8FVc78dhcmUMQL7+crY+v+Cdh116i7kNZQFUEil3Bvr0rk4u7RYmi3MN +7aVt1Ofqg7rzJOHfVMGCOUnVf5un1TFaNKuiYUdBPeeCtL0tQb5ocEwWtZEtKoml +cc+mCUzhKltlqG+AIzXVM5tbYJqywmbhlZ5mn33/X7jJlx6zWOg56xv2lcfNKyTw +1Ftzl6wnlfaOuQINBFHuXbYQCACPEP/qxCBgx/YQ77L0R7zIDa277WaxlTpSJ8qd +DpMXQGYffFyS9KhCyLj9B+loy0wWYCKTfDJXM0pjQHlM5vDvsM4MXVEdht07rJN1 +UqIKJBZpfeCz5MSgDcmVjRWqQ/PGupvH3VFNiNjWTk0965+sEhzdMH3V3tObT1go +R7cd91mYdrAMaSNSGsf6TgXdR5u9RDLSwmxP9woxZspTMTP0WBYwitiJKuYB/9BC +7T3mIilPMdECREZgCrih4gjVOcP9VfKJSxexPSTOgAGmBfggMz9lCUImBx1eaP6h +I1VQKPkN8938wiT096C1SES/hDnmjU4S/IItBl5v7m/JFcErAAMFCACGfSSd/A6h +/9sYHju3NaF/2/JWHejPOW9q8x0+uWoae/lmnspgbsNpCJD5aKqrnOrPi9pmHBCi +3Zc7MFDjBDcAF7enil+Cilzqbhw+WulyZjGTrC5SxjE/WJb0ruQNcQEn0e4rExi6 +dY5le2MZvnKyD0NqrodNOuGeEtk7Qfxf7+YO8DVmJrmrNPMT5yiyyU9D5J8Y/pm7 +jo5B5JW+eyBZggYivojpzGesXBZTPxogPTQPJ2JZvxaLrP/euJprHdqzZAwtSqnG +fTGJoFICfeVVV/vVHUrdY7/Yf2qeHtFDBFuM5l5Plrtq58ZuhJU5GEtVwgMe7Ifa ++hSii3F86i7kiQEfBBgBAgAJAhsMBQJYcluLAAoJEPhIQ4k3ms6sqFgH/30nRVWl +JabRtb7U1/o7R31SHdzYXOBnez6CYW8i+23SuAacQV4c7NKx6Rv8TkZ5skcarO6H +wDbEKt/Unk4ifoNCg5xrtnMWEjr7M+iR0eR5IN3McTYVvOcCwArV3KkLYwgW06Ip +w32+khBk9Ylnv7z0p1mjHSUo3uYPo6VcZ5nYd2FSVhajI0Mjwr20AVVBDD7Ndvm+ +AM82Z1cM5TZpW2y6V0aSHqZMM5x4iwOMRaqXrpO6TqnNTNkUgXKu/wdrv2tgb6Y6 +K1uqXVcXsdv3zvVBinTNAXBWWCOzK47bxAgAyrElSN8g7hL1oE0ErfoHHBMxtNnO +qCYfCP6UKnmUQYI= +=Ywhj +-----END PGP PUBLIC KEY BLOCK----- diff --git a/src/test/resources/helloworld-1.0.jar b/src/test/resources/helloworld-1.0.jar new file mode 100644 index 00000000..caec2f95 --- /dev/null +++ b/src/test/resources/helloworld-1.0.jar @@ -0,0 +1 @@ +Test content to sign diff --git a/src/test/resources/helloworld-1.0.jar.asc b/src/test/resources/helloworld-1.0.jar.asc index 36f42140..7374547e 100644 --- a/src/test/resources/helloworld-1.0.jar.asc +++ b/src/test/resources/helloworld-1.0.jar.asc @@ -1,11 +1,11 @@ -----BEGIN PGP SIGNATURE----- -iQEzBAABCgAdFiEERmWD+UgOviRixGswnxomPhX9CskFAl6+yzwACgkQnxomPhX9 -CslwWAf+IsAeoaZC0yo38k1PZ58IAeHQP9iavrfZ4LMCMxFacXmBGZF4SVniBmZ3 -o7gaQpp+EYi7LikfBDphX0iNchSn/7jGlDq8eK12JCeoyD7s0rYAYu94itQSPuvE -MZWDD//C0pGNSoK14EZB4TdzE2Ey87+lXqBd2NKNdmSTntL+ijyOPZRMTsLs7o6F -cEwRJQ1T2i26/uC2dpiQ4qelk/bo0eZM/BjJp6DZqjmh4CZDaY/vMTxrM5v7LNVE -4ChAcuu3V8oiNMgWicXFRGHNqyEMrJUM6f7yx325si7ziH3l/CL1iGymVt1DdLzq -3R/QK/dX6YGYEUjJJbxWmx7DTc3HDA== -=DNhd +iQEzBAABCAAdFiEEZjYnSy6L6p0VphFD+EhDiTeazqwFAl+Tz+cACgkQ+EhDiTea +zqzKDwf/SfnWdAtz2s75Nqu5+eA9sEGnfnGGVdjOLZHIGibdlO4WFi0m6R0YfCol +hU8lnKgOH2DzBm054cnT4YYufvRKXfKnL7qxGbApA/bybaE4TDuVYTOgviG0tdcf +m5kuJMPYIqub5PVouX9KJgZc+8H0xvrgTSRFKGr/K+SuX7oRQFNBkfTO3ejMtBuV +C+Bh4Thtc5XigLijKhHCHB4RTotChhLOjUzs/q4iE9DjXhb9hCPabF7DYcE3ai/R +6zeRrSnFGDhINhnqM2k0Htn952W1k0NmgtH2tn9l81cLgT7N2kME2E8WQxBrSL2p +k9XxhDrKSrb7PEI/kl3B/G1ywI1s3A== +=8EW/ -----END PGP SIGNATURE----- diff --git a/src/test/resources/wrong.asc b/src/test/resources/wrong.asc new file mode 100644 index 00000000..acc12918 --- /dev/null +++ b/src/test/resources/wrong.asc @@ -0,0 +1 @@ +xxxxxx