Browse Source

Allow to check for signing key

The new API is intended for UIs to check if signing will be possible or
would fail

Bug: 543579
Change-Id: I6ce1fd4210e46d49dcdf420c99d08c93e022136c
Signed-off-by: Gunnar Wagenknecht <gunnar@wagenknecht.org>
stable-5.4
Gunnar Wagenknecht 6 years ago committed by Matthias Sohn
parent
commit
c4420d2434
  1. 17
      org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java
  2. 32
      org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java
  3. 41
      org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java

17
org.eclipse.jgit.test/tst/org/eclipse/jgit/api/CommitCommandTest.java

@ -56,6 +56,7 @@ import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.EmptyCommitException; import org.eclipse.jgit.api.errors.EmptyCommitException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException; import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.diff.DiffEntry; import org.eclipse.jgit.diff.DiffEntry;
@ -651,6 +652,14 @@ public class CommitCommandTest extends RepositoryTestCase {
signingCommitters[0] = signingCommitter; signingCommitters[0] = signingCommitter;
callCount.incrementAndGet(); callCount.incrementAndGet();
} }
@Override
public boolean canLocateSigningKey(String gpgSigningKey,
PersonIdent signingCommitter,
CredentialsProvider credentialsProvider)
throws CanceledException {
return false;
}
}); });
// first call should use config, which is expected to be null at // first call should use config, which is expected to be null at
@ -706,6 +715,14 @@ public class CommitCommandTest extends RepositoryTestCase {
PersonIdent signingCommitter, CredentialsProvider credentialsProvider) { PersonIdent signingCommitter, CredentialsProvider credentialsProvider) {
callCount.incrementAndGet(); callCount.incrementAndGet();
} }
@Override
public boolean canLocateSigningKey(String gpgSigningKey,
PersonIdent signingCommitter,
CredentialsProvider credentialsProvider)
throws CanceledException {
return false;
}
}); });
// first call should use config, which is expected to be null at // first call should use config, which is expected to be null at

32
org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSigner.java

@ -43,6 +43,7 @@
package org.eclipse.jgit.lib; package org.eclipse.jgit.lib;
import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.CanceledException; import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.lib.internal.BouncyCastleGpgSigner; import org.eclipse.jgit.lib.internal.BouncyCastleGpgSigner;
import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.CredentialsProvider;
@ -95,9 +96,11 @@ public abstract class GpgSigner {
* the commit to sign (must not be <code>null</code> and must be * the commit to sign (must not be <code>null</code> and must be
* complete to allow proper calculation of payload) * complete to allow proper calculation of payload)
* @param gpgSigningKey * @param gpgSigningKey
* the signing key (passed as is to the GPG signing tool) * the signing key to locate (passed as is to the GPG signing
* tool as is; eg., value of <code>user.signingkey</code>)
* @param committer * @param committer
* the signing identity (to help with key lookup) * the signing identity (to help with key lookup in case signing
* key is not specified)
* @param credentialsProvider * @param credentialsProvider
* provider to use when querying for signing key credentials (eg. * provider to use when querying for signing key credentials (eg.
* passphrase) * passphrase)
@ -106,7 +109,30 @@ public abstract class GpgSigner {
* passphrase) * passphrase)
*/ */
public abstract void sign(@NonNull CommitBuilder commit, public abstract void sign(@NonNull CommitBuilder commit,
String gpgSigningKey, @NonNull PersonIdent committer, @Nullable String gpgSigningKey, @NonNull PersonIdent committer,
CredentialsProvider credentialsProvider) throws CanceledException;
/**
* Indicates if a signing key is available for the specified committer
* and/or signing key.
*
* @param gpgSigningKey
* the signing key to locate (passed as is to the GPG signing
* tool as is; eg., value of <code>user.signingkey</code>)
* @param committer
* the signing identity (to help with key lookup in case signing
* key is not specified)
* @param credentialsProvider
* provider to use when querying for signing key credentials (eg.
* passphrase)
* @return <code>true</code> if a signing key is available,
* <code>false</code> otherwise
* @throws CanceledException
* when signing was canceled (eg., user aborted when entering
* passphrase)
*/
public abstract boolean canLocateSigningKey(@Nullable String gpgSigningKey,
@NonNull PersonIdent committer,
CredentialsProvider credentialsProvider) throws CanceledException; CredentialsProvider credentialsProvider) throws CanceledException;
} }

41
org.eclipse.jgit/src/org/eclipse/jgit/lib/internal/BouncyCastleGpgSigner.java

@ -59,8 +59,10 @@ import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.eclipse.jgit.annotations.NonNull; import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.api.errors.CanceledException; import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.GpgSignature; import org.eclipse.jgit.lib.GpgSignature;
@ -90,26 +92,49 @@ public class BouncyCastleGpgSigner extends GpgSigner {
} }
@Override @Override
public void sign(@NonNull CommitBuilder commit, String gpgSigningKey, public boolean canLocateSigningKey(@Nullable String gpgSigningKey,
@NonNull PersonIdent committer, PersonIdent committer, CredentialsProvider credentialsProvider)
CredentialsProvider credentialsProvider) throws CanceledException { throws CanceledException {
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
committer, passphrasePrompt);
return gpgKey != null;
} catch (PGPException | IOException | URISyntaxException e) {
return false;
}
}
private BouncyCastleGpgKey locateSigningKey(@Nullable String gpgSigningKey,
PersonIdent committer,
BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt)
throws CanceledException, UnsupportedCredentialItem, IOException,
PGPException, URISyntaxException {
if (gpgSigningKey == null || gpgSigningKey.isEmpty()) { if (gpgSigningKey == null || gpgSigningKey.isEmpty()) {
gpgSigningKey = committer.getEmailAddress(); gpgSigningKey = committer.getEmailAddress();
} }
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKeyLocator keyHelper = new BouncyCastleGpgKeyLocator( BouncyCastleGpgKeyLocator keyHelper = new BouncyCastleGpgKeyLocator(
gpgSigningKey, passphrasePrompt); gpgSigningKey, passphrasePrompt);
BouncyCastleGpgKey gpgKey = keyHelper.findSecretKey(); return keyHelper.findSecretKey();
}
@Override
public void sign(@NonNull CommitBuilder commit,
@Nullable String gpgSigningKey, @NonNull PersonIdent committer,
CredentialsProvider credentialsProvider) throws CanceledException {
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt(
credentialsProvider)) {
BouncyCastleGpgKey gpgKey = locateSigningKey(gpgSigningKey,
committer, passphrasePrompt);
PGPSecretKey secretKey = gpgKey.getSecretKey(); PGPSecretKey secretKey = gpgKey.getSecretKey();
if (secretKey == null) { if (secretKey == null) {
throw new JGitInternalException( throw new JGitInternalException(
JGitText.get().unableToSignCommitNoSecretKey); JGitText.get().unableToSignCommitNoSecretKey);
} }
char[] passphrase = passphrasePrompt char[] passphrase = passphrasePrompt.getPassphrase(
.getPassphrase(secretKey.getPublicKey().getFingerprint(), secretKey.getPublicKey().getFingerprint(),
gpgKey.getOrigin()); gpgKey.getOrigin());
PGPPrivateKey privateKey = secretKey PGPPrivateKey privateKey = secretKey
.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder() .extractPrivateKey(new JcePBESecretKeyDecryptorBuilder()

Loading…
Cancel
Save