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 javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.ByteArrayInputStream;
26 import java.io.IOException;
27 import java.io.UncheckedIOException;
28 import java.time.LocalDateTime;
29 import java.time.ZoneId;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.Iterator;
33 import java.util.Map;
34 import java.util.function.Predicate;
35
36 import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
37 import org.bouncycastle.openpgp.PGPException;
38 import org.bouncycastle.openpgp.PGPPrivateKey;
39 import org.bouncycastle.openpgp.PGPSecretKey;
40 import org.bouncycastle.openpgp.PGPSecretKeyRing;
41 import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
42 import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
43 import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
44 import org.bouncycastle.openpgp.PGPUtil;
45 import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
46 import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
47 import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
48 import org.bouncycastle.util.encoders.Hex;
49 import org.eclipse.aether.RepositorySystemSession;
50 import org.eclipse.aether.artifact.Artifact;
51 import org.eclipse.aether.deployment.DeployRequest;
52 import org.eclipse.aether.installation.InstallRequest;
53 import org.eclipse.aether.spi.artifact.ArtifactPredicateFactory;
54 import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator;
55 import org.eclipse.aether.spi.artifact.generator.ArtifactGeneratorFactory;
56 import org.eclipse.aether.util.ConfigUtils;
57
58 @Singleton
59 @Named(GnupgSignatureArtifactGeneratorFactory.NAME)
60 public final class GnupgSignatureArtifactGeneratorFactory implements ArtifactGeneratorFactory {
61
62 public interface Loader {
63
64
65
66 default byte[] loadKeyRingMaterial(RepositorySystemSession session) throws IOException {
67 return null;
68 }
69
70
71
72
73 default byte[] loadKeyFingerprint(RepositorySystemSession session) throws IOException {
74 return null;
75 }
76
77
78
79
80 default char[] loadPassword(RepositorySystemSession session, byte[] fingerprint) throws IOException {
81 return null;
82 }
83 }
84
85 public static final String NAME = "gnupg";
86
87 private final ArtifactPredicateFactory artifactPredicateFactory;
88 private final Map<String, Loader> loaders;
89
90 @Inject
91 public GnupgSignatureArtifactGeneratorFactory(
92 ArtifactPredicateFactory artifactPredicateFactory, Map<String, Loader> loaders) {
93 this.artifactPredicateFactory = artifactPredicateFactory;
94 this.loaders = loaders;
95 }
96
97 @Override
98 public ArtifactGenerator newInstance(RepositorySystemSession session, InstallRequest request) {
99 return null;
100 }
101
102 @Override
103 public ArtifactGenerator newInstance(RepositorySystemSession session, DeployRequest request) {
104 final boolean enabled = ConfigUtils.getBoolean(
105 session, GnupgConfigurationKeys.DEFAULT_ENABLED, GnupgConfigurationKeys.CONFIG_PROP_ENABLED);
106 if (!enabled) {
107 return null;
108 }
109
110 try {
111 return doCreateArtifactGenerator(
112 session, request.getArtifacts(), artifactPredicateFactory.newInstance(session)::hasChecksums);
113 } catch (IOException e) {
114 throw new UncheckedIOException(e);
115 }
116 }
117
118 @Override
119 public float getPriority() {
120 return 100;
121 }
122
123 private GnupgSignatureArtifactGenerator doCreateArtifactGenerator(
124 RepositorySystemSession session, Collection<Artifact> artifacts, Predicate<Artifact> artifactPredicate)
125 throws IOException {
126
127 byte[] keyRingMaterial = null;
128 for (Loader loader : loaders.values()) {
129 keyRingMaterial = loader.loadKeyRingMaterial(session);
130 if (keyRingMaterial != null) {
131 break;
132 }
133 }
134 if (keyRingMaterial == null) {
135 throw new IllegalArgumentException("Key ring material not found");
136 }
137
138 byte[] fingerprint = null;
139 for (Loader loader : loaders.values()) {
140 fingerprint = loader.loadKeyFingerprint(session);
141 if (fingerprint != null) {
142 break;
143 }
144 }
145
146 try {
147 PGPSecretKeyRingCollection pgpSecretKeyRingCollection = new PGPSecretKeyRingCollection(
148 PGPUtil.getDecoderStream(new ByteArrayInputStream(keyRingMaterial)),
149 new BcKeyFingerprintCalculator());
150
151 PGPSecretKey secretKey = null;
152 for (PGPSecretKeyRing ring : pgpSecretKeyRingCollection) {
153 for (PGPSecretKey key : ring) {
154 if (!key.isPrivateKeyEmpty()) {
155 if (fingerprint == null || Arrays.equals(fingerprint, key.getFingerprint())) {
156 secretKey = key;
157 break;
158 }
159 }
160 }
161 }
162 if (secretKey == null) {
163 throw new IllegalArgumentException("Secret key not found");
164 }
165 if (secretKey.isPrivateKeyEmpty()) {
166 throw new IllegalArgumentException("Private key not found in Secret key");
167 }
168
169 long validSeconds = secretKey.getPublicKey().getValidSeconds();
170 if (validSeconds > 0) {
171 LocalDateTime expireDateTime = secretKey
172 .getPublicKey()
173 .getCreationTime()
174 .toInstant()
175 .atZone(ZoneId.systemDefault())
176 .toLocalDateTime()
177 .plusSeconds(validSeconds);
178 if (LocalDateTime.now().isAfter(expireDateTime)) {
179 throw new IllegalArgumentException("Secret key expired at: " + expireDateTime);
180 }
181 }
182
183 char[] keyPassword = null;
184 final boolean keyPassNeeded = secretKey.getKeyEncryptionAlgorithm() != SymmetricKeyAlgorithmTags.NULL;
185 if (keyPassNeeded) {
186 for (Loader loader : loaders.values()) {
187 keyPassword = loader.loadPassword(session, secretKey.getFingerprint());
188 if (keyPassword != null) {
189 break;
190 }
191 }
192 if (keyPassword == null) {
193 throw new IllegalArgumentException("Secret key is encrypted but no key password provided");
194 }
195 }
196
197 PGPPrivateKey privateKey = secretKey.extractPrivateKey(
198 new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(keyPassword));
199 if (keyPassword != null) {
200 Arrays.fill(keyPassword, ' ');
201 }
202 PGPSignatureSubpacketGenerator subPacketGenerator = new PGPSignatureSubpacketGenerator();
203 subPacketGenerator.setIssuerFingerprint(false, secretKey);
204 PGPSignatureSubpacketVector hashSubPackets = subPacketGenerator.generate();
205
206 return new GnupgSignatureArtifactGenerator(
207 artifacts, artifactPredicate, secretKey, privateKey, hashSubPackets, getKeyInfo(secretKey));
208 } catch (PGPException | IOException e) {
209 throw new IllegalStateException(e);
210 }
211 }
212
213 private static String getKeyInfo(PGPSecretKey secretKey) {
214 Iterator<String> userIds = secretKey.getPublicKey().getUserIDs();
215 if (userIds.hasNext()) {
216 return userIds.next();
217 }
218 return Hex.toHexString(secretKey.getPublicKey().getFingerprint());
219 }
220 }