1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.lib.internal;
11
12 import java.io.ByteArrayOutputStream;
13 import java.io.IOException;
14 import java.net.URISyntaxException;
15 import java.security.NoSuchAlgorithmException;
16 import java.security.NoSuchProviderException;
17 import java.security.Security;
18
19 import org.bouncycastle.bcpg.ArmoredOutputStream;
20 import org.bouncycastle.bcpg.BCPGOutputStream;
21 import org.bouncycastle.bcpg.HashAlgorithmTags;
22 import org.bouncycastle.jce.provider.BouncyCastleProvider;
23 import org.bouncycastle.openpgp.PGPException;
24 import org.bouncycastle.openpgp.PGPPrivateKey;
25 import org.bouncycastle.openpgp.PGPSecretKey;
26 import org.bouncycastle.openpgp.PGPSignature;
27 import org.bouncycastle.openpgp.PGPSignatureGenerator;
28 import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
29 import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
30 import org.eclipse.jgit.annotations.NonNull;
31 import org.eclipse.jgit.annotations.Nullable;
32 import org.eclipse.jgit.api.errors.CanceledException;
33 import org.eclipse.jgit.api.errors.JGitInternalException;
34 import org.eclipse.jgit.errors.UnsupportedCredentialItem;
35 import org.eclipse.jgit.internal.JGitText;
36 import org.eclipse.jgit.lib.CommitBuilder;
37 import org.eclipse.jgit.lib.GpgSignature;
38 import org.eclipse.jgit.lib.GpgSigner;
39 import org.eclipse.jgit.lib.PersonIdent;
40 import org.eclipse.jgit.transport.CredentialsProvider;
41
42
43
44
45 public class BouncyCastleGpgSigner extends GpgSigner {
46
47 private static void registerBouncyCastleProviderIfNecessary() {
48 if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
49 Security.addProvider(new BouncyCastleProvider());
50 }
51 }
52
53
54
55
56
57
58
59 public BouncyCastleGpgSigner() {
60 registerBouncyCastleProviderIfNecessary();
61 }
62
63 @Override
64 public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
65 PersonIdent committer, CredentialsProvider credentialsProvider)
66 throws CanceledException {
67 try (BouncyCastleGpgKeyPassphrasePromptt.html#BouncyCastleGpgKeyPassphrasePrompt">BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
68 credentialsProvider)) {
69 BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
70 committer, passphrasePrompt);
71 return gpgKey != null;
72 } catch (PGPException | IOException | NoSuchAlgorithmException
73 | NoSuchProviderException | URISyntaxException e) {
74 return false;
75 }
76 }
77
78 private BouncyCastleGpgKey locateSigningKey(@Nullable String gpgSigningKey,
79 PersonIdent committer,
80 BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt)
81 throws CanceledException, UnsupportedCredentialItem, IOException,
82 NoSuchAlgorithmException, NoSuchProviderException, PGPException,
83 URISyntaxException {
84 if (gpgSigningKey == null || gpgSigningKey.isEmpty()) {
85 gpgSigningKey = '<' + committer.getEmailAddress() + '>';
86 }
87
88 BouncyCastleGpgKeyLocator keyHelper = new BouncyCastleGpgKeyLocator(
89 gpgSigningKey, passphrasePrompt);
90
91 return keyHelper.findSecretKey();
92 }
93
94 @Override
95 public void sign(@NonNull CommitBuilder commit,
96 @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
97 CredentialsProvider credentialsProvider) throws CanceledException {
98 try (BouncyCastleGpgKeyPassphrasePromptt.html#BouncyCastleGpgKeyPassphrasePrompt">BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
99 credentialsProvider)) {
100 BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
101 committer, passphrasePrompt);
102 PGPSecretKey secretKey = gpgKey.getSecretKey();
103 if (secretKey == null) {
104 throw new JGitInternalException(
105 JGitText.get().unableToSignCommitNoSecretKey);
106 }
107 char[] passphrase = passphrasePrompt.getPassphrase(
108 secretKey.getPublicKey().getFingerprint(),
109 gpgKey.getOrigin());
110 PGPPrivateKey privateKey = secretKey
111 .extractPrivateKey(new JcePBESecretKeyDecryptorBuilder()
112 .setProvider(BouncyCastleProvider.PROVIDER_NAME)
113 .build(passphrase));
114 PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
115 new JcaPGPContentSignerBuilder(
116 secretKey.getPublicKey().getAlgorithm(),
117 HashAlgorithmTags.SHA256).setProvider(
118 BouncyCastleProvider.PROVIDER_NAME));
119 signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey);
120 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
121 try (BCPGOutputStream out = new BCPGOutputStream(
122 new ArmoredOutputStream(buffer))) {
123 signatureGenerator.update(commit.build());
124 signatureGenerator.generate().encode(out);
125 }
126 commit.setGpgSignature(new GpgSignature(buffer.toByteArray()));
127 } catch (PGPException | IOException | NoSuchAlgorithmException
128 | NoSuchProviderException | URISyntaxException e) {
129 throw new JGitInternalException(e.getMessage(), e);
130 }
131 }
132 }