Merge "Rename PackFile to Pack"
diff --git a/WORKSPACE b/WORKSPACE
index 5ce9dd7..224968a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -237,55 +237,55 @@
     sha1 = "9180733b7df8542621dc12e21e87557e8c99b8cb",
 )
 
-JETTY_VER = "9.4.35.v20201120"
+JETTY_VER = "9.4.36.v20210114"
 
 maven_jar(
     name = "jetty-servlet",
     artifact = "org.eclipse.jetty:jetty-servlet:" + JETTY_VER,
-    sha1 = "3e61bcb471e1bfc545ce866cbbe33c3aedeec9b1",
-    src_sha1 = "e237af9dd6556756736fcdc7da00194fa00d3c2b",
+    sha1 = "b189e52a5ee55ae172e4e99e29c5c314f5daf4b9",
+    src_sha1 = "3a0fa449772ab0d76625f6afb81f60c32a490613",
 )
 
 maven_jar(
     name = "jetty-security",
     artifact = "org.eclipse.jetty:jetty-security:" + JETTY_VER,
-    sha1 = "80dc2f422789c78315de76d289b7a5b36c3232d5",
-    src_sha1 = "3c0d03191fdffd9cf1e46fa206d79eeb9e3adb90",
+    sha1 = "42030d6ed7dfc0f75818cde0adcf738efc477574",
+    src_sha1 = "612220a97d45fad3983ccc56b0cb9a271f3fd003",
 )
 
 maven_jar(
     name = "jetty-server",
     artifact = "org.eclipse.jetty:jetty-server:" + JETTY_VER,
-    sha1 = "513502352fd689d4730b2935421b990ada8cc818",
-    src_sha1 = "760d3574ebc7f9b9b1ba51242b9c1dda6fe5505b",
+    sha1 = "88a7d342974aadca658e7386e8d0fcc5c0788f41",
+    src_sha1 = "4552c0c6db2948e8557db477b6b48d291006e481",
 )
 
 maven_jar(
     name = "jetty-http",
     artifact = "org.eclipse.jetty:jetty-http:" + JETTY_VER,
-    sha1 = "45d35131a35a1e76991682174421e8cdf765fb9f",
-    src_sha1 = "491cd5b8abe8fe6f3db0086907a3f12440188e73",
+    sha1 = "1eee89a55e04ff94df0f85d95200fc48acb43d86",
+    src_sha1 = "552a784ec789c7ba581c5341ae6d8b6353ed5ace",
 )
 
 maven_jar(
     name = "jetty-io",
     artifact = "org.eclipse.jetty:jetty-io:" + JETTY_VER,
-    sha1 = "eb9460700b99b71ecd82a53697f5ff99f69b9e1c",
-    src_sha1 = "fed96a4559a49e7173dbe2d9d8e100d72aee2a10",
+    sha1 = "84a8faf9031eb45a5a2ddb7681e22c483d81ab3a",
+    src_sha1 = "72d5fc6d909e28f8720394b25babda80805a46b9",
 )
 
 maven_jar(
     name = "jetty-util",
     artifact = "org.eclipse.jetty:jetty-util:" + JETTY_VER,
-    sha1 = "ef61b83f9715c3b5355b633d9f01d2834f908ece",
-    src_sha1 = "8f178bebeb34c8365a06d854daa9b22da1b301d7",
+    sha1 = "925257fbcca6b501a25252c7447dbedb021f7404",
+    src_sha1 = "532e8b66044f4e58ca5da3aec19f02a2f3c16ddd",
 )
 
 maven_jar(
     name = "jetty-util-ajax",
     artifact = "org.eclipse.jetty:jetty-util-ajax:" + JETTY_VER,
-    sha1 = "ebbb43912c6423bedb3458e44aee28eeb4d66f27",
-    src_sha1 = "b3acea974a17493afb125a9dfbe783870ce1d2f9",
+    sha1 = "2f478130c21787073facb64d7242e06f94980c60",
+    src_sha1 = "7153d7ca38878d971fd90992c303bb7719ba7a21",
 )
 
 BOUNCYCASTLE_VER = "1.65"
diff --git a/lib/BUILD b/lib/BUILD
index b56ba2f..8ad49d4 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -162,6 +162,7 @@
         "//org.eclipse.jgit:__pkg__",
         "//org.eclipse.jgit.gpg.bc:__pkg__",
         "//org.eclipse.jgit.test:__pkg__",
+        "//org.eclipse.jgit.gpg.bc.test:__pkg__",
     ],
     exports = ["@bcpg//jar"],
 )
@@ -172,6 +173,7 @@
         "//org.eclipse.jgit:__pkg__",
         "//org.eclipse.jgit.gpg.bc:__pkg__",
         "//org.eclipse.jgit.test:__pkg__",
+        "//org.eclipse.jgit.gpg.bc.test:__pkg__",
     ],
     exports = ["@bcprov//jar"],
 )
diff --git a/org.eclipse.jgit.gpg.bc.test/.classpath b/org.eclipse.jgit.gpg.bc.test/.classpath
index f08af0a..0acccba 100644
--- a/org.eclipse.jgit.gpg.bc.test/.classpath
+++ b/org.eclipse.jgit.gpg.bc.test/.classpath
@@ -2,10 +2,15 @@
 <classpath>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-	<classpathentry kind="src" path="tst">
+	<classpathentry kind="src" output="bin-tst" path="tst">
 		<attributes>
 			<attribute name="test" value="true"/>
 		</attributes>
 	</classpathentry>
-	<classpathentry kind="output" path="bin"/>
+	<classpathentry kind="src" output="bin-tst" path="tst-rsrc">
+		<attributes>
+			<attribute name="test" value="true"/>
+		</attributes>
+	</classpathentry>
+	<classpathentry kind="output" path="bin-tst"/>
 </classpath>
diff --git a/org.eclipse.jgit.gpg.bc.test/.gitignore b/org.eclipse.jgit.gpg.bc.test/.gitignore
index 934e0e0..8b6760c 100644
--- a/org.eclipse.jgit.gpg.bc.test/.gitignore
+++ b/org.eclipse.jgit.gpg.bc.test/.gitignore
@@ -1,2 +1,3 @@
 /bin
+/bin-tst
 /target
diff --git a/org.eclipse.jgit.gpg.bc.test/BUILD b/org.eclipse.jgit.gpg.bc.test/BUILD
index 1e3677d..59859b2 100644
--- a/org.eclipse.jgit.gpg.bc.test/BUILD
+++ b/org.eclipse.jgit.gpg.bc.test/BUILD
@@ -1,4 +1,9 @@
 load(
+    "@com_googlesource_gerrit_bazlets//tools:genrule2.bzl",
+    "genrule2",
+)
+load("@rules_java//java:defs.bzl", "java_import")
+load(
     "@com_googlesource_gerrit_bazlets//tools:junit.bzl",
     "junit_tests",
 )
@@ -8,7 +13,22 @@
     srcs = glob(["tst/**/*.java"]),
     tags = ["bc"],
     deps = [
+        "//lib:bcpg",
+        "//lib:bcprov",
         "//lib:junit",
         "//org.eclipse.jgit.gpg.bc:gpg-bc",
+        "//org.eclipse.jgit.gpg.bc.test:tst_rsrc",
     ],
 )
+
+java_import(
+    name = "tst_rsrc",
+    jars = [":tst_rsrc_jar"],
+)
+
+genrule2(
+    name = "tst_rsrc_jar",
+    srcs = glob(["tst-rsrc/**"]),
+    outs = ["tst_rsrc.jar"],
+    cmd = "o=$$PWD/$@ && tar cf - $(SRCS) | tar -C $$TMP --strip-components=2 -xf - && cd  $$TMP && zip -qr $$o .",
+)
diff --git a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
index 35a418c..39ece1f 100644
--- a/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc.test/META-INF/MANIFEST.MF
@@ -7,8 +7,16 @@
 Bundle-Vendor: %Bundle-Vendor
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.eclipse.jgit.gpg.bc.internal;version="[5.11.0,5.12.0)",
- org.junit;version="[4.13,5.0.0)"
+Import-Package: org.bouncycastle.jce.provider;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp.operator.jcajce;version="[1.65.0,2.0.0)",
+ org.bouncycastle.util.encoders;version="[1.65.0,2.0.0)",
+ org.eclipse.jgit.gpg.bc.internal;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="[5.11.0,5.12.0)",
+ org.eclipse.jgit.util.sha1;version="[5.11.0,5.12.0)",
+ org.junit;version="[4.13,5.0.0)",
+ org.junit.runner;version="[4.13,5.0.0)",
+ org.junit.runners;version="[4.13,5.0.0)"
 Export-Package: org.eclipse.jgit.gpg.bc.internal;x-internal:=true
 Require-Bundle: org.hamcrest.core;bundle-version="[1.1.0,2.0.0)",
  org.hamcrest.library;bundle-version="[1.1.0,2.0.0)"
diff --git a/org.eclipse.jgit.gpg.bc.test/build.properties b/org.eclipse.jgit.gpg.bc.test/build.properties
index 9ffa0ca..e36d666 100644
--- a/org.eclipse.jgit.gpg.bc.test/build.properties
+++ b/org.eclipse.jgit.gpg.bc.test/build.properties
@@ -1,5 +1,5 @@
 source.. = tst/
-output.. = bin/
+output.. = bin-tst/
 bin.includes = META-INF/,\
                .,\
                plugin.properties
diff --git a/org.eclipse.jgit.gpg.bc.test/pom.xml b/org.eclipse.jgit.gpg.bc.test/pom.xml
index f244fb4..cac7e15 100644
--- a/org.eclipse.jgit.gpg.bc.test/pom.xml
+++ b/org.eclipse.jgit.gpg.bc.test/pom.xml
@@ -85,6 +85,12 @@
     <sourceDirectory>src/</sourceDirectory>
     <testSourceDirectory>tst/</testSourceDirectory>
 
+    <testResources>
+      <testResource>
+        <directory>tst-rsrc/</directory>
+      </testResource>
+    </testResources>
+
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc
new file mode 100644
index 0000000..8427cfc
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool256.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mFMEWsODvRMJKyQDAwIIAQEHAgMEQLOxiiHZ/V6v3kvrhbnRtTp+oOPVpuvDKOiy
+gJOCZ7EWMVAwTr4syaSh8W8hdRgZ85Evv/1PYNFovYb6vzgVr7QJZWNjLWJwMjU2
+iJQEExMIADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4DAheAFiEEBjPF9yoZ
+j1HmUOSr0Mij2vngY0oFAlxVsKIACgkQ0Mij2vngY0pARAD/RozGDidH/0aFlxeU
+VWNJKjPiax6vdHqur5dqBS/RhhIA/1sPUnyAIvAXXID1uhK6oIBRKi7WJ5rI7vSy
+rBR5MlNJuFcEWsODvRIJKyQDAwIIAQEHAgMEE9Vd8dIjHJkmRs/8MLz4Krfwz5BK
+hunq1T0xnp65OEZJd00VxA+VUXdEUHfaDehtSv7izCpq4lbXGCkEGFN7QwMBCAeI
+eAQYEwgAIAIbDBYhBAYzxfcqGY9R5lDkq9DIo9r54GNKBQJcVbCpAAoJENDIo9r5
+4GNK0MYA/2p5cq5smjSvKD/EGkosQwfcqkeygsQuEpDDLeEdsv8vAP9j+RHKX2tl
+W08zbayxGB0E+aCHuKCF8iLPeI4eroi/fw==
+=vsa4
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc
new file mode 100644
index 0000000..bdb20fe
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool384.asc
@@ -0,0 +1,17 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mHMEWsOEUBMJKyQDAwIIAQELAwMEivcvlPJsPmivhJcrfHx+ORxyum57GtRhWM49
+Yr8fJ48gyFqj9cLYOBdhEVvcfceyBPXmyt0TozWtjkGzgbF4LIvN1EB0DW0Rlsdn
+p72/hf0gnXvWZdD8euArX4RaAYuQtAllY2MtYnAzODSItAQTEwkAPAIbAwULCQgH
+AgMiAgEGFQoJCAsCBBYCAwECHgMCF4AWIQRbiiVMgjztmN7NEO1s8tzoVZmtogUC
+XFWwugAKCRBs8tzoVZmtoj1yAX9P1UV7FYpGUIP13aPP0d5Bx8HdQDAoexdXz3WW
+WPL/7OhSjPde23Q8TfgWyO21M2wBf1oWjOsDSjO5mDLCr7ypAFF6IJAgx76tSUe9
+Qmy7sL94OWDQ4+1Dccnc9GGiHLtRI7h3BFrDhFASCSskAwMCCAEBCwMDBETUkqGr
+7p8kX2dm38jzzxXRh1+OL7nmY168Zeo9yfwDbnyx8BoihP9ZgPWjGXmefT78GSfw
+ZDaYgC2NFQOcI/b8agh3PcjrXgZaFCZbUR9v2DnLUpCF8ZbxDJwEqweNTAMBCQmI
+mAQYEwkAIAIbDBYhBFuKJUyCPO2Y3s0Q7Wzy3OhVma2iBQJcVbDCAAoJEGzy3OhV
+ma2ig1IBfifduIiwdAlD45MOolSpHMX0AT7KoJHpt9ZFvWnjQkq9ZGEA/RA9vx7Z
+sLb7IsG1GgF/Sn+gtf/JIteXaZMnOhEOZ2oFUufij6o8gII8/9s8mkIjkrIICy//
+0n3q82ndFg0c
+=fcpz
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc
new file mode 100644
index 0000000..5b4bca2
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/brainpool512.asc
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mJMEWsOEhxMJKyQDAwIIAQENBAMEA28ylDnn3LR7t6EQow5DszCpmUA+yup03LsT
+9o0Tw4/asi8nAz+1tlRY5HD49j53PziOlsGzKYa/jxGWjhVESgqLrJp/Eo65zK9v
+yDhX+iCkSYQ15WGKr3QaRUmBOUbX9PqE6dY+DDGQ1kewI93QIGCB1gn+OSmyKPm3
+YaVIbo60CWVjYy1icDUxMojUBBMTCgA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBExZq5JyqmofYLhb0KpcWNFPe49IBQJcVbDYAAoJEKpcWNFPe49I
+F8UB/i8DwypbNusdbAqTbu1Twpn/QFMaVKHn8Ysgzqpimv+6hRq7uzekCvEOPOjl
+Oke5yLp8bpTTMPRKyfjNatQhdn8B/2+54qtXJuQd9oTSz7f2eFYvA8ZsMQgApYNl
+ksvKSw6dhSNX/DXK7JYIlgZXx7UGTkP4h3FQSiyCUJhVpClVVGa4lwRaw4SHEgkr
+JAMDAggBAQ0EAwRCtEqQkEgzQDxNGCj0duj0aGvnH+DHKlP4V6p9LJVIL0TuF1uZ
+BzP04efRvZT2vzCTcvvdE/G6G/txEZsR/989OchbkVWOPM/oCVktkaA02rBMcefh
+k9wKD+O9E3oHEN+tBt3yhmsv0MIR9IfPwi1GCsu55p4WUI//+ysB2T0YaQMBCgmI
+uAQYEwoAIAIbDBYhBExZq5JyqmofYLhb0KpcWNFPe49IBQJcVbDgAAoJEKpcWNFP
+e49IZQUB/R4U4aWBMimZSL8kyaK+/Y8NcInIGqRzrm5zPnTSHrgQeH55SVKahzsq
+j57D1Ec1HnUd4ctISVocOxpUfnJq5NAB/1fzbh+1RN2ZyNW6tAJlA/Irkwzzbil9
+6fAIvRolwwaGsUZNMEiCF3rTcFaenJg9UhQvX6BoqXCdwawqTZCRN6E=
+=h+On
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc
new file mode 100644
index 0000000..db06732
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/dsa-elgamal.asc
@@ -0,0 +1,44 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQMuBFrDf8YRCACHbPXT8jG3RNWdNms9xvdaiLrY+Iui1Gq2WGLSajPEZVASEWN+
+JuuX8k9d05rb+F2VAqLnW3CQreDW6unVNeRf52tdM8J4eXmeu/Bkk8y1Qx/HbGca
+sAGSIEKg34TuV5Ly5m4Z07bs3HPYUUQbmu0uclGfnX/ArZ+4Jp+uypC9bErdiXM0
+cM7d52tb9IvOlXNu23rzHDbgVP6qF/AxLSRD8SQPvshu3/5b0bvdBkHVk+dHoLO0
+fC5j476ibHGGZcnPMrTwqEIAxUCy5wQ3Lb/2/G31kuV55bAZ41tUNEvfzbiRN1L5
+1uiO+XX96bRqLN13t0Coaba9fq1aN5Zr6piXAQCuNzvj8aaLXAOEXVRej6a2k+/C
+Jny91MgjSM701twUDQf/RMWHwQuFPe6zSDQs4pWlxkHwXJw3AVidkoWg/DCwv2pJ
+5VYQxBXRwND2OhcZvmeDT94UzPws0dFbprWyymtA49ZXitPGzFARAFWHWxk/IsOf
+Idc6w5eHXDMHxLhiPFqfjKeNpibzO2P7LXP2bUKzwybkKZarz1N6pfanDXAtC4DU
+SC3qWNqywYlfINAGCdwsPu5qFUNSnkjTYxe/MiHb4kL1p/z8qFNWrvg6GryXygp5
+cLdqckjPaUHlR+B9wQZIVRzVdlFAbMDJ0EERLFG7FbIuY8dzy5x7n+oBOgRxee2I
+ytUpGVMLIJuecARLXNKsMXviCMYVE7Tj5hiSoM0TIgf8CwLLFsSa0EDm/wlXYZMj
+2gg3Z8iCz6ycxvFD9PXNt+8jyELO8CwS2pWu7ptBgaugkinqd40EQslQoP76CcHq
+bGQEohm4SnmfGsV8dicuziMVVKkVrYgbGvZ5cQ/ONGTTnSJuiTPN19oztwh8JOEc
+Jd4l+wFuVSm8OS1mj5eexeX1Tz3NfWQMT4deKh+jiTLe9Sw/57sSjxiw/8IczqhN
+Fu3YIy40d3Bv+OF6i8I+94WLbJPiX1ban1wqcA0bMaps1aYVtTRZ+mP0b3M9W7qa
+383/SLCBjUzQ7zm6PX/7uAXFyZOQfcyLaJ8Hc34yOE0git1DWmRS7U16Zv54v1Uh
+HbQGZHNhLWVniJQEExEIADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4DAheA
+FiEECRxEzpz7w/9+x6ZNyKEKfXgnPhAFAlxVr2sACgkQyKEKfXgnPhABkgD7BfEd
+jhB+ApC9icNLs893i2jiHbAxZGSOQMRhCaJ7AzoBAIipcSIsBa3LJ4eTec1esiCY
+a8xzxquCTA+oANNoX7p6uQMNBFrDf8YQDADMPZ6+/YAIjXMLfQKX80jz6FZ4Kdfx
+Dc60m4O+ZElMv7eXtQJC2L/xOh4Th1fZUQIhSdtFiwaCSkCD8occfJwyt+lH3Dj0
+Qrh3mIycAfPrjj0Rgxz8nRQbBLDbLF1QGPimt0zP69ByJ3opLujVVi5ixwgwza9S
+eGffKwGdyb9uFcB9MVnC997zfLvx/uNV44BwLnCH6Tp68Lynf+FpuvSX+Rsj4li3
+UiLoVxEIbBZ/5Bn3ygc7aW4fM4bR4hKjWwJR0Hh1y/kt6A7dEAypVKBfSqAtAu2A
+zYAq3USsbtq1X1FaGEsmvcJY8IGa+aLTArq7dkhXzcv7K3EVdqOawS1zS/ARuG+B
+k6kct3zzyj1EitiTvdMAkGOoyk16qKVzUcbFRVC1HsZtxYj6OxU4Eazvh0LjvZ0A
++eft/XO/ZmN6vyRaF1/10z+uHPfj1FLMpS8Zn4SN6x7Qtsx3iLL1n9cKBDFRXCqD
+HDaxLVC3N4zAI2hMMmZid+fbTuhsqYbSX2cAAwUL/j/H4/9Ml7PUUCXoozX2V4K+
+gi6WEYmY+pXN/we9NuFulW4aURo7jK4wRYBu0BS3K9e8f8WUMAV5V6ShPWHXcobt
+iuSjLYJwdBJkgHbnKFWPZUozJ3Ftyp0Lh1M5bN7/ECofAxLHbRpCVrcOP2LC7vAU
+AeMgdiFDqEiLCnr/aGvqUOxbGO6Isi4jvaM/ZUfGjGe/Z6yVoqm6wEsNM7+9cFGQ
+QR1lRPeCPKcLeasCdbM5EIt1aLFNijZigWuDRLIgG5PuzA8Kpdk/u/UuCUeUFwJN
+ym8MEv2JJDiWHmb8IcgFMp40VenUs0fte0LWwrMjWVPpLsHKmkraRjQ1UtarRhT0
+ANYilGjZWCnCb11xGKhlM7r5IkLGY/L/Eh4vjLgg9T5rGwOF8p1jSgx9mA8SpHV0
+O0BoKNX1ApWEHayTLcyayCnTYbY/e4axnSKodixAI/NghOnJHqGr4LeZeKk/Q0mm
+GlljzFv3EAdoru4DVowWGFBmrwBy7o+GLgHs6K/+yIh4BBgRCAAgAhsMFiEECRxE
+zpz7w/9+x6ZNyKEKfXgnPhAFAlxVr64ACgkQyKEKfXgnPhCETQEApruWUqCwfibQ
+vyI/OZohPzljlvIoioj3rFjYNpufQD8A/RTaYtnPiEvsPynEZCj9zTV/SuHiKbHS
+v5BhpoOOm+jM
+=PnGk
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc
new file mode 100644
index 0000000..636a5a9
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/ed25519.asc
@@ -0,0 +1,9 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEWsN6MBYJKwYBBAHaRw8BAQdAAS+nkv9BdVi0JX7g6d+O201bdKhdowbielOo
+ugCpCfi0CWVjYy0yNTUxOYiUBBMWCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgID
+AQIeAwIXgBYhBCH8aCdKrjtd45pCd8x4YniYGwcoBQJcVa/NAAoJEMx4YniYGwco
+lFAA/jMt3RUUb5xt63JW6HFcrYq0RrDAcYMsXAY73iZpPsEcAQDmKbH21LkwoClU
+9RrUJSYZnMla/pQdgOxd7/PjRCpbCg==
+=miZp
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc
new file mode 100644
index 0000000..fd1509e
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp256.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mFIEWsOCNRMIKoZIzj0DAQcCAwQS5G6mn5dhamZ6678SXE1azavqf8BItWO9Qv8V
+dS1vEEoD14urr5OQKTLuHhDRjvSQdaxRtkf0sI51T7230sT3tAhlY2MtcDI1NoiU
+BBMTCAA8AhsDBQsJCAcCAyICAQYVCgkICwIEFgIDAQIeAwIXgBYhBLVP3ru2c0I6
+XQqlRCNnTyGyRBUnBQJcVa/nAAoJECNnTyGyRBUn1ycA+wVg9sEfHDBaGtLqlUSB
+WdGKURrHN7CJe2UTz1/7oQCBAQDDi4RQyLHs+TfOrBNSbLEswCu1oEh8VmHt/SN7
++mqNLbhWBFrDgjUSCCqGSM49AwEHAgMELDOArLIG85ABQu1IwgQMpiIuUwj+N7ib
+gGenTRck5dkBpX48eK3lbjovXn4YkBneA7z14iez3+Sdg6UFAMFV2QMBCAeIeAQY
+EwgAIAIbDBYhBLVP3ru2c0I6XQqlRCNnTyGyRBUnBQJcVa/vAAoJECNnTyGyRBUn
+ZKoBAJ64gv3w27nFBERvIsRqufvR6xcimqS7Gif+WehBU+P5AQC5bqoISh0oSQid
+adI84f60RuOaozpjvR3B1bPZiR6u7w==
+=H2xn
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc
new file mode 100644
index 0000000..b2b5995
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp384.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mG8EWsOCnBMFK4EEACIDAwTRTCLBHlq6SUTiXZfWR0vbUh/0VAlrePaqHVIE4LEK
+0UBhPCIQOGuGL4JIufc8UzBWhiMLJ0z7KRjBWufsMZR2+rqj+unOK2lLu7sc9or8
+X6B74hhP3Ili24PgGFAeAG+0CGVjYy1wMzg0iLQEExMJADwCGwMFCwkIBwIDIgIB
+BhUKCQgLAgQWAgMBAh4DAheAFiEEqyXLoELdkkw6zD7TJCo6peqF9EoFAlxVsBEA
+CgkQJCo6peqF9EooJQF7BPZelriXwZ/kJzaamImHBddkLFc7d2WbuSfDxEZQ+Mfw
+BAP3+QYUaFtfeqApjY69AX4w6LhTUgI2kl4O0Vc7ZOlqZBlwAc8CMV08TTfOEio2
+b51SItvhLdDrFRJ2K4jiO+a4cwRaw4KcEgUrgQQAIgMDBORWqhYflSrYzF04SK8q
+8Om+DYTvwRtUlr3Aoq44+gm5yBcmJmgT3TKrp/bx5Jg/zwzIASFn0agbxkqKpQqH
+sHeelWsSBROQzy98HXdCp3nVmghI2aDk8zdD6AV4m7c2ewMBCQmImAQYEwkAIAIb
+DBYhBKsly6BC3ZJMOsw+0yQqOqXqhfRKBQJcVbAZAAoJECQqOqXqhfRKgAIBf3Wk
+TsqUA1JXkPGetA9sjHglIICN+DZY5k+PwTJUxaW2zrkiPJ3BYEnKbmmBLzA7BgGA
+4RYatyl2WOUYh/poRLgu7JpE4oRqdmNA+QOpCILMId1AeXfj4W01RKFWaKeH+3Yy
+=2H/0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc
new file mode 100644
index 0000000..db18f81
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/nistp521.asc
@@ -0,0 +1,19 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mJMEWsODGxMFK4EEACMEIwQA8OZCJ8Iv4Qr2oRr0kqez0nPSW/vNxYBZRpCJ9ab8
+kVaRhW7+5vEsecm5XugZSePbAmERmk3oEiSgu35r6aovowcAGrOBfBm9fyIVqaoX
+veTS3yRHH6NEf044+pC+uBaaFukkrDmVTecdRvYr3Yrdc5ifyOve053znlpQ6a4n
+9bh4GGy0CGVjYy1wNTIxiNYEExMKADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMB
+Ah4DAheAFiEET7Of9vpIV6S9fvW0IJLKgyQmO2oFAlxVsCkACgkQIJLKgyQmO2oK
+DwIGO72zo6otVkbHfeI9hWx/8FAOXh4MT4YtDicF/sj8QbHzdbEBHcLCByLYAnph
+8VVoCxpPcBLmNSHbNyreoksjEE0CB10P5kPrd/bYkdNx/HTu+1i8f7a448+Nr2rF
+PwdI9tOsghkT41qobZavjjnBlT/xv5DqXldJkEfaeiJxPHOKvMhWuJcEWsODGxIF
+K4EEACMEIwQBAY7ZCAjks1MWWxibg/EVaz5t6iEKJTwu8mGGKWdPZAQRKKNtNpf0
+pZAMV3I8ue/WQMsYKRYv5AGq1PnjV19DmLsA0aGw4MDM260coctkcn/2MAJQMC9+
+3Z+BJS3hqzwDuZ+LS13r0RLpgnt3ks+3ucG4II38ZZ1lTwKoIc+w/OuhsOIDAQoJ
+iLsEGBMKACACGwwWIQRPs5/2+khXpL1+9bQgksqDJCY7agUCXFWwLgAKCRAgksqD
+JCY7ahqbAgkBiXYtiBlp5dmSYnbc4JoIYWcxTBQ+/dGHyU6ZEfC5VQz2mrdJetK1
+bIID0rFSsd24/8IzAqM3L+nY9h9bULWroroCBjTohh0j2EbW+hFOrRqL01osnlY+
+1/G8e44blB5JqsPI9FqOZOUj6IzsUuV1N9gJbm1RHu/hSpm52d6rX4nOTbqt
+=3jnl
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc
new file mode 100644
index 0000000..e74df7a
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/rsa.asc
@@ -0,0 +1,40 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGNBFrDgZ4BDACxg83nNkYvIAz6Nk4Np4BscWDrrL3tyiiQSz1yfCotxO8zUtVl
+JorSlqRurNdAYU2XJLakMqpQHE7VVIMI/WdXCC8CrQbULOBxIf/LGiQf0VDo8ukg
+iFFd5vUeMRRILWKnMc/GCmFkFOUHd1Y60h96oe5f/d286fRZQnCO8PGS8CgB1mDJ
+GBY8U1DCCkt6g9O6bfFkfcwetr1+kB2cBIY7uDzN1Sm8dz2VrkqPqtqt/F02giRN
+hD2GxX9HVYCkCKEE9DFsB+MDKR9z5lXrI3SAsL/3htXK/ukWgBe4DIFjTXbrzgP+
+nuWb4s1NxwSD7yjlnqVD7mZTGAwMsQbCjhwIeY4t5onsCJ4/40zm1jIZ5TJsfQ/b
+5gA7iu9kMKzxO0bPI2loJEB/K9aH7qsyQHgtt7G3+G8JrixLBUobtreY9V2QvhtC
+Q4OAHRO5nIKYIazDuKv9bxautZ0WuLOk/qmWMMoqFAn5bzQeVTTBD6kvr6l8/+MZ
+zaij9YPeUbeKJuMAEQEAAbQHcnNhLXJzYYkB0gQTAQIAPAIbAwULCQgHAgMiAgEG
+FQoJCAsCBBYCAwECHgMCF4AWIQRrwEpaPds1dmuaQNgvuReRGImOiwUCXFWwhwAK
+CRAvuReRGImOi1NiC/0RPjbQTWMw4/GxIMkRb9kmmsSUe7kCgqBCqZTxcI7rxZdn
+JFDbP5c6DAS/11bRQJ9OsoUjbDx0d81UuBlXB/mNsb9nCcXOrqAUHRqgWoNSDk4g
+9Oa1Kx77OM9BvRJbJGch2YW5Wcch5vcqQNu+6x3VGt7ipljYEJSQ6Dre+dgxYjXK
+60x63/ulFk2XImPQYjQ8VHbW/HDg/+DLf++phjVy9l58U1sUKSSdO8uuYoW6dBv2
+xRg18Sn3DWOU+mrkV8Ld95+NRRE1cSHTQv5hu4ELqrV+YdGNmv9DgQAMJOl3xy8i
+vOz4cpKaOBasm423wr5Y56nOTzLFN+dxnYR8tbqswLkCldRY6fvL1NsS77rj0yZp
+pecCyi0E6RAcmSiZJqpnOpcuI76AkZuWSDEP3Y3x5QBf5fu2uiQQsPXYN8ie6xcC
+zeYtXsHyNxF0oBh2c26po8fo4E4T70RSO8Oqs1XzXnjIIle8pKU9M5U5ISbWS3Hj
+vtOn5ZrLC5KYnVRna3G5AY0EWsOBngEMALmXpJoPC1m4THYrfHibtt2/OwAlDm20
+3xn+Klw69bkeXdc71wsLOAHVL3+7gXpip+IYmN7CBIyqlOCtsluu+gwP3MczPJZX
+vk1uXMMfLKiXl9Kodx/Fqq0Y26Tqse5PPMlagPStIvKyT0WTa3RCD28uVklapLuy
+1w1k4G5hIDPt6uKyxXq/HzneRSGWafmqoCWOmXQZzfOMG249bMXNOcPMJhOejPRS
+jREnnntbpvZ8DU/38+JFtqCUkPwuqYQkvGCKReSBifMiG3XAhHWOGzXPzdW1XdAi
+aA+NQP/kMUs45jS3hDdp4EObllYRBsQwtFpKPMNmwaVuOmVlbrXTP0YsDYGndkE6
+5nJ46/2xPhl8+nIgDLg3SBBzQdOiPOGtHYjs0bRKdwXTeAq4fDq0vCQXMJF2fwAQ
+LEYWs7kabKhcPpWzTtoCG78WzR3TgldEPhPjE0offvVQO56x1XDqMBctoiDWkWkS
+bdi03GhbFdK5A7uYBTJYEoo61Yp/2/MjyQARAQABiQG2BBgBAgAgAhsMFiEEa8BK
+Wj3bNXZrmkDYL7kXkRiJjosFAlxVsI8ACgkQL7kXkRiJjoumjwv/c6a9G1bi3sh7
+wRFhpsrFUoFfEBDI4eyI/haWhCIfI8n7p3lCSIy8lmf9yvUs75d5M3EQW08NQjIs
+/o8FcFoUBnKQv2whWSHTpx/BkuhcNVY+NIwyBKomU0WkFSm3+80ix0uh97KSlRlW
+Q5nMVExNxZ4mRFAhDQrJ2/pZ2DaddeO+4uZ7Twquaix+PMxpNKvkj2+757L23YjF
+QmHdk6E8burofpSCfBTB84eUSDvzs6Eb/34/KlbBZhKMYdffMDCSAZIMfIav6YVJ
+UDzT40kmS0vRW6bDIetSbpBM+GD1cSq0wKdlt+Giur9ZiaiyHIEqbPgr6WgdND25
+Vx/i23Ik2o8wMb0Ub8cKD9wjdGAk+Rt2r7d2RzyO/R3ThKbUOGkQX6acAAZAjhPs
+UGxt1dDojmQ3nF4l2hZ9PcsyD3pz226wUUPT4JA1eE6tdoVjzY2J7EhfNaVcQQlb
+bQJQ+BQcO4oP1mPRCx1GiSmB+jRNQ4npxVJxLO/j7T27CrSZbhT7
+=aibx
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc
new file mode 100644
index 0000000..837f8a8
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/secp256k1.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mE8EWsOE8xMFK4EEAAoCAwQVHqFZWqedXUIkNFs82PsQB3bsCDhrL/73xZca3+vo
+kB4T7jHcACThuMZYuUqUo9NzNTJioluOvZG+UdYXPdfdtAplY2MtcDI1NmsxiJQE
+ExMIADwCGwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4DAheAFiEEgfdytX1Ov+cA
+CmYjPqW7b5aSwaAFAlxVsQ0ACgkQPqW7b5aSwaD2tQD/R4d15NBuSJ6IB1brH0E9
+nEWkqo892PaAY5akdCO/i9EBAMsjE5NPxBnCs03c+VHFU200k27ixdrWpUa+HZEI
+A5wSuFMEWsOE8xIFK4EEAAoCAwSUWwe7CaaOYRANiKet2evLiOumefIHuvRpyOSK
+hyRdclIWpBUCAWEnmalkEL/8cEM5fjtILtCOKXqCOBsPv45HAwEIB4h4BBgTCAAg
+AhsMFiEEgfdytX1Ov+cACmYjPqW7b5aSwaAFAlxVsRUACgkQPqW7b5aSwaCETgD/
+YXzCMYMbPGAU2oTitjAno8hDWmgTeaFWeCmqf6l9mP8BAKvpewWeFGZfWGAQcWPi
+E+jv7vadvEt1yMA8rmT041F5
+=mDCI
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc
new file mode 100644
index 0000000..d531d7a
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst-rsrc/org/eclipse/jgit/gpg/bc/internal/keys/x25519.asc
@@ -0,0 +1,13 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mDMEW8SVGhYJKwYBBAHaRw8BAQdA9SMZ2uw0YugMFcl5TpEZeBRAGniEk9a42XNs
+7QA4Tky0DGVkZHNhLXgyNTUxOYiQBBMWCAA4FiEETJc4pvK+Thp5bJt7lBgioPwb
+MKUFAlvElRoCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQlBgioPwbMKUi
+1wEAgMq3X7o17OJBPfY3He/exDR6LhWwAAXrVQR/WdRiHkEBALd1Mj0BlZZLoKTr
+uJ4MD5CYZLicXTRwOv6e52F/DHwJuDgEW8SVGhIKKwYBBAGXVQEFAQEHQA0Lh2mG
+lB1O4xDYgztm/aX7+8AdHEGaMsCF1RQ6wVUeAwEIB4h4BBgWCAAgFiEETJc4pvK+
+Thp5bJt7lBgioPwbMKUFAlvElRoCGwwACgkQlBgioPwbMKXmlQD+KxVg2dGL8lRW
+rQajwzmuwMrJX1lvJylg5Ozk6SGrBeABANZrdt8bmArEqeRVxFO2F4P7btyIpf1w
+5aNpqqtvkRcB
+=EYfV
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java
new file mode 100644
index 0000000..e300802
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip25519Test.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import static org.junit.Assert.assertEquals;
+
+import java.math.BigInteger;
+import java.util.Locale;
+
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.util.encoders.Hex;
+import org.eclipse.jgit.util.sha1.SHA1;
+import org.junit.Test;
+
+public class KeyGrip25519Test {
+
+	interface Hash {
+		byte[] hash(SHA1 sha, BigInteger q) throws PGPException;
+	}
+
+	private void assertKeyGrip(String key, String expectedKeyGrip, Hash hash)
+			throws Exception {
+		SHA1 grip = SHA1.newInstance();
+		grip.setDetectCollision(false);
+		BigInteger pk = new BigInteger(key, 16);
+		byte[] keyGrip = hash.hash(grip, pk);
+		assertEquals("Keygrip should match", expectedKeyGrip,
+				Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT));
+	}
+
+	@Test
+	public void testCompressed() throws Exception {
+		assertKeyGrip("40"
+				+ "773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB",
+				"9DB6C64A38830F4960701789475520BE8C821F47",
+				KeyGrip::hashEd25519);
+	}
+
+	@Test
+	public void testCompressedNoPrefix() throws Exception {
+		assertKeyGrip(
+				"773E72848C1FD5F9652B29E2E7AF79571A04990E96F2016BF4E0EC1890C2B7DB",
+				"9DB6C64A38830F4960701789475520BE8C821F47",
+				KeyGrip::hashEd25519);
+	}
+
+	@Test
+	public void testCurve25519() throws Exception {
+		assertKeyGrip("40"
+				+ "918C1733127F6BF2646FAE3D081A18AE77111C903B906310B077505EFFF12740",
+				"0F89A565D3EA187CE839332398F5D480677DF49C",
+				KeyGrip::hashCurve25519);
+	}
+}
diff --git a/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java
new file mode 100644
index 0000000..a4aaf40
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc.test/tst/org/eclipse/jgit/gpg/bc/internal/keys/KeyGripTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPPublicKeyRing;
+import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.bouncycastle.util.encoders.Hex;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class KeyGripTest {
+
+	@BeforeClass
+	public static void ensureBC() {
+		if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+			Security.addProvider(new BouncyCastleProvider());
+		}
+	}
+
+	protected static class TestData {
+
+		String filename;
+
+		String[] expectedKeyGrips;
+
+		TestData(String filename, String... keyGrips) {
+			this.filename = filename;
+			this.expectedKeyGrips = keyGrips;
+		}
+
+		@Override
+		public String toString() {
+			return filename;
+		}
+	}
+
+	@Parameters(name = "{0}")
+	public static TestData[] initTestData() {
+		return new TestData[] {
+				new TestData("rsa.asc",
+						"D148210FAF36468055B83D0F5A6DEB83FBC8E864",
+						"A5E4CD2CBBE44A16E4D6EC05C2E3C3A599DC763C"),
+				new TestData("dsa-elgamal.asc",
+						"552286BEB2999F0A9E26A50385B90D9724001187",
+						"CED7034A8EB5F4CE90DF99147EC33D86FCD3296C"),
+				new TestData("brainpool256.asc",
+						"A01BAA22A72F09A0FF0A1D4CBCE70844DD52DDD7",
+						"C1678B7DE5F144C93B89468D5F9764ACE182ED36"),
+				new TestData("brainpool384.asc",
+						"2F25DB025DEBF3EA2715350209B985829B04F50A",
+						"B6BD8B81F75AF914163D97DF8DE8F6FC64C283F8"),
+				new TestData("brainpool512.asc",
+						"5A484F56AB4B8B6583B6365034999F6543FAE1AE",
+						"9133E4A7E8FC8515518DF444C3F2F247EEBBADEC"),
+				new TestData("nistp256.asc",
+						"FC81AECE90BCE6E54D0D637D266109783AC8DAC0",
+						"A56DC8DB8355747A809037459B4258B8A743EAB5"),
+				new TestData("nistp384.asc",
+						"A1338230AED1C9C125663518470B49056C9D1733",
+						"797A83FE041FFE06A7F4B1D32C6F4AE0F6D87ADF"),
+				new TestData("nistp521.asc",
+						"D91B789603EC9138AA20342A2B6DC86C81B70F5D",
+						"FD048B2CA1919CB241DC8A2C7FA3E742EF343DCA"),
+				new TestData("secp256k1.asc",
+						"498B89C485489BA16B40755C0EBA580166393074",
+						"48FFED40D018747363BDEFFDD404D1F4870F8064"),
+				new TestData("ed25519.asc",
+						"940D97D75C306D737A59A98EAFF1272832CEDC0B"),
+				new TestData("x25519.asc",
+						"A77DC8173DA6BEE126F5BD6F5A14E01200B52FCE",
+						"636C983EDB558527BA82780B52CB5DAE011BE46B")
+		};
+	}
+
+	// Injected by JUnit
+	@Parameter
+	public TestData data;
+
+	private void readAsc(InputStream in, Consumer<PGPPublicKey> process)
+			throws IOException, PGPException {
+		PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
+			PGPUtil.getDecoderStream(in), new JcaKeyFingerprintCalculator());
+
+		Iterator<PGPPublicKeyRing> keyRings = pgpPub.getKeyRings();
+		while (keyRings.hasNext()) {
+			PGPPublicKeyRing keyRing = keyRings.next();
+
+			Iterator<PGPPublicKey> keys = keyRing.getPublicKeys();
+			while (keys.hasNext()) {
+				process.accept(keys.next());
+			}
+		}
+	}
+
+	@Test
+	public void testGrip() throws Exception {
+		try (InputStream in = this.getClass()
+				.getResourceAsStream(data.filename)) {
+			int index[] = { 0 };
+			readAsc(in, key -> {
+				byte[] keyGrip = null;
+				try {
+					keyGrip = KeyGrip.getKeyGrip(key);
+				} catch (PGPException e) {
+					throw new RuntimeException(e);
+				}
+				assertTrue("More keys than expected",
+						index[0] < data.expectedKeyGrips.length);
+				assertEquals("Wrong keygrip", data.expectedKeyGrips[index[0]++],
+						Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT));
+			});
+			assertEquals("Missing keys", data.expectedKeyGrips.length,
+					index[0]);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
index 655dcca..b379a2b 100644
--- a/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.gpg.bc/META-INF/MANIFEST.MF
@@ -8,12 +8,21 @@
 Bundle-Localization: plugin
 Bundle-Version: 5.11.0.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: org.bouncycastle.bcpg;version="[1.65.0,2.0.0)",
+Import-Package: org.bouncycastle.asn1;version="[1.65.0,2.0.0)",
+ org.bouncycastle.asn1.cryptlib;version="[1.65.0,2.0.0)",
+ org.bouncycastle.asn1.x9;version="[1.65.0,2.0.0)",
+ org.bouncycastle.bcpg;version="[1.65.0,2.0.0)",
+ org.bouncycastle.bcpg.sig;version="[1.65.0,2.0.0)",
+ org.bouncycastle.crypto.ec;version="[1.65.0,2.0.0)",
  org.bouncycastle.gpg;version="[1.65.0,2.0.0)",
  org.bouncycastle.gpg.keybox;version="[1.65.0,2.0.0)",
  org.bouncycastle.gpg.keybox.jcajce;version="[1.65.0,2.0.0)",
+ org.bouncycastle.jcajce.interfaces;version="[1.65.0,2.0.0)",
  org.bouncycastle.jce.provider;version="[1.65.0,2.0.0)",
+ org.bouncycastle.math.ec;version="[1.65.0,2.0.0)",
+ org.bouncycastle.math.field;version="[1.65.0,2.0.0)",
  org.bouncycastle.openpgp;version="[1.65.0,2.0.0)",
+ org.bouncycastle.openpgp.jcajce;version="[1.65.0,2.0.0)",
  org.bouncycastle.openpgp.operator;version="[1.65.0,2.0.0)",
  org.bouncycastle.openpgp.operator.jcajce;version="[1.65.0,2.0.0)",
  org.bouncycastle.util.encoders;version="[1.65.0,2.0.0)",
@@ -25,5 +34,5 @@
  org.eclipse.jgit.transport;version="[5.11.0,5.12.0)",
  org.eclipse.jgit.util;version="[5.11.0,5.12.0)",
  org.slf4j;version="[1.7.0,2.0.0)"
-Export-Package: org.eclipse.jgit.gpg.bc.internal;version="5.11.0";
-  x-friends:="org.eclipse.jgit.gpg.bc.test"
+Export-Package: org.eclipse.jgit.gpg.bc.internal;version="5.11.0";x-friends:="org.eclipse.jgit.gpg.bc.test",
+ org.eclipse.jgit.gpg.bc.internal.keys;version="5.11.0";x-friends:="org.eclipse.jgit.gpg.bc.test"
diff --git a/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory
new file mode 100644
index 0000000..17ab30f
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/resources/META-INF/services/org.eclipse.jgit.lib.GpgSignatureVerifierFactory
@@ -0,0 +1 @@
+org.eclipse.jgit.gpg.bc.internal.BouncyCastleGpgSignatureVerifierFactory
\ No newline at end of file
diff --git a/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties b/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
index 1441c63..f2aa014 100644
--- a/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
+++ b/org.eclipse.jgit.gpg.bc/resources/org/eclipse/jgit/gpg/bc/internal/BCText.properties
@@ -1,6 +1,8 @@
+corrupt25519Key=Ed25519/Curve25519 public key has wrong length: {0}
 credentialPassphrase=Passphrase
-gpgFailedToParseSecretKey=Failed to parse secret key file in directory: {0}. Is the entered passphrase correct?
+gpgFailedToParseSecretKey=Failed to parse secret key file {0}. Is the entered passphrase correct?
 gpgNoCredentialsProvider=missing credentials provider
+gpgNoKeygrip=Cannot find key {0}: cannot determine key grip
 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}
@@ -8,4 +10,15 @@
 gpgNotASigningKey=Secret key ({0}) is not suitable for signing
 gpgKeyInfo=GPG Key (fingerprint {0})
 gpgSigningCancelled=Signing was cancelled
+nonSignatureError=Signature does not decode into a signature object
+signatureInconsistent=Inconsistent signature; key ID {0} does not match issuer fingerprint {1}
+signatureKeyLookupError=Error occurred while looking for public key
+signatureNoKeyInfo=No way to determine a public key from the signature
+signatureNoPublicKey=No public key found to verify the signature
+signatureParseError=Signature cannot be parsed
+signatureVerificationError=Signature verification failed
 unableToSignCommitNoSecretKey=Unable to sign commit. Signing key not available.
+uncompressed25519Key=Cannot handle ed25519 public key with uncompressed data: {0}
+unknownCurve=Unknown curve {0}
+unknownCurveParameters=Curve {0} does not have a prime field
+unknownKeyType=Unknown key type {0}
\ No newline at end of file
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
index 1a00b0f..4753ac1 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BCText.java
@@ -1,3 +1,12 @@
+/*
+ * Copyright (C) 2018, 2021 Salesforce and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
 package org.eclipse.jgit.gpg.bc.internal;
 
 import org.eclipse.jgit.nls.NLS;
@@ -18,9 +27,11 @@
 	}
 
 	// @formatter:off
+	/***/ public String corrupt25519Key;
 	/***/ public String credentialPassphrase;
 	/***/ public String gpgFailedToParseSecretKey;
 	/***/ public String gpgNoCredentialsProvider;
+	/***/ public String gpgNoKeygrip;
 	/***/ public String gpgNoKeyring;
 	/***/ public String gpgNoKeyInLegacySecring;
 	/***/ public String gpgNoPublicKeyFound;
@@ -28,6 +39,17 @@
 	/***/ public String gpgNotASigningKey;
 	/***/ public String gpgKeyInfo;
 	/***/ public String gpgSigningCancelled;
+	/***/ public String nonSignatureError;
+	/***/ public String signatureInconsistent;
+	/***/ public String signatureKeyLookupError;
+	/***/ public String signatureNoKeyInfo;
+	/***/ public String signatureNoPublicKey;
+	/***/ public String signatureParseError;
+	/***/ public String signatureVerificationError;
 	/***/ public String unableToSignCommitNoSecretKey;
+	/***/ public String uncompressed25519Key;
+	/***/ public String unknownCurve;
+	/***/ public String unknownCurveParameters;
+	/***/ public String unknownKeyType;
 
 }
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
index 1389f8b..7f0f32a 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgKeyLocator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, 2020 Salesforce and others
+ * Copyright (C) 2018, 2021 Salesforce and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -14,23 +14,21 @@
 
 import java.io.BufferedInputStream;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URISyntaxException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.InvalidPathException;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.text.MessageFormat;
-import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.List;
 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;
@@ -59,6 +57,7 @@
 import org.eclipse.jgit.annotations.NonNull;
 import org.eclipse.jgit.api.errors.CanceledException;
 import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.gpg.bc.internal.keys.KeyGrip;
 import org.eclipse.jgit.util.FS;
 import org.eclipse.jgit.util.StringUtils;
 import org.eclipse.jgit.util.SystemReader;
@@ -156,15 +155,10 @@
 	private PGPSecretKey attemptParseSecretKey(Path keyFile,
 			PGPDigestCalculatorProvider calculatorProvider,
 			PBEProtectionRemoverFactory passphraseProvider,
-			PGPPublicKey publicKey) {
+			PGPPublicKey publicKey) throws IOException, PGPException {
 		try (InputStream in = newInputStream(keyFile)) {
 			return new SExprParser(calculatorProvider).parseSecretKey(
 					new BufferedInputStream(in), passphraseProvider, publicKey);
-		} catch (IOException | PGPException | ClassCastException e) {
-			if (log.isDebugEnabled())
-				log.debug("Ignoring unreadable file '{}': {}", keyFile, //$NON-NLS-1$
-						e.getMessage(), e);
-			return null;
 		}
 	}
 
@@ -247,16 +241,32 @@
 		return false;
 	}
 
-	private String toFingerprint(String keyId) {
+	private static String toFingerprint(String keyId) {
 		if (keyId.startsWith("0x")) { //$NON-NLS-1$
 			return keyId.substring(2);
 		}
 		return keyId;
 	}
 
-	private PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob)
+	static PGPPublicKey findPublicKey(String fingerprint, String keySpec)
+			throws IOException, PGPException {
+		PGPPublicKey result = findPublicKeyInPubring(USER_PGP_PUBRING_FILE,
+				fingerprint, keySpec);
+		if (result == null && exists(USER_KEYBOX_PATH)) {
+			try {
+				result = findPublicKeyInKeyBox(USER_KEYBOX_PATH, fingerprint,
+						keySpec);
+			} catch (NoSuchAlgorithmException | NoSuchProviderException
+					| IOException | NoOpenPgpKeyException e) {
+				log.error(e.getMessage(), e);
+			}
+		}
+		return result;
+	}
+
+	private static PGPPublicKey findPublicKeyByKeyId(KeyBlob keyBlob,
+			String keyId)
 			throws IOException {
-		String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
 		if (keyId.isEmpty()) {
 			return null;
 		}
@@ -270,10 +280,11 @@
 		return null;
 	}
 
-	private PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob)
+	private static PGPPublicKey findPublicKeyByUserId(KeyBlob keyBlob,
+			String keySpec)
 			throws IOException {
 		for (UserID userID : keyBlob.getUserIds()) {
-			if (containsSigningKey(userID.getUserIDAsString(), signingKey)) {
+			if (containsSigningKey(userID.getUserIDAsString(), keySpec)) {
 				return getSigningPublicKey(keyBlob);
 			}
 		}
@@ -285,6 +296,10 @@
 	 *
 	 * @param keyboxFile
 	 *            the KeyBox file
+	 * @param keyId
+	 *            to look for, may be null
+	 * @param keySpec
+	 *            to look for
 	 * @return publicKey the public key (maybe <code>null</code>)
 	 * @throws IOException
 	 *             in case of problems reading the file
@@ -293,19 +308,22 @@
 	 * @throws NoOpenPgpKeyException
 	 *             if the file does not contain any OpenPGP key
 	 */
-	private PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile)
+	private static PGPPublicKey findPublicKeyInKeyBox(Path keyboxFile,
+			String keyId, String keySpec)
 			throws IOException, NoSuchAlgorithmException,
 			NoSuchProviderException, NoOpenPgpKeyException {
 		KeyBox keyBox = readKeyBoxFile(keyboxFile);
+		String id = keyId != null ? keyId
+				: toFingerprint(keySpec).toLowerCase(Locale.ROOT);
 		boolean hasOpenPgpKey = false;
 		for (KeyBlob keyBlob : keyBox.getKeyBlobs()) {
 			if (keyBlob.getType() == BlobType.OPEN_PGP_BLOB) {
 				hasOpenPgpKey = true;
-				PGPPublicKey key = findPublicKeyByKeyId(keyBlob);
+				PGPPublicKey key = findPublicKeyByKeyId(keyBlob, id);
 				if (key != null) {
 					return key;
 				}
-				key = findPublicKeyByUserId(keyBlob);
+				key = findPublicKeyByUserId(keyBlob, keySpec);
 				if (key != null) {
 					return key;
 				}
@@ -349,7 +367,8 @@
 			// pubring.gpg also try secring.gpg to find the secret key.
 			if (exists(USER_KEYBOX_PATH)) {
 				try {
-					publicKey = findPublicKeyInKeyBox(USER_KEYBOX_PATH);
+					publicKey = findPublicKeyInKeyBox(USER_KEYBOX_PATH, null,
+							signingKey);
 					if (publicKey != null) {
 						key = findSecretKeyForKeyBoxPublicKey(publicKey,
 								USER_KEYBOX_PATH);
@@ -372,7 +391,8 @@
 				}
 			}
 			if (exists(USER_PGP_PUBRING_FILE)) {
-				publicKey = findPublicKeyInPubring(USER_PGP_PUBRING_FILE);
+				publicKey = findPublicKeyInPubring(USER_PGP_PUBRING_FILE, null,
+						signingKey);
 				if (publicKey != null) {
 					// GPG < 2.1 may have both; the agent using the directory
 					// and gpg using secring.gpg. GPG >= 2.1 delegates all
@@ -444,67 +464,71 @@
 			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();
-
-		try (Stream<Path> keyFiles = Files.walk(USER_SECRET_KEY_DIR)) {
-			List<Path> allPaths = keyFiles.filter(Files::isRegularFile)
-					.collect(Collectors.toCollection(ArrayList::new));
-			if (allPaths.isEmpty()) {
-				return null;
-			}
+		byte[] keyGrip = null;
+		try {
+			keyGrip = KeyGrip.getKeyGrip(publicKey);
+		} catch (PGPException e) {
+			throw new PGPException(
+					MessageFormat.format(BCText.get().gpgNoKeygrip,
+							Hex.toHexString(publicKey.getFingerprint())),
+					e);
+		}
+		String filename = Hex.toHexString(keyGrip).toUpperCase(Locale.ROOT)
+				+ ".key"; //$NON-NLS-1$
+		Path keyFile = USER_SECRET_KEY_DIR.resolve(filename);
+		if (!Files.exists(keyFile)) {
+			return null;
+		}
+		boolean clearPrompt = false;
+		try {
+			PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder()
+					.build();
 			PBEProtectionRemoverFactory passphraseProvider = p -> {
 				throw new EncryptedPgpKeyException();
 			};
-			for (int attempts = 0; attempts < 2; attempts++) {
-				// Second pass will traverse only the encrypted keys with a real
-				// passphrase provider.
-				Iterator<Path> pathIterator = allPaths.iterator();
-				while (pathIterator.hasNext()) {
-					Path keyFile = pathIterator.next();
-					try {
-						PGPSecretKey secretKey = attemptParseSecretKey(keyFile,
-								calculatorProvider, passphraseProvider,
-								publicKey);
-						pathIterator.remove();
-						if (secretKey != null) {
-							if (!secretKey.isSigningKey()) {
-								throw new PGPException(MessageFormat.format(
-										BCText.get().gpgNotASigningKey,
-										signingKey));
-							}
-							return new BouncyCastleGpgKey(secretKey,
-									userKeyboxPath);
-						}
-					} catch (EncryptedPgpKeyException e) {
-						// Ignore; we'll try again.
-					}
-				}
-				if (attempts > 0 || allPaths.isEmpty()) {
-					break;
-				}
-				// allPaths contains only the encrypted keys now.
+			PGPSecretKey secretKey = null;
+			try {
+				// Try without passphrase
+				secretKey = attemptParseSecretKey(keyFile, calculatorProvider,
+						passphraseProvider, publicKey);
+			} catch (EncryptedPgpKeyException e) {
+				// Let's try again with a passphrase
 				passphraseProvider = new JcePBEProtectionRemoverFactory(
 						passphrasePrompt.getPassphrase(
 								publicKey.getFingerprint(), userKeyboxPath));
-			}
+				clearPrompt = true;
+				try {
+					secretKey = attemptParseSecretKey(keyFile, calculatorProvider,
+							passphraseProvider, publicKey);
+				} catch (PGPException e1) {
+					throw new PGPException(MessageFormat.format(
+							BCText.get().gpgFailedToParseSecretKey,
+							keyFile.toAbsolutePath()), e);
 
-			passphrasePrompt.clear();
+				}
+			}
+			if (secretKey != null) {
+				if (!secretKey.isSigningKey()) {
+					throw new PGPException(MessageFormat.format(
+							BCText.get().gpgNotASigningKey, signingKey));
+				}
+				clearPrompt = false;
+				return new BouncyCastleGpgKey(secretKey, userKeyboxPath);
+			}
 			return null;
 		} catch (RuntimeException e) {
-			passphrasePrompt.clear();
 			throw e;
+		} catch (FileNotFoundException | NoSuchFileException e) {
+			clearPrompt = false;
+			return null;
 		} catch (IOException e) {
-			passphrasePrompt.clear();
 			throw new PGPException(MessageFormat.format(
 					BCText.get().gpgFailedToParseSecretKey,
-					USER_SECRET_KEY_DIR.toAbsolutePath()), e);
+					keyFile.toAbsolutePath()), e);
+		} finally {
+			if (clearPrompt) {
+				passphrasePrompt.clear();
+			}
 		}
 	}
 
@@ -562,6 +586,11 @@
 	 * Return the first public key matching the key id ({@link #signingKey}.
 	 *
 	 * @param pubringFile
+	 *            to search
+	 * @param keyId
+	 *            to look for, may be null
+	 * @param keySpec
+	 *            to look for
 	 *
 	 * @return the PGP public key, or {@code null} if none found
 	 * @throws IOException
@@ -569,14 +598,16 @@
 	 * @throws PGPException
 	 *             on BouncyCastle errors
 	 */
-	private PGPPublicKey findPublicKeyInPubring(Path pubringFile)
+	private static PGPPublicKey findPublicKeyInPubring(Path pubringFile,
+			String keyId, String keySpec)
 			throws IOException, PGPException {
 		try (InputStream in = newInputStream(pubringFile)) {
 			PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
 					new BufferedInputStream(in),
 					new JcaKeyFingerprintCalculator());
 
-			String keyId = toFingerprint(signingKey).toLowerCase(Locale.ROOT);
+			String id = keyId != null ? keyId
+					: toFingerprint(keySpec).toLowerCase(Locale.ROOT);
 			Iterator<PGPPublicKeyRing> keyrings = pgpPub.getKeyRings();
 			while (keyrings.hasNext()) {
 				PGPPublicKeyRing keyRing = keyrings.next();
@@ -586,30 +617,33 @@
 					// try key id
 					String fingerprint = Hex.toHexString(key.getFingerprint())
 							.toLowerCase(Locale.ROOT);
-					if (fingerprint.endsWith(keyId)) {
+					if (fingerprint.endsWith(id)) {
 						return key;
 					}
 					// try user id
 					Iterator<String> userIDs = key.getUserIDs();
 					while (userIDs.hasNext()) {
 						String userId = userIDs.next();
-						if (containsSigningKey(userId, signingKey)) {
+						if (containsSigningKey(userId, keySpec)) {
 							return key;
 						}
 					}
 				}
 			}
+		} catch (FileNotFoundException | NoSuchFileException e) {
+			// Ignore and return null
 		}
 		return null;
 	}
 
-	private PGPPublicKey getPublicKey(KeyBlob blob, byte[] fingerprint)
+	private static PGPPublicKey getPublicKey(KeyBlob blob, byte[] fingerprint)
 			throws IOException {
 		return ((PublicKeyRingBlob) blob).getPGPPublicKeyRing()
 				.getPublicKey(fingerprint);
 	}
 
-	private PGPPublicKey getSigningPublicKey(KeyBlob blob) throws IOException {
+	private static PGPPublicKey getSigningPublicKey(KeyBlob blob)
+			throws IOException {
 		PGPPublicKey masterKey = null;
 		Iterator<PGPPublicKey> keys = ((PublicKeyRingBlob) blob)
 				.getPGPPublicKeyRing().getPublicKeys();
@@ -629,7 +663,7 @@
 		return masterKey;
 	}
 
-	private boolean isSigningKey(PGPPublicKey key) {
+	private static boolean isSigningKey(PGPPublicKey key) {
 		Iterator signatures = key.getSignatures();
 		while (signatures.hasNext()) {
 			PGPSignature sig = (PGPSignature) signatures.next();
@@ -641,7 +675,7 @@
 		return false;
 	}
 
-	private KeyBox readKeyBoxFile(Path keyboxFile) throws IOException,
+	private static KeyBox readKeyBoxFile(Path keyboxFile) throws IOException,
 			NoSuchAlgorithmException, NoSuchProviderException,
 			NoOpenPgpKeyException {
 		if (keyboxFile.toFile().length() == 0) {
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
new file mode 100644
index 0000000..7161895
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.text.MessageFormat;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Locale;
+
+import org.bouncycastle.bcpg.sig.IssuerFingerprint;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPCompressedData;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureList;
+import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
+import org.bouncycastle.openpgp.PGPUtil;
+import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.util.LRUMap;
+import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
+
+/**
+ * A {@link GpgSignatureVerifier} to verify GPG signatures using BouncyCastle.
+ */
+public class BouncyCastleGpgSignatureVerifier implements GpgSignatureVerifier {
+
+	private static void registerBouncyCastleProviderIfNecessary() {
+		if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+			Security.addProvider(new BouncyCastleProvider());
+		}
+	}
+
+	/**
+	 * Creates a new instance and registers the BouncyCastle security provider
+	 * if needed.
+	 */
+	public BouncyCastleGpgSignatureVerifier() {
+		registerBouncyCastleProviderIfNecessary();
+	}
+
+	// To support more efficient signature verification of multiple objects we
+	// cache public keys once found in a LRU cache.
+
+	private static final Object NO_KEY = new Object();
+
+	private LRUMap<String, Object> byFingerprint = new LRUMap<>(16, 200);
+
+	private LRUMap<String, Object> bySigner = new LRUMap<>(16, 200);
+
+	@Override
+	public String getName() {
+		return "bc"; //$NON-NLS-1$
+	}
+
+	@Override
+	@Nullable
+	public SignatureVerification verifySignature(@NonNull RevObject object,
+			@NonNull GpgConfig config) throws IOException {
+		if (object instanceof RevCommit) {
+			RevCommit commit = (RevCommit) object;
+			byte[] signatureData = commit.getRawGpgSignature();
+			if (signatureData == null) {
+				return null;
+			}
+			byte[] raw = commit.getRawBuffer();
+			// Now remove the GPG signature
+			byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
+			int start = RawParseUtils.headerStart(header, raw, 0);
+			if (start < 0) {
+				return null;
+			}
+			int end = RawParseUtils.headerEnd(raw, start);
+			// start is at the beginning of the header's content
+			start -= header.length + 1;
+			// end is on the terminating LF; we need to skip that, too
+			if (end < raw.length) {
+				end++;
+			}
+			byte[] data = new byte[raw.length - (end - start)];
+			System.arraycopy(raw, 0, data, 0, start);
+			System.arraycopy(raw, end, data, start, raw.length - end);
+			return verify(data, signatureData);
+		} else if (object instanceof RevTag) {
+			RevTag tag = (RevTag) object;
+			byte[] signatureData = tag.getRawGpgSignature();
+			if (signatureData == null) {
+				return null;
+			}
+			byte[] raw = tag.getRawBuffer();
+			// The signature is just tacked onto the end of the message, which
+			// is last in the buffer.
+			byte[] data = Arrays.copyOfRange(raw, 0,
+					raw.length - signatureData.length);
+			return verify(data, signatureData);
+		}
+		return null;
+	}
+
+	static PGPSignature parseSignature(InputStream in)
+			throws IOException, PGPException {
+		try (InputStream sigIn = PGPUtil.getDecoderStream(in)) {
+			JcaPGPObjectFactory pgpFactory = new JcaPGPObjectFactory(sigIn);
+			Object obj = pgpFactory.nextObject();
+			if (obj instanceof PGPCompressedData) {
+				obj = new JcaPGPObjectFactory(
+						((PGPCompressedData) obj).getDataStream()).nextObject();
+			}
+			if (obj instanceof PGPSignatureList) {
+				return ((PGPSignatureList) obj).get(0);
+			}
+			return null;
+		}
+	}
+
+	@Override
+	public SignatureVerification verify(byte[] data, byte[] signatureData)
+			throws IOException {
+		PGPSignature signature = null;
+		String fingerprint = null;
+		String signer = null;
+		String keyId = null;
+		try (InputStream sigIn = new ByteArrayInputStream(signatureData)) {
+			signature = parseSignature(sigIn);
+			if (signature != null) {
+				// Try to figure out something to find the public key with.
+				if (signature.hasSubpackets()) {
+					PGPSignatureSubpacketVector packets = signature
+							.getHashedSubPackets();
+					IssuerFingerprint fingerprintPacket = packets
+							.getIssuerFingerprint();
+					if (fingerprintPacket != null) {
+						fingerprint = Hex
+								.toHexString(fingerprintPacket.getFingerprint())
+								.toLowerCase(Locale.ROOT);
+					}
+					signer = packets.getSignerUserID();
+					if (signer != null) {
+						signer = BouncyCastleGpgSigner.extractSignerId(signer);
+					}
+				}
+				keyId = Long.toUnsignedString(signature.getKeyID(), 16)
+						.toLowerCase(Locale.ROOT);
+			} else {
+				throw new JGitInternalException(BCText.get().nonSignatureError);
+			}
+		} catch (PGPException e) {
+			throw new JGitInternalException(BCText.get().signatureParseError,
+					e);
+		}
+		Date signatureCreatedAt = signature.getCreationTime();
+		if (fingerprint == null && signer == null && keyId == null) {
+			return new VerificationResult(signatureCreatedAt, null, null, null,
+					false, false, TrustLevel.UNKNOWN,
+					BCText.get().signatureNoKeyInfo);
+		}
+		if (fingerprint != null && keyId != null
+				&& !fingerprint.endsWith(keyId)) {
+			return new VerificationResult(signatureCreatedAt, signer, fingerprint,
+					null, false, false, TrustLevel.UNKNOWN,
+					MessageFormat.format(BCText.get().signatureInconsistent,
+							keyId, fingerprint));
+		}
+		if (fingerprint == null && keyId != null) {
+			fingerprint = keyId;
+		}
+		// Try to find the public key
+		String keySpec = '<' + signer + '>';
+		Object cached = null;
+		PGPPublicKey publicKey = null;
+		try {
+			cached = byFingerprint.get(fingerprint);
+			if (cached != null) {
+				if (cached instanceof PGPPublicKey) {
+					publicKey = (PGPPublicKey) cached;
+				}
+			} else if (!StringUtils.isEmptyOrNull(signer)) {
+				cached = bySigner.get(signer);
+				if (cached != null) {
+					if (cached instanceof PGPPublicKey) {
+						publicKey = (PGPPublicKey) cached;
+					}
+				}
+			}
+			if (cached == null) {
+				publicKey = BouncyCastleGpgKeyLocator.findPublicKey(fingerprint,
+						keySpec);
+			}
+		} catch (IOException | PGPException e) {
+			throw new JGitInternalException(
+					BCText.get().signatureKeyLookupError, e);
+		}
+		if (publicKey == null) {
+			if (cached == null) {
+				byFingerprint.put(fingerprint, NO_KEY);
+				byFingerprint.put(keyId, NO_KEY);
+				if (signer != null) {
+					bySigner.put(signer, NO_KEY);
+				}
+			}
+			return new VerificationResult(signatureCreatedAt, signer,
+					fingerprint, null, false, false, TrustLevel.UNKNOWN,
+					BCText.get().signatureNoPublicKey);
+		}
+		if (cached == null) {
+			byFingerprint.put(fingerprint, publicKey);
+			byFingerprint.put(keyId, publicKey);
+			if (signer != null) {
+				bySigner.put(signer, publicKey);
+			}
+		}
+		String user = null;
+		Iterator<String> userIds = publicKey.getUserIDs();
+		if (!StringUtils.isEmptyOrNull(signer)) {
+			while (userIds.hasNext()) {
+				String userId = userIds.next();
+				if (BouncyCastleGpgKeyLocator.containsSigningKey(userId,
+						keySpec)) {
+					user = userId;
+					break;
+				}
+			}
+		}
+		if (user == null) {
+			userIds = publicKey.getUserIDs();
+			if (userIds.hasNext()) {
+				user = userIds.next();
+			}
+		}
+		boolean expired = false;
+		long validFor = publicKey.getValidSeconds();
+		if (validFor > 0 && signatureCreatedAt != null) {
+			Instant expiredAt = publicKey.getCreationTime().toInstant()
+					.plusSeconds(validFor);
+			expired = expiredAt.isBefore(signatureCreatedAt.toInstant());
+		}
+		// Trust data is not defined in OpenPGP; the format is implementation
+		// specific. We don't use the GPG trustdb but simply the trust packet
+		// on the public key, if present. Even if present, it may or may not
+		// be set.
+		byte[] trustData = publicKey.getTrustData();
+		TrustLevel trust = parseGpgTrustPacket(trustData);
+		boolean verified = false;
+		try {
+			signature.init(
+					new JcaPGPContentVerifierBuilderProvider()
+							.setProvider(BouncyCastleProvider.PROVIDER_NAME),
+					publicKey);
+			signature.update(data);
+			verified = signature.verify();
+		} catch (PGPException e) {
+			throw new JGitInternalException(
+					BCText.get().signatureVerificationError, e);
+		}
+		return new VerificationResult(signatureCreatedAt, signer, fingerprint, user,
+				verified, expired, trust, null);
+	}
+
+	private TrustLevel parseGpgTrustPacket(byte[] packet) {
+		if (packet == null || packet.length < 6) {
+			// A GPG trust packet has at least 6 bytes.
+			return TrustLevel.UNKNOWN;
+		}
+		if (packet[2] != 'g' || packet[3] != 'p' || packet[4] != 'g') {
+			// Not a GPG trust packet
+			return TrustLevel.UNKNOWN;
+		}
+		int trust = packet[0] & 0x0F;
+		switch (trust) {
+		case 0: // No determined/set
+		case 1: // Trust expired; i.e., calculation outdated or key expired
+		case 2: // Undefined: not enough information to set
+			return TrustLevel.UNKNOWN;
+		case 3:
+			return TrustLevel.NEVER;
+		case 4:
+			return TrustLevel.MARGINAL;
+		case 5:
+			return TrustLevel.FULL;
+		case 6:
+			return TrustLevel.ULTIMATE;
+		default:
+			return TrustLevel.UNKNOWN;
+		}
+	}
+
+	@Override
+	public void clear() {
+		byFingerprint.clear();
+		bySigner.clear();
+	}
+
+	private static class VerificationResult implements SignatureVerification {
+
+		private final Date creationDate;
+
+		private final String signer;
+
+		private final String keyUser;
+
+		private final String fingerprint;
+
+		private final boolean verified;
+
+		private final boolean expired;
+
+		private final @NonNull TrustLevel trustLevel;
+
+		private final String message;
+
+		public VerificationResult(Date creationDate, String signer,
+				String fingerprint, String user, boolean verified,
+				boolean expired, @NonNull TrustLevel trust, String message) {
+			this.creationDate = creationDate;
+			this.signer = signer;
+			this.fingerprint = fingerprint;
+			this.keyUser = user;
+			this.verified = verified;
+			this.expired = expired;
+			this.trustLevel = trust;
+			this.message = message;
+		}
+
+		@Override
+		public Date getCreationDate() {
+			return creationDate;
+		}
+
+		@Override
+		public String getSigner() {
+			return signer;
+		}
+
+		@Override
+		public String getKeyUser() {
+			return keyUser;
+		}
+
+		@Override
+		public String getKeyFingerprint() {
+			return fingerprint;
+		}
+
+		@Override
+		public boolean isExpired() {
+			return expired;
+		}
+
+		@Override
+		public TrustLevel getTrustLevel() {
+			return trustLevel;
+		}
+
+		@Override
+		public String getMessage() {
+			return message;
+		}
+
+		@Override
+		public boolean getVerified() {
+			return verified;
+		}
+	}
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java
new file mode 100644
index 0000000..ae82b75
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifierFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal;
+
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
+
+/**
+ * A {@link GpgSignatureVerifierFactory} that creates
+ * {@link GpgSignatureVerifier} instances that verify GPG signatures using
+ * BouncyCastle and that do cache public keys.
+ */
+public final class BouncyCastleGpgSignatureVerifierFactory
+		extends GpgSignatureVerifierFactory {
+
+	@Override
+	public GpgSignatureVerifier getVerifier() {
+		return new BouncyCastleGpgSignatureVerifier();
+	}
+
+}
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
index f448d5e..9f48e54 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSigner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018, 2020, Salesforce and others
+ * Copyright (C) 2018, 2021, Salesforce and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -39,9 +39,9 @@
 import org.eclipse.jgit.internal.JGitText;
 import org.eclipse.jgit.lib.CommitBuilder;
 import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgObjectSigner;
 import org.eclipse.jgit.lib.GpgSignature;
 import org.eclipse.jgit.lib.GpgSigner;
-import org.eclipse.jgit.lib.GpgObjectSigner;
 import org.eclipse.jgit.lib.ObjectBuilder;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.GpgConfig.GpgFormat;
@@ -210,7 +210,7 @@
 		}
 	}
 
-	private String extractSignerId(String pgpUserId) {
+	static String extractSignerId(String pgpUserId) {
 		int from = pgpUserId.indexOf('<');
 		if (from >= 0) {
 			int to = pgpUserId.indexOf('>', from + 1);
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java
new file mode 100644
index 0000000..b1d4446
--- /dev/null
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/keys/KeyGrip.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.gpg.bc.internal.keys;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.text.MessageFormat;
+import java.util.Arrays;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.bcpg.DSAPublicBCPGKey;
+import org.bouncycastle.bcpg.ECPublicBCPGKey;
+import org.bouncycastle.bcpg.ElGamalPublicBCPGKey;
+import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
+import org.bouncycastle.bcpg.RSAPublicBCPGKey;
+import org.bouncycastle.crypto.ec.CustomNamedCurves;
+import org.bouncycastle.math.ec.ECAlgorithms;
+import org.bouncycastle.math.field.FiniteField;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPublicKey;
+import org.bouncycastle.util.encoders.Hex;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.gpg.bc.internal.BCText;
+import org.eclipse.jgit.util.sha1.SHA1;
+
+/**
+ * Utilities to compute the <em>keygrip</em> of a key. A keygrip is a SHA1 hash
+ * over the public key parameters and is used internally by the gpg-agent to
+ * find the secret key belonging to a public key: the secret key is stored in a
+ * file under ~/.gnupg/private-keys-v1.d/ with a name "&lt;keygrip>.key". While
+ * this storage organization is an implementation detail of GPG, the way
+ * keygrips are computed is not; they are computed by libgcrypt and their
+ * definition is stable.
+ */
+public final class KeyGrip {
+
+	// Some OIDs apparently unknown to BouncyCastle.
+
+	private static String OID_OPENPGP_ED25519 = "1.3.6.1.4.1.11591.15.1"; //$NON-NLS-1$
+
+	private static String OID_RFC8410_CURVE25519 = "1.3.101.110"; //$NON-NLS-1$
+
+	private static String OID_RFC8410_ED25519 = "1.3.101.112"; //$NON-NLS-1$
+
+	private KeyGrip() {
+		// No instantiation
+	}
+
+	/**
+	 * Computes the keygrip for a {@link PGPPublicKey}.
+	 *
+	 * @param publicKey
+	 *            to get the keygrip of
+	 * @return the keygrip
+	 * @throws PGPException
+	 *             if an unknown key type is encountered.
+	 */
+	@NonNull
+	public static byte[] getKeyGrip(PGPPublicKey publicKey)
+			throws PGPException {
+		SHA1 grip = SHA1.newInstance();
+		grip.setDetectCollision(false);
+
+		switch (publicKey.getAlgorithm()) {
+		case PublicKeyAlgorithmTags.RSA_GENERAL:
+		case PublicKeyAlgorithmTags.RSA_ENCRYPT:
+		case PublicKeyAlgorithmTags.RSA_SIGN:
+			BigInteger modulus = ((RSAPublicBCPGKey) publicKey
+					.getPublicKeyPacket().getKey()).getModulus();
+			hash(grip, modulus.toByteArray());
+			break;
+		case PublicKeyAlgorithmTags.DSA:
+			DSAPublicBCPGKey dsa = (DSAPublicBCPGKey) publicKey
+					.getPublicKeyPacket().getKey();
+			hash(grip, dsa.getP().toByteArray(), 'p', true);
+			hash(grip, dsa.getQ().toByteArray(), 'q', true);
+			hash(grip, dsa.getG().toByteArray(), 'g', true);
+			hash(grip, dsa.getY().toByteArray(), 'y', true);
+			break;
+		case PublicKeyAlgorithmTags.ELGAMAL_GENERAL:
+		case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
+			ElGamalPublicBCPGKey eg = (ElGamalPublicBCPGKey) publicKey
+					.getPublicKeyPacket().getKey();
+			hash(grip, eg.getP().toByteArray(), 'p', true);
+			hash(grip, eg.getG().toByteArray(), 'g', true);
+			hash(grip, eg.getY().toByteArray(), 'y', true);
+			break;
+		case PublicKeyAlgorithmTags.ECDH:
+		case PublicKeyAlgorithmTags.ECDSA:
+		case PublicKeyAlgorithmTags.EDDSA:
+			ECPublicBCPGKey ec = (ECPublicBCPGKey) publicKey
+					.getPublicKeyPacket().getKey();
+			ASN1ObjectIdentifier curveOID = ec.getCurveOID();
+			// BC doesn't know these OIDs.
+			if (OID_OPENPGP_ED25519.equals(curveOID.getId())
+					|| OID_RFC8410_ED25519.equals(curveOID.getId())) {
+				return hashEd25519(grip, ec.getEncodedPoint());
+			} else if (CryptlibObjectIdentifiers.curvey25519.equals(curveOID)
+					|| OID_RFC8410_CURVE25519.equals(curveOID.getId())) {
+				// curvey25519 actually is the OpenPGP OID for Curve25519 and is
+				// known to BC, but the parameters are for the short Weierstrass
+				// form. See https://github.com/bcgit/bc-java/issues/399 .
+				// libgcrypt uses Montgomery form.
+				return hashCurve25519(grip, ec.getEncodedPoint());
+			}
+			X9ECParameters params = getX9Parameters(curveOID);
+			if (params == null) {
+				throw new PGPException(MessageFormat
+						.format(BCText.get().unknownCurve, curveOID.getId()));
+			}
+			// Need to write p, a, b, g, n, q
+			BigInteger q = ec.getEncodedPoint();
+			byte[] g = params.getG().getEncoded(false);
+			BigInteger a = params.getCurve().getA().toBigInteger();
+			BigInteger b = params.getCurve().getB().toBigInteger();
+			BigInteger n = params.getN();
+			BigInteger p = null;
+			FiniteField field = params.getCurve().getField();
+			if (ECAlgorithms.isFpField(field)) {
+				p = field.getCharacteristic();
+			}
+			if (p == null) {
+				// Don't know...
+				throw new PGPException(MessageFormat.format(
+						BCText.get().unknownCurveParameters, curveOID.getId()));
+			}
+			hash(grip, p.toByteArray(), 'p', false);
+			hash(grip, a.toByteArray(), 'a', false);
+			hash(grip, b.toByteArray(), 'b', false);
+			hash(grip, g, 'g', false);
+			hash(grip, n.toByteArray(), 'n', false);
+			if (publicKey.getAlgorithm() == PublicKeyAlgorithmTags.EDDSA) {
+				hashQ25519(grip, q);
+			} else {
+				hash(grip, q.toByteArray(), 'q', false);
+			}
+			break;
+		default:
+			throw new PGPException(
+					MessageFormat.format(BCText.get().unknownKeyType,
+							Integer.toString(publicKey.getAlgorithm())));
+		}
+		return grip.digest();
+	}
+
+	private static void hash(SHA1 grip, byte[] data) {
+		// Need to skip leading zero bytes
+		int i = 0;
+		while (i < data.length && data[i] == 0) {
+			i++;
+		}
+		int length = data.length - i;
+		if (i < data.length) {
+			if ((data[i] & 0x80) != 0) {
+				grip.update((byte) 0);
+			}
+			grip.update(data, i, length);
+		}
+	}
+
+	private static void hash(SHA1 grip, byte[] data, char id, boolean zeroPad) {
+		// Need to skip leading zero bytes
+		int i = 0;
+		while (i < data.length && data[i] == 0) {
+			i++;
+		}
+		int length = data.length - i;
+		boolean addZero = false;
+		if (i < data.length && zeroPad && (data[i] & 0x80) != 0) {
+			addZero = true;
+		}
+		// libgcrypt includes an SExp in the hash
+		String prefix = "(1:" + id + (addZero ? length + 1 : length) + ':'; //$NON-NLS-1$
+		grip.update(prefix.getBytes(StandardCharsets.US_ASCII));
+		// For some items, gcrypt prepends a zero byte if the high bit is set
+		if (addZero) {
+			grip.update((byte) 0);
+		}
+		if (i < data.length) {
+			grip.update(data, i, length);
+		}
+		grip.update((byte) ')');
+	}
+
+	private static void hashQ25519(SHA1 grip, BigInteger q)
+			throws PGPException {
+		byte[] data = q.toByteArray();
+		switch (data[0]) {
+		case 0x04:
+			if (data.length != 65) {
+				throw new PGPException(MessageFormat.format(
+						BCText.get().corrupt25519Key, Hex.toHexString(data)));
+			}
+			// Uncompressed: should not occur with ed25519 or curve25519
+			throw new PGPException(MessageFormat.format(
+					BCText.get().uncompressed25519Key, Hex.toHexString(data)));
+		case 0x40:
+			if (data.length != 33) {
+				throw new PGPException(MessageFormat.format(
+						BCText.get().corrupt25519Key, Hex.toHexString(data)));
+			}
+			// Compressed; normal case. Skip prefix.
+			hash(grip, Arrays.copyOfRange(data, 1, data.length), 'q', false);
+			break;
+		default:
+			if (data.length != 32) {
+				throw new PGPException(MessageFormat.format(
+						BCText.get().corrupt25519Key, Hex.toHexString(data)));
+			}
+			// Compressed format without prefix. Should not occur?
+			hash(grip, data, 'q', false);
+			break;
+		}
+	}
+
+	/**
+	 * Computes the keygrip for an ed25519 public key.
+	 * <p>
+	 * Package-visible for tests only.
+	 * </p>
+	 *
+	 * @param grip
+	 *            initialized {@link SHA1}
+	 * @param q
+	 *            the public key's EC point
+	 * @return the keygrip
+	 * @throws PGPException
+	 *             if q indicates uncompressed format
+	 */
+	@SuppressWarnings("nls")
+	static byte[] hashEd25519(SHA1 grip, BigInteger q) throws PGPException {
+		// For the values, see RFC 7748: https://tools.ietf.org/html/rfc7748
+		// p = 2^255 - 19
+		hash(grip, Hex.decodeStrict(
+				"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED"),
+				'p', false);
+		// Field: a = 1
+		hash(grip, new byte[] { 0x01 }, 'a', false);
+		// Field: b = 121665/121666 (mod p)
+		// See Berstein et.al., "Twisted Edwards Curves",
+		// https://doi.org/10.1007/978-3-540-68164-9_26
+		hash(grip, Hex.decodeStrict(
+				"2DFC9311D490018C7338BF8688861767FF8FF5B2BEBE27548A14B235ECA6874A"),
+				'b', false);
+		// Generator point with affine X,Y
+		// @formatter:off
+		// X(P) = 15112221349535400772501151409588531511454012693041857206046113283949847762202
+		// Y(P) = 46316835694926478169428394003475163141307993866256225615783033603165251855960
+		// the "04" signifies uncompressed format.
+		// @formatter:on
+		hash(grip, Hex.decodeStrict("04"
+				+ "216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A"
+				+ "6666666666666666666666666666666666666666666666666666666666666658"),
+				'g', false);
+		// order = 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed
+		hash(grip, Hex.decodeStrict(
+				"1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"),
+				'n', false);
+		hashQ25519(grip, q);
+		return grip.digest();
+	}
+
+	/**
+	 * Computes the keygrip for a curve25519 public key.
+	 * <p>
+	 * Package-visible for tests only.
+	 * </p>
+	 *
+	 * @param grip
+	 *            initialized {@link SHA1}
+	 * @param q
+	 *            the public key's EC point
+	 * @return the keygrip
+	 * @throws PGPException
+	 *             if q indicates uncompressed format
+	 */
+	@SuppressWarnings("nls")
+	static byte[] hashCurve25519(SHA1 grip, BigInteger q) throws PGPException {
+		hash(grip, Hex.decodeStrict(
+				"7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED"),
+				'p', false);
+		// Unclear: RFC 7748 says A = 486662. This value here is (A-2)/4 =
+		// 121665. Compare ecc-curves.c in libgcrypt:
+		// https://github.com/gpg/libgcrypt/blob/361a058/cipher/ecc-curves.c#L146
+		hash(grip, new byte[] { 0x01, (byte) 0xDB, 0x41 }, 'a', false);
+		hash(grip, new byte[] { 0x01 }, 'b', false);
+		// libgcrypt uses the old g.y value before the erratum to RFC 7748 for
+		// the keygrip. The new value would be
+		// 5F51E65E475F794B1FE122D388B72EB36DC2B28192839E4DD6163A5D81312C14. See
+		// https://www.rfc-editor.org/errata/eid4730 and
+		// https://github.com/gpg/libgcrypt/commit/f67b6492e0b0
+		hash(grip, Hex.decodeStrict("04"
+				+ "0000000000000000000000000000000000000000000000000000000000000009"
+				+ "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9"),
+				'g', false);
+		hash(grip, Hex.decodeStrict(
+				"1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED"),
+				'n', false);
+		hashQ25519(grip, q);
+		return grip.digest();
+	}
+
+	private static X9ECParameters getX9Parameters(
+			ASN1ObjectIdentifier curveOID) {
+		X9ECParameters params = CustomNamedCurves.getByOID(curveOID);
+		if (params == null) {
+			params = ECNamedCurveTable.getByOID(curveOID);
+		}
+		return params;
+	}
+
+}
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
index 73c1b34..016d250 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.10.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.10" sequenceNumber="1612392501">
+<target name="jgit-4.10" sequenceNumber="1613246177">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
index 6b78962..1153b29 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.11.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.11" sequenceNumber="1612392505">
+<target name="jgit-4.11" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
index aa8c724..7d7cdc0 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.12.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.12" sequenceNumber="1612392510">
+<target name="jgit-4.12" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
index fa89537..559c9ee 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.13.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.13" sequenceNumber="1612392517">
+<target name="jgit-4.13" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
index 166b8d8..0da5f51 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.14.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.14" sequenceNumber="1612392520">
+<target name="jgit-4.14" sequenceNumber="1613246201">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
index 0080015..b93d67e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.15.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.15" sequenceNumber="1612392526">
+<target name="jgit-4.15" sequenceNumber="1613246201">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
index 476a08f..28d7be258 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.16.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.16" sequenceNumber="1612392531">
+<target name="jgit-4.16" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
index 3e619ed..34b88e5 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.17.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.17" sequenceNumber="1612392542">
+<target name="jgit-4.17" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
index bf73bc9..dc10862 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.18.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.18" sequenceNumber="1612392552">
+<target name="jgit-4.18" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.target
index 5819225..3f0b6c5 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.19-staging.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.19-staging" sequenceNumber="1612392559">
+<target name="jgit-4.19-staging" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
index b384956..6526d2e 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.6.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.6" sequenceNumber="1612392473">
+<target name="jgit-4.6" sequenceNumber="1613246220">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
index d606a5e..e62a310 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.7.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.7" sequenceNumber="1612392486">
+<target name="jgit-4.7" sequenceNumber="1613246211">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
index 6c65c24..a5d9229 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.8.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.8" sequenceNumber="1612392491">
+<target name="jgit-4.8" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
index 9662b05..69201e9 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/jgit-4.9.target
@@ -1,28 +1,28 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?pde?>
 <!-- generated with https://github.com/eclipse-cbi/targetplatform-dsl -->
-<target name="jgit-4.9" sequenceNumber="1612392495">
+<target name="jgit-4.9" sequenceNumber="1613246205">
   <locations>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
-      <unit id="org.eclipse.jetty.client" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.client.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.continuation.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.http.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.io.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.security.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.server.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.servlet.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.source" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax" version="9.4.35.v20201120"/>
-      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.35.v20201120"/>
-      <repository id="jetty-9.4.30" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/"/>
+      <unit id="org.eclipse.jetty.client" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.client.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.continuation.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.http.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.io.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.security.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.server.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.servlet.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.source" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax" version="9.4.36.v20210114"/>
+      <unit id="org.eclipse.jetty.util.ajax.source" version="9.4.36.v20210114"/>
+      <repository id="jetty-9.4.36" location="https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/"/>
     </location>
     <location includeMode="slicer" includeAllPlatforms="false" includeSource="true" includeConfigurePhase="true" type="InstallableUnit">
       <unit id="com.google.gson" version="2.8.6.v20201231-1626"/>
diff --git a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
index 72f20c4..4eec8aa 100644
--- a/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
+++ b/org.eclipse.jgit.packaging/org.eclipse.jgit.target/projects/jetty-9.4.x.tpd
@@ -1,22 +1,22 @@
 target "jetty-9.4.x" with source configurePhase
 
-location jetty-9.4.30 "https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.35.v20201120/" {
-	org.eclipse.jetty.client [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.client.source [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.continuation [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.continuation.source [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.http [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.http.source [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.io [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.io.source [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.security [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.security.source [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.server [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.server.source [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.servlet [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.servlet.source [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.util [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.util.source [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.util.ajax [9.4.35.v20201120,9.4.35.v20201120]
-	org.eclipse.jetty.util.ajax.source [9.4.35.v20201120,9.4.35.v20201120]
+location jetty-9.4.36 "https://download.eclipse.org/jetty/updates/jetty-bundles-9.x/9.4.36.v20210114/" {
+	org.eclipse.jetty.client [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.client.source [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.continuation [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.continuation.source [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.http [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.http.source [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.io [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.io.source [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.security [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.security.source [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.server [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.server.source [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.servlet [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.servlet.source [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.util [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.util.source [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.util.ajax [9.4.36.v20210114,9.4.36.v20210114]
+	org.eclipse.jetty.util.ajax.source [9.4.36.v20210114,9.4.36.v20210114]
 }
diff --git a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
index afa253e..df55eb0 100644
--- a/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
+++ b/org.eclipse.jgit.pgm/resources/org/eclipse/jgit/pgm/internal/CLIText.properties
@@ -77,14 +77,15 @@
 invalidRecurseSubmodulesMode=Invalid recurse submodules mode: {0}
 invalidUntrackedFilesMode=Invalid untracked files mode ''{0}''
 jgitVersion=jgit version {0}
-lineFormat={0}
-listeningOn=Listening on {0}
 lfsNoAccessKey=No accessKey in {0}
 lfsNoSecretKey=No secretKey in {0}
 lfsProtocolUrl=LFS protocol URL: {0}
 lfsStoreDirectory=LFS objects stored in: {0}
 lfsStoreUrl=LFS store URL: {0}
 lfsUnknownStoreType="Unknown LFS store type: {0}"
+lineFormat={0}
+listeningOn=Listening on {0}
+logNoSignatureVerifier="No signature verifier available"
 mergeConflict=CONFLICT(content): Merge conflict in {0}
 mergeCheckoutConflict=error: Your local changes to the following files would be overwritten by merge:
 mergeFailed=Automatic merge failed; fix conflicts and then commit the result
@@ -411,6 +412,7 @@
 usage_showRefNamesMatchingCommits=Show ref names matching commits
 usage_showPatch=display patch
 usage_showNotes=Add this ref to the list of note branches from which notes are displayed
+usage_showSignature=Verify signatures of signed commits in the log
 usage_showTimeInMilliseconds=Show mtime in milliseconds
 usage_squash=Squash commits as if a real merge happened, but do not make a commit or move the HEAD.
 usage_srcPrefix=show the source prefix instead of "a/"
@@ -424,6 +426,7 @@
 usage_tagMessage=create an annotated tag with the given message, unsigned unless -s or -u are given, or config tag.gpgSign is true, or tar.forceSignAnnotated is true and -a is not given
 usage_tagSign=create a signed annotated tag
 usage_tagNoSign=suppress signing the tag
+usage_tagVerify=Verify the GPG signature
 usage_untrackedFilesMode=show untracked files
 usage_updateRef=reference to update
 usage_updateRemoteRefsFromAnotherRepository=Update remote refs from another repository
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
index 55efd23..353b64b 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Log.java
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2010, Google Inc.
- * Copyright (C) 2006-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2006, 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, 2021, Shawn O. Pearce <spearce@spearce.org> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -31,12 +31,17 @@
 import org.eclipse.jgit.errors.LargeObjectException;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.notes.NoteMap;
 import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.internal.VerificationUtils;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.util.GitDateFormatter;
@@ -68,6 +73,9 @@
 		additionalNoteRefs.add(notesRef);
 	}
 
+	@Option(name = "--show-signature", usage = "usage_showSignature")
+	private boolean showSignature;
+
 	@Option(name = "--date", usage = "usage_date")
 	void dateFormat(String date) {
 		if (date.toLowerCase(Locale.ROOT).equals(date))
@@ -147,6 +155,10 @@
 	// END -- Options shared with Diff
 
 
+	private GpgSignatureVerifier verifier;
+
+	private GpgConfig config;
+
 	Log() {
 		dateFormatter = new GitDateFormatter(Format.DEFAULT);
 	}
@@ -161,6 +173,7 @@
 	/** {@inheritDoc} */
 	@Override
 	protected void run() {
+		config = new GpgConfig(db.getConfig());
 		diffFmt.setRepository(db);
 		try {
 			diffFmt.setPathFilter(pathFilter);
@@ -197,6 +210,9 @@
 			throw die(e.getMessage(), e);
 		} finally {
 			diffFmt.close();
+			if (verifier != null) {
+				verifier.clear();
+			}
 		}
 	}
 
@@ -229,6 +245,9 @@
 		}
 		outw.println();
 
+		if (showSignature) {
+			showSignature(c);
+		}
 		final PersonIdent author = c.getAuthorIdent();
 		outw.println(MessageFormat.format(CLIText.get().authorInfo, author.getName(), author.getEmailAddress()));
 		outw.println(MessageFormat.format(CLIText.get().dateInfo,
@@ -252,6 +271,27 @@
 		outw.flush();
 	}
 
+	private void showSignature(RevCommit c) throws IOException {
+		if (c.getRawGpgSignature() == null) {
+			return;
+		}
+		if (verifier == null) {
+			GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
+					.getDefault();
+			if (factory == null) {
+				throw die(CLIText.get().logNoSignatureVerifier, null);
+			}
+			verifier = factory.getVerifier();
+		}
+		SignatureVerification verification = verifier.verifySignature(c,
+				config);
+		if (verification == null) {
+			return;
+		}
+		VerificationUtils.writeVerification(outw, verification,
+				verifier.getName(), c.getCommitterIdent());
+	}
+
 	/**
 	 * @param c
 	 * @return <code>true</code> if at least one note was printed,
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
index 1d43220..3beab60 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Show.java
@@ -29,10 +29,15 @@
 import org.eclipse.jgit.errors.RevisionSyntaxException;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.PersonIdent;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
 import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.internal.VerificationUtils;
 import org.eclipse.jgit.pgm.opt.PathTreeFilterHandler;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevObject;
@@ -59,6 +64,9 @@
 	@Option(name = "--", metaVar = "metaVar_path", handler = PathTreeFilterHandler.class)
 	protected TreeFilter pathFilter = TreeFilter.ALL;
 
+	@Option(name = "--show-signature", usage = "usage_showSignature")
+	private boolean showSignature;
+
 	// BEGIN -- Options shared with Diff
 	@Option(name = "-p", usage = "usage_showPatch")
 	boolean showPatch;
@@ -220,13 +228,16 @@
 		}
 
 		outw.println();
-		String[] lines = tag.getFullMessage().split("\n"); //$NON-NLS-1$
-		for (String s : lines) {
-			outw.println(s);
+		String fullMessage = tag.getFullMessage();
+		if (!fullMessage.isEmpty()) {
+			String[] lines = tag.getFullMessage().split("\n"); //$NON-NLS-1$
+			for (String s : lines) {
+				outw.println(s);
+			}
 		}
 		byte[] rawSignature = tag.getRawGpgSignature();
 		if (rawSignature != null) {
-			lines = RawParseUtils.decode(rawSignature).split("\n"); //$NON-NLS-1$
+			String[] lines = RawParseUtils.decode(rawSignature).split("\n"); //$NON-NLS-1$
 			for (String s : lines) {
 				outw.println(s);
 			}
@@ -258,6 +269,10 @@
 		c.getId().copyTo(outbuffer, outw);
 		outw.println();
 
+		if (showSignature) {
+			showSignature(c);
+		}
+
 		final PersonIdent author = c.getAuthorIdent();
 		outw.println(MessageFormat.format(CLIText.get().authorInfo,
 				author.getName(), author.getEmailAddress()));
@@ -296,4 +311,28 @@
 		}
 		outw.println();
 	}
+
+	private void showSignature(RevCommit c) throws IOException {
+		if (c.getRawGpgSignature() == null) {
+			return;
+		}
+		GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
+				.getDefault();
+		if (factory == null) {
+			throw die(CLIText.get().logNoSignatureVerifier, null);
+		}
+		GpgSignatureVerifier verifier = factory.getVerifier();
+		GpgConfig config = new GpgConfig(db.getConfig());
+		try {
+			SignatureVerification verification = verifier.verifySignature(c,
+					config);
+			if (verification == null) {
+				return;
+			}
+			VerificationUtils.writeVerification(outw, verification,
+					verifier.getName(), c.getCommitterIdent());
+		} finally {
+			verifier.clear();
+		}
+	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
index 4cc62b3..e2cd31d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Tag.java
@@ -4,7 +4,7 @@
  * Copyright (C) 2008, Charles O'Farrell <charleso@charleso.org>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg.lists@dewire.com>
  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, 2020 Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2021 Shawn O. Pearce <spearce@spearce.org> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -22,43 +22,60 @@
 import org.eclipse.jgit.api.Git;
 import org.eclipse.jgit.api.ListTagCommand;
 import org.eclipse.jgit.api.TagCommand;
+import org.eclipse.jgit.api.VerificationResult;
+import org.eclipse.jgit.api.VerifySignatureCommand;
 import org.eclipse.jgit.api.errors.GitAPIException;
 import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Ref;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.pgm.internal.CLIText;
+import org.eclipse.jgit.pgm.internal.VerificationUtils;
+import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
 
 @Command(common = true, usage = "usage_CreateATag")
 class Tag extends TextBuiltin {
-	@Option(name = "-f", usage = "usage_forceReplacingAnExistingTag")
+
+	@Option(name = "--force", aliases = { "-f" }, forbids = { "--delete",
+			"--verify" }, usage = "usage_forceReplacingAnExistingTag")
 	private boolean force;
 
-	@Option(name = "-d", usage = "usage_tagDelete")
+	@Option(name = "--delete", aliases = { "-d" }, forbids = {
+			"--verify" }, usage = "usage_tagDelete")
 	private boolean delete;
 
 	@Option(name = "--annotate", aliases = {
-			"-a" }, usage = "usage_tagAnnotated")
+			"-a" }, forbids = { "--delete",
+					"--verify" }, usage = "usage_tagAnnotated")
 	private boolean annotated;
 
-	@Option(name = "-m", metaVar = "metaVar_message", usage = "usage_tagMessage")
+	@Option(name = "-m", forbids = { "--delete",
+			"--verify" }, metaVar = "metaVar_message", usage = "usage_tagMessage")
 	private String message;
 
 	@Option(name = "--sign", aliases = { "-s" }, forbids = {
-			"--no-sign" }, usage = "usage_tagSign")
+			"--no-sign", "--delete", "--verify" }, usage = "usage_tagSign")
 	private boolean sign;
 
 	@Option(name = "--no-sign", usage = "usage_tagNoSign", forbids = {
-			"--sign" })
+			"--sign", "--delete", "--verify" })
 	private boolean noSign;
 
 	@Option(name = "--local-user", aliases = {
-			"-u" }, metaVar = "metaVar_tagLocalUser", usage = "usage_tagLocalUser")
+			"-u" }, forbids = { "--delete",
+					"--verify" }, metaVar = "metaVar_tagLocalUser", usage = "usage_tagLocalUser")
 	private String gpgKeyId;
 
+	@Option(name = "--verify", aliases = { "-v" }, forbids = { "--delete",
+			"--force", "--annotate", "-m", "--sign", "--no-sign",
+			"--local-user" }, usage = "usage_tagVerify")
+	private boolean verify;
+
 	@Argument(index = 0, metaVar = "metaVar_name")
 	private String tagName;
 
@@ -70,7 +87,25 @@
 	protected void run() {
 		try (Git git = new Git(db)) {
 			if (tagName != null) {
-				if (delete) {
+				if (verify) {
+					VerifySignatureCommand verifySig = git.verifySignature()
+							.setMode(VerifySignatureCommand.VerifyMode.TAGS)
+							.addName(tagName);
+
+					VerificationResult verification = verifySig.call()
+							.get(tagName);
+					if (verification == null) {
+						showUnsigned(git, tagName);
+					} else {
+						Throwable error = verification.getException();
+						if (error != null) {
+							throw die(error.getMessage(), error);
+						}
+						writeVerification(verifySig.getVerifier().getName(),
+								(RevTag) verification.getObject(),
+								verification.getVerification());
+					}
+				} else if (delete) {
 					List<String> deletedTags = git.tagDelete().setTags(tagName)
 							.call();
 					if (deletedTags.isEmpty()) {
@@ -116,4 +151,36 @@
 			throw die(e.getMessage(), e);
 		}
 	}
+
+	private void showUnsigned(Git git, String wantedTag) throws IOException {
+		ObjectId id = git.getRepository().resolve(wantedTag);
+		if (id != null && !ObjectId.zeroId().equals(id)) {
+			try (RevWalk walk = new RevWalk(git.getRepository())) {
+				showTag(walk.parseTag(id));
+			}
+		} else {
+			throw die(
+					MessageFormat.format(CLIText.get().tagNotFound, wantedTag));
+		}
+	}
+
+	private void showTag(RevTag tag) throws IOException {
+		outw.println("object " + tag.getObject().name()); //$NON-NLS-1$
+		outw.println("type " + Constants.typeString(tag.getObject().getType())); //$NON-NLS-1$
+		outw.println("tag " + tag.getTagName()); //$NON-NLS-1$
+		outw.println("tagger " + tag.getTaggerIdent().toExternalString()); //$NON-NLS-1$
+		outw.println();
+		outw.print(tag.getFullMessage());
+	}
+
+	private void writeVerification(String name, RevTag tag,
+			SignatureVerification verification) throws IOException {
+		showTag(tag);
+		if (verification == null) {
+			outw.println();
+			return;
+		}
+		VerificationUtils.writeVerification(outw, verification, name,
+				tag.getTaggerIdent());
+	}
 }
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
index c68019e..991b3ba 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/CLIText.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
- * Copyright (C) 2013, Obeo and others
+ * Copyright (C) 2013, 2021 Obeo and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -163,6 +163,7 @@
 	/***/ public String lfsUnknownStoreType;
 	/***/ public String lineFormat;
 	/***/ public String listeningOn;
+	/***/ public String logNoSignatureVerifier;
 	/***/ public String mergeCheckoutConflict;
 	/***/ public String mergeConflict;
 	/***/ public String mergeFailed;
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java
new file mode 100644
index 0000000..c1f8a86
--- /dev/null
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/internal/VerificationUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.pgm.internal;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.util.GitDateFormatter;
+import org.eclipse.jgit.util.SignatureUtils;
+import org.eclipse.jgit.util.io.ThrowingPrintWriter;
+
+/**
+ * Utilities for signature verification.
+ */
+public final class VerificationUtils {
+
+	private VerificationUtils() {
+		// No instantiation
+	}
+
+	/**
+	 * Writes information about a signature verification to the given writer.
+	 *
+	 * @param out
+	 *            to write to
+	 * @param verification
+	 *            to show
+	 * @param name
+	 *            of the verifier used
+	 * @param creator
+	 *            of the object verified; used for time zone information
+	 * @throws IOException
+	 *             if writing fails
+	 */
+	public static void writeVerification(ThrowingPrintWriter out,
+			SignatureVerification verification, String name,
+			PersonIdent creator) throws IOException {
+		String[] text = SignatureUtils
+				.toString(verification, creator,
+						new GitDateFormatter(GitDateFormatter.Format.LOCALE))
+				.split("\n"); //$NON-NLS-1$
+		for (String line : text) {
+			out.print(name);
+			out.print(": "); //$NON-NLS-1$
+			out.println(line);
+		}
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
index 56f881e..0d739b9 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
@@ -487,7 +487,7 @@
 	public void seekPastWithLotsOfRefs() throws IOException {
 		Ref[] refs = new Ref[500];
 		for (int i = 1; i <= 500; i++) {
-			refs[i - 1] = ref(String.format("refs/%d", i), i);
+			refs[i - 1] = ref(String.format("refs/%d", Integer.valueOf(i)), i);
 		}
 		ReftableReader t = read(write(refs));
 		try (RefCursor rc = t.allRefs()) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
index e2ac89b..eecf25b 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/merge/MergerTest.java
@@ -1384,6 +1384,270 @@
 		git.merge().include(commitB).call();
 	}
 
+	/**
+	 * Merging two commits with a file/dir conflict in the virtual ancestor.
+	 *
+	 * <p>
+	 * Those conflicts should be ignored, otherwise the found base can not be used by the
+	 * RecursiveMerger.
+	 * <pre>
+	 *  --------------
+	 * |              \
+	 * |         C1 - C4 --- ?     master
+	 * |        /          /
+	 * |  I - A1 - C2 - C3         second-branch
+	 * |   \            /
+	 * \    \          /
+	 *  ----A2--------             branch-to-merge
+	 *  </pre>
+	 * <p>
+	 * <p>
+	 * Path "a" is initially a file in I and A1. It is changed to a directory in A2
+	 * ("branch-to-merge").
+	 * <p>
+	 * A2 is merged into "master" and "second-branch". The dir/file merge conflict is resolved
+	 * manually, results in C4 and C3.
+	 * <p>
+	 * While merging C3 and C4, A1 and A2 are the base commits found by the recursive merge that
+	 * have the dir/file conflict.
+	 */
+	@Theory
+	public void checkFileDirMergeConflictInVirtualAncestor_NoConflictInChildren(
+			MergeStrategy strategy)
+			throws Exception {
+		if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+			return;
+		}
+
+		Git git = Git.wrap(db);
+
+		// master
+		writeTrashFile("a", "initial content");
+		git.add().addFilepattern("a").call();
+		RevCommit commitI = git.commit().setMessage("Initial commit").call();
+
+		writeTrashFile("a", "content in Ancestor 1");
+		git.add().addFilepattern("a").call();
+		RevCommit commitA1 = git.commit().setMessage("Ancestor 1").call();
+
+		writeTrashFile("a", "content in Child 1 (commited on master)");
+		git.add().addFilepattern("a").call();
+		// commit C1M
+		git.commit().setMessage("Child 1 on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(commitI).setName("branch-to-merge").call();
+		// "a" becomes a directory in A2
+		git.rm().addFilepattern("a").call();
+		writeTrashFile("a/content", "content in Ancestor 2 (commited on branch-to-merge)");
+		git.add().addFilepattern("a/content").call();
+		RevCommit commitA2 = git.commit().setMessage("Ancestor 2").call();
+
+		// second branch
+		git.checkout().setCreateBranch(true).setStartPoint(commitA1).setName("second-branch").call();
+		writeTrashFile("a", "content in Child 2 (commited on second-branch)");
+		git.add().addFilepattern("a").call();
+		// commit C2S
+		git.commit().setMessage("Child 2 on second-branch").call();
+
+		// Merge branch-to-merge into second-branch
+		MergeResult mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+		assertEquals(mergeResult.getNewHead(), null);
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+		// Resolve the conflict manually, merge "a" as a file
+		git.rm().addFilepattern("a").call();
+		git.rm().addFilepattern("a/content").call();
+		writeTrashFile("a", "merge conflict resolution");
+		git.add().addFilepattern("a").call();
+		RevCommit commitC3S = git.commit().setMessage("Child 3 on second bug - resolve merge conflict")
+				.call();
+
+		// Merge branch-to-merge into master
+		git.checkout().setName("master").call();
+		mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+		assertEquals(mergeResult.getNewHead(), null);
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+
+		// Resolve the conflict manually - merge "a" as a file
+		git.rm().addFilepattern("a").call();
+		git.rm().addFilepattern("a/content").call();
+		writeTrashFile("a", "merge conflict resolution");
+		git.add().addFilepattern("a").call();
+		// commit C4M
+		git.commit().setMessage("Child 4 on master - resolve merge conflict").call();
+
+		// Merge C4M (second-branch) into master (C3S)
+		// Conflict in virtual base should be here, but there are no conflicts in
+		// children
+		mergeResult = git.merge().include(commitC3S).call();
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.MERGED);
+
+	}
+
+	@Theory
+	public void checkFileDirMergeConflictInVirtualAncestor_ConflictInChildren_FileDir(MergeStrategy strategy)
+			throws Exception {
+		if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+			return;
+		}
+
+		Git git = Git.wrap(db);
+
+		// master
+		writeTrashFile("a", "initial content");
+		git.add().addFilepattern("a").call();
+		RevCommit commitI = git.commit().setMessage("Initial commit").call();
+
+		writeTrashFile("a", "content in Ancestor 1");
+		git.add().addFilepattern("a").call();
+		RevCommit commitA1 = git.commit().setMessage("Ancestor 1").call();
+
+		writeTrashFile("a", "content in Child 1 (commited on master)");
+		git.add().addFilepattern("a").call();
+		// commit C1M
+		git.commit().setMessage("Child 1 on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(commitI).setName("branch-to-merge").call();
+
+		// "a" becomes a directory in A2
+		git.rm().addFilepattern("a").call();
+		writeTrashFile("a/content", "content in Ancestor 2 (commited on branch-to-merge)");
+		git.add().addFilepattern("a/content").call();
+		RevCommit commitA2 = git.commit().setMessage("Ancestor 2").call();
+
+		// second branch
+		git.checkout().setCreateBranch(true).setStartPoint(commitA1).setName("second-branch").call();
+		writeTrashFile("a", "content in Child 2 (commited on second-branch)");
+		git.add().addFilepattern("a").call();
+		// commit C2S
+		git.commit().setMessage("Child 2 on second-branch").call();
+
+		// Merge branch-to-merge into second-branch
+		MergeResult mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+		assertEquals(mergeResult.getNewHead(), null);
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+		// Resolve the conflict manually - write a file
+		git.rm().addFilepattern("a").call();
+		git.rm().addFilepattern("a/content").call();
+		writeTrashFile("a",
+				"content in Child 3 (commited on second-branch) - merge conflict resolution");
+		git.add().addFilepattern("a").call();
+		RevCommit commitC3S = git.commit().setMessage("Child 3 on second bug - resolve merge conflict")
+				.call();
+
+		// Merge branch-to-merge into master
+		git.checkout().setName("master").call();
+		mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+		assertEquals(mergeResult.getNewHead(), null);
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+
+		// Resolve the conflict manually - write a file
+		git.rm().addFilepattern("a").call();
+		git.rm().addFilepattern("a/content").call();
+		writeTrashFile("a", "content in Child 4 (commited on master) - merge conflict resolution");
+		git.add().addFilepattern("a").call();
+		// commit C4M
+		git.commit().setMessage("Child 4 on master - resolve merge conflict").call();
+
+		// Merge C4M (second-branch) into master (C3S)
+		// Conflict in virtual base should be here
+		mergeResult = git.merge().include(commitC3S).call();
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+		String expected =
+				"<<<<<<< HEAD\n" + "content in Child 4 (commited on master) - merge conflict resolution\n"
+						+ "=======\n"
+						+ "content in Child 3 (commited on second-branch) - merge conflict resolution\n"
+						+ ">>>>>>> " + commitC3S.name() + "\n";
+		assertEquals(expected, read("a"));
+		// Nothing was populated from the ancestors.
+		assertEquals(
+				"[a, mode:100644, stage:2, content:content in Child 4 (commited on master) - merge conflict resolution][a, mode:100644, stage:3, content:content in Child 3 (commited on second-branch) - merge conflict resolution]",
+				indexState(CONTENT));
+	}
+
+	/**
+	 * Same test as above, but "a" is a dir in A1 and a file in A2
+	 */
+	@Theory
+	public void checkFileDirMergeConflictInVirtualAncestor_ConflictInChildren_DirFile(MergeStrategy strategy)
+			throws Exception {
+		if (!strategy.equals(MergeStrategy.RECURSIVE)) {
+			return;
+		}
+
+		Git git = Git.wrap(db);
+
+		// master
+		writeTrashFile("a/content", "initial content");
+		git.add().addFilepattern("a/content").call();
+		RevCommit commitI = git.commit().setMessage("Initial commit").call();
+
+		writeTrashFile("a/content", "content in Ancestor 1");
+		git.add().addFilepattern("a/content").call();
+		RevCommit commitA1 = git.commit().setMessage("Ancestor 1").call();
+
+		writeTrashFile("a/content", "content in Child 1 (commited on master)");
+		git.add().addFilepattern("a/content").call();
+		// commit C1M
+		git.commit().setMessage("Child 1 on master").call();
+
+		git.checkout().setCreateBranch(true).setStartPoint(commitI).setName("branch-to-merge").call();
+
+		// "a" becomes a file in A2
+		git.rm().addFilepattern("a/content").call();
+		writeTrashFile("a", "content in Ancestor 2 (commited on branch-to-merge)");
+		git.add().addFilepattern("a").call();
+		RevCommit commitA2 = git.commit().setMessage("Ancestor 2").call();
+
+		// second branch
+		git.checkout().setCreateBranch(true).setStartPoint(commitA1).setName("second-branch").call();
+		writeTrashFile("a/content", "content in Child 2 (commited on second-branch)");
+		git.add().addFilepattern("a/content").call();
+		// commit C2S
+		git.commit().setMessage("Child 2 on second-branch").call();
+
+		// Merge branch-to-merge into second-branch
+		MergeResult mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+		assertEquals(mergeResult.getNewHead(), null);
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+		// Resolve the conflict manually - write a file
+		git.rm().addFilepattern("a").call();
+		git.rm().addFilepattern("a/content").call();
+		deleteTrashFile("a/content");
+		deleteTrashFile("a");
+		writeTrashFile("a", "content in Child 3 (commited on second-branch) - merge conflict resolution");
+		git.add().addFilepattern("a").call();
+		RevCommit commitC3S = git.commit().setMessage("Child 3 on second bug - resolve merge conflict").call();
+
+		// Merge branch-to-merge into master
+		git.checkout().setName("master").call();
+		mergeResult = git.merge().include(commitA2).setStrategy(strategy).call();
+		assertEquals(mergeResult.getNewHead(), null);
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+
+		// Resolve the conflict manually - write a file
+		git.rm().addFilepattern("a").call();
+		git.rm().addFilepattern("a/content").call();
+		deleteTrashFile("a/content");
+		deleteTrashFile("a");
+		writeTrashFile("a", "content in Child 4 (commited on master) - merge conflict resolution");
+		git.add().addFilepattern("a").call();
+		// commit C4M
+		git.commit().setMessage("Child 4 on master - resolve merge conflict").call();
+
+		// Merge C4M (second-branch) into master (C3S)
+		// Conflict in virtual base should be here
+		mergeResult = git.merge().include(commitC3S).call();
+		assertEquals(mergeResult.getMergeStatus(), MergeStatus.CONFLICTING);
+		String expected = "<<<<<<< HEAD\n" + "content in Child 4 (commited on master) - merge conflict resolution\n"
+				+ "=======\n" + "content in Child 3 (commited on second-branch) - merge conflict resolution\n"
+				+ ">>>>>>> " + commitC3S.name() + "\n";
+		assertEquals(expected, read("a"));
+		// Nothing was populated from the ancestors.
+		assertEquals(
+				"[a, mode:100644, stage:2, content:content in Child 4 (commited on master) - merge conflict resolution][a, mode:100644, stage:3, content:content in Child 3 (commited on second-branch) - merge conflict resolution]",
+				indexState(CONTENT));
+	}
+
 	private void writeSubmodule(String path, ObjectId commit)
 			throws IOException, ConfigInvalidException {
 		addSubmoduleToIndex(path, commit);
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index b74e703..d389ac5 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -24,4 +24,34 @@
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/util/FS.java" type="org.eclipse.jgit.util.FS">
+        <filter id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.util.FS"/>
+                <message_argument value="internalRunHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)"/>
+            </message_arguments>
+        </filter>
+        <filter id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.util.FS"/>
+                <message_argument value="runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/util/FS_POSIX.java" type="org.eclipse.jgit.util.FS_POSIX">
+        <filter id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.util.FS_POSIX"/>
+                <message_argument value="runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)"/>
+            </message_arguments>
+        </filter>
+    </resource>
+    <resource path="src/org/eclipse/jgit/util/FS_Win32_Cygwin.java" type="org.eclipse.jgit.util.FS_Win32_Cygwin">
+        <filter id="338792546">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.util.FS_Win32_Cygwin"/>
+                <message_argument value="runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)"/>
+            </message_arguments>
+        </filter>
+    </resource>
 </component>
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index a8b2e56..1924621 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -622,6 +622,8 @@
 shortReadOfBlock=Short read of block.
 shortReadOfOptionalDIRCExtensionExpectedAnotherBytes=Short read of optional DIRC extension {0}; expected another {1} bytes within the section.
 shortSkipOfBlock=Short skip of block.
+signatureVerificationError=Signature verification failed
+signatureVerificationUnavailable=No signature verifier registered
 signedTagMessageNoLf=A non-empty message of a signed tag must end in LF.
 signingServiceUnavailable=Signing service is not available
 similarityScoreMustBeWithinBounds=Similarity score must be between 0 and 100.
@@ -763,6 +765,13 @@
 URINotSupported=URI not supported: {0}
 userConfigInvalid=Git config in the user's home directory {0} is invalid {1}
 validatingGitModules=Validating .gitmodules files
+verifySignatureBad=BAD signature from "{0}"
+verifySignatureExpired=Expired signature from "{0}"
+verifySignatureGood=Good signature from "{0}"
+verifySignatureIssuer=issuer "{0}"
+verifySignatureKey=using key {0}
+verifySignatureMade=Signature made {0}
+verifySignatureTrust=[{0}]
 walkFailure=Walk failure.
 wantNoSpaceWithCapabilities=No space between oid and first capability in first want line
 wantNotValid=want {0} not valid
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
index 6431477..3b3e10e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/Git.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
- * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> and others
+ * Copyright (C) 2010, 2021 Chris Aniszczyk <caniszczyk@gmail.com> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -773,6 +773,16 @@
 	}
 
 	/**
+	 * Return a command to verify signatures of tags or commits.
+	 *
+	 * @return a {@link VerifySignatureCommand}
+	 * @since 5.11
+	 */
+	public VerifySignatureCommand verifySignature() {
+		return new VerifySignatureCommand(repo);
+	}
+
+	/**
 	 * Get repository
 	 *
 	 * @return the git repository this class is interacting with; see
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
new file mode 100644
index 0000000..21cddf7
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerificationResult.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.api;
+
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.revwalk.RevObject;
+
+/**
+ * A {@code VerificationResult} describes the outcome of a signature
+ * verification.
+ *
+ * @see VerifySignatureCommand
+ *
+ * @since 5.11
+ */
+public interface VerificationResult {
+
+	/**
+	 * If an error occurred during signature verification, this retrieves the
+	 * exception.
+	 *
+	 * @return the exception, or {@code null} if none occurred
+	 */
+	Throwable getException();
+
+	/**
+	 * Retrieves the signature verification result.
+	 *
+	 * @return the result, or {@code null} if none was computed
+	 */
+	GpgSignatureVerifier.SignatureVerification getVerification();
+
+	/**
+	 * Retrieves the git object of which the signature was verified.
+	 *
+	 * @return the git object
+	 */
+	RevObject getObject();
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
new file mode 100644
index 0000000..6a2a44e
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/VerifySignatureCommand.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.api;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.api.errors.ServiceUnavailableException;
+import org.eclipse.jgit.api.errors.WrongObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GpgConfig;
+import org.eclipse.jgit.lib.GpgSignatureVerifier;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.GpgSignatureVerifierFactory;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+/**
+ * A command to verify GPG signatures on tags or commits.
+ *
+ * @since 5.11
+ */
+public class VerifySignatureCommand extends GitCommand<Map<String, VerificationResult>> {
+
+	/**
+	 * Describes what kind of objects shall be handled by a
+	 * {@link VerifySignatureCommand}.
+	 */
+	public enum VerifyMode {
+		/**
+		 * Handle any object type, ignore anything that is not a commit or tag.
+		 */
+		ANY,
+		/**
+		 * Handle only commits; throw a {@link WrongObjectTypeException} for
+		 * anything else.
+		 */
+		COMMITS,
+		/**
+		 * Handle only tags; throw a {@link WrongObjectTypeException} for
+		 * anything else.
+		 */
+		TAGS
+	}
+
+	private final Set<String> namesToCheck = new HashSet<>();
+
+	private VerifyMode mode = VerifyMode.ANY;
+
+	private GpgSignatureVerifier verifier;
+
+	private GpgConfig config;
+
+	private boolean ownVerifier;
+
+	/**
+	 * Creates a new {@link VerifySignatureCommand} for the given {@link Repository}.
+	 *
+	 * @param repo
+	 *            to operate on
+	 */
+	public VerifySignatureCommand(Repository repo) {
+		super(repo);
+	}
+
+	/**
+	 * Add a name of an object (SHA-1, ref name; anything that can be
+	 * {@link Repository#resolve(String) resolved}) to the command to have its
+	 * signature verified.
+	 *
+	 * @param name
+	 *            to add
+	 * @return {@code this}
+	 */
+	public VerifySignatureCommand addName(String name) {
+		checkCallable();
+		namesToCheck.add(name);
+		return this;
+	}
+
+	/**
+	 * Add names of objects (SHA-1, ref name; anything that can be
+	 * {@link Repository#resolve(String) resolved}) to the command to have their
+	 * signatures verified.
+	 *
+	 * @param names
+	 *            to add; duplicates will be ignored
+	 * @return {@code this}
+	 */
+	public VerifySignatureCommand addNames(String... names) {
+		checkCallable();
+		namesToCheck.addAll(Arrays.asList(names));
+		return this;
+	}
+
+	/**
+	 * Add names of objects (SHA-1, ref name; anything that can be
+	 * {@link Repository#resolve(String) resolved}) to the command to have their
+	 * signatures verified.
+	 *
+	 * @param names
+	 *            to add; duplicates will be ignored
+	 * @return {@code this}
+	 */
+	public VerifySignatureCommand addNames(Collection<String> names) {
+		checkCallable();
+		namesToCheck.addAll(names);
+		return this;
+	}
+
+	/**
+	 * Sets the mode of operation for this command.
+	 *
+	 * @param mode
+	 *            the {@link VerifyMode} to set
+	 * @return {@code this}
+	 */
+	public VerifySignatureCommand setMode(@NonNull VerifyMode mode) {
+		checkCallable();
+		this.mode = mode;
+		return this;
+	}
+
+	/**
+	 * Sets the {@link GpgSignatureVerifier} to use.
+	 *
+	 * @param verifier
+	 *            the {@link GpgSignatureVerifier} to use, or {@code null} to
+	 *            use the default verifier
+	 * @return {@code this}
+	 */
+	public VerifySignatureCommand setVerifier(GpgSignatureVerifier verifier) {
+		checkCallable();
+		this.verifier = verifier;
+		return this;
+	}
+
+	/**
+	 * Sets an external {@link GpgConfig} to use. Whether it will be used it at
+	 * the discretion of the {@link #setVerifier(GpgSignatureVerifier)}.
+	 *
+	 * @param config
+	 *            to set; if {@code null}, the config will be loaded from the
+	 *            git config of the repository
+	 * @return {@code this}
+	 * @since 5.11
+	 */
+	public VerifySignatureCommand setGpgConfig(GpgConfig config) {
+		checkCallable();
+		this.config = config;
+		return this;
+	}
+
+	/**
+	 * Retrieves the currently set {@link GpgSignatureVerifier}. Can be used
+	 * after a successful {@link #call()} to get the verifier that was used.
+	 *
+	 * @return the {@link GpgSignatureVerifier}
+	 */
+	public GpgSignatureVerifier getVerifier() {
+		return verifier;
+	}
+
+	/**
+	 * {@link Repository#resolve(String) Resolves} all names added to the
+	 * command to git objects and verifies their signature. Non-existing objects
+	 * are ignored.
+	 * <p>
+	 * Depending on the {@link #setMode(VerifyMode)}, only tags or commits or
+	 * any kind of objects are allowed.
+	 * </p>
+	 * <p>
+	 * Unsigned objects are silently skipped.
+	 * </p>
+	 *
+	 * @return a map of the given names to the corresponding
+	 *         {@link VerificationResult}, excluding ignored or skipped objects.
+	 * @throws ServiceUnavailableException
+	 *             if no {@link GpgSignatureVerifier} was set and no
+	 *             {@link GpgSignatureVerifierFactory} is available
+	 * @throws WrongObjectTypeException
+	 *             if a name resolves to an object of a type not allowed by the
+	 *             {@link #setMode(VerifyMode)} mode
+	 */
+	@Override
+	@NonNull
+	public Map<String, VerificationResult> call()
+			throws ServiceUnavailableException, WrongObjectTypeException {
+		checkCallable();
+		setCallable(false);
+		Map<String, VerificationResult> result = new HashMap<>();
+		if (verifier == null) {
+			GpgSignatureVerifierFactory factory = GpgSignatureVerifierFactory
+					.getDefault();
+			if (factory == null) {
+				throw new ServiceUnavailableException(
+						JGitText.get().signatureVerificationUnavailable);
+			}
+			verifier = factory.getVerifier();
+			ownVerifier = true;
+		}
+		if (config == null) {
+			config = new GpgConfig(repo.getConfig());
+		}
+		try (RevWalk walk = new RevWalk(repo)) {
+			for (String toCheck : namesToCheck) {
+				ObjectId id = repo.resolve(toCheck);
+				if (id != null && !ObjectId.zeroId().equals(id)) {
+					RevObject object;
+					try {
+						object = walk.parseAny(id);
+					} catch (MissingObjectException e) {
+						continue;
+					}
+					VerificationResult verification = verifyOne(object);
+					if (verification != null) {
+						result.put(toCheck, verification);
+					}
+				}
+			}
+		} catch (IOException e) {
+			throw new JGitInternalException(
+					JGitText.get().signatureVerificationError, e);
+		} finally {
+			if (ownVerifier) {
+				verifier.clear();
+			}
+		}
+		return result;
+	}
+
+	private VerificationResult verifyOne(RevObject object)
+			throws WrongObjectTypeException, IOException {
+		int type = object.getType();
+		if (VerifyMode.TAGS.equals(mode) && type != Constants.OBJ_TAG) {
+			throw new WrongObjectTypeException(object, Constants.OBJ_TAG);
+		} else if (VerifyMode.COMMITS.equals(mode)
+				&& type != Constants.OBJ_COMMIT) {
+			throw new WrongObjectTypeException(object, Constants.OBJ_COMMIT);
+		}
+		if (type == Constants.OBJ_COMMIT || type == Constants.OBJ_TAG) {
+			try {
+				GpgSignatureVerifier.SignatureVerification verification = verifier
+						.verifySignature(object, config);
+				if (verification == null) {
+					// Not signed
+					return null;
+				}
+				// Create new result
+				return new Result(object, verification, null);
+			} catch (JGitInternalException e) {
+				return new Result(object, null, e);
+			}
+		}
+		return null;
+	}
+
+	private static class Result implements VerificationResult {
+
+		private final Throwable throwable;
+
+		private final SignatureVerification verification;
+
+		private final RevObject object;
+
+		public Result(RevObject object, SignatureVerification verification,
+				Throwable throwable) {
+			this.object = object;
+			this.verification = verification;
+			this.throwable = throwable;
+		}
+
+		@Override
+		public Throwable getException() {
+			return throwable;
+		}
+
+		@Override
+		public SignatureVerification getVerification() {
+			return verification;
+		}
+
+		@Override
+		public RevObject getObject() {
+			return object;
+		}
+
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/WrongObjectTypeException.java b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/WrongObjectTypeException.java
new file mode 100644
index 0000000..f639c2f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/api/errors/WrongObjectTypeException.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.api.errors;
+
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+
+/**
+ * A given object is not of an expected object type.
+ *
+ * @since 5.11
+ */
+public class WrongObjectTypeException extends GitAPIException {
+
+	private static final long serialVersionUID = 1L;
+
+	private String name;
+
+	private int type;
+
+	/**
+	 * Construct a {@link WrongObjectTypeException} for the specified object id,
+	 * giving the expected type.
+	 *
+	 * @param id
+	 *            {@link ObjectId} of the object with the unexpected type
+	 * @param type
+	 *            expected object type code; see
+	 *            {@link Constants}{@code .OBJ_*}.
+	 */
+	public WrongObjectTypeException(ObjectId id, int type) {
+		super(MessageFormat.format(JGitText.get().objectIsNotA, id.name(),
+				Constants.typeString(type)));
+		this.name = id.name();
+		this.type = type;
+	}
+
+	/**
+	 * Retrieves the name (SHA-1) of the object.
+	 *
+	 * @return the name
+	 */
+	public String getObjectId() {
+		return name;
+	}
+
+	/**
+	 * Retrieves the expected type code. See {@link Constants}{@code .OBJ_*}.
+	 *
+	 * @return the type code
+	 */
+	public int getExpectedType() {
+		return type;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
index 53f4819..ce3ad22 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/GitHook.java
@@ -9,12 +9,10 @@
  */
 package org.eclipse.jgit.hooks;
 
-import static java.nio.charset.StandardCharsets.UTF_8;
-
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
 import java.util.concurrent.Callable;
 
 import org.eclipse.jgit.api.errors.AbortedByHookException;
@@ -35,21 +33,21 @@
  *            the return type which is expected from {@link #call()}
  * @see <a href="http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git
  *      Hooks on the git-scm official site</a>
- * @since 4.0
+ * @since 5.11
  */
-abstract class GitHook<T> implements Callable<T> {
+public abstract class GitHook<T> implements Callable<T> {
 
 	private final Repository repo;
 
 	/**
 	 * The output stream to be used by the hook.
 	 */
-	private final PrintStream outputStream;
+	private final OutputStream outputStream;
 
 	/**
 	 * The error stream to be used by the hook.
 	 */
-	private final PrintStream errorStream;
+	private final OutputStream errorStream;
 
 	/**
 	 * Constructor for GitHook.
@@ -63,7 +61,7 @@
 	 *            The output stream the hook must use. {@code null} is allowed,
 	 *            in which case the hook will use {@code System.out}.
 	 */
-	protected GitHook(Repository repo, PrintStream outputStream) {
+	protected GitHook(Repository repo, OutputStream outputStream) {
 		this(repo, outputStream, null);
 	}
 
@@ -79,8 +77,8 @@
 	 *            The error stream the hook must use. {@code null} is allowed,
 	 *            in which case the hook will use {@code System.err}.
 	 */
-	protected GitHook(Repository repo, PrintStream outputStream,
-			PrintStream errorStream) {
+	protected GitHook(Repository repo, OutputStream outputStream,
+			OutputStream errorStream) {
 		this.repo = repo;
 		this.outputStream = outputStream;
 		this.errorStream = errorStream;
@@ -137,7 +135,7 @@
 	 * @return The output stream the hook must use. Never {@code null},
 	 *         {@code System.out} is returned by default.
 	 */
-	protected PrintStream getOutputStream() {
+	protected OutputStream getOutputStream() {
 		return outputStream == null ? System.out : outputStream;
 	}
 
@@ -147,7 +145,7 @@
 	 * @return The error stream the hook must use. Never {@code null},
 	 *         {@code System.err} is returned by default.
 	 */
-	protected PrintStream getErrorStream() {
+	protected OutputStream getErrorStream() {
 		return errorStream == null ? System.err : errorStream;
 	}
 
@@ -156,34 +154,48 @@
 	 *
 	 * @throws org.eclipse.jgit.api.errors.AbortedByHookException
 	 *             If the underlying hook script exited with non-zero.
+	 * @throws IOException
+	 *             if an IO error occurred
 	 */
-	protected void doRun() throws AbortedByHookException {
+	protected void doRun() throws AbortedByHookException, IOException {
 		final ByteArrayOutputStream errorByteArray = new ByteArrayOutputStream();
 		final TeeOutputStream stderrStream = new TeeOutputStream(errorByteArray,
 				getErrorStream());
-		PrintStream hookErrRedirect = null;
-		try {
-			hookErrRedirect = new PrintStream(stderrStream, false,
-					UTF_8.name());
-		} catch (UnsupportedEncodingException e) {
-			// UTF-8 is guaranteed to be available
-		}
 		Repository repository = getRepository();
 		FS fs = repository.getFS();
 		if (fs == null) {
 			fs = FS.DETECTED;
 		}
 		ProcessResult result = fs.runHookIfPresent(repository, getHookName(),
-				getParameters(), getOutputStream(), hookErrRedirect,
+				getParameters(), getOutputStream(), stderrStream,
 				getStdinArgs());
 		if (result.isExecutedWithError()) {
-			throw new AbortedByHookException(
-					new String(errorByteArray.toByteArray(), UTF_8),
-					getHookName(), result.getExitCode());
+			handleError(new String(errorByteArray.toByteArray(),
+					Charset.defaultCharset().name()), result);
 		}
 	}
 
 	/**
+	 * Process that the hook exited with an error. This default implementation
+	 * throws an {@link AbortedByHookException }. Hooks which need a different
+	 * behavior can overwrite this method.
+	 *
+	 * @param message
+	 *            error message
+	 * @param result
+	 *            The process result of the hook
+	 * @throws AbortedByHookException
+	 *             When the hook should be aborted
+	 * @since 5.11
+	 */
+	protected void handleError(String message,
+			final ProcessResult result)
+			throws AbortedByHookException {
+		throw new AbortedByHookException(message, getHookName(),
+				result.getExitCode());
+	}
+
+	/**
 	 * Check whether a 'native' (i.e. script) hook is installed in the
 	 * repository.
 	 *
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
index 0b61ebe..b9dafcc 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/hooks/PostCommitHook.java
@@ -14,6 +14,7 @@
 
 import org.eclipse.jgit.api.errors.AbortedByHookException;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.util.ProcessResult;
 
 /**
  * The <code>post-commit</code> hook implementation. This hook is run after the
@@ -73,4 +74,16 @@
 		return NAME;
 	}
 
+
+	/**
+	 * Overwrites the default implementation to never throw an
+	 * {@link AbortedByHookException}, as the commit has already been done and
+	 * the exit code of the post-commit hook has no effect.
+	 */
+	@Override
+	protected void handleError(String message, ProcessResult result)
+			throws AbortedByHookException {
+		// Do nothing as the exit code of the post-commit hook has no effect.
+	}
+
 }
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 07fb59d..aaba8d6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010, 2013 Sasa Zivkov <sasa.zivkov@sap.com>
- * Copyright (C) 2012, Research In Motion Limited and others
+ * Copyright (C) 2012, 2021 Research In Motion Limited and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -650,6 +650,8 @@
 	/***/ public String shortReadOfBlock;
 	/***/ public String shortReadOfOptionalDIRCExtensionExpectedAnotherBytes;
 	/***/ public String shortSkipOfBlock;
+	/***/ public String signatureVerificationError;
+	/***/ public String signatureVerificationUnavailable;
 	/***/ public String signedTagMessageNoLf;
 	/***/ public String signingServiceUnavailable;
 	/***/ public String similarityScoreMustBeWithinBounds;
@@ -791,6 +793,13 @@
 	/***/ public String URINotSupported;
 	/***/ public String userConfigInvalid;
 	/***/ public String validatingGitModules;
+	/***/ public String verifySignatureBad;
+	/***/ public String verifySignatureExpired;
+	/***/ public String verifySignatureGood;
+	/***/ public String verifySignatureIssuer;
+	/***/ public String verifySignatureKey;
+	/***/ public String verifySignatureMade;
+	/***/ public String verifySignatureTrust;
 	/***/ public String walkFailure;
 	/***/ public String wantNoSpaceWithCapabilities;
 	/***/ public String wantNotValid;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
index e422767..b5e3927 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableStack.java
@@ -370,7 +370,7 @@
 	private String filename(long low, long high) {
 		return String.format("%012x-%012x-%08x", //$NON-NLS-1$
 				Long.valueOf(low), Long.valueOf(high),
-				random.nextInt());
+				Integer.valueOf(random.nextInt()));
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
new file mode 100644
index 0000000..a7a39c9
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.revwalk.RevObject;
+
+/**
+ * A {@code GpgVerifier} can verify GPG signatures on git commits and tags.
+ *
+ * @since 5.11
+ */
+public interface GpgSignatureVerifier {
+
+	/**
+	 * Verifies the signature on a signed commit or tag.
+	 *
+	 * @param object
+	 *            to verify
+	 * @param config
+	 *            the {@link GpgConfig} to use
+	 * @return a {@link SignatureVerification} describing the outcome of the
+	 *         verification, or {@code null} if the object was not signed
+	 * @throws IOException
+	 *             if an error occurs getting a public key
+	 * @throws org.eclipse.jgit.api.errors.JGitInternalException
+	 *             if signature verification fails
+	 */
+	@Nullable
+	SignatureVerification verifySignature(@NonNull RevObject object,
+			@NonNull GpgConfig config) throws IOException;
+
+
+	/**
+	 * Verifies a given signature for given data.
+	 *
+	 * @param data
+	 *            the signature is for
+	 * @param signatureData
+	 *            the ASCII-armored signature
+	 * @return a {@link SignatureVerification} describing the outcome
+	 * @throws IOException
+	 *             if the signature cannot be parsed
+	 * @throws JGitInternalException
+	 *             if signature verification fails
+	 */
+	public SignatureVerification verify(byte[] data, byte[] signatureData)
+			throws IOException;
+
+	/**
+	 * Retrieves the name of this verifier. This should be a short string
+	 * identifying the engine that verified the signature, like "gpg" if GPG is
+	 * used, or "bc" for a BouncyCastle implementation.
+	 *
+	 * @return the name
+	 */
+	@NonNull
+	String getName();
+
+	/**
+	 * A {@link GpgSignatureVerifier} may cache public keys to speed up
+	 * verifying signatures on multiple objects. This clears this cache, if any.
+	 */
+	void clear();
+
+	/**
+	 * A {@code SignatureVerification} returns data about a (positively or
+	 * negatively) verified signature.
+	 */
+	interface SignatureVerification {
+
+		// Data about the signature.
+
+		@NonNull
+		Date getCreationDate();
+
+		// Data from the signature used to find a public key.
+
+		/**
+		 * Obtains the signer as stored in the signature, if known.
+		 *
+		 * @return the signer, or {@code null} if unknown
+		 */
+		String getSigner();
+
+		/**
+		 * Obtains the short or long fingerprint of the public key as stored in
+		 * the signature, if known.
+		 *
+		 * @return the fingerprint, or {@code null} if unknown
+		 */
+		String getKeyFingerprint();
+
+		// Some information about the found public key.
+
+		/**
+		 * Obtains the OpenPGP user ID associated with the key.
+		 *
+		 * @return the user id, or {@code null} if unknown
+		 */
+		String getKeyUser();
+
+		/**
+		 * Tells whether the public key used for this signature verification was
+		 * expired when the signature was created.
+		 *
+		 * @return {@code true} if the key was expired already, {@code false}
+		 *         otherwise
+		 */
+		boolean isExpired();
+
+		/**
+		 * Obtains the trust level of the public key used to verify the
+		 * signature.
+		 *
+		 * @return the trust level
+		 */
+		@NonNull
+		TrustLevel getTrustLevel();
+
+		// The verification result.
+
+		/**
+		 * Tells whether the signature verification was successful.
+		 *
+		 * @return {@code true} if the signature was verified successfully;
+		 *         {@code false} if not.
+		 */
+		boolean getVerified();
+
+		/**
+		 * Obtains a human-readable message giving additional information about
+		 * the outcome of the verification.
+		 *
+		 * @return the message, or {@code null} if none set.
+		 */
+		String getMessage();
+	}
+
+	/**
+	 * The owner's trust in a public key.
+	 */
+	enum TrustLevel {
+		UNKNOWN, NEVER, MARGINAL, FULL, ULTIMATE
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java
new file mode 100644
index 0000000..4b1dbed
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifierFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lib;
+
+import java.util.Iterator;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@code GpgSignatureVerifierFactory} creates {@link GpgSignatureVerifier} instances.
+ *
+ * @since 5.11
+ */
+public abstract class GpgSignatureVerifierFactory {
+
+	private static final Logger LOG = LoggerFactory
+			.getLogger(GpgSignatureVerifierFactory.class);
+
+	private static volatile GpgSignatureVerifierFactory defaultFactory = loadDefault();
+
+	private static GpgSignatureVerifierFactory loadDefault() {
+		try {
+			ServiceLoader<GpgSignatureVerifierFactory> loader = ServiceLoader
+					.load(GpgSignatureVerifierFactory.class);
+			Iterator<GpgSignatureVerifierFactory> iter = loader.iterator();
+			if (iter.hasNext()) {
+				return iter.next();
+			}
+		} catch (ServiceConfigurationError e) {
+			LOG.error(e.getMessage(), e);
+		}
+		return null;
+	}
+
+	/**
+	 * Retrieves the default factory.
+	 *
+	 * @return the default factory or {@code null} if none set
+	 */
+	public static GpgSignatureVerifierFactory getDefault() {
+		return defaultFactory;
+	}
+
+	/**
+	 * Sets the default factory.
+	 *
+	 * @param factory
+	 *            the new default factory
+	 */
+	public static void setDefault(GpgSignatureVerifierFactory factory) {
+		defaultFactory = factory;
+	}
+
+	/**
+	 * Creates a new {@link GpgSignatureVerifier}.
+	 *
+	 * @return the new {@link GpgSignatureVerifier}
+	 */
+	public abstract GpgSignatureVerifier getVerifier();
+
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
index 6c217fd..4bfb38d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/merge/ResolveMerger.java
@@ -703,18 +703,21 @@
 			// conflict between ours and theirs. file/folder conflicts between
 			// base/index/workingTree and something else are not relevant or
 			// detected later
-			if (nonTree(modeO) && !nonTree(modeT)) {
+			if (nonTree(modeO) != nonTree(modeT)) {
+				if (ignoreConflicts) {
+					// In case of merge failures, ignore this path instead of reporting unmerged, so
+					// a caller can use virtual commit. This will not result in files with conflict
+					// markers in the index/working tree. The actual diff on the path will be
+					// computed directly on children.
+					enterSubtree = false;
+					return true;
+				}
 				if (nonTree(modeB))
 					add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-				add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
-				unmergedPaths.add(tw.getPathString());
-				enterSubtree = false;
-				return true;
-			}
-			if (nonTree(modeT) && !nonTree(modeO)) {
-				if (nonTree(modeB))
-					add(tw.getRawPath(), base, DirCacheEntry.STAGE_1, EPOCH, 0);
-				add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
+				if (nonTree(modeO))
+					add(tw.getRawPath(), ours, DirCacheEntry.STAGE_2, EPOCH, 0);
+				if (nonTree(modeT))
+					add(tw.getRawPath(), theirs, DirCacheEntry.STAGE_3, EPOCH, 0);
 				unmergedPaths.add(tw.getPathString());
 				enterSubtree = false;
 				return true;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
index 3499136..b9d1450 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevTag.java
@@ -1,7 +1,7 @@
 /*
- * Copyright (C) 2008-2009, Google Inc.
+ * Copyright (C) 2008, 2009, Google Inc.
  * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
+ * Copyright (C) 2008, 2021, Shawn O. Pearce <spearce@spearce.org> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -344,6 +344,22 @@
 	}
 
 	/**
+	 * Obtain the raw unparsed tag body (<b>NOTE - THIS IS NOT A COPY</b>).
+	 * <p>
+	 * This method is exposed only to provide very fast, efficient access to
+	 * this tag's message buffer. Applications relying on this buffer should be
+	 * very careful to ensure they do not modify its contents during their use
+	 * of it.
+	 *
+	 * @return the raw unparsed tag body. This is <b>NOT A COPY</b>. Do not
+	 *         alter the returned array.
+	 * @since 5.11
+	 */
+	public final byte[] getRawBuffer() {
+		return buffer;
+	}
+
+	/**
 	 * Discard the message buffer to reduce memory usage.
 	 * <p>
 	 * After discarding the memory usage of the {@code RevTag} is reduced to
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
index c80a3a4..0946f64 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS.java
@@ -22,7 +22,6 @@
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
-import java.io.PrintStream;
 import java.io.Writer;
 import java.nio.charset.Charset;
 import java.nio.file.AccessDeniedException;
@@ -1873,18 +1872,18 @@
 	 * @throws org.eclipse.jgit.api.errors.JGitInternalException
 	 *             if we fail to run the hook somehow. Causes may include an
 	 *             interrupted process or I/O errors.
-	 * @since 4.0
+	 * @since 5.11
 	 */
 	public ProcessResult runHookIfPresent(Repository repository,
 			final String hookName,
-			String[] args, PrintStream outRedirect, PrintStream errRedirect,
+			String[] args, OutputStream outRedirect, OutputStream errRedirect,
 			String stdinArgs) throws JGitInternalException {
 		return new ProcessResult(Status.NOT_SUPPORTED);
 	}
 
 	/**
 	 * See
-	 * {@link #runHookIfPresent(Repository, String, String[], PrintStream, PrintStream, String)}
+	 * {@link #runHookIfPresent(Repository, String, String[], OutputStream, OutputStream, String)}
 	 * . Should only be called by FS supporting shell scripts execution.
 	 *
 	 * @param repository
@@ -1909,11 +1908,11 @@
 	 * @throws org.eclipse.jgit.api.errors.JGitInternalException
 	 *             if we fail to run the hook somehow. Causes may include an
 	 *             interrupted process or I/O errors.
-	 * @since 4.0
+	 * @since 5.11
 	 */
 	protected ProcessResult internalRunHookIfPresent(Repository repository,
-			final String hookName, String[] args, PrintStream outRedirect,
-			PrintStream errRedirect, String stdinArgs)
+			final String hookName, String[] args, OutputStream outRedirect,
+			OutputStream errRedirect, String stdinArgs)
 			throws JGitInternalException {
 		File hookFile = findHook(repository, hookName);
 		if (hookFile == null || hookName == null) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
index fb63dc0..946d81c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_POSIX.java
@@ -16,7 +16,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.io.PrintStream;
+import java.io.OutputStream;
 import java.nio.charset.Charset;
 import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.FileStore;
@@ -268,7 +268,7 @@
 	/** {@inheritDoc} */
 	@Override
 	public ProcessResult runHookIfPresent(Repository repository, String hookName,
-			String[] args, PrintStream outRedirect, PrintStream errRedirect,
+			String[] args, OutputStream outRedirect, OutputStream errRedirect,
 			String stdinArgs) throws JGitInternalException {
 		return internalRunHookIfPresent(repository, hookName, args, outRedirect,
 				errRedirect, stdinArgs);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
index d53bff7..add5498 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/FS_Win32_Cygwin.java
@@ -13,7 +13,7 @@
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import java.io.File;
-import java.io.PrintStream;
+import java.io.OutputStream;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
@@ -139,7 +139,7 @@
 	/** {@inheritDoc} */
 	@Override
 	public ProcessResult runHookIfPresent(Repository repository, String hookName,
-			String[] args, PrintStream outRedirect, PrintStream errRedirect,
+			String[] args, OutputStream outRedirect, OutputStream errRedirect,
 			String stdinArgs) throws JGitInternalException {
 		return internalRunHookIfPresent(repository, hookName, args, outRedirect,
 				errRedirect, stdinArgs);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
new file mode 100644
index 0000000..cf06172
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/SignatureUtils.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.util;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.SignatureVerification;
+import org.eclipse.jgit.lib.GpgSignatureVerifier.TrustLevel;
+import org.eclipse.jgit.lib.PersonIdent;
+
+/**
+ * Utilities for signature verification.
+ *
+ * @since 5.11
+ */
+public final class SignatureUtils {
+
+	private SignatureUtils() {
+		// No instantiation
+	}
+
+	/**
+	 * Writes information about a signature verification to a string.
+	 *
+	 * @param verification
+	 *            to show
+	 * @param creator
+	 *            of the object verified; used for time zone information
+	 * @param formatter
+	 *            to use for dates
+	 * @return a textual representation of the {@link SignatureVerification},
+	 *         using LF as line separator
+	 */
+	public static String toString(SignatureVerification verification,
+			PersonIdent creator, GitDateFormatter formatter) {
+		StringBuilder result = new StringBuilder();
+		// Use the creator's timezone for the signature date
+		PersonIdent dateId = new PersonIdent(creator,
+				verification.getCreationDate());
+		result.append(MessageFormat.format(JGitText.get().verifySignatureMade,
+				formatter.formatDate(dateId)));
+		result.append('\n');
+		result.append(MessageFormat.format(
+				JGitText.get().verifySignatureKey,
+				verification.getKeyFingerprint().toUpperCase(Locale.ROOT)));
+		result.append('\n');
+		if (!StringUtils.isEmptyOrNull(verification.getSigner())) {
+			result.append(
+					MessageFormat.format(JGitText.get().verifySignatureIssuer,
+							verification.getSigner()));
+			result.append('\n');
+		}
+		String msg;
+		if (verification.getVerified()) {
+			if (verification.isExpired()) {
+				msg = JGitText.get().verifySignatureExpired;
+			} else {
+				msg = JGitText.get().verifySignatureGood;
+			}
+		} else {
+			msg = JGitText.get().verifySignatureBad;
+		}
+		result.append(MessageFormat.format(msg, verification.getKeyUser()));
+		if (!TrustLevel.UNKNOWN.equals(verification.getTrustLevel())) {
+			result.append(' ' + MessageFormat
+					.format(JGitText.get().verifySignatureTrust, verification
+							.getTrustLevel().name().toLowerCase(Locale.ROOT)));
+		}
+		result.append('\n');
+		msg = verification.getMessage();
+		if (!StringUtils.isEmptyOrNull(msg)) {
+			result.append(msg);
+			result.append('\n');
+		}
+		return result.toString();
+	}
+}
diff --git a/pom.xml b/pom.xml
index 8257f95..7e3acbc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -162,7 +162,7 @@
     <commons-compress-version>1.19</commons-compress-version>
     <osgi-core-version>4.3.1</osgi-core-version>
     <servlet-api-version>3.1.0</servlet-api-version>
-    <jetty-version>9.4.35.v20201120</jetty-version>
+    <jetty-version>9.4.36.v20210114</jetty-version>
     <japicmp-version>0.14.4</japicmp-version>
     <httpclient-version>4.5.13</httpclient-version>
     <httpcore-version>4.4.14</httpcore-version>