1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.generator.gnupg;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.io.UncheckedIOException;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.function.Predicate;
31
32 import org.bouncycastle.bcpg.ArmoredOutputStream;
33 import org.bouncycastle.bcpg.BCPGOutputStream;
34 import org.bouncycastle.bcpg.HashAlgorithmTags;
35 import org.bouncycastle.openpgp.PGPException;
36 import org.bouncycastle.openpgp.PGPPrivateKey;
37 import org.bouncycastle.openpgp.PGPSecretKey;
38 import org.bouncycastle.openpgp.PGPSignature;
39 import org.bouncycastle.openpgp.PGPSignatureGenerator;
40 import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
41 import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
42 import org.eclipse.aether.artifact.Artifact;
43 import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator;
44 import org.eclipse.aether.util.artifact.SubArtifact;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48 final class GnupgSignatureArtifactGenerator implements ArtifactGenerator {
49 private static final String ARTIFACT_EXTENSION = ".asc";
50 private final Logger logger = LoggerFactory.getLogger(getClass());
51 private final ArrayList<Artifact> artifacts;
52 private final Predicate<Artifact> signableArtifactPredicate;
53 private final PGPSecretKey secretKey;
54 private final PGPPrivateKey privateKey;
55 private final PGPSignatureSubpacketVector hashSubPackets;
56 private final String keyInfo;
57 private final ArrayList<Path> signatureTempFiles;
58
59 GnupgSignatureArtifactGenerator(
60 Collection<Artifact> artifacts,
61 Predicate<Artifact> signableArtifactPredicate,
62 PGPSecretKey secretKey,
63 PGPPrivateKey privateKey,
64 PGPSignatureSubpacketVector hashSubPackets,
65 String keyInfo) {
66 this.artifacts = new ArrayList<>(artifacts);
67 this.signableArtifactPredicate = signableArtifactPredicate;
68 this.secretKey = secretKey;
69 this.privateKey = privateKey;
70 this.hashSubPackets = hashSubPackets;
71 this.keyInfo = keyInfo;
72 this.signatureTempFiles = new ArrayList<>();
73 logger.debug("Created generator using key {}", keyInfo);
74 }
75
76 @Override
77 public String generatorId() {
78 return GnupgSignatureArtifactGeneratorFactory.NAME;
79 }
80
81 @Override
82 public Collection<? extends Artifact> generate(Collection<? extends Artifact> generatedArtifacts) {
83 try {
84 artifacts.addAll(generatedArtifacts);
85
86
87 if (artifacts.stream().anyMatch(a -> a.getExtension().endsWith(ARTIFACT_EXTENSION))) {
88 logger.debug("GPG signatures are present among artifacts, bailing out");
89 return Collections.emptyList();
90 }
91
92
93 ArrayList<Artifact> result = new ArrayList<>();
94 for (Artifact artifact : artifacts) {
95 if (signableArtifactPredicate.test(artifact)) {
96 Path signatureTempFile = Files.createTempFile("signer-pgp", "tmp");
97 signatureTempFiles.add(signatureTempFile);
98 try (InputStream artifactContent = Files.newInputStream(artifact.getPath());
99 OutputStream signatureContent = Files.newOutputStream(signatureTempFile)) {
100 sign(artifactContent, signatureContent);
101 }
102 result.add(new SubArtifact(
103 artifact,
104 artifact.getClassifier(),
105 artifact.getExtension() + ARTIFACT_EXTENSION,
106 signatureTempFile.toFile()));
107 }
108 }
109 logger.debug("Signed {} artifacts with key {}", result.size(), keyInfo);
110 return result;
111 } catch (IOException e) {
112 throw new UncheckedIOException(e);
113 }
114 }
115
116 @Override
117 public void close() {
118 signatureTempFiles.forEach(p -> {
119 try {
120 Files.deleteIfExists(p);
121 } catch (IOException e) {
122 p.toFile().deleteOnExit();
123 }
124 });
125 }
126
127 private void sign(InputStream content, OutputStream signature) throws IOException {
128 PGPSignatureGenerator sGen = new PGPSignatureGenerator(
129 new BcPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA512));
130 try {
131 sGen.init(PGPSignature.BINARY_DOCUMENT, privateKey);
132 sGen.setHashedSubpackets(hashSubPackets);
133 int len;
134 byte[] buffer = new byte[8 * 1024];
135 while ((len = content.read(buffer)) >= 0) {
136 sGen.update(buffer, 0, len);
137 }
138 try (BCPGOutputStream bcpgOutputStream = new BCPGOutputStream(new ArmoredOutputStream(signature))) {
139 sGen.generate().encode(bcpgOutputStream);
140 }
141 } catch (PGPException e) {
142 throw new IllegalStateException(e);
143 }
144 }
145 }