Bug 541781 - Support dynamically registered MessageDigest implementations

Extend o.e.e.p2.artifact.repository.artifactChecksums extension point
with attribute providerName to support custom MessageDigest
implementations as per Java Security API.

To get an instance of custom MessageDigest in ChecksumProducer's
getMessageDigest(String, String), look for service object under
java.security.Provider interface filtered by providerName property and
pass it to java.security.MessageDigest's getInstance(String,
Provider). Throws NoSuchProviderException if no such service was
found.

Bundle that contributes such implementation should register it with
the Framework service registry under interface
java.security.Provider. The registration properties of the service
should contain property 'providerName' with value of type String as
returned by implementation's java.security.Provider.getName(). The
same value should be used for providerName attribute in
artifactChecksums extension point.

o.e.equinox.p2.artifact.checksums.bouncycastle bundle demonstrates how
this works by adding support for Whirlpool and DSTU7564 message
digests using the Bouncy Castle Crypto APIs [1]. It is not part of the
distribution and used by unit tests only.

[1] https://bouncycastle.org/

Change-Id: I0cfd06ceca6e1911d69bab09331399500a00dcee
Signed-off-by: Mykola Nikishov <mn@mn.com.ua>
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.classpath b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.classpath
new file mode 100644
index 0000000..4f83b23
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.gitignore b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.gitignore
new file mode 100644
index 0000000..c5e82d7
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.gitignore
@@ -0,0 +1 @@
+bin
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.project b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.project
new file mode 100644
index 0000000..9ef20c1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/.project
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.equinox.p2.artifact.checksums.bouncycastle</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.api.tools.apiAnalysisBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ds.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+		<nature>org.eclipse.pde.api.tools.apiAnalysisNature</nature>
+	</natures>
+</projectDescription>
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..28688b9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/META-INF/MANIFEST.MF
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-SymbolicName: org.eclipse.equinox.p2.artifact.checksums.bouncycastle;singleton:=true
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: %providerName
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Bundle-ActivationPolicy: lazy
+Require-Bundle: org.eclipse.equinox.p2.artifact.repository;bundle-version="[1.3.200,2.0.0)"
+Import-Package: org.bouncycastle.jce.provider;version="[1.59.0,2.0.0)"
+Service-Component: OSGi-INF/securityProvider.xml
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/OSGi-INF/securityProvider.xml b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/OSGi-INF/securityProvider.xml
new file mode 100644
index 0000000..a9e7e8c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/OSGi-INF/securityProvider.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.equinox.p2.artifact.checksums.bouncycastle">
+   <implementation class="org.bouncycastle.jce.provider.BouncyCastleProvider"/>
+   <service>
+   <provide interface="java.security.Provider"/>
+   </service>
+   <property name="providerName" type="String" value="BC"/>
+</scr:component>
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/about.html b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/about.html
new file mode 100644
index 0000000..164f781
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/about.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<title>About</title>
+</head>
+<body lang="EN-US">
+	<h2>About This Content</h2>
+
+	<p>November 30, 2017</p>
+	<h3>License</h3>
+
+	<p>
+		The Eclipse Foundation makes available all content in this plug-in
+		(&quot;Content&quot;). Unless otherwise indicated below, the Content
+		is provided to you under the terms and conditions of the Eclipse
+		Public License Version 2.0 (&quot;EPL&quot;). A copy of the EPL is
+		available at <a href="http://www.eclipse.org/legal/epl-2.0">http://www.eclipse.org/legal/epl-2.0</a>.
+		For purposes of the EPL, &quot;Program&quot; will mean the Content.
+	</p>
+
+	<p>
+		If you did not receive this Content directly from the Eclipse
+		Foundation, the Content is being redistributed by another party
+		(&quot;Redistributor&quot;) and different terms and conditions may
+		apply to your use of any object code in the Content. Check the
+		Redistributor's license that was provided with the Content. If no such
+		license exists, contact the Redistributor. Unless otherwise indicated
+		below, the terms and conditions of the EPL still apply to any source
+		code in the Content and such source code may be obtained at <a
+			href="http://www.eclipse.org/">http://www.eclipse.org</a>.
+	</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/build.properties b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/build.properties
new file mode 100644
index 0000000..e396356
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/build.properties
@@ -0,0 +1,20 @@
+###############################################################################
+# Copyright (c) 2016, 2019 Mykola Nikishov.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+#     Mykola Nikishov - initial API and implementation
+###############################################################################
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+               .,\
+               plugin.xml,\
+               plugin.properties,\
+               OSGi-INF/securityProvider.xml
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/plugin.properties b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/plugin.properties
new file mode 100644
index 0000000..0308609
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/plugin.properties
@@ -0,0 +1,15 @@
+###############################################################################
+# Copyright (c) 2016, 2019 Mykola Nikishov.
+#
+# This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License 2.0
+# which accompanies this distribution, and is available at
+# https://www.eclipse.org/legal/epl-2.0/
+#
+# SPDX-License-Identifier: EPL-2.0
+#
+# Contributors:
+#     Mykola Nikishov - initial API and implementation
+###############################################################################
+pluginName = Equinox p2 Artifact Checksum Support
+providerName = Eclipse.org - Equinox
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/plugin.xml b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/plugin.xml
new file mode 100644
index 0000000..8d00dbf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/plugin.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.2"?>
+<plugin>
+	<extension point="org.eclipse.equinox.p2.artifact.repository.artifactChecksums">
+		<artifactChecksum
+			algorithm="Whirlpool"
+			id="whirlpool"
+			providerName="BC">
+		</artifactChecksum>
+	</extension>
+	<extension point="org.eclipse.equinox.p2.artifact.repository.artifactChecksums">
+		<artifactChecksum
+			algorithm="DSTU7564-512"
+			id="dstu7564-512"
+			providerName="BC">
+		</artifactChecksum>
+	</extension>
+</plugin>
diff --git a/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml
new file mode 100644
index 0000000..78d0946
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle/pom.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.eclipse.equinox.p2</groupId>
+    <artifactId>rt.equinox.p2</artifactId>
+    <version>4.13.0-SNAPSHOT</version>
+    <relativePath>../..</relativePath>
+  </parent>
+  <groupId>org.eclipse.equinox</groupId>
+  <artifactId>org.eclipse.equinox.p2.artifact.checksums.bouncycastle</artifactId>
+  <version>1.0.0-SNAPSHOT</version>
+  <packaging>eclipse-plugin</packaging>
+  <properties>
+    <skipAPIAnalysis>true</skipAPIAnalysis>
+  </properties>
+</project>
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/schema/artifactChecksums.exsd b/bundles/org.eclipse.equinox.p2.artifact.repository/schema/artifactChecksums.exsd
index 8c2a306..0f7b4b0 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/schema/artifactChecksums.exsd
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/schema/artifactChecksums.exsd
@@ -69,6 +69,13 @@
                </documentation>
             </annotation>
          </attribute>
+         <attribute name="providerName" type="string">
+            <annotation>
+               <documentation>
+                  For custom security provider, name of the security provider that provides this message digest implementation, the value returned by &lt;code&gt;java.security.Provider.getName()&lt;/code&gt;.
+               </documentation>
+            </annotation>
+         </attribute>
       </complexType>
    </element>
 
@@ -97,18 +104,35 @@
 &lt;/extension&gt;
 &lt;/pre&gt;
 
-If the MessageDigest implementation is provided by a custom Provider (from the contributing bundle itself or some other bundle), it should be first dynamically registered:
+If the MessageDigest implementation is provided by a custom security provider (from the contributing bundle itself or some other bundle), it should be registered first with the Framework service registry under interface &lt;code&gt;java.security.Provider&lt;/code&gt;:
 
 &lt;pre&gt;
-import java.security.Security;
+import java.security.Provider;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 
-public class Activator implements BundleActivator {
+...
 
-  public void start(BundleContext context) throws Exception {
-    Security.addProvider(new BouncyCastleProvider());
-  }
-}
+Dictionary&lt;String, Object&gt; props = new Hashtable&lt;&gt;();
+props.put(&quot;providerName&quot;, &quot;BC&quot;);
+ServiceRegistration&lt;Provider&gt; registration = context.registerService(Provider.class, new BouncyCastleProvider(), props);
+&lt;/pre&gt;
+
+and then register an extension using &lt;code&gt;providerName&lt;/code&gt; attribute:
+
+&lt;pre&gt;
+&lt;extension point=&quot;org.eclipse.equinox.p2.artifact.repository.artifactChecksums&quot;&gt;
+  &lt;artifactChecksum
+   algorithm=&quot;Whirlpool&quot;
+   id=&quot;whirlpool&quot;
+   providerName=&quot;BC&quot;&gt;
+  &lt;/artifactChecksum&gt;
+&lt;/extension&gt;
 &lt;/pre&gt;
       </documentation>
    </annotation>
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumUtilities.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumUtilities.java
index 012b396..f45b7f2 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumUtilities.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumUtilities.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2018 Mykola Nikishov.
+ * Copyright (c) 2015, 2019 Mykola Nikishov.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -16,6 +16,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import java.util.*;
 import java.util.Map.Entry;
 import org.eclipse.core.runtime.*;
@@ -56,7 +57,8 @@
 				String checksumId = checksumVerifierConfiguration.getAttribute("id"); //$NON-NLS-1$
 				if (checksumEntry.getKey().equals(checksumId)) {
 					String checksumAlgorithm = checksumVerifierConfiguration.getAttribute("algorithm"); //$NON-NLS-1$
-					ChecksumVerifier checksumVerifier = new ChecksumVerifier(checksumAlgorithm, checksumId);
+					String providerName = checksumVerifierConfiguration.getAttribute("providerName"); //$NON-NLS-1$
+					ChecksumVerifier checksumVerifier = new ChecksumVerifier(checksumAlgorithm, providerName, checksumId);
 					checksumVerifier.initialize(null, new ProcessingStepDescriptor(null, checksumEntry.getValue(), true), descriptor);
 					if (checksumVerifier.getStatus().isOK())
 						steps.add(checksumVerifier);
@@ -91,21 +93,22 @@
 				// don't calculate checksum if algo is disabled
 				continue;
 			String algorithm = checksumVerifierConfiguration.getAttribute("algorithm"); //$NON-NLS-1$
-			Optional<String> checksum = calculateChecksum(pathOnDisk, status, id, algorithm);
+			String providerName = checksumVerifierConfiguration.getAttribute("providerName"); //$NON-NLS-1$
+			Optional<String> checksum = calculateChecksum(pathOnDisk, status, id, algorithm, providerName);
 			checksum.ifPresent(c -> checksums.put(id, c));
 		}
 
 		return status;
 	}
 
-	private static Optional<String> calculateChecksum(File pathOnDisk, MultiStatus status, String id, String algorithm) {
+	private static Optional<String> calculateChecksum(File pathOnDisk, MultiStatus status, String id, String algorithm, String providerName) {
 		try {
-			String checksum = ChecksumProducer.produce(pathOnDisk, algorithm);
-			String message = NLS.bind(Messages.calculateChecksum_ok, new Object[] {id, algorithm, checksum});
+			String checksum = ChecksumProducer.produce(pathOnDisk, algorithm, providerName);
+			String message = NLS.bind(Messages.calculateChecksum_ok, new Object[] {id, algorithm, providerName, checksum});
 			status.add(new Status(IStatus.OK, Activator.ID, message));
 			return Optional.of(checksum);
-		} catch (NoSuchAlgorithmException e) {
-			String message = NLS.bind(Messages.calculateChecksum_error, id, algorithm);
+		} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
+			String message = NLS.bind(Messages.calculateChecksum_providerError, new Object[] {id, algorithm, providerName});
 			status.add(new Status(IStatus.ERROR, Activator.ID, message, e));
 		} catch (IOException e) {
 			String message = NLS.bind(Messages.calculateChecksum_error, id, algorithm);
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java
index 34df102..4d913c4 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/processors/checksum/ChecksumVerifier.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2018 Mykola Nikishov.
+ * Copyright (c) 2015, 2019 Mykola Nikishov.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -15,11 +15,12 @@
 
 import static java.util.Optional.ofNullable;
 
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.equinox.internal.p2.artifact.repository.Activator;
+import org.eclipse.equinox.internal.p2.repository.helpers.ChecksumProducer;
 import org.eclipse.equinox.p2.core.IProvisioningAgent;
 import org.eclipse.equinox.p2.core.ProvisionException;
 import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor;
@@ -30,11 +31,13 @@
 
 	private String expectedChecksum;
 	final private String algorithmName;
+	final private String providerName;
 	final private String algorithmId;
 
 	// public to access from tests
-	public ChecksumVerifier(String digestAlgorithm, String algorithmId) {
+	public ChecksumVerifier(String digestAlgorithm, String providerName, String algorithmId) {
 		this.algorithmName = digestAlgorithm;
+		this.providerName = providerName;
 		this.algorithmId = algorithmId;
 		basicInitialize(null);
 	}
@@ -56,9 +59,9 @@
 
 	private void basicInitialize(IProcessingStepDescriptor descriptor) {
 		try {
-			messageDigest = MessageDigest.getInstance(algorithmName);
+			messageDigest = ChecksumProducer.getMessageDigest(algorithmName, providerName);
 			setStatus(Status.OK_STATUS);
-		} catch (NoSuchAlgorithmException e) {
+		} catch (NoSuchProviderException | NoSuchAlgorithmException e) {
 			int code = buildErrorCode(descriptor);
 			setStatus(new Status(code, Activator.ID, NLS.bind(Messages.Error_checksum_unavailable, algorithmName), e));
 		}
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/Messages.java b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/Messages.java
index 9208be0..f4eba31 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/Messages.java
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/Messages.java
@@ -70,6 +70,7 @@
 	public static String calculateChecksum_file;
 	public static String calculateChecksum_ok;
 	public static String calculateChecksum_error;
+	public static String calculateChecksum_providerError;
 
 	static {
 		// initialize resource bundles
diff --git a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/messages.properties b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/messages.properties
index 2efde4d..fa54c3f 100644
--- a/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/messages.properties
+++ b/bundles/org.eclipse.equinox.p2.artifact.repository/src/org/eclipse/equinox/internal/p2/artifact/repository/messages.properties
@@ -55,8 +55,9 @@
 exception_unsupportedRemoveFromComposite = Cannot remove descriptors from a composite repository.
 
 calculateChecksum_file=Calculating checksums for file {0}.
-calculateChecksum_ok=Calculated checksum using id={0} algorithm={1}: {2}.
-calculateChecksum_error=Error calculating checksum using id={0} algorithm={1}.
+calculateChecksum_ok=Calculated checksum using id={0} algorithm={1} provider={2}: {3}.
+calculateChecksum_error=Error calculating checksum using id={0} algorithm={1} provider={2}.
+calculateChecksum_providerError=Checksum provider id={0} algorithm={1} provider={2} error.
 
 exception_unableToCreateParentDir = Unable to create parent directory.
 folder_artifact_not_file_repo=Artifact {0} is a folder but the repository is an archive or remote location.
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/ChecksumProducer.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/ChecksumProducer.java
index 9f16033..5e4e230 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/ChecksumProducer.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/ChecksumProducer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2009, 2018 IBM Corporation and others.
+ * Copyright (c) 2009, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -17,6 +17,9 @@
 
 import java.io.*;
 import java.security.*;
+import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
+import org.eclipse.equinox.internal.p2.repository.Activator;
+import org.eclipse.osgi.util.NLS;
 
 /**
  * Calculates a checksum using {@link java.security.MessageDigest}
@@ -34,8 +37,8 @@
 	// bug #509401 - still here to not break x-friends like in bug #507193
 	public static String computeMD5(File file) throws IOException {
 		try {
-			return produce(file, "MD5"); //$NON-NLS-1$
-		} catch (NoSuchAlgorithmException e) {
+			return produce(file, "MD5", null); //$NON-NLS-1$
+		} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
 			return null;
 		}
 	}
@@ -43,20 +46,34 @@
 	/**
 	 * @param file should not be <code>null</code>
 	 * @param algorithm {@link java.security.MessageDigest#getInstance(String)}
+	 * @param providerName {@link Provider#getName()}
 	 * @return checksum of the file
 	 * @throws IOException
 	 * @throws NoSuchAlgorithmException
+	 * @throws NoSuchProviderException
+	 * @see {@link java.security.MessageDigest#getInstance(String, Provider)}
 	 */
-	public static String produce(File file, String algorithm) throws IOException, NoSuchAlgorithmException {
-		MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
+	public static String produce(File file, String algorithm, String providerName) throws IOException, NoSuchAlgorithmException, NoSuchProviderException {
+		MessageDigest messageDigest = getMessageDigest(algorithm, providerName);
 		try (InputStream fis = new DigestInputStream(new BufferedInputStream(new FileInputStream(file)), messageDigest)) {
 			byte[] buffer = new byte[BUFFER_SIZE];
 			while (fis.read(buffer) != -1) {
 				// consume stream to update digest
 			}
+			byte[] digest = messageDigest.digest();
+			return ChecksumHelper.toHexString(digest);
 		}
-		byte[] digest = messageDigest.digest();
-		return ChecksumHelper.toHexString(digest);
+	}
+
+	public static MessageDigest getMessageDigest(String algorithm, String providerName) throws NoSuchAlgorithmException, NoSuchProviderException {
+		if (providerName == null)
+			return MessageDigest.getInstance(algorithm);
+
+		Provider provider = ServiceHelper.getService(Activator.getContext(), Provider.class, "(providerName=" + providerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+		if (provider == null)
+			throw new NoSuchProviderException(NLS.bind(Messages.noSuchProvider, providerName));
+
+		return MessageDigest.getInstance(algorithm, provider);
 	}
 
 }
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/Messages.java b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/Messages.java
index 0dcc7e3..b07ee2c 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/Messages.java
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/Messages.java
@@ -38,5 +38,5 @@
 	public static String DestinationNotModifiable;
 	public static String locationMustBeAbsolute;
 	public static String schemeNotSupported;
-
+	public static String noSuchProvider;
 }
diff --git a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/messages.properties b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/messages.properties
index 03fb402..7261cda 100644
--- a/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/messages.properties
+++ b/bundles/org.eclipse.equinox.p2.repository/src/org/eclipse/equinox/internal/p2/repository/helpers/messages.properties
@@ -22,3 +22,4 @@
 DestinationNotModifiable=Destination repository is not modifiable: {0}
 locationMustBeAbsolute=Location must be absolute
 schemeNotSupported=Scheme not supported
+noSuchProvider=No such provider: {0}
diff --git a/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF
index d2533da..edddbe6 100644
--- a/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.p2.tests/META-INF/MANIFEST.MF
@@ -54,7 +54,8 @@
  org.eclipse.equinox.p2.publisher.eclipse;bundle-version="1.0.0",
  org.eclipse.equinox.p2.operations;bundle-version="2.1.0",
  org.eclipse.equinox.p2.ui.sdk.scheduler,
- org.eclipse.equinox.p2.artifact.repository;bundle-version="[1.3.0,2.0.0)"
+ org.eclipse.equinox.p2.artifact.repository;bundle-version="[1.3.0,2.0.0)",
+ org.eclipse.equinox.p2.artifact.checksums.bouncycastle;bundle-version="[1.0.0,2.0.0)"
 Eclipse-RegisterBuddy: org.eclipse.equinox.p2.artifact.repository
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Eclipse-BundleShape: dir
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/ChecksumUtilitiesTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/ChecksumUtilitiesTest.java
index 9b2ba10..9f95fbd 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/ChecksumUtilitiesTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/ChecksumUtilitiesTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- *  Copyright (c) 2018 Mykola Nikishov
+ *  Copyright (c) 2018, 2019 Mykola Nikishov
  *
  *  This program and the accompanying materials
  *  are made available under the terms of the Eclipse Public License 2.0
@@ -54,9 +54,13 @@
 		return asList(new Object[][] {{IArtifactDescriptor.ARTIFACT_CHECKSUM, IArtifactDescriptor.ARTIFACT_MD5, "123456789_123456789_123456789_12", "MD5", "md5"},
 			{IArtifactDescriptor.ARTIFACT_CHECKSUM, IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".md5"), "123456789_123456789_123456789_12", "MD5", "md5"},
 			{IArtifactDescriptor.ARTIFACT_CHECKSUM, IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".sha-256"), "123456789_123456789_123456789_123456789_123456789_123456789_1234", "SHA-256", "sha-256"},
+			{IArtifactDescriptor.ARTIFACT_CHECKSUM, IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".dstu7564-512"), "123456789_123456789_123456789_123456789_123456789_123456789_1234", "DSTU7564-512", "dstu7564-512"},
+			{IArtifactDescriptor.ARTIFACT_CHECKSUM, IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".whirlpool"), "123456789_123456789_123456789_123456789_123456789_123456789_1234", "Whirlpool", "whirlpool"},
 			{IArtifactDescriptor.DOWNLOAD_CHECKSUM, IArtifactDescriptor.DOWNLOAD_MD5, "123456789_123456789_123456789_12", "MD5", "md5"},
 			{IArtifactDescriptor.DOWNLOAD_CHECKSUM, IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".md5"), "123456789_123456789_123456789_12", "MD5", "md5"},
-			{IArtifactDescriptor.DOWNLOAD_CHECKSUM, IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".sha-256"), "123456789_123456789_123456789_123456789_123456789_123456789_1234", "SHA-256", "sha-256"}});
+			{IArtifactDescriptor.DOWNLOAD_CHECKSUM, IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".sha-256"), "123456789_123456789_123456789_123456789_123456789_123456789_1234", "SHA-256", "sha-256"},
+			{IArtifactDescriptor.DOWNLOAD_CHECKSUM, IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".dstu7564-512"), "123456789_123456789_123456789_123456789_123456789_123456789_1234", "DSTU7564-512", "dstu7564-512"},
+			{IArtifactDescriptor.DOWNLOAD_CHECKSUM, IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".whirlpool"), "123456789_123456789_123456789_123456789_123456789_123456789_1234", "Whirlpool", "whirlpool"}});
 	}
 
 	private ArtifactDescriptor artifactDescriptor;
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/ChecksumVerifierTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/ChecksumVerifierTest.java
index 3c7fe79..fb01289 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/ChecksumVerifierTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/artifact/processors/ChecksumVerifierTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2018 Mykola Nikishov.
+ * Copyright (c) 2015, 2019 Mykola Nikishov.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -13,7 +13,12 @@
  *******************************************************************************/
 package org.eclipse.equinox.p2.tests.artifact.processors;
 
-import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.not;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -35,19 +40,26 @@
 	@Parameters
 	public static Collection<Object[]> generateData() {
 		return Arrays.asList(new Object[][] {
-			{"MD5", "md5", IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".md5"), IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".md5"), "123456789_123456789_123456789_12"},
-			{"SHA-256", "sha-256", IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".sha-256"), IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".sha-256"), "123456789_123456789_123456789_123456789_123456789_123456789_1234"}});
+			// legacy MD5 checksum location
+			{"MD5", null, "md5", IArtifactDescriptor.DOWNLOAD_MD5, IArtifactDescriptor.ARTIFACT_MD5, "123456789_123456789_123456789_12"},
+			// new checksum location
+			{"MD5", null, "md5", IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".md5"), IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".md5"), "123456789_123456789_123456789_12"},
+			{"SHA-256", null, "sha-256", IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".sha-256"), IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".sha-256"), "123456789_123456789_123456789_123456789_123456789_123456789_1234"},
+			{"Whirlpool", "BC", "whirlpool", IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".whirlpool"), IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".whirlpool"), "f3073bf4b0867c7456850fbe317b322c03b00198e15fe40b9a455abde6e1c77e31d6ed6963a6755564a1adec0ed9bb8aac71d4a457256a85e9fc55a964ede598"},
+			{"DSTU7564-512", "BC", "dstu7564-512", IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".dstu7564-512"), IArtifactDescriptor.ARTIFACT_CHECKSUM.concat(".dstu7564-512"), "b776aaeae5c45826515365fe017138eb6ac1e1ad866f7b7bcfba2ca752268afc493e3c19a9217e1ae07733676efb81123e5677dcadaf5c0ca1b530ab9f718b2c"}});
 	}
 
 	@Parameter(0)
 	public String digestAlgorithm;
 	@Parameter(1)
-	public String algorithmId;
+	public String providerName;
 	@Parameter(2)
-	public String downloadProperty;
+	public String algorithmId;
 	@Parameter(3)
-	public String artifactProperty;
+	public String downloadProperty;
 	@Parameter(4)
+	public String artifactProperty;
+	@Parameter(5)
 	public String checksum;
 
 	@Test
@@ -56,7 +68,7 @@
 		expect(processingStepDescriptor.getData()).andReturn(checksum);
 		replay(processingStepDescriptor);
 
-		ChecksumVerifier verifier = new ChecksumVerifier(digestAlgorithm, algorithmId);
+		ChecksumVerifier verifier = new ChecksumVerifier(digestAlgorithm, providerName, algorithmId);
 
 		verifier.initialize(null, processingStepDescriptor, null);
 
@@ -79,7 +91,7 @@
 		expect(artifactDescriptor.getProperties()).andReturn(properties);
 		replay(artifactDescriptor);
 
-		ChecksumVerifier verifier = new ChecksumVerifier(digestAlgorithm, algorithmId);
+		ChecksumVerifier verifier = new ChecksumVerifier(digestAlgorithm, providerName, algorithmId);
 
 		verifier.initialize(null, processingStepDescriptor, artifactDescriptor);
 
@@ -102,7 +114,7 @@
 		expect(artifactDescriptor.getProperty(not(eq(artifactProperty)))).andReturn(null).times(1, 2);
 		replay(artifactDescriptor);
 
-		ChecksumVerifier verifier = new ChecksumVerifier(digestAlgorithm, algorithmId);
+		ChecksumVerifier verifier = new ChecksumVerifier(digestAlgorithm, providerName, algorithmId);
 
 		verifier.initialize(null, processingStepDescriptor, artifactDescriptor);
 
diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/ChecksumGenerationTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/ChecksumGenerationTest.java
index 08ff108..8b9d303 100644
--- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/ChecksumGenerationTest.java
+++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/publisher/ChecksumGenerationTest.java
@@ -37,7 +37,12 @@
 
 	@Parameters
 	public static Collection<Object[]> generateChecksums() {
-		return Arrays.asList(new Object[][] {{IArtifactDescriptor.DOWNLOAD_MD5, "50d4ea58b02706ab373a908338877e02"}, {IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".sha-256"), "11da2dd636ab76f460513cbcbfe8c56a6e5ad47aa9b38b36c6d04f8ee7722252"}});
+		return Arrays.asList(new Object[][] {{IArtifactDescriptor.DOWNLOAD_MD5, "50d4ea58b02706ab373a908338877e02"},
+			{IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".md5"), "50d4ea58b02706ab373a908338877e02"},
+			{IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".sha-256"), "11da2dd636ab76f460513cbcbfe8c56a6e5ad47aa9b38b36c6d04f8ee7722252"},
+			// TODO values are from the BC itself, check by external means
+			{IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".dstu7564-512"), "b776aaeae5c45826515365fe017138eb6ac1e1ad866f7b7bcfba2ca752268afc493e3c19a9217e1ae07733676efb81123e5677dcadaf5c0ca1b530ab9f718b2c"},
+			{IArtifactDescriptor.DOWNLOAD_CHECKSUM.concat(".whirlpool"), "b951e497a53600173a0c464d451672086175530deaffb9f376962153e573e99de53131910ea9c3c8243afb73444dadfade5989dc6f8c6e4a96141eaf956daa22"}});
 	}
 
 	@Test
diff --git a/org.eclipse.equinox.p2.releng/default.target b/org.eclipse.equinox.p2.releng/default.target
index 37cd62a..b2a3993 100644
--- a/org.eclipse.equinox.p2.releng/default.target
+++ b/org.eclipse.equinox.p2.releng/default.target
@@ -6,6 +6,10 @@
 <unit id="org.eclipse.test.feature.group" version="0.0.0"/>
 <repository location="https://download.eclipse.org/eclipse/updates/4.13-I-builds"/>
 </location>
+<location includeAllPlatforms="false" includeConfigurePhase="false" includeMode="planner" includeSource="true" type="InstallableUnit">
+<unit id="org.bouncycastle.bcprov" version="0.0.0"/>
+<repository location="https://download.eclipse.org/tools/orbit/downloads/latest-R"/>
+</location>
 </locations>
 <targetJRE path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 </target>
diff --git a/pom.xml b/pom.xml
index 3759516..48d8748 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
-  Copyright (c) 2012 Eclipse Foundation.
+  Copyright (c) 2012, 2019 Eclipse Foundation.
   All rights reserved. This program and the accompanying materials
   are made available under the terms of the Eclipse Distribution License v1.0
   which accompanies this distribution, and is available at
@@ -166,6 +166,8 @@
     <module>bundles/org.eclipse.equinox.p2.tests.ui</module>
     <module>bundles/org.eclipse.equinox.p2.tests.verifier</module>
 
+    <module>bundles/org.eclipse.equinox.p2.artifact.checksums.bouncycastle</module>
+
     <module>examples</module>
   </modules>