Skip to content

Commit 7e90621

Browse files
authored
[MRESOLVER-516] Align GPG generator (apache#448)
Align with latest changes. Also, trivial fix to align doco generator, now "java.lang." prefix is stripped off to align all the doco. --- https://issues.apache.org/jira/browse/MRESOLVER-516
1 parent 39452f7 commit 7e90621

File tree

8 files changed

+216
-153
lines changed

8 files changed

+216
-153
lines changed

maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgConfigurationKeys.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ private GnupgConfigurationKeys() {}
6464

6565
public static final String DEFAULT_KEY_FILE_PATH = "maven-signing-key.key";
6666

67+
/**
68+
* Whether GnuPG agent should be used.
69+
*
70+
* @configurationSource {@link RepositorySystemSession#getConfigProperties()}
71+
* @configurationType {@link java.lang.Boolean}
72+
* @configurationDefaultValue {@link #DEFAULT_USE_AGENT}
73+
*/
74+
public static final String CONFIG_PROP_USE_AGENT = CONFIG_PROPS_PREFIX + "useAgent";
75+
76+
public static final boolean DEFAULT_USE_AGENT = true;
77+
6778
/**
6879
* The GnuPG agent socket(s) to try. Comma separated list of socket paths. If relative, will be resolved from
6980
* user home directory.

maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,33 +32,45 @@
3232
import org.bouncycastle.bcpg.ArmoredOutputStream;
3333
import org.bouncycastle.bcpg.BCPGOutputStream;
3434
import org.bouncycastle.bcpg.HashAlgorithmTags;
35-
import org.bouncycastle.openpgp.*;
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;
3641
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
3742
import org.eclipse.aether.artifact.Artifact;
3843
import org.eclipse.aether.spi.artifact.generator.ArtifactGenerator;
3944
import org.eclipse.aether.util.artifact.SubArtifact;
45+
import org.slf4j.Logger;
46+
import org.slf4j.LoggerFactory;
4047

4148
final class GnupgSignatureArtifactGenerator implements ArtifactGenerator {
4249
private static final String ARTIFACT_EXTENSION = ".asc";
50+
private final Logger logger = LoggerFactory.getLogger(getClass());
4351
private final ArrayList<Artifact> artifacts;
4452
private final Predicate<Artifact> signableArtifactPredicate;
4553
private final PGPSecretKey secretKey;
4654
private final PGPPrivateKey privateKey;
4755
private final PGPSignatureSubpacketVector hashSubPackets;
56+
private final String keyInfo;
4857
private final ArrayList<Path> signatureTempFiles;
4958

5059
GnupgSignatureArtifactGenerator(
5160
Collection<Artifact> artifacts,
5261
Predicate<Artifact> signableArtifactPredicate,
5362
PGPSecretKey secretKey,
5463
PGPPrivateKey privateKey,
55-
PGPSignatureSubpacketVector hashSubPackets) {
64+
PGPSignatureSubpacketVector hashSubPackets,
65+
String keyInfo) {
5666
this.artifacts = new ArrayList<>(artifacts);
5767
this.signableArtifactPredicate = signableArtifactPredicate;
5868
this.secretKey = secretKey;
5969
this.privateKey = privateKey;
6070
this.hashSubPackets = hashSubPackets;
71+
this.keyInfo = keyInfo;
6172
this.signatureTempFiles = new ArrayList<>();
73+
logger.debug("Created generator using key {}", keyInfo);
6274
}
6375

6476
@Override
@@ -73,6 +85,7 @@ public Collection<? extends Artifact> generate(Collection<? extends Artifact> ge
7385

7486
// back out if PGP signatures found among artifacts
7587
if (artifacts.stream().anyMatch(a -> a.getExtension().endsWith(ARTIFACT_EXTENSION))) {
88+
logger.debug("GPG signatures are present among artifacts, bailing out");
7689
return Collections.emptyList();
7790
}
7891

@@ -93,6 +106,7 @@ public Collection<? extends Artifact> generate(Collection<? extends Artifact> ge
93106
signatureTempFile.toFile()));
94107
}
95108
}
109+
logger.debug("Signed {} artifacts with key {}", result.size(), keyInfo);
96110
return result;
97111
} catch (IOException e) {
98112
throw new UncheckedIOException(e);

maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGeneratorFactory.java

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,23 @@
2929
import java.time.ZoneId;
3030
import java.util.Arrays;
3131
import java.util.Collection;
32-
import java.util.List;
32+
import java.util.Iterator;
3333
import java.util.Map;
3434
import java.util.function.Predicate;
3535

3636
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
37-
import org.bouncycastle.openpgp.*;
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;
3845
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
3946
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
4047
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
41-
import org.eclipse.aether.ConfigurationProperties;
48+
import org.bouncycastle.util.encoders.Hex;
4249
import org.eclipse.aether.RepositorySystemSession;
4350
import org.eclipse.aether.artifact.Artifact;
4451
import org.eclipse.aether.deployment.DeployRequest;
@@ -53,11 +60,6 @@
5360
public final class GnupgSignatureArtifactGeneratorFactory implements ArtifactGeneratorFactory {
5461

5562
public interface Loader {
56-
/**
57-
* Returns {@code true} if this loader requires user interactivity.
58-
*/
59-
boolean isInteractive();
60-
6163
/**
6264
* Returns the key ring material, or {@code null}.
6365
*/
@@ -75,7 +77,7 @@ default byte[] loadKeyFingerprint(RepositorySystemSession session) throws IOExce
7577
/**
7678
* Returns the key password, or {@code null}.
7779
*/
78-
default char[] loadPassword(RepositorySystemSession session, long keyId) throws IOException {
80+
default char[] loadPassword(RepositorySystemSession session, byte[] fingerprint) throws IOException {
7981
return null;
8082
}
8183
}
@@ -121,14 +123,9 @@ public float getPriority() {
121123
private GnupgSignatureArtifactGenerator doCreateArtifactGenerator(
122124
RepositorySystemSession session, Collection<Artifact> artifacts, Predicate<Artifact> artifactPredicate)
123125
throws IOException {
124-
boolean interactive = ConfigUtils.getBoolean(
125-
session, ConfigurationProperties.DEFAULT_INTERACTIVE, ConfigurationProperties.INTERACTIVE);
126-
List<Loader> loaders = this.loaders.values().stream()
127-
.filter(l -> interactive || !l.isInteractive())
128-
.toList();
129126

130127
byte[] keyRingMaterial = null;
131-
for (Loader loader : loaders) {
128+
for (Loader loader : loaders.values()) {
132129
keyRingMaterial = loader.loadKeyRingMaterial(session);
133130
if (keyRingMaterial != null) {
134131
break;
@@ -139,7 +136,7 @@ private GnupgSignatureArtifactGenerator doCreateArtifactGenerator(
139136
}
140137

141138
byte[] fingerprint = null;
142-
for (Loader loader : loaders) {
139+
for (Loader loader : loaders.values()) {
143140
fingerprint = loader.loadKeyFingerprint(session);
144141
if (fingerprint != null) {
145142
break;
@@ -186,8 +183,8 @@ private GnupgSignatureArtifactGenerator doCreateArtifactGenerator(
186183
char[] keyPassword = null;
187184
final boolean keyPassNeeded = secretKey.getKeyEncryptionAlgorithm() != SymmetricKeyAlgorithmTags.NULL;
188185
if (keyPassNeeded) {
189-
for (Loader loader : loaders) {
190-
keyPassword = loader.loadPassword(session, secretKey.getKeyID());
186+
for (Loader loader : loaders.values()) {
187+
keyPassword = loader.loadPassword(session, secretKey.getFingerprint());
191188
if (keyPassword != null) {
192189
break;
193190
}
@@ -199,14 +196,25 @@ private GnupgSignatureArtifactGenerator doCreateArtifactGenerator(
199196

200197
PGPPrivateKey privateKey = secretKey.extractPrivateKey(
201198
new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(keyPassword));
199+
if (keyPassword != null) {
200+
Arrays.fill(keyPassword, ' ');
201+
}
202202
PGPSignatureSubpacketGenerator subPacketGenerator = new PGPSignatureSubpacketGenerator();
203203
subPacketGenerator.setIssuerFingerprint(false, secretKey);
204204
PGPSignatureSubpacketVector hashSubPackets = subPacketGenerator.generate();
205205

206206
return new GnupgSignatureArtifactGenerator(
207-
artifacts, artifactPredicate, secretKey, privateKey, hashSubPackets);
207+
artifacts, artifactPredicate, secretKey, privateKey, hashSubPackets, getKeyInfo(secretKey));
208208
} catch (PGPException | IOException e) {
209209
throw new IllegalStateException(e);
210210
}
211211
}
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+
}
212220
}

maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/loaders/GpgAgentPasswordLoader.java

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@
3434
import java.nio.file.Paths;
3535
import java.util.Arrays;
3636
import java.util.List;
37+
import java.util.Locale;
3738
import java.util.stream.Collectors;
3839

3940
import org.bouncycastle.util.encoders.Hex;
41+
import org.eclipse.aether.ConfigurationProperties;
4042
import org.eclipse.aether.RepositorySystemSession;
4143
import org.eclipse.aether.generator.gnupg.GnupgSignatureArtifactGeneratorFactory;
4244
import org.eclipse.aether.util.ConfigUtils;
@@ -45,7 +47,9 @@
4547
import org.slf4j.LoggerFactory;
4648

4749
import static org.eclipse.aether.generator.gnupg.GnupgConfigurationKeys.CONFIG_PROP_AGENT_SOCKET_LOCATIONS;
50+
import static org.eclipse.aether.generator.gnupg.GnupgConfigurationKeys.CONFIG_PROP_USE_AGENT;
4851
import static org.eclipse.aether.generator.gnupg.GnupgConfigurationKeys.DEFAULT_AGENT_SOCKET_LOCATIONS;
52+
import static org.eclipse.aether.generator.gnupg.GnupgConfigurationKeys.DEFAULT_USE_AGENT;
4953

5054
/**
5155
* Password loader that uses GnuPG Agent. Is interactive.
@@ -59,21 +63,26 @@ public final class GpgAgentPasswordLoader implements GnupgSignatureArtifactGener
5963
private final Logger logger = LoggerFactory.getLogger(getClass());
6064

6165
@Override
62-
public boolean isInteractive() {
63-
return true;
64-
}
65-
66-
@Override
67-
public char[] loadPassword(RepositorySystemSession session, long keyId) throws IOException {
66+
public char[] loadPassword(RepositorySystemSession session, byte[] fingerprint) throws IOException {
67+
if (!ConfigUtils.getBoolean(session, DEFAULT_USE_AGENT, CONFIG_PROP_USE_AGENT)) {
68+
return null;
69+
}
6870
String socketLocationsStr =
6971
ConfigUtils.getString(session, DEFAULT_AGENT_SOCKET_LOCATIONS, CONFIG_PROP_AGENT_SOCKET_LOCATIONS);
72+
boolean interactive = ConfigUtils.getBoolean(
73+
session, ConfigurationProperties.DEFAULT_INTERACTIVE, ConfigurationProperties.INTERACTIVE);
7074
List<String> socketLocations = Arrays.stream(socketLocationsStr.split(","))
7175
.filter(s -> s != null && !s.isEmpty())
7276
.collect(Collectors.toList());
7377
for (String socketLocation : socketLocations) {
7478
try {
75-
return load(keyId, Paths.get(System.getProperty("user.home"), socketLocation))
76-
.toCharArray();
79+
Path socketLocationPath = Paths.get(socketLocation);
80+
if (!socketLocationPath.isAbsolute()) {
81+
socketLocationPath = Paths.get(System.getProperty("user.home"))
82+
.resolve(socketLocationPath)
83+
.toAbsolutePath();
84+
}
85+
return load(fingerprint, socketLocationPath, interactive);
7786
} catch (SocketException e) {
7887
// try next location
7988
logger.debug("Problem communicating with agent on socket: {}", socketLocation, e);
@@ -83,7 +92,7 @@ public char[] loadPassword(RepositorySystemSession session, long keyId) throws I
8392
return null;
8493
}
8594

86-
private String load(long keyId, Path socketPath) throws IOException {
95+
private char[] load(byte[] fingerprint, Path socketPath, boolean interactive) throws IOException {
8796
try (SocketChannel sock = SocketChannel.open(StandardProtocolFamily.UNIX)) {
8897
sock.connect(UnixDomainSocketAddress.of(socketPath));
8998
try (BufferedReader in = new BufferedReader(new InputStreamReader(Channels.newInputStream(sock)));
@@ -102,23 +111,41 @@ private String load(long keyId, Path socketPath) throws IOException {
102111
os.flush();
103112
expectOK(in);
104113
}
105-
String hexKeyId = Long.toHexString(keyId & 0xFFFFFFFFL);
114+
String hexKeyFingerprint = Hex.toHexString(fingerprint);
115+
String displayFingerprint = hexKeyFingerprint.toUpperCase(Locale.ROOT);
106116
// https://unix.stackexchange.com/questions/71135/how-can-i-find-out-what-keys-gpg-agent-has-cached-like-how-ssh-add-l-shows-yo
107-
String instruction = "GET_PASSPHRASE " + hexKeyId + " " + "Passphrase+incorrect"
108-
+ " GnuPG+Key+Passphrase Enter+passphrase+for+encrypted+GnuPG+key+" + hexKeyId
117+
String instruction = "GET_PASSPHRASE "
118+
+ (!interactive ? "--no-ask " : "")
119+
+ hexKeyFingerprint
120+
+ " "
121+
+ "X "
122+
+ "GnuPG+Passphrase "
123+
+ "Please+enter+the+passphrase+to+unlock+the+OpenPGP+secret+key+with+fingerprint:+"
124+
+ displayFingerprint
109125
+ "+to+use+it+for+signing+Maven+Artifacts\n";
110126
os.write((instruction).getBytes());
111127
os.flush();
112-
return new String(Hex.decode(expectOK(in).trim()));
128+
return mayExpectOK(in);
113129
}
114130
}
115131
}
116132

117-
private String expectOK(BufferedReader in) throws IOException {
133+
private void expectOK(BufferedReader in) throws IOException {
118134
String response = in.readLine();
119135
if (!response.startsWith("OK")) {
120136
throw new IOException("Expected OK but got this instead: " + response);
121137
}
122-
return response.substring(Math.min(response.length(), 3));
138+
}
139+
140+
private char[] mayExpectOK(BufferedReader in) throws IOException {
141+
String response = in.readLine();
142+
if (response.startsWith("ERR")) {
143+
return null;
144+
} else if (!response.startsWith("OK")) {
145+
throw new IOException("Expected OK/ERR but got this instead: " + response);
146+
}
147+
return new String(Hex.decode(
148+
response.substring(Math.min(response.length(), 3)).trim()))
149+
.toCharArray();
123150
}
124151
}

maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/loaders/GpgConfLoader.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ public final class GpgConfLoader implements GnupgSignatureArtifactGeneratorFacto
5050
private final Logger logger = LoggerFactory.getLogger(getClass());
5151

5252
/**
53-
* Maximum key size, see <a href="https://wiki.gnupg.org/LargeKeys">Large Keys</a>.
53+
* Maximum file size allowed to load (as we load it into heap).
54+
* <p>
55+
* This barrier exists to prevent us to load big/huge files, if this code is pointed at one
56+
* (by mistake or by malicious intent).
57+
*
58+
* @see <a href="https://wiki.gnupg.org/LargeKeys">Large Keys</a>
5459
*/
55-
private static final long MAX_SIZE = 5 * 1024 + 1L;
56-
57-
@Override
58-
public boolean isInteractive() {
59-
return false;
60-
}
60+
private static final long MAX_SIZE = 64 * 1000 + 1L;
6161

6262
@Override
6363
public byte[] loadKeyRingMaterial(RepositorySystemSession session) throws IOException {
@@ -66,13 +66,14 @@ public byte[] loadKeyRingMaterial(RepositorySystemSession session) throws IOExce
6666
GnupgConfigurationKeys.DEFAULT_KEY_FILE_PATH,
6767
GnupgConfigurationKeys.CONFIG_PROP_KEY_FILE_PATH));
6868
if (!keyPath.isAbsolute()) {
69-
keyPath = session.getLocalRepository().getBasePath().resolve(keyPath);
69+
keyPath =
70+
Paths.get(System.getProperty("user.home")).resolve(keyPath).toAbsolutePath();
7071
}
7172
if (Files.isRegularFile(keyPath)) {
7273
if (Files.size(keyPath) < MAX_SIZE) {
7374
return Files.readAllBytes(keyPath);
7475
} else {
75-
logger.warn("Refusing to load key {}; is larger than 5KB", keyPath);
76+
logger.warn("Refusing to load file {}; is larger than 64 kB", keyPath);
7677
}
7778
}
7879
return null;

maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/loaders/GpgEnvLoader.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,6 @@
4141
public final class GpgEnvLoader implements GnupgSignatureArtifactGeneratorFactory.Loader {
4242
public static final String NAME = "env";
4343

44-
@Override
45-
public boolean isInteractive() {
46-
return false;
47-
}
48-
4944
@Override
5045
public byte[] loadKeyRingMaterial(RepositorySystemSession session) {
5146
String keyMaterial = ConfigUtils.getString(session, null, "env." + RESOLVER_GPG_KEY);
@@ -70,7 +65,7 @@ public byte[] loadKeyFingerprint(RepositorySystemSession session) {
7065
}
7166

7267
@Override
73-
public char[] loadPassword(RepositorySystemSession session, long keyId) {
68+
public char[] loadPassword(RepositorySystemSession session, byte[] fingerprint) {
7469
String keyPassword = ConfigUtils.getString(session, null, "env." + RESOLVER_GPG_KEY_PASS);
7570
if (keyPassword != null) {
7671
return keyPassword.toCharArray();

maven-resolver-tools/src/main/java/org/eclipse/aether/tools/CollectConfiguration.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,14 @@ private static boolean hasConfigurationSource(JavaDocCapable<?> javaDocCapable)
205205
private static String getConfigurationType(JavaDocCapable<?> javaDocCapable) {
206206
String type = getTag(javaDocCapable, "@configurationType");
207207
if (type != null) {
208-
if (type.startsWith("{@link ") && type.endsWith("}")) {
209-
type = type.substring(7, type.length() - 1);
208+
String linkPrefix = "{@link ";
209+
String linkSuffix = "}";
210+
if (type.startsWith(linkPrefix) && type.endsWith(linkSuffix)) {
211+
type = type.substring(linkPrefix.length(), type.length() - linkSuffix.length());
212+
}
213+
String javaLangPackage = "java.lang.";
214+
if (type.startsWith(javaLangPackage)) {
215+
type = type.substring(javaLangPackage.length());
210216
}
211217
}
212218
return nvl(type, "n/a");

0 commit comments

Comments
 (0)