View Javadoc
1   /*-
2    * Copyright (C) 2019, 2020 Salesforce and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  package org.eclipse.jgit.gpg.bc.internal;
11  
12  import java.net.URISyntaxException;
13  import java.nio.file.Path;
14  import java.text.MessageFormat;
15  
16  import org.bouncycastle.openpgp.PGPException;
17  import org.bouncycastle.util.encoders.Hex;
18  import org.eclipse.jgit.api.errors.CanceledException;
19  import org.eclipse.jgit.errors.UnsupportedCredentialItem;
20  import org.eclipse.jgit.transport.CredentialItem.InformationalMessage;
21  import org.eclipse.jgit.transport.CredentialItem.Password;
22  import org.eclipse.jgit.transport.CredentialsProvider;
23  import org.eclipse.jgit.transport.URIish;
24  
25  /**
26   * Prompts for a passphrase and caches it until {@link #clear() cleared}.
27   * <p>
28   * Implements {@link AutoCloseable} so it can be used within a
29   * try-with-resources block.
30   * </p>
31   */
32  class BouncyCastleGpgKeyPassphrasePrompt implements AutoCloseable {
33  
34  	private Password passphrase;
35  
36  	private CredentialsProvider credentialsProvider;
37  
38  	public BouncyCastleGpgKeyPassphrasePrompt(
39  			CredentialsProvider credentialsProvider) {
40  		this.credentialsProvider = credentialsProvider;
41  	}
42  
43  	/**
44  	 * Clears any cached passphrase
45  	 */
46  	public void clear() {
47  		if (passphrase != null) {
48  			passphrase.clear();
49  			passphrase = null;
50  		}
51  	}
52  
53  	@Override
54  	public void close() {
55  		clear();
56  	}
57  
58  	private URIish createURI(Path keyLocation) throws URISyntaxException {
59  		return new URIish(keyLocation.toUri().toString());
60  	}
61  
62  	/**
63  	 * Prompts use for a passphrase unless one was cached from a previous
64  	 * prompt.
65  	 *
66  	 * @param keyFingerprint
67  	 *            the fingerprint to show to the user during prompting
68  	 * @param keyLocation
69  	 *            the location the key was loaded from
70  	 * @return the passphrase (maybe <code>null</code>)
71  	 * @throws PGPException
72  	 * @throws CanceledException
73  	 *             in case passphrase was not entered by user
74  	 * @throws URISyntaxException
75  	 * @throws UnsupportedCredentialItem
76  	 */
77  	public char[] getPassphrase(byte[] keyFingerprint, Path keyLocation)
78  			throws PGPException, CanceledException, UnsupportedCredentialItem,
79  			URISyntaxException {
80  		if (passphrase == null) {
81  			passphrase = new Password(BCText.get().credentialPassphrase);
82  		}
83  
84  		if (credentialsProvider == null) {
85  			throw new PGPException(BCText.get().gpgNoCredentialsProvider);
86  		}
87  
88  		if (passphrase.getValue() == null
89  				&& !credentialsProvider.get(createURI(keyLocation),
90  						new InformationalMessage(
91  								MessageFormat.format(BCText.get().gpgKeyInfo,
92  										Hex.toHexString(keyFingerprint))),
93  						passphrase)) {
94  			throw new CanceledException(BCText.get().gpgSigningCancelled);
95  		}
96  		return passphrase.getValue();
97  	}
98  
99  	/**
100 	 * Determines whether a passphrase was already obtained.
101 	 *
102 	 * @return {@code true} if a passphrase is already set, {@code false}
103 	 *         otherwise
104 	 */
105 	public boolean hasPassphrase() {
106 		return passphrase != null && passphrase.getValue() != null;
107 	}
108 }