This also includes a change to generating the jgit CLI jar. Shading is no longer possible because it breaks the signature of BouncyCastle. Instead, the Spring Boot Loader Maven plug-in is now used to generate an executable jar. Bug: 382212 Change-Id: I35ee3d4b06d9d479475ab2e51b29bed49661bbdc Also-by: Gunnar Wagenknecht <gunnar@wagenknecht.org> Signed-off-by: Gunnar Wagenknecht <gunnar@wagenknecht.org> Signed-off-by: Medha Bhargav Prabhala <mprabhala@salesforce.com> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>tags/v5.3.0.201901161700-m1
sha1 = "5bb3d7a38f7ea54138336591d89dd5867b806c02", | sha1 = "5bb3d7a38f7ea54138336591d89dd5867b806c02", | ||||
src_sha1 = "94e89a8c9f82e38555e95b9f7f58344a247e862c", | src_sha1 = "94e89a8c9f82e38555e95b9f7f58344a247e862c", | ||||
) | ) | ||||
BOUNCYCASTLE_VER = "1.60" | |||||
maven_jar( | |||||
name = "bcpg-jdk15on", | |||||
artifact = "org.bouncycastle:bcpg-jdk15on:" + BOUNCYCASTLE_VER, | |||||
sha1 = "13c7a199c484127daad298996e95818478431a2c", | |||||
src_sha1 = "edcd9e86d95e39b4da39bb295efd93bc4f56266e", | |||||
) | |||||
maven_jar( | |||||
name = "bcprov-jdk15on", | |||||
artifact = "org.bouncycastle:bcprov-jdk15on:" + BOUNCYCASTLE_VER, | |||||
sha1 = "bd47ad3bd14b8e82595c7adaa143501e60842a84", | |||||
src_sha1 = "7c57a4d13fe53d9abb967bba600dd0b293dafd6a", | |||||
) | |||||
maven_jar( | |||||
name = "bcpkix-jdk15on", | |||||
artifact = "org.bouncycastle:bcpkix-jdk15on:" + BOUNCYCASTLE_VER, | |||||
sha1 = "d0c46320fbc07be3a24eb13a56cee4e3d38e0c75", | |||||
src_sha1 = "a25f041293f401af08efba63ff4bbdce98134a03", | |||||
) |
exports = ["@jsch//jar"], | exports = ["@jsch//jar"], | ||||
) | ) | ||||
java_library( | |||||
name = "bcpg", | |||||
visibility = [ | |||||
"//org.eclipse.jgit:__pkg__", | |||||
"//org.eclipse.jgit.test:__pkg__", | |||||
], | |||||
exports = ["@bcpg-jdk15on//jar"], | |||||
) | |||||
java_library( | |||||
name = "bcprov", | |||||
visibility = [ | |||||
"//org.eclipse.jgit:__pkg__", | |||||
"//org.eclipse.jgit.test:__pkg__", | |||||
], | |||||
exports = ["@bcprov-jdk15on//jar"], | |||||
) | |||||
java_library( | |||||
name = "bcpkix", | |||||
visibility = [ | |||||
"//org.eclipse.jgit:__pkg__", | |||||
"//org.eclipse.jgit.test:__pkg__", | |||||
], | |||||
exports = ["@bcpkix-jdk15on//jar"], | |||||
) | |||||
java_library( | java_library( | ||||
name = "jzlib", | name = "jzlib", | ||||
visibility = [ | visibility = [ |
version="0.0.0" | version="0.0.0" | ||||
unpack="false"/> | unpack="false"/> | ||||
<plugin | |||||
id="org.bouncycastle.bcpg" | |||||
download-size="0" | |||||
install-size="0" | |||||
version="0.0.0" | |||||
unpack="false"/> | |||||
<plugin | |||||
id="org.bouncycastle.bcpkix" | |||||
download-size="0" | |||||
install-size="0" | |||||
version="0.0.0" | |||||
unpack="false"/> | |||||
<plugin | |||||
id="org.bouncycastle.bcprov" | |||||
download-size="0" | |||||
install-size="0" | |||||
version="0.0.0" | |||||
unpack="false"/> | |||||
</feature> | </feature> |
<unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | ||||
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | ||||
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | ||||
<unit id="org.bouncycastle.bcpg" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpg.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.hamcrest" version="1.1.0.v20090501071000"/> | <unit id="org.hamcrest" version="1.1.0.v20090501071000"/> |
<unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | ||||
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | ||||
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | ||||
<unit id="org.bouncycastle.bcpg" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpg.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.hamcrest" version="1.1.0.v20090501071000"/> | <unit id="org.hamcrest" version="1.1.0.v20090501071000"/> |
<unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | ||||
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | ||||
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | ||||
<unit id="org.bouncycastle.bcpg" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpg.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.hamcrest" version="1.1.0.v20090501071000"/> | <unit id="org.hamcrest" version="1.1.0.v20090501071000"/> |
<unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | ||||
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | ||||
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | ||||
<unit id="org.bouncycastle.bcpg" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpg.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.hamcrest" version="1.1.0.v20090501071000"/> | <unit id="org.hamcrest" version="1.1.0.v20090501071000"/> |
<unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | ||||
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | ||||
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | ||||
<unit id="org.bouncycastle.bcpg" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpg.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.hamcrest" version="1.1.0.v20090501071000"/> | <unit id="org.hamcrest" version="1.1.0.v20090501071000"/> |
<unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | <unit id="org.apache.httpcomponents.httpcore.source" version="4.4.9.v20180409-1525"/> | ||||
<unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j" version="1.2.15.v201012070815"/> | ||||
<unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | <unit id="org.apache.log4j.source" version="1.2.15.v201012070815"/> | ||||
<unit id="org.bouncycastle.bcpg" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpg.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcpkix.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.bouncycastle.bcprov.source" version="1.60.0.v20181107-1520"/> | |||||
<unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | <unit id="org.kohsuke.args4j.source" version="2.33.0.v20160323-2218"/> | ||||
<unit id="org.hamcrest" version="1.1.0.v20090501071000"/> | <unit id="org.hamcrest" version="1.1.0.v20090501071000"/> |
org.apache.httpcomponents.httpcore.source [4.4.9.v20180409-1525,4.4.9.v20180409-1525] | org.apache.httpcomponents.httpcore.source [4.4.9.v20180409-1525,4.4.9.v20180409-1525] | ||||
org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815] | org.apache.log4j [1.2.15.v201012070815,1.2.15.v201012070815] | ||||
org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815] | org.apache.log4j.source [1.2.15.v201012070815,1.2.15.v201012070815] | ||||
org.bouncycastle.bcpg [1.60.0.v20181107-1520,1.60.0.v20181107-1520] | |||||
org.bouncycastle.bcpg.source [1.60.0.v20181107-1520,1.60.0.v20181107-1520] | |||||
org.bouncycastle.bcpkix [1.60.0.v20181107-1520,1.60.0.v20181107-1520] | |||||
org.bouncycastle.bcpkix.source [1.60.0.v20181107-1520,1.60.0.v20181107-1520] | |||||
org.bouncycastle.bcprov [1.60.0.v20181107-1520,1.60.0.v20181107-1520] | |||||
org.bouncycastle.bcprov.source [1.60.0.v20181107-1520,1.60.0.v20181107-1520] | |||||
org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218] | org.kohsuke.args4j [2.33.0.v20160323-2218,2.33.0.v20160323-2218] | ||||
org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218] | org.kohsuke.args4j.source [2.33.0.v20160323-2218,2.33.0.v20160323-2218] | ||||
org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000] | org.hamcrest [1.1.0.v20090501071000,1.1.0.v20090501071000] |
LESS=${LESS:-FSRX} | LESS=${LESS:-FSRX} | ||||
export LESS | export LESS | ||||
"$java" $java_args org.eclipse.jgit.pgm.Main "$@" | $use_pager | |||||
"$java" $java_args org.springframework.boot.loader.JarLauncher "$@" | $use_pager | |||||
exit | exit | ||||
else | else | ||||
exec "$java" $java_args org.eclipse.jgit.pgm.Main "$@" | |||||
exec "$java" $java_args org.springframework.boot.loader.JarLauncher "$@" | |||||
exit 1 | exit 1 | ||||
fi | fi |
</plugin> | </plugin> | ||||
<plugin> | <plugin> | ||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-shade-plugin</artifactId> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-maven-plugin</artifactId> | |||||
<executions> | <executions> | ||||
<execution> | <execution> | ||||
<phase>package</phase> | |||||
<goals> | <goals> | ||||
<goal>shade</goal> | |||||
<goal>repackage</goal> | |||||
</goals> | </goals> | ||||
<configuration> | <configuration> | ||||
<finalName>jgit-cli</finalName> | <finalName>jgit-cli</finalName> | ||||
<createDependencyReducedPom>false</createDependencyReducedPom> | |||||
<transformers> | |||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> | |||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> | |||||
<manifestEntries> | |||||
<Main-Class>org.eclipse.jgit.pgm.Main</Main-Class> | |||||
<Implementation-Title>JGit Command Line Interface</Implementation-Title> | |||||
</manifestEntries> | |||||
</transformer> | |||||
</transformers> | |||||
<filters> | |||||
<!-- exclude the signing data for individual jars, ueberjar will be signed again --> | |||||
<filter> | |||||
<artifact>*:*</artifact> | |||||
<excludes> | |||||
<exclude>META-INF/*.SF</exclude> | |||||
<exclude>META-INF/*.DSA</exclude> | |||||
<exclude>META-INF/*.RSA</exclude> | |||||
<exclude>OSGI-OPT/**</exclude> | |||||
</excludes> | |||||
</filter> | |||||
</filters> | |||||
<shadedArtifactAttached>true</shadedArtifactAttached> | |||||
<shadedClassifierName>shaded</shadedClassifierName> <!-- Any name that makes sense --> | |||||
<attach>false</attach> | |||||
<mainClass>org.eclipse.jgit.pgm.Main</mainClass> | |||||
<executable>true</executable> | |||||
<embeddedLaunchScript>jgit.sh</embeddedLaunchScript> | |||||
</configuration> | </configuration> | ||||
</execution> | </execution> | ||||
</executions> | </executions> | ||||
<phase>package</phase> | <phase>package</phase> | ||||
<configuration> | <configuration> | ||||
<target> | <target> | ||||
<concat destfile="${basedir}/target/jgit" force="yes" binary="true"> | |||||
<fileset file="${basedir}/jgit.sh" /> | |||||
<fileset file="${basedir}/target/jgit-cli.jar" /> | |||||
</concat> | |||||
<chmod file="${basedir}/target/jgit" perm="a+x"/> | |||||
<move | |||||
file="${basedir}/target/jgit-cli.jar" | |||||
force="yes" | |||||
tofile="${basedir}/target/jgit" /> | |||||
<chmod | |||||
file="${basedir}/target/jgit" | |||||
perm="a+x" /> | |||||
</target> | </target> | ||||
</configuration> | </configuration> | ||||
<goals> | <goals> |
try { | try { | ||||
commit = commitCmd.call(); | commit = commitCmd.call(); | ||||
} catch (JGitInternalException e) { | } catch (JGitInternalException e) { | ||||
throw die(e.getMessage()); | |||||
throw die(e.getMessage(), e); | |||||
} | } | ||||
String branchName; | String branchName; |
Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", | Import-Package: com.googlecode.javaewah;version="[1.1.6,2.0.0)", | ||||
com.jcraft.jsch;version="[0.1.54,0.2.0)", | com.jcraft.jsch;version="[0.1.54,0.2.0)", | ||||
net.bytebuddy.dynamic.loading;version="[1.7.0,2.0.0)", | net.bytebuddy.dynamic.loading;version="[1.7.0,2.0.0)", | ||||
org.bouncycastle.util.encoders;version="[1.60.0,2.0.0)", | |||||
org.eclipse.jgit.annotations;version="[5.3.0,5.4.0)", | org.eclipse.jgit.annotations;version="[5.3.0,5.4.0)", | ||||
org.eclipse.jgit.api;version="[5.3.0,5.4.0)", | org.eclipse.jgit.api;version="[5.3.0,5.4.0)", | ||||
org.eclipse.jgit.api.errors;version="[5.3.0,5.4.0)", | org.eclipse.jgit.api.errors;version="[5.3.0,5.4.0)", |
<dependency> | <dependency> | ||||
<groupId>org.bouncycastle</groupId> | <groupId>org.bouncycastle</groupId> | ||||
<artifactId>bcprov-jdk15on</artifactId> | <artifactId>bcprov-jdk15on</artifactId> | ||||
<version>1.59</version> | |||||
<scope>test</scope> | <scope>test</scope> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcpg-jdk15on</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcpkix-jdk15on</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
<dependency> | <dependency> | ||||
<groupId>org.hamcrest</groupId> | <groupId>org.hamcrest</groupId> | ||||
<artifactId>hamcrest-library</artifactId> | <artifactId>hamcrest-library</artifactId> |
import static org.junit.Assert.assertNotEquals; | import static org.junit.Assert.assertNotEquals; | ||||
import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
import static org.junit.Assert.assertNull; | import static org.junit.Assert.assertNull; | ||||
import static org.junit.Assert.assertSame; | |||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assert.fail; | import static org.junit.Assert.fail; | ||||
import org.eclipse.jgit.lib.StoredConfig; | import org.eclipse.jgit.lib.StoredConfig; | ||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
import org.eclipse.jgit.submodule.SubmoduleWalk; | import org.eclipse.jgit.submodule.SubmoduleWalk; | ||||
import org.eclipse.jgit.transport.CredentialsProvider; | |||||
import org.eclipse.jgit.treewalk.TreeWalk; | import org.eclipse.jgit.treewalk.TreeWalk; | ||||
import org.eclipse.jgit.treewalk.filter.TreeFilter; | import org.eclipse.jgit.treewalk.filter.TreeFilter; | ||||
import org.eclipse.jgit.util.FS; | import org.eclipse.jgit.util.FS; | ||||
git.add().addFilepattern("file1").call(); | git.add().addFilepattern("file1").call(); | ||||
String[] signingKey = new String[1]; | String[] signingKey = new String[1]; | ||||
PersonIdent[] signingCommitters = new PersonIdent[1]; | |||||
AtomicInteger callCount = new AtomicInteger(); | AtomicInteger callCount = new AtomicInteger(); | ||||
GpgSigner.setDefault(new GpgSigner() { | GpgSigner.setDefault(new GpgSigner() { | ||||
@Override | @Override | ||||
public void sign(CommitBuilder commit, String gpgSigningKey) { | |||||
public void sign(CommitBuilder commit, String gpgSigningKey, | |||||
PersonIdent signingCommitter, CredentialsProvider credentialsProvider) { | |||||
signingKey[0] = gpgSigningKey; | signingKey[0] = gpgSigningKey; | ||||
signingCommitters[0] = signingCommitter; | |||||
callCount.incrementAndGet(); | callCount.incrementAndGet(); | ||||
} | } | ||||
}); | }); | ||||
// first call should use config, which is expected to be null at | // first call should use config, which is expected to be null at | ||||
// this time | // this time | ||||
git.commit().setSign(Boolean.TRUE).setMessage("initial commit") | |||||
git.commit().setCommitter(committer).setSign(Boolean.TRUE) | |||||
.setMessage("initial commit") | |||||
.call(); | .call(); | ||||
assertNull(signingKey[0]); | assertNull(signingKey[0]); | ||||
assertEquals(1, callCount.get()); | assertEquals(1, callCount.get()); | ||||
assertSame(committer, signingCommitters[0]); | |||||
writeTrashFile("file2", "file2"); | writeTrashFile("file2", "file2"); | ||||
git.add().addFilepattern("file2").call(); | git.add().addFilepattern("file2").call(); | ||||
expectedConfigSigningKey); | expectedConfigSigningKey); | ||||
config.save(); | config.save(); | ||||
git.commit().setSign(Boolean.TRUE).setMessage("initial commit") | |||||
git.commit().setCommitter(committer).setSign(Boolean.TRUE) | |||||
.setMessage("initial commit") | |||||
.call(); | .call(); | ||||
assertEquals(expectedConfigSigningKey, signingKey[0]); | assertEquals(expectedConfigSigningKey, signingKey[0]); | ||||
assertEquals(2, callCount.get()); | assertEquals(2, callCount.get()); | ||||
assertSame(committer, signingCommitters[0]); | |||||
writeTrashFile("file3", "file3"); | writeTrashFile("file3", "file3"); | ||||
git.add().addFilepattern("file3").call(); | git.add().addFilepattern("file3").call(); | ||||
// now use specific on api | // now use specific on api | ||||
String expectedSigningKey = "my-" + System.nanoTime(); | String expectedSigningKey = "my-" + System.nanoTime(); | ||||
git.commit().setSign(Boolean.TRUE).setSigningKey(expectedSigningKey) | |||||
git.commit().setCommitter(committer).setSign(Boolean.TRUE) | |||||
.setSigningKey(expectedSigningKey) | |||||
.setMessage("initial commit").call(); | .setMessage("initial commit").call(); | ||||
assertEquals(expectedSigningKey, signingKey[0]); | assertEquals(expectedSigningKey, signingKey[0]); | ||||
assertEquals(3, callCount.get()); | assertEquals(3, callCount.get()); | ||||
assertSame(committer, signingCommitters[0]); | |||||
} | } | ||||
} | } | ||||
AtomicInteger callCount = new AtomicInteger(); | AtomicInteger callCount = new AtomicInteger(); | ||||
GpgSigner.setDefault(new GpgSigner() { | GpgSigner.setDefault(new GpgSigner() { | ||||
@Override | @Override | ||||
public void sign(CommitBuilder commit, String gpgSigningKey) { | |||||
public void sign(CommitBuilder commit, String gpgSigningKey, | |||||
PersonIdent signingCommitter, CredentialsProvider credentialsProvider) { | |||||
callCount.incrementAndGet(); | callCount.incrementAndGet(); | ||||
} | } | ||||
}); | }); |
"//lib:jsch", | "//lib:jsch", | ||||
"//lib:jzlib", | "//lib:jzlib", | ||||
"//lib:slf4j-api", | "//lib:slf4j-api", | ||||
"//lib:bcpg", | |||||
"//lib:bcprov", | |||||
"//lib:bcpkix" | |||||
], | ], | ||||
) | ) | ||||
com.jcraft.jsch;version="[0.1.37,0.2.0)", | com.jcraft.jsch;version="[0.1.37,0.2.0)", | ||||
javax.crypto, | javax.crypto, | ||||
javax.net.ssl, | javax.net.ssl, | ||||
org.bouncycastle;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.bcpg;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.gpg;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.gpg.keybox;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.jce.provider;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.openpgp;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.openpgp.jcajce;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.openpgp.operator;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.openpgp.operator.jcajce;version="[1.60.0,2.0.0)", | |||||
org.bouncycastle.util.encoders;version="[1.60.0,2.0.0)", | |||||
org.slf4j;version="[1.7.0,2.0.0)", | org.slf4j;version="[1.7.0,2.0.0)", | ||||
org.xml.sax, | org.xml.sax, | ||||
org.xml.sax.helpers | org.xml.sax.helpers |
<artifactId>JavaEWAH</artifactId> | <artifactId>JavaEWAH</artifactId> | ||||
</dependency> | </dependency> | ||||
<dependency> | <dependency> | ||||
<groupId>org.slf4j</groupId> | <groupId>org.slf4j</groupId> | ||||
<artifactId>slf4j-api</artifactId> | <artifactId>slf4j-api</artifactId> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcpg-jdk15on</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcprov-jdk15on</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcpkix-jdk15on</artifactId> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
<build> | <build> |
createNewFileFailed=Could not create new file {0} | createNewFileFailed=Could not create new file {0} | ||||
createRequiresZeroOldId=Create requires old ID to be zero | createRequiresZeroOldId=Create requires old ID to be zero | ||||
credentialPassword=Password | credentialPassword=Password | ||||
credentialPassphrase=Passphrase | |||||
credentialUsername=Username | credentialUsername=Username | ||||
daemonAlreadyRunning=Daemon already running | daemonAlreadyRunning=Daemon already running | ||||
daysAgo={0} days ago | daysAgo={0} days ago | ||||
gcLogExists=A previous GC run reported an error: ''{0}''. Automatic gc will fail until ''{1}'' is removed. | gcLogExists=A previous GC run reported an error: ''{0}''. Automatic gc will fail until ''{1}'' is removed. | ||||
gcTooManyUnpruned=Too many loose, unpruneable objects after garbage collection. Consider adjusting gc.auto or gc.pruneExpire. | gcTooManyUnpruned=Too many loose, unpruneable objects after garbage collection. Consider adjusting gc.auto or gc.pruneExpire. | ||||
gitmodulesNotFound=.gitmodules not found in tree. | gitmodulesNotFound=.gitmodules not found in tree. | ||||
gpgFailedToParseSecretKey=Failed to parse secret key file in directory: {0}. Is the entered passphrase correct? | |||||
gpgNoCredentialsProvider=missing credentials provider | |||||
gpgNoKeyring=neither pubring.kbx nor secring.gpg files found | |||||
gpgNoKeyInLegacySecring=no matching secret key found in legacy secring.gpg for key or user id: {0} | |||||
gpgNoPublicKeyFound=Unable to find a public-key with key or user id: {0} | |||||
gpgNoSecretKeyForPublicKey=unable to find associated secret key for public key: {0} | |||||
gpgKeyInfo=GPG Key (fingerprint {0}) | |||||
gpgSigningCancelled=Signing was cancelled | |||||
headRequiredToStash=HEAD required to stash local changes | headRequiredToStash=HEAD required to stash local changes | ||||
hoursAgo={0} hours ago | hoursAgo={0} hours ago | ||||
httpConfigCannotNormalizeURL=Cannot normalize URL path {0}: too many .. segments | httpConfigCannotNormalizeURL=Cannot normalize URL path {0}: too many .. segments | ||||
unableToRemovePath=Unable to remove path ''{0}'' | unableToRemovePath=Unable to remove path ''{0}'' | ||||
unableToStore=Unable to store {0}. | unableToStore=Unable to store {0}. | ||||
unableToWrite=Unable to write {0} | unableToWrite=Unable to write {0} | ||||
unableToSignCommitNoSecretKey=Unable to sign commit. Signing key not available. | |||||
unauthorized=Unauthorized | unauthorized=Unauthorized | ||||
underflowedReftableBlock=Underflowed reftable block | underflowedReftableBlock=Underflowed reftable block | ||||
unencodeableFile=Unencodable file: {0} | unencodeableFile=Unencodable file: {0} |
import org.eclipse.jgit.lib.RefUpdate.Result; | import org.eclipse.jgit.lib.RefUpdate.Result; | ||||
import org.eclipse.jgit.lib.Repository; | import org.eclipse.jgit.lib.Repository; | ||||
import org.eclipse.jgit.lib.RepositoryState; | import org.eclipse.jgit.lib.RepositoryState; | ||||
import org.eclipse.jgit.lib.internal.BouncyCastleGpgSigner; | |||||
import org.eclipse.jgit.revwalk.RevCommit; | import org.eclipse.jgit.revwalk.RevCommit; | ||||
import org.eclipse.jgit.revwalk.RevObject; | import org.eclipse.jgit.revwalk.RevObject; | ||||
import org.eclipse.jgit.revwalk.RevTag; | import org.eclipse.jgit.revwalk.RevTag; | ||||
import org.eclipse.jgit.revwalk.RevWalk; | import org.eclipse.jgit.revwalk.RevWalk; | ||||
import org.eclipse.jgit.transport.CredentialsProvider; | |||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser; | import org.eclipse.jgit.treewalk.CanonicalTreeParser; | ||||
import org.eclipse.jgit.treewalk.FileTreeIterator; | import org.eclipse.jgit.treewalk.FileTreeIterator; | ||||
import org.eclipse.jgit.treewalk.TreeWalk; | import org.eclipse.jgit.treewalk.TreeWalk; | ||||
private GpgSigner gpgSigner; | private GpgSigner gpgSigner; | ||||
private CredentialsProvider credentialsProvider; | |||||
/** | /** | ||||
* Constructor for CommitCommand | * Constructor for CommitCommand | ||||
* | * | ||||
*/ | */ | ||||
protected CommitCommand(Repository repo) { | protected CommitCommand(Repository repo) { | ||||
super(repo); | super(repo); | ||||
this.credentialsProvider = CredentialsProvider.getDefault(); | |||||
} | } | ||||
/** | /** | ||||
commit.setTreeId(indexTreeId); | commit.setTreeId(indexTreeId); | ||||
if (signCommit.booleanValue()) { | if (signCommit.booleanValue()) { | ||||
gpgSigner.sign(commit, signingKey); | |||||
gpgSigner.sign(commit, signingKey, committer, | |||||
credentialsProvider); | |||||
} | } | ||||
ObjectId commitId = odi.insert(commit); | ObjectId commitId = odi.insert(commit); | ||||
JGitText.get().onlyOpenPgpSupportedForSigning); | JGitText.get().onlyOpenPgpSupportedForSigning); | ||||
} | } | ||||
gpgSigner = GpgSigner.getDefault(); | gpgSigner = GpgSigner.getDefault(); | ||||
if (gpgSigner == null) { | |||||
gpgSigner = new BouncyCastleGpgSigner(); | |||||
} | |||||
} | } | ||||
} | } | ||||
this.signCommit = sign; | this.signCommit = sign; | ||||
return this; | return this; | ||||
} | } | ||||
/** | |||||
* Sets a {@link CredentialsProvider} | |||||
* | |||||
* @param credentialsProvider | |||||
* the provider to use when querying for credentials (eg., during | |||||
* signing) | |||||
* @since 5.3 | |||||
*/ | |||||
public void setCredentialsProvider( | |||||
CredentialsProvider credentialsProvider) { | |||||
this.credentialsProvider = credentialsProvider; | |||||
} | |||||
} | } |
/***/ public String createNewFileFailed; | /***/ public String createNewFileFailed; | ||||
/***/ public String createRequiresZeroOldId; | /***/ public String createRequiresZeroOldId; | ||||
/***/ public String credentialPassword; | /***/ public String credentialPassword; | ||||
/***/ public String credentialPassphrase; | |||||
/***/ public String credentialUsername; | /***/ public String credentialUsername; | ||||
/***/ public String daemonAlreadyRunning; | /***/ public String daemonAlreadyRunning; | ||||
/***/ public String daysAgo; | /***/ public String daysAgo; | ||||
/***/ public String gcLogExists; | /***/ public String gcLogExists; | ||||
/***/ public String gcTooManyUnpruned; | /***/ public String gcTooManyUnpruned; | ||||
/***/ public String gitmodulesNotFound; | /***/ public String gitmodulesNotFound; | ||||
/***/ public String gpgFailedToParseSecretKey; | |||||
/***/ public String gpgNoCredentialsProvider; | |||||
/***/ public String gpgNoKeyring; | |||||
/***/ public String gpgNoKeyInLegacySecring; | |||||
/***/ public String gpgNoPublicKeyFound; | |||||
/***/ public String gpgNoSecretKeyForPublicKey; | |||||
/***/ public String gpgKeyInfo; | |||||
/***/ public String gpgSigningCancelled; | |||||
/***/ public String headRequiredToStash; | /***/ public String headRequiredToStash; | ||||
/***/ public String hoursAgo; | /***/ public String hoursAgo; | ||||
/***/ public String httpConfigCannotNormalizeURL; | /***/ public String httpConfigCannotNormalizeURL; | ||||
/***/ public String unableToRemovePath; | /***/ public String unableToRemovePath; | ||||
/***/ public String unableToStore; | /***/ public String unableToStore; | ||||
/***/ public String unableToWrite; | /***/ public String unableToWrite; | ||||
/***/ public String unableToSignCommitNoSecretKey; | |||||
/***/ public String unauthorized; | /***/ public String unauthorized; | ||||
/***/ public String underflowedReftableBlock; | /***/ public String underflowedReftableBlock; | ||||
/***/ public String unencodeableFile; | /***/ public String unencodeableFile; |
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.api.errors.CanceledException; | |||||
import org.eclipse.jgit.lib.internal.BouncyCastleGpgSigner; | |||||
import org.eclipse.jgit.transport.CredentialsProvider; | |||||
/** | /** | ||||
* Creates GPG signatures for Git objects. | * Creates GPG signatures for Git objects. | ||||
*/ | */ | ||||
public abstract class GpgSigner { | public abstract class GpgSigner { | ||||
private static GpgSigner defaultSigner; | |||||
private static GpgSigner defaultSigner = new BouncyCastleGpgSigner(); | |||||
/** | /** | ||||
* Get the default signer, or <code>null</code>. | * Get the default signer, or <code>null</code>. | ||||
* 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 (passed as is to the GPG signing tool) | ||||
* @param committer | |||||
* the signing identity (to help with key lookup) | |||||
* @param credentialsProvider | |||||
* provider to use when querying for signing key credentials (eg. | |||||
* passphrase) | |||||
* @throws CanceledException | |||||
* when signing was canceled (eg., user aborted when entering | |||||
* passphrase) | |||||
*/ | */ | ||||
public abstract void sign(@NonNull CommitBuilder commit, | public abstract void sign(@NonNull CommitBuilder commit, | ||||
String gpgSigningKey); | |||||
String gpgSigningKey, @NonNull PersonIdent committer, | |||||
CredentialsProvider credentialsProvider) throws CanceledException; | |||||
} | } |
/* | |||||
* Copyright (C) 2018, Salesforce. | |||||
* and other copyright owners as documented in the project's IP log. | |||||
* | |||||
* This program and the accompanying materials are made available | |||||
* under the terms of the Eclipse Distribution License v1.0 which | |||||
* accompanies this distribution, is reproduced below, and is | |||||
* available at http://www.eclipse.org/org/documents/edl-v10.php | |||||
* | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or | |||||
* without modification, are permitted provided that the following | |||||
* conditions are met: | |||||
* | |||||
* - Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* | |||||
* - Redistributions in binary form must reproduce the above | |||||
* copyright notice, this list of conditions and the following | |||||
* disclaimer in the documentation and/or other materials provided | |||||
* with the distribution. | |||||
* | |||||
* - Neither the name of the Eclipse Foundation, Inc. nor the | |||||
* names of its contributors may be used to endorse or promote | |||||
* products derived from this software without specific prior | |||||
* written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
package org.eclipse.jgit.lib.internal; | |||||
import java.nio.file.Path; | |||||
import org.bouncycastle.openpgp.PGPSecretKey; | |||||
/** | |||||
* Container which holds a {@link #getSecretKey()} together with the | |||||
* {@link #getOrigin() path it was loaded from}. | |||||
*/ | |||||
class BouncyCastleGpgKey { | |||||
private PGPSecretKey secretKey; | |||||
private Path origin; | |||||
public BouncyCastleGpgKey(PGPSecretKey secretKey, Path origin) { | |||||
this.secretKey = secretKey; | |||||
this.origin = origin; | |||||
} | |||||
public PGPSecretKey getSecretKey() { | |||||
return secretKey; | |||||
} | |||||
public Path getOrigin() { | |||||
return origin; | |||||
} | |||||
} |
/* | |||||
* Copyright (C) 2018, Salesforce. | |||||
* and other copyright owners as documented in the project's IP log. | |||||
* | |||||
* This program and the accompanying materials are made available | |||||
* under the terms of the Eclipse Distribution License v1.0 which | |||||
* accompanies this distribution, is reproduced below, and is | |||||
* available at http://www.eclipse.org/org/documents/edl-v10.php | |||||
* | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or | |||||
* without modification, are permitted provided that the following | |||||
* conditions are met: | |||||
* | |||||
* - Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* | |||||
* - Redistributions in binary form must reproduce the above | |||||
* copyright notice, this list of conditions and the following | |||||
* disclaimer in the documentation and/or other materials provided | |||||
* with the distribution. | |||||
* | |||||
* - Neither the name of the Eclipse Foundation, Inc. nor the | |||||
* names of its contributors may be used to endorse or promote | |||||
* products derived from this software without specific prior | |||||
* written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
package org.eclipse.jgit.lib.internal; | |||||
import static java.nio.file.Files.exists; | |||||
import static java.nio.file.Files.newInputStream; | |||||
import java.io.BufferedInputStream; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.net.URISyntaxException; | |||||
import java.nio.file.Files; | |||||
import java.nio.file.Path; | |||||
import java.nio.file.Paths; | |||||
import java.text.MessageFormat; | |||||
import java.util.Iterator; | |||||
import java.util.Locale; | |||||
import java.util.stream.Collectors; | |||||
import java.util.stream.Stream; | |||||
import org.bouncycastle.gpg.SExprParser; | |||||
import org.bouncycastle.gpg.keybox.BlobType; | |||||
import org.bouncycastle.gpg.keybox.KeyBlob; | |||||
import org.bouncycastle.gpg.keybox.KeyBox; | |||||
import org.bouncycastle.gpg.keybox.KeyInformation; | |||||
import org.bouncycastle.gpg.keybox.PublicKeyRingBlob; | |||||
import org.bouncycastle.gpg.keybox.UserID; | |||||
import org.bouncycastle.openpgp.PGPException; | |||||
import org.bouncycastle.openpgp.PGPPublicKey; | |||||
import org.bouncycastle.openpgp.PGPSecretKey; | |||||
import org.bouncycastle.openpgp.PGPSecretKeyRing; | |||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; | |||||
import org.bouncycastle.openpgp.PGPUtil; | |||||
import org.bouncycastle.openpgp.operator.PBEProtectionRemoverFactory; | |||||
import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; | |||||
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; | |||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; | |||||
import org.bouncycastle.openpgp.operator.jcajce.JcePBEProtectionRemoverFactory; | |||||
import org.bouncycastle.util.encoders.Hex; | |||||
import org.eclipse.jgit.annotations.NonNull; | |||||
import org.eclipse.jgit.api.errors.CanceledException; | |||||
import org.eclipse.jgit.errors.UnsupportedCredentialItem; | |||||
import org.eclipse.jgit.internal.JGitText; | |||||
/** | |||||
* Locates GPG keys from either <code>~/.gnupg/private-keys-v1.d</code> or | |||||
* <code>~/.gnupg/secring.gpg</code> | |||||
*/ | |||||
class BouncyCastleGpgKeyLocator { | |||||
private static final Path USER_KEYBOX_PATH = Paths | |||||
.get(System.getProperty("user.home"), ".gnupg", "pubring.kbx"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |||||
private static final Path USER_SECRET_KEY_DIR = Paths.get( | |||||
System.getProperty("user.home"), ".gnupg", "private-keys-v1.d"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |||||
private static final Path USER_PGP_LEGACY_SECRING_FILE = Paths | |||||
.get(System.getProperty("user.home"), ".gnupg", "secring.gpg"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ | |||||
private final String signingKey; | |||||
private BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt; | |||||
/** | |||||
* Create a new key locator for the specified signing key. | |||||
* <p> | |||||
* The signing key must either be a hex representation of a specific key or | |||||
* a user identity substring (eg., email address). All keys in the KeyBox | |||||
* will be looked up in the order as returned by the KeyBox. A key id will | |||||
* be searched before attempting to find a key by user id. | |||||
* </p> | |||||
* | |||||
* @param signingKey | |||||
* the signing key to search for | |||||
* @param passphrasePrompt | |||||
* the provider to use when asking for key passphrase | |||||
*/ | |||||
public BouncyCastleGpgKeyLocator(String signingKey, | |||||
@NonNull BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt) { | |||||
this.signingKey = signingKey; | |||||
this.passphrasePrompt = passphrasePrompt; | |||||
} | |||||
private PGPSecretKey attemptParseSecretKey(Path keyFile, | |||||
PGPDigestCalculatorProvider calculatorProvider, | |||||
PBEProtectionRemoverFactory passphraseProvider, | |||||
PGPPublicKey publicKey) throws IOException { | |||||
try (InputStream in = newInputStream(keyFile)) { | |||||
return new SExprParser(calculatorProvider).parseSecretKey( | |||||
new BufferedInputStream(in), passphraseProvider, publicKey); | |||||
} catch (PGPException | ClassCastException e) { | |||||
return null; | |||||
} | |||||
} | |||||
private boolean containsSigningKey(String userId) { | |||||
return userId.toLowerCase(Locale.ROOT) | |||||
.contains(signingKey.toLowerCase(Locale.ROOT)); | |||||
} | |||||
private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob) | |||||
throws IOException { | |||||
for (KeyInformation keyInfo : keyBlob.getKeyInformation()) { | |||||
if (signingKey.toLowerCase(Locale.ROOT) | |||||
.equals(Hex.toHexString(keyInfo.getKeyID()) | |||||
.toLowerCase(Locale.ROOT))) { | |||||
return getFirstPublicKey(keyBlob); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob) | |||||
throws IOException { | |||||
for (UserID userID : keyBlob.getUserIds()) { | |||||
if (containsSigningKey(userID.getUserIDAsString())) { | |||||
return getFirstPublicKey(keyBlob); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* Finds a public key associated with the signing key. | |||||
* | |||||
* @param keyboxFile | |||||
* the KeyBox file | |||||
* @return publicKey the public key (maybe <code>null</code>) | |||||
* @throws IOException | |||||
* in case of problems reading the file | |||||
*/ | |||||
private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile) | |||||
throws IOException { | |||||
KeyBox keyBox = readKeyBoxFile(keyboxFile); | |||||
for (KeyBlob keyBlob : keyBox.getKeyBlobs()) { | |||||
if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) { | |||||
PGPPublicKey key = findPublicKeyByKeyId(keyBlob); | |||||
if (key != null) { | |||||
return key; | |||||
} | |||||
key = findPublicKeyByUserId(keyBlob); | |||||
if (key != null) { | |||||
return key; | |||||
} | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
/** | |||||
* Use pubring.kbx when available, if not fallback to secring.gpg or secret | |||||
* key path provided to parse and return secret key | |||||
* | |||||
* @return the secret key | |||||
* @throws IOException | |||||
* in case of issues reading key files | |||||
* @throws PGPException | |||||
* in case of issues finding a key | |||||
* @throws CanceledException | |||||
* @throws URISyntaxException | |||||
* @throws UnsupportedCredentialItem | |||||
*/ | |||||
public BouncyCastleGpgKey findSecretKey() | |||||
throws IOException, PGPException, CanceledException, | |||||
UnsupportedCredentialItem, URISyntaxException { | |||||
if (exists(USER_KEYBOX_PATH)) { | |||||
PGPPublicKey publicKey = // | |||||
findPublicKeyInKeyBox(USER_KEYBOX_PATH); | |||||
if (publicKey != null) { | |||||
return findSecretKeyForKeyBoxPublicKey(publicKey, | |||||
USER_KEYBOX_PATH); | |||||
} | |||||
throw new PGPException(MessageFormat | |||||
.format(JGitText.get().gpgNoPublicKeyFound, signingKey)); | |||||
} else if (exists(USER_PGP_LEGACY_SECRING_FILE)) { | |||||
PGPSecretKey secretKey = findSecretKeyInLegacySecring(signingKey, | |||||
USER_PGP_LEGACY_SECRING_FILE); | |||||
if (secretKey != null) { | |||||
return new BouncyCastleGpgKey(secretKey, USER_PGP_LEGACY_SECRING_FILE); | |||||
} | |||||
throw new PGPException(MessageFormat.format( | |||||
JGitText.get().gpgNoKeyInLegacySecring, signingKey)); | |||||
} | |||||
throw new PGPException(JGitText.get().gpgNoKeyring); | |||||
} | |||||
private BouncyCastleGpgKey findSecretKeyForKeyBoxPublicKey( | |||||
PGPPublicKey publicKey, Path userKeyboxPath) | |||||
throws PGPException, CanceledException, UnsupportedCredentialItem, | |||||
URISyntaxException { | |||||
/* | |||||
* this is somewhat brute-force but there doesn't seem to be another | |||||
* way; we have to walk all private key files we find and try to open | |||||
* them | |||||
*/ | |||||
PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder() | |||||
.build(); | |||||
PBEProtectionRemoverFactory passphraseProvider = new JcePBEProtectionRemoverFactory( | |||||
passphrasePrompt.getPassphrase(publicKey.getFingerprint(), | |||||
userKeyboxPath)); | |||||
try (Stream<Path> keyFiles = Files.walk(USER_SECRET_KEY_DIR)) { | |||||
for (Path keyFile : keyFiles.filter(Files::isRegularFile) | |||||
.collect(Collectors.toList())) { | |||||
PGPSecretKey secretKey = attemptParseSecretKey(keyFile, | |||||
calculatorProvider, passphraseProvider, publicKey); | |||||
if (secretKey != null) { | |||||
return new BouncyCastleGpgKey(secretKey, userKeyboxPath); | |||||
} | |||||
} | |||||
passphrasePrompt.clear(); | |||||
throw new PGPException(MessageFormat.format( | |||||
JGitText.get().gpgNoSecretKeyForPublicKey, | |||||
Long.toHexString(publicKey.getKeyID()))); | |||||
} catch (RuntimeException e) { | |||||
passphrasePrompt.clear(); | |||||
throw e; | |||||
} catch (IOException e) { | |||||
passphrasePrompt.clear(); | |||||
throw new PGPException(MessageFormat.format( | |||||
JGitText.get().gpgFailedToParseSecretKey, | |||||
USER_SECRET_KEY_DIR.toAbsolutePath()), e); | |||||
} | |||||
} | |||||
/** | |||||
* Return the first suitable key for signing in the key ring collection. For | |||||
* this case we only expect there to be one key available for signing. | |||||
* </p> | |||||
* | |||||
* @param signingkey | |||||
* @param secringFile | |||||
* | |||||
* @return the first suitable PGP secret key found for signing | |||||
* @throws IOException | |||||
* on I/O related errors | |||||
* @throws PGPException | |||||
* on BouncyCastle errors | |||||
*/ | |||||
private PGPSecretKey findSecretKeyInLegacySecring(String signingkey, | |||||
Path secringFile) throws IOException, PGPException { | |||||
try (InputStream in = newInputStream(secringFile)) { | |||||
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection( | |||||
PGPUtil.getDecoderStream(new BufferedInputStream(in)), | |||||
new JcaKeyFingerprintCalculator()); | |||||
Iterator<PGPSecretKeyRing> keyrings = pgpSec.getKeyRings(); | |||||
while (keyrings.hasNext()) { | |||||
PGPSecretKeyRing keyRing = keyrings.next(); | |||||
Iterator<PGPSecretKey> keys = keyRing.getSecretKeys(); | |||||
while (keys.hasNext()) { | |||||
PGPSecretKey key = keys.next(); | |||||
// try key id | |||||
String fingerprint = Hex | |||||
.toHexString(key.getPublicKey().getFingerprint()) | |||||
.toLowerCase(Locale.ROOT); | |||||
if (fingerprint | |||||
.endsWith(signingkey.toLowerCase(Locale.ROOT))) { | |||||
return key; | |||||
} | |||||
// try user id | |||||
Iterator<String> userIDs = key.getUserIDs(); | |||||
while (userIDs.hasNext()) { | |||||
String userId = userIDs.next(); | |||||
if (containsSigningKey(userId)) { | |||||
return key; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private PGPPublicKey getFirstPublicKey(KeyBlob keyBlob) throws IOException { | |||||
return ((PublicKeyRingBlob) keyBlob).getPGPPublicKeyRing() | |||||
.getPublicKey(); | |||||
} | |||||
private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException { | |||||
KeyBox keyBox; | |||||
try (InputStream in = new BufferedInputStream( | |||||
newInputStream(keyboxFile))) { | |||||
// note: KeyBox constructor reads in the whole InputStream at once | |||||
// this code will change in 1.61 to | |||||
// either 'new BcKeyBox(in)' or 'new JcaKeyBoxBuilder().build(in)' | |||||
keyBox = new KeyBox(in, new JcaKeyFingerprintCalculator()); | |||||
} | |||||
return keyBox; | |||||
} | |||||
} |
/*- | |||||
* Copyright (C) 2019, Salesforce. | |||||
* and other copyright owners as documented in the project's IP log. | |||||
* | |||||
* This program and the accompanying materials are made available | |||||
* under the terms of the Eclipse Distribution License v1.0 which | |||||
* accompanies this distribution, is reproduced below, and is | |||||
* available at http://www.eclipse.org/org/documents/edl-v10.php | |||||
* | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or | |||||
* without modification, are permitted provided that the following | |||||
* conditions are met: | |||||
* | |||||
* - Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* | |||||
* - Redistributions in binary form must reproduce the above | |||||
* copyright notice, this list of conditions and the following | |||||
* disclaimer in the documentation and/or other materials provided | |||||
* with the distribution. | |||||
* | |||||
* - Neither the name of the Eclipse Foundation, Inc. nor the | |||||
* names of its contributors may be used to endorse or promote | |||||
* products derived from this software without specific prior | |||||
* written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
package org.eclipse.jgit.lib.internal; | |||||
import java.net.URISyntaxException; | |||||
import java.nio.file.Path; | |||||
import java.text.MessageFormat; | |||||
import org.bouncycastle.openpgp.PGPException; | |||||
import org.bouncycastle.util.encoders.Hex; | |||||
import org.eclipse.jgit.api.errors.CanceledException; | |||||
import org.eclipse.jgit.errors.UnsupportedCredentialItem; | |||||
import org.eclipse.jgit.internal.JGitText; | |||||
import org.eclipse.jgit.transport.CredentialItem.CharArrayType; | |||||
import org.eclipse.jgit.transport.CredentialItem.InformationalMessage; | |||||
import org.eclipse.jgit.transport.CredentialsProvider; | |||||
import org.eclipse.jgit.transport.URIish; | |||||
/** | |||||
* Prompts for a passphrase and caches it until {@link #clear() cleared}. | |||||
* <p> | |||||
* Implements {@link AutoCloseable} so it can be used within a | |||||
* try-with-resources block. | |||||
* </p> | |||||
*/ | |||||
class BouncyCastleGpgKeyPassphrasePrompt implements AutoCloseable { | |||||
private CharArrayType passphrase; | |||||
private CredentialsProvider credentialsProvider; | |||||
public BouncyCastleGpgKeyPassphrasePrompt( | |||||
CredentialsProvider credentialsProvider) { | |||||
this.credentialsProvider = credentialsProvider; | |||||
} | |||||
/** | |||||
* Clears any cached passphrase | |||||
*/ | |||||
public void clear() { | |||||
if (passphrase != null) { | |||||
passphrase.clear(); | |||||
passphrase = null; | |||||
} | |||||
} | |||||
@Override | |||||
public void close() { | |||||
clear(); | |||||
} | |||||
private URIish createURI(Path keyLocation) throws URISyntaxException { | |||||
return new URIish(keyLocation.toUri().toString()); | |||||
} | |||||
/** | |||||
* Prompts use for a passphrase unless one was cached from a previous | |||||
* prompt. | |||||
* | |||||
* @param keyFingerprint | |||||
* the fingerprint to show to the user during prompting | |||||
* @param keyLocation | |||||
* the location the key was loaded from | |||||
* @return the passphrase (maybe <code>null</code>) | |||||
* @throws PGPException | |||||
* @throws CanceledException | |||||
* in case passphrase was not entered by user | |||||
* @throws URISyntaxException | |||||
* @throws UnsupportedCredentialItem | |||||
*/ | |||||
public char[] getPassphrase(byte[] keyFingerprint, Path keyLocation) | |||||
throws PGPException, CanceledException, UnsupportedCredentialItem, | |||||
URISyntaxException { | |||||
if (passphrase == null) { | |||||
passphrase = new CharArrayType(JGitText.get().credentialPassphrase, | |||||
true); | |||||
} | |||||
if (credentialsProvider == null) { | |||||
throw new PGPException(JGitText.get().gpgNoCredentialsProvider); | |||||
} | |||||
if (passphrase.getValue() == null | |||||
&& !credentialsProvider.get(createURI(keyLocation), | |||||
new InformationalMessage( | |||||
MessageFormat.format(JGitText.get().gpgKeyInfo, | |||||
Hex.toHexString(keyFingerprint))), | |||||
passphrase)) { | |||||
throw new CanceledException(JGitText.get().gpgSigningCancelled); | |||||
} | |||||
return passphrase.getValue(); | |||||
} | |||||
} |
/* | |||||
* Copyright (C) 2018, Salesforce. | |||||
* and other copyright owners as documented in the project's IP log. | |||||
* | |||||
* This program and the accompanying materials are made available | |||||
* under the terms of the Eclipse Distribution License v1.0 which | |||||
* accompanies this distribution, is reproduced below, and is | |||||
* available at http://www.eclipse.org/org/documents/edl-v10.php | |||||
* | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or | |||||
* without modification, are permitted provided that the following | |||||
* conditions are met: | |||||
* | |||||
* - Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* | |||||
* - Redistributions in binary form must reproduce the above | |||||
* copyright notice, this list of conditions and the following | |||||
* disclaimer in the documentation and/or other materials provided | |||||
* with the distribution. | |||||
* | |||||
* - Neither the name of the Eclipse Foundation, Inc. nor the | |||||
* names of its contributors may be used to endorse or promote | |||||
* products derived from this software without specific prior | |||||
* written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | |||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | |||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
package org.eclipse.jgit.lib.internal; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.IOException; | |||||
import java.net.URISyntaxException; | |||||
import java.security.Security; | |||||
import org.bouncycastle.bcpg.ArmoredOutputStream; | |||||
import org.bouncycastle.bcpg.BCPGOutputStream; | |||||
import org.bouncycastle.bcpg.HashAlgorithmTags; | |||||
import org.bouncycastle.jce.provider.BouncyCastleProvider; | |||||
import org.bouncycastle.openpgp.PGPException; | |||||
import org.bouncycastle.openpgp.PGPPrivateKey; | |||||
import org.bouncycastle.openpgp.PGPSecretKey; | |||||
import org.bouncycastle.openpgp.PGPSignature; | |||||
import org.bouncycastle.openpgp.PGPSignatureGenerator; | |||||
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; | |||||
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; | |||||
import org.eclipse.jgit.annotations.NonNull; | |||||
import org.eclipse.jgit.api.errors.CanceledException; | |||||
import org.eclipse.jgit.api.errors.JGitInternalException; | |||||
import org.eclipse.jgit.internal.JGitText; | |||||
import org.eclipse.jgit.lib.CommitBuilder; | |||||
import org.eclipse.jgit.lib.GpgSignature; | |||||
import org.eclipse.jgit.lib.GpgSigner; | |||||
import org.eclipse.jgit.lib.PersonIdent; | |||||
import org.eclipse.jgit.transport.CredentialsProvider; | |||||
/** | |||||
* GPG Signer using BouncyCastle library | |||||
*/ | |||||
public class BouncyCastleGpgSigner extends GpgSigner { | |||||
private static void registerBouncyCastleProviderIfNecessary() { | |||||
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { | |||||
Security.addProvider(new BouncyCastleProvider()); | |||||
} | |||||
} | |||||
/** | |||||
* Create a new instance. | |||||
* <p> | |||||
* The BounceCastleProvider will be registered if necessary. | |||||
* </p> | |||||
*/ | |||||
public BouncyCastleGpgSigner() { | |||||
registerBouncyCastleProviderIfNecessary(); | |||||
} | |||||
@Override | |||||
public void sign(@NonNull CommitBuilder commit, String gpgSigningKey, | |||||
@NonNull PersonIdent committer, | |||||
CredentialsProvider credentialsProvider) throws CanceledException { | |||||
if (gpgSigningKey == null || gpgSigningKey.isEmpty()) { | |||||
gpgSigningKey = committer.getEmailAddress(); | |||||
} | |||||
try (BouncyCastleGpgKeyPassphrasePrompt passphrasePrompt = new BouncyCastleGpgKeyPassphrasePrompt( | |||||
credentialsProvider)) { | |||||
BouncyCastleGpgKeyLocator keyHelper = new BouncyCastleGpgKeyLocator( | |||||
gpgSigningKey, passphrasePrompt); | |||||
BouncyCastleGpgKey gpgKey = keyHelper.findSecretKey(); | |||||
PGPSecretKey secretKey = gpgKey.getSecretKey(); | |||||
if (secretKey == null) { | |||||
throw new JGitInternalException( | |||||
JGitText.get().unableToSignCommitNoSecretKey); | |||||
} | |||||
char[] passphrase = passphrasePrompt | |||||
.getPassphrase(secretKey.getPublicKey().getFingerprint(), | |||||
gpgKey.getOrigin()); | |||||
PGPPrivateKey privateKey = secretKey | |||||
.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder() | |||||
.setProvider(BouncyCastleProvider.PROVIDER_NAME) | |||||
.build(passphrase)); | |||||
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( | |||||
new JcaPGPContentSignerBuilder( | |||||
secretKey.getPublicKey().getAlgorithm(), | |||||
HashAlgorithmTags.SHA256).setProvider( | |||||
BouncyCastleProvider.PROVIDER_NAME)); | |||||
signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, privateKey); | |||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | |||||
try (BCPGOutputStream out = new BCPGOutputStream( | |||||
new ArmoredOutputStream(buffer))) { | |||||
signatureGenerator.update(commit.build()); | |||||
signatureGenerator.generate().encode(out); | |||||
} | |||||
commit.setGpgSignature(new GpgSignature(buffer.toByteArray())); | |||||
} catch (PGPException | IOException | URISyntaxException e) { | |||||
throw new JGitInternalException(e.getMessage(), e); | |||||
} | |||||
} | |||||
} |
<maven-javadoc-plugin-version>3.0.1</maven-javadoc-plugin-version> | <maven-javadoc-plugin-version>3.0.1</maven-javadoc-plugin-version> | ||||
<tycho-extras-version>1.3.0</tycho-extras-version> | <tycho-extras-version>1.3.0</tycho-extras-version> | ||||
<gson-version>2.8.2</gson-version> | <gson-version>2.8.2</gson-version> | ||||
<bouncycastle-version>1.60</bouncycastle-version> | |||||
<spotbugs-maven-plugin-version>3.1.10</spotbugs-maven-plugin-version> | <spotbugs-maven-plugin-version>3.1.10</spotbugs-maven-plugin-version> | ||||
<maven-surefire-version>2.22.1</maven-surefire-version> | <maven-surefire-version>2.22.1</maven-surefire-version> | ||||
<maven-compiler-plugin-version>3.8.0</maven-compiler-plugin-version> | <maven-compiler-plugin-version>3.8.0</maven-compiler-plugin-version> | ||||
<artifactId>maven-project-info-reports-plugin</artifactId> | <artifactId>maven-project-info-reports-plugin</artifactId> | ||||
<version>${maven-project-info-reports-plugin-version}</version> | <version>${maven-project-info-reports-plugin-version}</version> | ||||
</plugin> | </plugin> | ||||
<plugin> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-maven-plugin</artifactId> | |||||
<version>2.1.2.RELEASE</version> | |||||
</plugin> | |||||
</plugins> | </plugins> | ||||
</pluginManagement> | </pluginManagement> | ||||
<artifactId>gson</artifactId> | <artifactId>gson</artifactId> | ||||
<version>${gson-version}</version> | <version>${gson-version}</version> | ||||
</dependency> | </dependency> | ||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcpg-jdk15on</artifactId> | |||||
<version>${bouncycastle-version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcprov-jdk15on</artifactId> | |||||
<version>${bouncycastle-version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.bouncycastle</groupId> | |||||
<artifactId>bcpkix-jdk15on</artifactId> | |||||
<version>${bouncycastle-version}</version> | |||||
</dependency> | |||||
</dependencies> | </dependencies> | ||||
</dependencyManagement> | </dependencyManagement> | ||||