You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2022/01/27 20:43:17 UTC

[maven-resolver] branch master updated: [MRESOLVER-230] Make supported checksum algorithms extensible

This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/master by this push:
     new 375a290  [MRESOLVER-230] Make supported checksum algorithms extensible
375a290 is described below

commit 375a2909b86248cc6c1ebf747149ac123df16b9f
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Tue Dec 14 15:25:09 2021 +0100

    [MRESOLVER-230] Make supported checksum algorithms extensible
    
    This closes #139
---
 .../connector/basic/BasicRepositoryConnector.java  | 157 ++++++++++++---------
 .../aether/connector/basic/ChecksumCalculator.java | 102 +++++--------
 .../aether/connector/basic/ChecksumValidator.java  |  67 +++++----
 .../connector/basic/ChecksumCalculatorTest.java    |  33 ++---
 .../connector/basic/ChecksumValidatorTest.java     |  39 ++---
 .../basic/TestChecksumAlgorithmSelector.java       | 124 ++++++++++++++++
 .../eclipse/aether/impl/DefaultServiceLocator.java |   3 +
 .../eclipse/aether/impl/guice/AetherModule.java    | 106 +++++++++-----
 .../internal/impl/AbstractChecksumPolicy.java      |  17 ++-
 .../aether/internal/impl/DefaultFileProcessor.java |  28 ++--
 .../aether/internal/impl/FailChecksumPolicy.java   |   1 +
 .../impl/Maven2RepositoryLayoutFactory.java        |  89 +++++++++---
 .../aether/internal/impl/WarnChecksumPolicy.java   |   1 +
 .../DefaultChecksumAlgorithmFactorySelector.java   |  87 ++++++++++++
 .../Md5ChecksumAlgorithmFactory.java}              |  33 ++---
 ...ssageDigestChecksumAlgorithmFactorySupport.java |  71 ++++++++++
 .../Sha1ChecksumAlgorithmFactory.java}             |  33 ++---
 .../Sha256ChecksumAlgorithmFactory.java}           |  33 ++---
 .../Sha512ChecksumAlgorithmFactory.java}           |  33 ++---
 .../impl/Maven2RepositoryLayoutFactoryTest.java    | 114 ++++++++++-----
 .../spi/connector/checksum/ChecksumAlgorithm.java  |  46 ++++++
 .../checksum/ChecksumAlgorithmFactory.java         |  48 +++++++
 .../checksum/ChecksumAlgorithmFactorySelector.java |  44 ++++++
 .../checksum/ChecksumAlgorithmFactorySupport.java  |  59 ++++++++
 .../checksum/ChecksumAlgorithmHelper.java          | 103 ++++++++++++++
 .../spi/connector/checksum/ChecksumPolicy.java     |  55 ++++----
 .../spi/connector/layout/RepositoryLayout.java     | 118 ++++++++--------
 .../connector/transport/AbstractTransporter.java   |  12 +-
 .../org/eclipse/aether/spi/io/FileProcessor.java   |  16 +++
 .../spi/connector/layout/ChecksumLocationTest.java |  95 +++++++++++++
 .../aether/spi/connector/layout/ChecksumTest.java  |  65 ---------
 .../internal/test/util/TestFileProcessor.java      |  12 ++
 .../org/eclipse/aether/util/ChecksumUtils.java     |  10 +-
 src/site/markdown/about-checksums.md               |  73 ++++++++++
 src/site/markdown/configuration.md                 |   2 +-
 src/site/site.xml                                  |   1 +
 36 files changed, 1397 insertions(+), 533 deletions(-)

diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
index 3fed9a1..a338c6d 100644
--- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
+++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnector.java
@@ -26,12 +26,11 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -47,6 +46,8 @@ import org.eclipse.aether.spi.connector.ArtifactUpload;
 import org.eclipse.aether.spi.connector.MetadataDownload;
 import org.eclipse.aether.spi.connector.MetadataUpload;
 import org.eclipse.aether.spi.connector.RepositoryConnector;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmHelper;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
@@ -64,7 +65,6 @@ import org.eclipse.aether.transfer.NoTransporterException;
 import org.eclipse.aether.transfer.TransferEvent;
 import org.eclipse.aether.transfer.TransferResource;
 import org.eclipse.aether.transform.FileTransformer;
-import org.eclipse.aether.util.ChecksumUtils;
 import org.eclipse.aether.util.ConfigUtils;
 import org.eclipse.aether.util.concurrency.RunnableErrorForwarder;
 import org.eclipse.aether.util.concurrency.WorkerThreadFactory;
@@ -72,9 +72,10 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
+ *
  */
 final class BasicRepositoryConnector
-    implements RepositoryConnector
+        implements RepositoryConnector
 {
 
     private static final String CONFIG_PROP_THREADS = "aether.connector.basic.threads";
@@ -111,10 +112,13 @@ final class BasicRepositoryConnector
 
     private boolean closed;
 
-    BasicRepositoryConnector( RepositorySystemSession session, RemoteRepository repository,
-                                     TransporterProvider transporterProvider, RepositoryLayoutProvider layoutProvider,
-                                     ChecksumPolicyProvider checksumPolicyProvider, FileProcessor fileProcessor )
-        throws NoRepositoryConnectorException
+    BasicRepositoryConnector( RepositorySystemSession session,
+                              RemoteRepository repository,
+                              TransporterProvider transporterProvider,
+                              RepositoryLayoutProvider layoutProvider,
+                              ChecksumPolicyProvider checksumPolicyProvider,
+                              FileProcessor fileProcessor )
+            throws NoRepositoryConnectorException
     {
         try
         {
@@ -141,18 +145,19 @@ final class BasicRepositoryConnector
         maxThreads = ConfigUtils.getInteger( session, 5, CONFIG_PROP_THREADS, "maven.artifact.threads" );
         smartChecksums = ConfigUtils.getBoolean( session, true, CONFIG_PROP_SMART_CHECKSUMS );
         persistedChecksums =
-            ConfigUtils.getBoolean( session, ConfigurationProperties.DEFAULT_PERSISTED_CHECKSUMS,
-                                    ConfigurationProperties.PERSISTED_CHECKSUMS );
+                ConfigUtils.getBoolean( session, ConfigurationProperties.DEFAULT_PERSISTED_CHECKSUMS,
+                        ConfigurationProperties.PERSISTED_CHECKSUMS );
 
         boolean resumeDownloads =
-            ConfigUtils.getBoolean( session, true, CONFIG_PROP_RESUME + '.' + repository.getId(), CONFIG_PROP_RESUME );
+                ConfigUtils.getBoolean( session, true, CONFIG_PROP_RESUME + '.' + repository.getId(),
+                        CONFIG_PROP_RESUME );
         long resumeThreshold =
-            ConfigUtils.getLong( session, 64 * 1024, CONFIG_PROP_RESUME_THRESHOLD + '.' + repository.getId(),
-                                 CONFIG_PROP_RESUME_THRESHOLD );
+                ConfigUtils.getLong( session, 64 * 1024, CONFIG_PROP_RESUME_THRESHOLD + '.' + repository.getId(),
+                        CONFIG_PROP_RESUME_THRESHOLD );
         int requestTimeout =
-            ConfigUtils.getInteger( session, ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT,
-                                    ConfigurationProperties.REQUEST_TIMEOUT + '.' + repository.getId(),
-                                    ConfigurationProperties.REQUEST_TIMEOUT );
+                ConfigUtils.getInteger( session, ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT,
+                        ConfigurationProperties.REQUEST_TIMEOUT + '.' + repository.getId(),
+                        ConfigurationProperties.REQUEST_TIMEOUT );
         partialFileFactory = new PartialFile.Factory( resumeDownloads, resumeThreshold, requestTimeout );
     }
 
@@ -170,17 +175,17 @@ final class BasicRepositoryConnector
         if ( executor == null )
         {
             executor =
-                new ThreadPoolExecutor( maxThreads, maxThreads, 3L, TimeUnit.SECONDS,
-                                        new LinkedBlockingQueue<Runnable>(),
-                                        new WorkerThreadFactory( getClass().getSimpleName() + '-'
-                                            + repository.getHost() + '-' ) );
+                    new ThreadPoolExecutor( maxThreads, maxThreads, 3L, TimeUnit.SECONDS,
+                            new LinkedBlockingQueue<>(),
+                            new WorkerThreadFactory( getClass().getSimpleName() + '-'
+                                    + repository.getHost() + '-' ) );
         }
         return executor;
     }
 
     @Override
     protected void finalize()
-        throws Throwable
+            throws Throwable
     {
         try
         {
@@ -192,6 +197,7 @@ final class BasicRepositoryConnector
         }
     }
 
+    @Override
     public void close()
     {
         if ( !closed )
@@ -205,6 +211,7 @@ final class BasicRepositoryConnector
         }
     }
 
+    @Override
     public void get( Collection<? extends ArtifactDownload> artifactDownloads,
                      Collection<? extends MetadataDownload> metadataDownloads )
     {
@@ -225,13 +232,14 @@ final class BasicRepositoryConnector
             MetadataTransportListener listener = new MetadataTransportListener( transfer, repository, builder );
 
             ChecksumPolicy checksumPolicy = newChecksumPolicy( transfer.getChecksumPolicy(), resource );
-            List<RepositoryLayout.Checksum> checksums = null;
+            List<RepositoryLayout.ChecksumLocation> checksumLocations = null;
             if ( checksumPolicy != null )
             {
-                checksums = layout.getChecksums( transfer.getMetadata(), false, location );
+                checksumLocations = layout.getChecksumLocations( transfer.getMetadata(), false, location );
             }
 
-            Runnable task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy, checksums, listener );
+            Runnable task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy,
+                    checksumLocations, listener );
             executor.execute( errorForwarder.wrap( task ) );
         }
 
@@ -251,13 +259,13 @@ final class BasicRepositoryConnector
             else
             {
                 ChecksumPolicy checksumPolicy = newChecksumPolicy( transfer.getChecksumPolicy(), resource );
-                List<RepositoryLayout.Checksum> checksums = null;
+                List<RepositoryLayout.ChecksumLocation> checksumLocations = null;
                 if ( checksumPolicy != null )
                 {
-                    checksums = layout.getChecksums( transfer.getArtifact(), false, location );
+                    checksumLocations = layout.getChecksumLocations( transfer.getArtifact(), false, location );
                 }
 
-                task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy, checksums, listener );
+                task = new GetTaskRunner( location, transfer.getFile(), checksumPolicy, checksumLocations, listener );
             }
             executor.execute( errorForwarder.wrap( task ) );
         }
@@ -265,6 +273,7 @@ final class BasicRepositoryConnector
         errorForwarder.await();
     }
 
+    @Override
     public void put( Collection<? extends ArtifactUpload> artifactUploads,
                      Collection<? extends MetadataUpload> metadataUploads )
     {
@@ -281,10 +290,11 @@ final class BasicRepositoryConnector
             TransferEvent.Builder builder = newEventBuilder( resource, true, false );
             ArtifactTransportListener listener = new ArtifactTransportListener( transfer, repository, builder );
 
-            List<RepositoryLayout.Checksum> checksums = layout.getChecksums( transfer.getArtifact(), true, location );
+            List<RepositoryLayout.ChecksumLocation> checksumLocations =
+                    layout.getChecksumLocations( transfer.getArtifact(), true, location );
 
-            Runnable task = new PutTaskRunner( location, transfer.getFile(), transfer.getFileTransformer(), checksums,
-                    listener );
+            Runnable task = new PutTaskRunner( location, transfer.getFile(), transfer.getFileTransformer(),
+                    checksumLocations, listener );
             task.run();
         }
 
@@ -296,16 +306,17 @@ final class BasicRepositoryConnector
             TransferEvent.Builder builder = newEventBuilder( resource, true, false );
             MetadataTransportListener listener = new MetadataTransportListener( transfer, repository, builder );
 
-            List<RepositoryLayout.Checksum> checksums = layout.getChecksums( transfer.getMetadata(), true, location );
+            List<RepositoryLayout.ChecksumLocation> checksumLocations =
+                    layout.getChecksumLocations( transfer.getMetadata(), true, location );
 
-            Runnable task = new PutTaskRunner( location, transfer.getFile(), checksums, listener );
+            Runnable task = new PutTaskRunner( location, transfer.getFile(), checksumLocations, listener );
             task.run();
         }
     }
 
     private static <T> Collection<T> safe( Collection<T> items )
     {
-        return ( items != null ) ? items : Collections.<T>emptyList();
+        return ( items != null ) ? items : Collections.emptyList();
     }
 
     private TransferResource newTransferResource( URI path, File file, RequestTrace trace )
@@ -343,7 +354,7 @@ final class BasicRepositoryConnector
     }
 
     abstract class TaskRunner
-        implements Runnable
+            implements Runnable
     {
 
         protected final URI path;
@@ -356,6 +367,7 @@ final class BasicRepositoryConnector
             this.listener = listener;
         }
 
+        @Override
         public void run()
         {
             try
@@ -371,12 +383,12 @@ final class BasicRepositoryConnector
         }
 
         protected abstract void runTask()
-            throws Exception;
+                throws Exception;
 
     }
 
     class PeekTaskRunner
-        extends TaskRunner
+            extends TaskRunner
     {
 
         PeekTaskRunner( URI path, TransferTransportListener<?> listener )
@@ -384,8 +396,9 @@ final class BasicRepositoryConnector
             super( path, listener );
         }
 
+        @Override
         protected void runTask()
-            throws Exception
+                throws Exception
         {
             transporter.peek( new PeekTask( path ) );
         }
@@ -393,8 +406,8 @@ final class BasicRepositoryConnector
     }
 
     class GetTaskRunner
-        extends TaskRunner
-        implements PartialFile.RemoteAccessChecker, ChecksumValidator.ChecksumFetcher
+            extends TaskRunner
+            implements PartialFile.RemoteAccessChecker, ChecksumValidator.ChecksumFetcher
     {
 
         private final File file;
@@ -402,22 +415,25 @@ final class BasicRepositoryConnector
         private final ChecksumValidator checksumValidator;
 
         GetTaskRunner( URI path, File file, ChecksumPolicy checksumPolicy,
-                              List<RepositoryLayout.Checksum> checksums, TransferTransportListener<?> listener )
+                       List<RepositoryLayout.ChecksumLocation> checksumLocations,
+                       TransferTransportListener<?> listener )
         {
             super( path, listener );
             this.file = requireNonNull( file, "destination file cannot be null" );
-            checksumValidator =
-                new ChecksumValidator( file, fileProcessor, this, checksumPolicy, safe( checksums ) );
+            checksumValidator = new ChecksumValidator( file, fileProcessor, this,
+                    checksumPolicy, safe( checksumLocations ) );
         }
 
+        @Override
         public void checkRemoteAccess()
-            throws Exception
+                throws Exception
         {
             transporter.peek( new PeekTask( path ) );
         }
 
+        @Override
         public boolean fetchChecksum( URI remote, File local )
-            throws Exception
+                throws Exception
         {
             try
             {
@@ -434,8 +450,9 @@ final class BasicRepositoryConnector
             return true;
         }
 
+        @Override
         protected void runTask()
-            throws Exception
+                throws Exception
         {
             fileProcessor.mkdirs( file.getParentFile() );
 
@@ -450,7 +467,7 @@ final class BasicRepositoryConnector
             {
                 File tmp = partFile.getFile();
                 listener.setChecksumCalculator( checksumValidator.newChecksumCalculator( tmp ) );
-                for ( int firstTrial = 0, lastTrial = 1, trial = firstTrial;; trial++ )
+                for ( int firstTrial = 0, lastTrial = 1, trial = firstTrial; ; trial++ )
                 {
                     boolean resume = partFile.isResume() && trial <= firstTrial;
                     GetTask task = new GetTask( path ).setDataFile( tmp, resume ).setListener( listener );
@@ -458,7 +475,7 @@ final class BasicRepositoryConnector
                     try
                     {
                         checksumValidator.validate( listener.getChecksums(), smartChecksums ? task.getChecksums()
-                                        : null );
+                                : null );
                         break;
                     }
                     catch ( ChecksumFailureException e )
@@ -495,19 +512,19 @@ final class BasicRepositoryConnector
     }
 
     class PutTaskRunner
-        extends TaskRunner
+            extends TaskRunner
     {
 
         private final File file;
 
         private final FileTransformer fileTransformer;
 
-        private final Collection<RepositoryLayout.Checksum> checksums;
+        private final Collection<RepositoryLayout.ChecksumLocation> checksumLocations;
 
-        PutTaskRunner( URI path, File file, List<RepositoryLayout.Checksum> checksums,
+        PutTaskRunner( URI path, File file, List<RepositoryLayout.ChecksumLocation> checksumLocations,
                        TransferTransportListener<?> listener )
         {
-            this( path, file, null, checksums, listener );
+            this( path, file, null, checksumLocations, listener );
         }
 
         /**
@@ -517,21 +534,23 @@ final class BasicRepositoryConnector
          * @param path
          * @param file
          * @param fileTransformer
-         * @param checksums
+         * @param checksumLocations
          * @param listener
          */
-        PutTaskRunner( URI path, File file, FileTransformer fileTransformer, List<RepositoryLayout.Checksum> checksums,
-                              TransferTransportListener<?> listener )
+        PutTaskRunner( URI path, File file, FileTransformer fileTransformer,
+                       List<RepositoryLayout.ChecksumLocation> checksumLocations,
+                       TransferTransportListener<?> listener )
         {
             super( path, listener );
             this.file = requireNonNull( file, "source file cannot be null" );
             this.fileTransformer = fileTransformer;
-            this.checksums = safe( checksums );
+            this.checksumLocations = safe( checksumLocations );
         }
 
         @SuppressWarnings( "checkstyle:innerassignment" )
+        @Override
         protected void runTask()
-            throws Exception
+                throws Exception
         {
             if ( fileTransformer != null )
             {
@@ -559,37 +578,37 @@ final class BasicRepositoryConnector
         }
 
         /**
-         *
-         * @param file source
+         * @param file  source
          * @param bytes transformed data from file or {@code null}
          */
         private void uploadChecksums( File file, byte[] bytes )
         {
-            if ( checksums.isEmpty() )
+            if ( checksumLocations.isEmpty() )
             {
                 return;
             }
             try
             {
-                Set<String> algos = new HashSet<>();
-                for ( RepositoryLayout.Checksum checksum : checksums )
+                ArrayList<ChecksumAlgorithmFactory> algorithms = new ArrayList<>();
+                for ( RepositoryLayout.ChecksumLocation checksumLocation : checksumLocations )
                 {
-                    algos.add( checksum.getAlgorithm() );
+                    algorithms.add( checksumLocation.getChecksumAlgorithmFactory() );
                 }
 
-                Map<String, Object> sumsByAlgo;
+                Map<String, String> sumsByAlgo;
                 if ( bytes != null )
                 {
-                    sumsByAlgo = ChecksumUtils.calc( bytes, algos );
+                    sumsByAlgo = ChecksumAlgorithmHelper.calculate( bytes, algorithms );
                 }
                 else
                 {
-                    sumsByAlgo = ChecksumUtils.calc( file, algos );
+                    sumsByAlgo = ChecksumAlgorithmHelper.calculate( file, algorithms );
                 }
 
-                for ( RepositoryLayout.Checksum checksum : checksums )
+                for ( RepositoryLayout.ChecksumLocation checksumLocation : checksumLocations )
                 {
-                    uploadChecksum( checksum.getLocation(), sumsByAlgo.get( checksum.getAlgorithm() ) );
+                    uploadChecksum( checksumLocation.getLocation(),
+                            sumsByAlgo.get( checksumLocation.getChecksumAlgorithmFactory().getName() ) );
                 }
             }
             catch ( IOException e )
@@ -617,16 +636,16 @@ final class BasicRepositoryConnector
     }
 
     private static class DirectExecutor
-        implements Executor
+            implements Executor
     {
 
         static final Executor INSTANCE = new DirectExecutor();
 
+        @Override
         public void execute( Runnable command )
         {
             command.run();
         }
 
     }
-
 }
diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java
index 0173fbb..87eeb78 100644
--- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java
+++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumCalculator.java
@@ -19,14 +19,13 @@ package org.eclipse.aether.connector.basic;
  * under the License.
  */
 
+import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -35,8 +34,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
-import org.eclipse.aether.util.ChecksumUtils;
+
+import static java.util.Objects.requireNonNull;
 
 /**
  * Calculates checksums for a downloaded file.
@@ -46,50 +48,32 @@ final class ChecksumCalculator
 
     static class Checksum
     {
-        final String algorithm;
+        final ChecksumAlgorithmFactory checksumAlgorithmFactory;
 
-        final MessageDigest digest;
+        ChecksumAlgorithm algorithm;
 
         Exception error;
 
-        Checksum( String algorithm )
+        Checksum( ChecksumAlgorithmFactory checksumAlgorithmFactory )
         {
-            this.algorithm = algorithm;
-            MessageDigest digest = null;
-            try
-            {
-                digest = MessageDigest.getInstance( algorithm );
-            }
-            catch ( NoSuchAlgorithmException e )
-            {
-                error = e;
-            }
-            this.digest = digest;
+            this.checksumAlgorithmFactory = requireNonNull( checksumAlgorithmFactory );
+            this.algorithm = checksumAlgorithmFactory.getAlgorithm();
         }
 
-        public void update( ByteBuffer buffer )
+        public void reset()
         {
-            if ( digest != null )
-            {
-                digest.update( buffer );
-            }
+            this.algorithm = checksumAlgorithmFactory.getAlgorithm();
+            this.error = null;
         }
 
-        public void reset()
+        public void update( ByteBuffer buffer )
         {
-            if ( digest != null )
-            {
-                digest.reset();
-                error = null;
-            }
+            this.algorithm.update( buffer );
         }
 
         public void error( Exception error )
         {
-            if ( digest != null )
-            {
-                this.error = error;
-            }
+            this.error = error;
         }
 
         public Object get()
@@ -98,7 +82,7 @@ final class ChecksumCalculator
             {
                 return error;
             }
-            return ChecksumUtils.toHexString( digest.digest() );
+            return algorithm.checksum();
         }
 
     }
@@ -107,25 +91,26 @@ final class ChecksumCalculator
 
     private final File targetFile;
 
-    public static ChecksumCalculator newInstance( File targetFile, Collection<RepositoryLayout.Checksum> checksums )
+    public static ChecksumCalculator newInstance( File targetFile,
+                                                  Collection<RepositoryLayout.ChecksumLocation> checksumLocations )
     {
-        if ( checksums == null || checksums.isEmpty() )
+        if ( checksumLocations == null || checksumLocations.isEmpty() )
         {
             return null;
         }
-        return new ChecksumCalculator( targetFile, checksums );
+        return new ChecksumCalculator( targetFile, checksumLocations );
     }
 
-    private ChecksumCalculator( File targetFile, Collection<RepositoryLayout.Checksum> checksums )
+    private ChecksumCalculator( File targetFile,
+                                Collection<RepositoryLayout.ChecksumLocation> checksumLocations )
     {
         this.checksums = new ArrayList<>();
         Set<String> algos = new HashSet<>();
-        for ( RepositoryLayout.Checksum checksum : checksums )
+        for ( RepositoryLayout.ChecksumLocation checksumLocation : checksumLocations )
         {
-            String algo = checksum.getAlgorithm();
-            if ( algos.add( algo ) )
+            if ( algos.add( checksumLocation.getChecksumAlgorithmFactory().getName() ) )
             {
-                this.checksums.add( new Checksum( algo ) );
+                this.checksums.add( new Checksum( checksumLocation.getChecksumAlgorithmFactory() ) );
             }
         }
         this.targetFile = targetFile;
@@ -142,32 +127,25 @@ final class ChecksumCalculator
             return;
         }
 
-        InputStream in = null;
-        try
+        try ( InputStream in = new BufferedInputStream( new FileInputStream( targetFile ) ) )
         {
-            in = new FileInputStream( targetFile );
             long total = 0;
-            ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
-            for ( byte[] array = buffer.array(); total < dataOffset; )
+            final byte[] buffer = new byte[ 1024 * 32 ];
+            for ( ; total < dataOffset; )
             {
-                int read = in.read( array );
+                int read = in.read( buffer );
                 if ( read < 0 )
                 {
                     throw new IOException( targetFile + " contains only " + total
-                                               + " bytes, cannot resume download from offset " + dataOffset );
+                            + " bytes, cannot resume download from offset " + dataOffset );
                 }
                 total += read;
                 if ( total > dataOffset )
                 {
                     read -= total - dataOffset;
                 }
-                ( (Buffer) buffer ).rewind();
-                ( (Buffer) buffer ).limit( read );
-                update( buffer );
+                update( ByteBuffer.wrap( buffer, 0, read ) );
             }
-
-            in.close();
-            in = null;
         }
         catch ( IOException e )
         {
@@ -176,20 +154,6 @@ final class ChecksumCalculator
                 checksum.error( e );
             }
         }
-        finally
-        {
-            try
-            {
-                if ( in != null )
-                {
-                    in.close();
-                }
-            }
-            catch ( IOException e )
-            {
-                // Suppressed due to an exception already thrown in the try block.
-            }
-        }
     }
 
     public void update( ByteBuffer data )
@@ -207,7 +171,7 @@ final class ChecksumCalculator
         Map<String, Object> results = new HashMap<>();
         for ( Checksum checksum : checksums )
         {
-            results.put( checksum.algorithm, checksum.get() );
+            results.put( checksum.checksumAlgorithmFactory.getName(), checksum.get() );
         }
         return results;
     }
diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java
index 5a77bf7..2e4e3b7 100644
--- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java
+++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/ChecksumValidator.java
@@ -25,15 +25,14 @@ import java.net.URI;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Locale;
 import java.util.Map;
 import java.util.UUID;
 
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
-import org.eclipse.aether.spi.connector.layout.RepositoryLayout.Checksum;
+import org.eclipse.aether.spi.connector.layout.RepositoryLayout.ChecksumLocation;
 import org.eclipse.aether.spi.io.FileProcessor;
 import org.eclipse.aether.transfer.ChecksumFailureException;
-import org.eclipse.aether.util.ChecksumUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -63,28 +62,30 @@ final class ChecksumValidator
 
     private final ChecksumPolicy checksumPolicy;
 
-    private final Collection<Checksum> checksums;
+    private final Collection<ChecksumLocation> checksumLocations;
 
     private final Map<File, Object> checksumFiles;
 
-    ChecksumValidator( File dataFile, FileProcessor fileProcessor,
-                              ChecksumFetcher checksumFetcher, ChecksumPolicy checksumPolicy,
-                              Collection<Checksum> checksums )
+    ChecksumValidator( File dataFile,
+                       FileProcessor fileProcessor,
+                       ChecksumFetcher checksumFetcher,
+                       ChecksumPolicy checksumPolicy,
+                       Collection<ChecksumLocation> checksumLocations )
     {
         this.dataFile = dataFile;
         this.tempFiles = new HashSet<>();
         this.fileProcessor = fileProcessor;
         this.checksumFetcher = checksumFetcher;
         this.checksumPolicy = checksumPolicy;
-        this.checksums = checksums;
-        checksumFiles = new HashMap<>();
+        this.checksumLocations = checksumLocations;
+        this.checksumFiles = new HashMap<>();
     }
 
     public ChecksumCalculator newChecksumCalculator( File targetFile )
     {
         if ( checksumPolicy != null )
         {
-            return ChecksumCalculator.newInstance( targetFile, checksums );
+            return ChecksumCalculator.newInstance( targetFile, checksumLocations );
         }
         return null;
     }
@@ -118,17 +119,26 @@ final class ChecksumValidator
             {
                 continue;
             }
+            ChecksumLocation checksumLocation = checksumLocations.stream()
+                    .filter( a -> a.getChecksumAlgorithmFactory().getName().equals( algo ) )
+                    .findFirst()
+                    .orElse( null );
+            if ( checksumLocation == null )
+            {
+                continue;
+            }
 
             String actual = String.valueOf( calculated );
             String expected = entry.getValue().toString();
-            checksumFiles.put( getChecksumFile( algo ), expected );
+            ChecksumAlgorithmFactory factory = checksumLocation.getChecksumAlgorithmFactory();
+            checksumFiles.put( getChecksumFile( factory ), expected );
 
             if ( !isEqualChecksum( expected, actual ) )
             {
-                checksumPolicy.onChecksumMismatch( algo, ChecksumPolicy.KIND_UNOFFICIAL,
+                checksumPolicy.onChecksumMismatch( factory.getName(), ChecksumPolicy.KIND_UNOFFICIAL,
                                                    new ChecksumFailureException( expected, actual ) );
             }
-            else if ( checksumPolicy.onChecksumMatch( algo, ChecksumPolicy.KIND_UNOFFICIAL ) )
+            else if ( checksumPolicy.onChecksumMatch( factory.getName(), ChecksumPolicy.KIND_UNOFFICIAL ) )
             {
                 return true;
             }
@@ -139,48 +149,50 @@ final class ChecksumValidator
     private boolean validateExternalChecksums( Map<String, ?> actualChecksums )
         throws ChecksumFailureException
     {
-        for ( Checksum checksum : checksums )
+        for ( ChecksumLocation checksumLocation : checksumLocations )
         {
-            String algo = checksum.getAlgorithm();
-            Object calculated = actualChecksums.get( algo );
+            ChecksumAlgorithmFactory factory = checksumLocation.getChecksumAlgorithmFactory();
+            Object calculated = actualChecksums.get( factory.getName() );
             if ( calculated instanceof Exception )
             {
-                checksumPolicy.onChecksumError( algo, 0, new ChecksumFailureException( (Exception) calculated ) );
+                checksumPolicy.onChecksumError(
+                        factory.getName(), 0, new ChecksumFailureException( (Exception) calculated ) );
                 continue;
             }
             try
             {
-                File checksumFile = getChecksumFile( checksum.getAlgorithm() );
+                File checksumFile = getChecksumFile( checksumLocation.getChecksumAlgorithmFactory() );
                 File tmp = createTempFile( checksumFile );
                 try
                 {
-                    if ( !checksumFetcher.fetchChecksum( checksum.getLocation(), tmp ) )
+                    if ( !checksumFetcher.fetchChecksum( checksumLocation.getLocation(), tmp ) )
                     {
                         continue;
                     }
                 }
                 catch ( Exception e )
                 {
-                    checksumPolicy.onChecksumError( algo, 0, new ChecksumFailureException( e ) );
+                    checksumPolicy.onChecksumError( factory.getName(), 0, new ChecksumFailureException( e ) );
                     continue;
                 }
 
                 String actual = String.valueOf( calculated );
-                String expected = ChecksumUtils.read( tmp );
+                String expected = fileProcessor.readChecksum( tmp );
                 checksumFiles.put( checksumFile, tmp );
 
                 if ( !isEqualChecksum( expected, actual ) )
                 {
-                    checksumPolicy.onChecksumMismatch( algo, 0, new ChecksumFailureException( expected, actual ) );
+                    checksumPolicy.onChecksumMismatch(
+                            factory.getName(), 0, new ChecksumFailureException( expected, actual ) );
                 }
-                else if ( checksumPolicy.onChecksumMatch( algo, 0 ) )
+                else if ( checksumPolicy.onChecksumMatch( factory.getName(), 0 ) )
                 {
                     return true;
                 }
             }
             catch ( IOException e )
             {
-                checksumPolicy.onChecksumError( algo, 0, new ChecksumFailureException( e ) );
+                checksumPolicy.onChecksumError( factory.getName(), 0, new ChecksumFailureException( e ) );
             }
         }
         return false;
@@ -191,10 +203,9 @@ final class ChecksumValidator
         return expected.equalsIgnoreCase( actual );
     }
 
-    private File getChecksumFile( String algorithm )
+    private File getChecksumFile( ChecksumAlgorithmFactory factory )
     {
-        String ext = algorithm.replace( "-", "" ).toLowerCase( Locale.ENGLISH );
-        return new File( dataFile.getPath() + '.' + ext );
+        return new File( dataFile.getPath() + '.' + factory.getFileExtension() );
     }
 
     private File createTempFile( File path )
@@ -246,7 +257,7 @@ final class ChecksumValidator
                 }
                 else
                 {
-                    fileProcessor.write( checksumFile, String.valueOf( tmp ) );
+                    fileProcessor.writeChecksum( checksumFile, String.valueOf( tmp ) );
                 }
             }
             catch ( IOException e )
diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java
index a903386..818de7c 100644
--- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java
+++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumCalculatorTest.java
@@ -19,6 +19,10 @@ package org.eclipse.aether.connector.basic;
  * under the License.
  */
 
+import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.MD5;
+import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.SHA1;
+import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.SHA256;
+import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.SHA512;
 import static org.junit.Assert.*;
 
 import java.io.File;
@@ -26,7 +30,6 @@ import java.io.IOException;
 import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
-import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -39,24 +42,18 @@ import org.junit.Test;
 public class ChecksumCalculatorTest
 {
 
-    private static final String SHA512 = "SHA-512";
-
-    private static final String SHA256 = "SHA-256";
-
-    private static final String SHA1 = "SHA-1";
-
-    private static final String MD5 = "MD5";
-
     private File file;
 
+    private final TestChecksumAlgorithmSelector selector = new TestChecksumAlgorithmSelector();
+
     private ChecksumCalculator newCalculator( String... algos )
     {
-        List<RepositoryLayout.Checksum> checksums = new ArrayList<>();
+        List<RepositoryLayout.ChecksumLocation> checksumLocations = new ArrayList<>();
         for ( String algo : algos )
         {
-            checksums.add( new RepositoryLayout.Checksum( algo, URI.create( "irrelevant" ) ) );
+            checksumLocations.add( new RepositoryLayout.ChecksumLocation( URI.create( "irrelevant" ), selector.select( algo ) ) );
         }
-        return ChecksumCalculator.newInstance( file, checksums );
+        return ChecksumCalculator.newInstance( file, checksumLocations );
     }
 
     private ByteBuffer toBuffer( String data )
@@ -116,17 +113,11 @@ public class ChecksumCalculatorTest
         assertEquals( 4, digests.size() );
     }
 
-    @Test
+    @Test( expected = IllegalArgumentException.class )
     public void testUnknownAlgorithm()
     {
-        ChecksumCalculator calculator = newCalculator( "unknown", SHA1 );
-        calculator.init( 0 );
-        calculator.update( toBuffer( "Hello World!" ) );
-        Map<String, Object> digests = calculator.get();
-        assertNotNull( digests );
-        assertEquals( "2ef7bde608ce5404e97d5f042f95f89f1c232871", digests.get( SHA1 ) );
-        assertTrue( digests.get( "unknown" ) instanceof NoSuchAlgorithmException );
-        assertEquals( 2, digests.size() );
+        // resolver now does not tolerate unknown checksums: as they may be set by user only, it is user misconfiguration
+        newCalculator( "unknown", SHA1 );
     }
 
     @Test
diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java
index c0d1467..aac3c1b 100644
--- a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java
+++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/ChecksumValidatorTest.java
@@ -19,6 +19,8 @@ package org.eclipse.aether.connector.basic;
  * under the License.
  */
 
+import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.MD5;
+import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.SHA1;
 import static org.junit.Assert.*;
 
 import java.io.File;
@@ -50,10 +52,11 @@ public class ChecksumValidatorTest
 
         boolean tolerateFailure;
 
-        private List<String> callbacks = new ArrayList<>();
+        private final ArrayList<String> callbacks = new ArrayList<>();
 
         private Object conclusion;
 
+        @Override
         public boolean onChecksumMatch( String algorithm, int kind )
         {
             callbacks.add( String.format( "match(%s, %04x)", algorithm, kind ) );
@@ -68,6 +71,7 @@ public class ChecksumValidatorTest
             return true;
         }
 
+        @Override
         public void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception )
             throws ChecksumFailureException
         {
@@ -80,11 +84,13 @@ public class ChecksumValidatorTest
             throw exception;
         }
 
+        @Override
         public void onChecksumError( String algorithm, int kind, ChecksumFailureException exception )
         {
             callbacks.add( String.format( "error(%s, %04x, %s)", algorithm, kind, exception.getCause().getMessage() ) );
         }
 
+        @Override
         public void onNoMoreChecksums()
             throws ChecksumFailureException
         {
@@ -99,11 +105,13 @@ public class ChecksumValidatorTest
             }
         }
 
+        @Override
         public void onTransferRetry()
         {
             callbacks.add( String.format( "retry()" ) );
         }
 
+        @Override
         public boolean onTransferChecksumFailure( ChecksumFailureException exception )
         {
             callbacks.add( String.format( "fail(%s)", exception.getMessage() ) );
@@ -121,12 +129,13 @@ public class ChecksumValidatorTest
         implements ChecksumValidator.ChecksumFetcher
     {
 
-        Map<URI, Object> checksums = new HashMap<>();
+        HashMap<URI, Object> checksums = new HashMap<>();
 
-        List<File> checksumFiles = new ArrayList<>();
+        ArrayList<File> checksumFiles = new ArrayList<>();
 
-        private List<URI> fetchedFiles = new ArrayList<>();
+        private final ArrayList<URI> fetchedFiles = new ArrayList<>();
 
+        @Override
         public boolean fetchChecksum( URI remote, File local )
             throws Exception
         {
@@ -167,34 +176,32 @@ public class ChecksumValidatorTest
 
     }
 
-    private static final String SHA1 = "SHA-1";
-
-    private static final String MD5 = "MD5";
-
     private StubChecksumPolicy policy;
 
     private StubChecksumFetcher fetcher;
 
     private File dataFile;
 
-    private static RepositoryLayout.Checksum newChecksum( String algo )
+    private static final TestChecksumAlgorithmSelector selector = new TestChecksumAlgorithmSelector();
+
+    private static RepositoryLayout.ChecksumLocation newChecksum( String factory )
     {
-        return RepositoryLayout.Checksum.forLocation( URI.create( "file" ), algo );
+        return RepositoryLayout.ChecksumLocation.forLocation( URI.create( "file" ), selector.select( factory ) );
     }
 
-    private List<RepositoryLayout.Checksum> newChecksums( String... algos )
+    private List<RepositoryLayout.ChecksumLocation> newChecksums( String... factories )
     {
-        List<RepositoryLayout.Checksum> checksums = new ArrayList<>();
-        for ( String algo : algos )
+        List<RepositoryLayout.ChecksumLocation> checksums = new ArrayList<>();
+        for ( String factory : factories )
         {
-            checksums.add( newChecksum( algo ) );
+            checksums.add( newChecksum( factory ) );
         }
         return checksums;
     }
 
-    private ChecksumValidator newValidator( String... algos )
+    private ChecksumValidator newValidator( String... factories )
     {
-        return new ChecksumValidator( dataFile, new TestFileProcessor(), fetcher, policy, newChecksums( algos ) );
+        return new ChecksumValidator( dataFile, new TestFileProcessor(), fetcher, policy, newChecksums( factories ) );
     }
 
     private Map<String, ?> checksums( String... algoDigestPairs )
diff --git a/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java
new file mode 100644
index 0000000..8ebddaa
--- /dev/null
+++ b/maven-resolver-connector-basic/src/test/java/org/eclipse/aether/connector/basic/TestChecksumAlgorithmSelector.java
@@ -0,0 +1,124 @@
+package org.eclipse.aether.connector.basic;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Set;
+
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySupport;
+import org.eclipse.aether.util.ChecksumUtils;
+
+/**
+ * Test implementation of {@link ChecksumAlgorithmFactorySelector}.
+ */
+public class TestChecksumAlgorithmSelector
+        implements ChecksumAlgorithmFactorySelector
+{
+    public static final String SHA512 = "SHA-512";
+
+    public static final String SHA256 = "SHA-256";
+
+    public static final String SHA1 = "SHA-1";
+
+    public static final String MD5 = "MD5";
+
+    public static final String TEST_CHECKSUM = "test";
+
+    public static final String TEST_CHECKSUM_VALUE = "01020304";
+
+    @Override
+    public Set<String> getChecksumAlgorithmNames()
+    {
+        return Collections.emptySet(); // irrelevant
+    }
+
+    @Override
+    public ChecksumAlgorithmFactory select( final String algorithm )
+    {
+        if ( TEST_CHECKSUM.equals( algorithm ) )
+        {
+            return new ChecksumAlgorithmFactorySupport( TEST_CHECKSUM, "test" )
+            {
+                @Override
+                public ChecksumAlgorithm getAlgorithm()
+                {
+                    return new ChecksumAlgorithm()
+                    {
+                        @Override
+                        public void update( final ByteBuffer input )
+                        {
+
+                        }
+
+                        @Override
+                        public String checksum()
+                        {
+                            return TEST_CHECKSUM_VALUE;
+                        }
+                    };
+                }
+            };
+        }
+        return new MessageDigestChecksumAlgorithmFactory( algorithm );
+    }
+
+    private static class MessageDigestChecksumAlgorithmFactory
+            extends ChecksumAlgorithmFactorySupport
+    {
+        public MessageDigestChecksumAlgorithmFactory( String name )
+        {
+            super( name, name.replace( "-", "" ).toLowerCase( Locale.ENGLISH ) );
+        }
+
+        @Override
+        public ChecksumAlgorithm getAlgorithm()
+        {
+            try
+            {
+                MessageDigest messageDigest = MessageDigest.getInstance( getName() );
+                return new ChecksumAlgorithm()
+                {
+                    @Override
+                    public void update( final ByteBuffer input )
+                    {
+                        messageDigest.update( input );
+                    }
+
+                    @Override
+                    public String checksum()
+                    {
+                        return ChecksumUtils.toHexString( messageDigest.digest() );
+                    }
+                };
+            }
+            catch ( NoSuchAlgorithmException e )
+            {
+                throw new IllegalArgumentException( "Algorithm '" + getName() + "' not supported." );
+            }
+        }
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
index 037f302..c17e237 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/DefaultServiceLocator.java
@@ -35,6 +35,7 @@ import org.eclipse.aether.internal.impl.DefaultArtifactResolver;
 import org.eclipse.aether.internal.impl.DefaultChecksumPolicyProvider;
 import org.eclipse.aether.internal.impl.DefaultTrackingFileManager;
 import org.eclipse.aether.internal.impl.TrackingFileManager;
+import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector;
 import org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector;
 import org.eclipse.aether.internal.impl.DefaultDeployer;
 import org.eclipse.aether.internal.impl.DefaultFileProcessor;
@@ -57,6 +58,7 @@ import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
 import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory;
 import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector;
 import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
@@ -224,6 +226,7 @@ public final class DefaultServiceLocator
         addService( LoggerFactory.class, Slf4jLoggerFactory.class );
         addService( TrackingFileManager.class, DefaultTrackingFileManager.class );
         addService( NamedLockFactorySelector.class, SimpleNamedLockFactorySelector.class );
+        addService( ChecksumAlgorithmFactorySelector.class, DefaultChecksumAlgorithmFactorySelector.class );
     }
 
     private <T> Entry<T> getEntry( Class<T> type, boolean create )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
index 9edf5f9..c324fa9 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/impl/guice/AetherModule.java
@@ -42,6 +42,11 @@ import org.eclipse.aether.impl.RepositoryConnectorProvider;
 import org.eclipse.aether.impl.RepositoryEventDispatcher;
 import org.eclipse.aether.internal.impl.DefaultTrackingFileManager;
 import org.eclipse.aether.internal.impl.TrackingFileManager;
+import org.eclipse.aether.internal.impl.checksum.Md5ChecksumAlgorithmFactory;
+import org.eclipse.aether.internal.impl.checksum.Sha1ChecksumAlgorithmFactory;
+import org.eclipse.aether.internal.impl.checksum.Sha256ChecksumAlgorithmFactory;
+import org.eclipse.aether.internal.impl.checksum.Sha512ChecksumAlgorithmFactory;
+import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector;
 import org.eclipse.aether.internal.impl.synccontext.DefaultSyncContextFactory;
 import org.eclipse.aether.internal.impl.synccontext.named.NamedLockFactorySelector;
 import org.eclipse.aether.internal.impl.synccontext.named.SimpleNamedLockFactorySelector;
@@ -78,7 +83,9 @@ import org.eclipse.aether.internal.impl.Maven2RepositoryLayoutFactory;
 import org.eclipse.aether.internal.impl.SimpleLocalRepositoryManagerFactory;
 import org.eclipse.aether.internal.impl.slf4j.Slf4jLoggerFactory;
 import org.eclipse.aether.named.providers.NoopNamedLockFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutProvider;
 import org.eclipse.aether.spi.connector.transport.TransporterProvider;
@@ -99,10 +106,10 @@ import com.google.inject.name.Names;
  * repository connector and transporter factories to access remote repositories.
  *
  * @noextend This class must not be extended by clients and will eventually be marked {@code final} without prior
- *           notice.
+ * notice.
  */
 public class AetherModule
-    extends AbstractModule
+        extends AbstractModule
 {
 
     /**
@@ -120,47 +127,58 @@ public class AetherModule
     protected void configure()
     {
         bind( RepositorySystem.class ) //
-        .to( DefaultRepositorySystem.class ).in( Singleton.class );
+                .to( DefaultRepositorySystem.class ).in( Singleton.class );
         bind( ArtifactResolver.class ) //
-        .to( DefaultArtifactResolver.class ).in( Singleton.class );
+                .to( DefaultArtifactResolver.class ).in( Singleton.class );
         bind( DependencyCollector.class ) //
-        .to( DefaultDependencyCollector.class ).in( Singleton.class );
+                .to( DefaultDependencyCollector.class ).in( Singleton.class );
         bind( Deployer.class ) //
-        .to( DefaultDeployer.class ).in( Singleton.class );
+                .to( DefaultDeployer.class ).in( Singleton.class );
         bind( Installer.class ) //
-        .to( DefaultInstaller.class ).in( Singleton.class );
+                .to( DefaultInstaller.class ).in( Singleton.class );
         bind( MetadataResolver.class ) //
-        .to( DefaultMetadataResolver.class ).in( Singleton.class );
+                .to( DefaultMetadataResolver.class ).in( Singleton.class );
         bind( RepositoryLayoutProvider.class ) //
-        .to( DefaultRepositoryLayoutProvider.class ).in( Singleton.class );
+                .to( DefaultRepositoryLayoutProvider.class ).in( Singleton.class );
         bind( RepositoryLayoutFactory.class ).annotatedWith( Names.named( "maven2" ) ) //
-        .to( Maven2RepositoryLayoutFactory.class ).in( Singleton.class );
+                .to( Maven2RepositoryLayoutFactory.class ).in( Singleton.class );
         bind( TransporterProvider.class ) //
-        .to( DefaultTransporterProvider.class ).in( Singleton.class );
+                .to( DefaultTransporterProvider.class ).in( Singleton.class );
         bind( ChecksumPolicyProvider.class ) //
-        .to( DefaultChecksumPolicyProvider.class ).in( Singleton.class );
+                .to( DefaultChecksumPolicyProvider.class ).in( Singleton.class );
         bind( RepositoryConnectorProvider.class ) //
-        .to( DefaultRepositoryConnectorProvider.class ).in( Singleton.class );
+                .to( DefaultRepositoryConnectorProvider.class ).in( Singleton.class );
         bind( RemoteRepositoryManager.class ) //
-        .to( DefaultRemoteRepositoryManager.class ).in( Singleton.class );
+                .to( DefaultRemoteRepositoryManager.class ).in( Singleton.class );
         bind( UpdateCheckManager.class ) //
-        .to( DefaultUpdateCheckManager.class ).in( Singleton.class );
+                .to( DefaultUpdateCheckManager.class ).in( Singleton.class );
         bind( UpdatePolicyAnalyzer.class ) //
-        .to( DefaultUpdatePolicyAnalyzer.class ).in( Singleton.class );
+                .to( DefaultUpdatePolicyAnalyzer.class ).in( Singleton.class );
         bind( FileProcessor.class ) //
-        .to( DefaultFileProcessor.class ).in( Singleton.class );
+                .to( DefaultFileProcessor.class ).in( Singleton.class );
         bind( RepositoryEventDispatcher.class ) //
-        .to( DefaultRepositoryEventDispatcher.class ).in( Singleton.class );
+                .to( DefaultRepositoryEventDispatcher.class ).in( Singleton.class );
         bind( OfflineController.class ) //
-        .to( DefaultOfflineController.class ).in( Singleton.class );
+                .to( DefaultOfflineController.class ).in( Singleton.class );
         bind( LocalRepositoryProvider.class ) //
-        .to( DefaultLocalRepositoryProvider.class ).in( Singleton.class );
+                .to( DefaultLocalRepositoryProvider.class ).in( Singleton.class );
         bind( LocalRepositoryManagerFactory.class ).annotatedWith( Names.named( "simple" ) ) //
-        .to( SimpleLocalRepositoryManagerFactory.class ).in( Singleton.class );
+                .to( SimpleLocalRepositoryManagerFactory.class ).in( Singleton.class );
         bind( LocalRepositoryManagerFactory.class ).annotatedWith( Names.named( "enhanced" ) ) //
-        .to( EnhancedLocalRepositoryManagerFactory.class ).in( Singleton.class );
+                .to( EnhancedLocalRepositoryManagerFactory.class ).in( Singleton.class );
         bind( TrackingFileManager.class ).to( DefaultTrackingFileManager.class ).in( Singleton.class );
 
+        bind( ChecksumAlgorithmFactory.class ).annotatedWith( Names.named( Md5ChecksumAlgorithmFactory.NAME ) )
+                .to( Md5ChecksumAlgorithmFactory.class );
+        bind( ChecksumAlgorithmFactory.class ).annotatedWith( Names.named( Sha1ChecksumAlgorithmFactory.NAME ) )
+                .to( Sha1ChecksumAlgorithmFactory.class );
+        bind( ChecksumAlgorithmFactory.class ).annotatedWith( Names.named( Sha256ChecksumAlgorithmFactory.NAME ) )
+                .to( Sha256ChecksumAlgorithmFactory.class );
+        bind( ChecksumAlgorithmFactory.class ).annotatedWith( Names.named( Sha512ChecksumAlgorithmFactory.NAME ) )
+                .to( Sha512ChecksumAlgorithmFactory.class );
+        bind( ChecksumAlgorithmFactorySelector.class )
+                .to( DefaultChecksumAlgorithmFactorySelector.class ).in ( Singleton.class );
+
         bind( NamedLockFactorySelector.class ).to( SimpleNamedLockFactorySelector.class ).in( Singleton.class );
         bind( SyncContextFactory.class ).to( DefaultSyncContextFactory.class ).in( Singleton.class );
         bind( org.eclipse.aether.impl.SyncContextFactory.class )
@@ -168,22 +186,22 @@ public class AetherModule
                 .in( Singleton.class );
 
         bind( NameMapper.class ).annotatedWith( Names.named( StaticNameMapper.NAME ) )
-            .to( StaticNameMapper.class ).in( Singleton.class );
+                .to( StaticNameMapper.class ).in( Singleton.class );
         bind( NameMapper.class ).annotatedWith( Names.named( GAVNameMapper.NAME ) )
-            .to( GAVNameMapper.class ).in( Singleton.class );
+                .to( GAVNameMapper.class ).in( Singleton.class );
         bind( NameMapper.class ).annotatedWith( Names.named( DiscriminatingNameMapper.NAME ) )
-            .to( DiscriminatingNameMapper.class ).in( Singleton.class );
+                .to( DiscriminatingNameMapper.class ).in( Singleton.class );
         bind( NameMapper.class ).annotatedWith( Names.named( FileGAVNameMapper.NAME ) )
-            .to( FileGAVNameMapper.class ).in( Singleton.class );
+                .to( FileGAVNameMapper.class ).in( Singleton.class );
 
         bind( NamedLockFactory.class ).annotatedWith( Names.named( NoopNamedLockFactory.NAME ) )
-            .to( NoopNamedLockFactory.class ).in( Singleton.class );
+                .to( NoopNamedLockFactory.class ).in( Singleton.class );
         bind( NamedLockFactory.class ).annotatedWith( Names.named( LocalReadWriteLockNamedLockFactory.NAME ) )
-            .to( LocalReadWriteLockNamedLockFactory.class ).in( Singleton.class );
+                .to( LocalReadWriteLockNamedLockFactory.class ).in( Singleton.class );
         bind( NamedLockFactory.class ).annotatedWith( Names.named( LocalSemaphoreNamedLockFactory.NAME ) )
-            .to( LocalSemaphoreNamedLockFactory.class ).in( Singleton.class );
+                .to( LocalSemaphoreNamedLockFactory.class ).in( Singleton.class );
         bind( NamedLockFactory.class ).annotatedWith( Names.named( FileLockNamedLockFactory.NAME ) )
-            .to( FileLockNamedLockFactory.class ).in( Singleton.class );
+                .to( FileLockNamedLockFactory.class ).in( Singleton.class );
 
         install( new Slf4jModule() );
 
@@ -191,11 +209,27 @@ public class AetherModule
 
     @Provides
     @Singleton
+    Map<String, ChecksumAlgorithmFactory> provideChecksumTypes(
+            @Named( Sha512ChecksumAlgorithmFactory.NAME ) ChecksumAlgorithmFactory sha512,
+            @Named( Sha256ChecksumAlgorithmFactory.NAME ) ChecksumAlgorithmFactory sha256,
+            @Named( Sha1ChecksumAlgorithmFactory.NAME ) ChecksumAlgorithmFactory sha1,
+            @Named( Md5ChecksumAlgorithmFactory.NAME ) ChecksumAlgorithmFactory md5 )
+    {
+        Map<String, ChecksumAlgorithmFactory> checksumTypes = new HashMap<>();
+        checksumTypes.put( Sha512ChecksumAlgorithmFactory.NAME, sha512 );
+        checksumTypes.put( Sha256ChecksumAlgorithmFactory.NAME, sha256 );
+        checksumTypes.put( Sha1ChecksumAlgorithmFactory.NAME, sha1 );
+        checksumTypes.put( Md5ChecksumAlgorithmFactory.NAME, md5 );
+        return Collections.unmodifiableMap( checksumTypes );
+    }
+
+    @Provides
+    @Singleton
     Map<String, NameMapper> provideNameMappers(
-        @Named( StaticNameMapper.NAME ) NameMapper staticNameMapper,
-        @Named( GAVNameMapper.NAME ) NameMapper gavNameMapper,
-        @Named( DiscriminatingNameMapper.NAME ) NameMapper discriminatingNameMapper,
-        @Named( FileGAVNameMapper.NAME ) NameMapper fileGavNameMapper )
+            @Named( StaticNameMapper.NAME ) NameMapper staticNameMapper,
+            @Named( GAVNameMapper.NAME ) NameMapper gavNameMapper,
+            @Named( DiscriminatingNameMapper.NAME ) NameMapper discriminatingNameMapper,
+            @Named( FileGAVNameMapper.NAME ) NameMapper fileGavNameMapper )
     {
         Map<String, NameMapper> nameMappers = new HashMap<>();
         nameMappers.put( StaticNameMapper.NAME, staticNameMapper );
@@ -248,14 +282,14 @@ public class AetherModule
     }
 
     private static class Slf4jModule
-        extends AbstractModule
+            extends AbstractModule
     {
 
         @Override
         protected void configure()
         {
             bind( LoggerFactory.class ) //
-            .to( Slf4jLoggerFactory.class );
+                    .to( Slf4jLoggerFactory.class );
         }
 
         @Provides
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/AbstractChecksumPolicy.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/AbstractChecksumPolicy.java
index 32fb0a9..d5e1ecb 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/AbstractChecksumPolicy.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/AbstractChecksumPolicy.java
@@ -8,9 +8,9 @@ package org.eclipse.aether.internal.impl;
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
 import static java.util.Objects.requireNonNull;
 
 abstract class AbstractChecksumPolicy
-    implements ChecksumPolicy
+        implements ChecksumPolicy
 {
 
     protected final Logger logger = LoggerFactory.getLogger( getClass() );
@@ -40,14 +40,16 @@ abstract class AbstractChecksumPolicy
         this.resource = resource;
     }
 
+    @Override
     public boolean onChecksumMatch( String algorithm, int kind )
     {
         requireNonNull( algorithm, "algorithm cannot be null" );
         return true;
     }
 
+    @Override
     public void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception )
-        throws ChecksumFailureException
+            throws ChecksumFailureException
     {
         requireNonNull( algorithm, "algorithm cannot be null" );
         requireNonNull( exception, "exception cannot be null" );
@@ -57,20 +59,23 @@ abstract class AbstractChecksumPolicy
         }
     }
 
+    @Override
     public void onChecksumError( String algorithm, int kind, ChecksumFailureException exception )
-        throws ChecksumFailureException
+            throws ChecksumFailureException
     {
         requireNonNull( algorithm, "algorithm cannot be null" );
         requireNonNull( exception, "exception cannot be null" );
         logger.debug( "Could not validate {} checksum for {}", algorithm, resource.getResourceName(), exception );
     }
 
+    @Override
     public void onNoMoreChecksums()
-        throws ChecksumFailureException
+            throws ChecksumFailureException
     {
         throw new ChecksumFailureException( "Checksum validation failed, no checksums available" );
     }
 
+    @Override
     public void onTransferRetry()
     {
     }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java
index 99d5861..3aebd78 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultFileProcessor.java
@@ -25,7 +25,6 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 
@@ -33,6 +32,7 @@ import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.eclipse.aether.spi.io.FileProcessor;
+import org.eclipse.aether.util.ChecksumUtils;
 
 /**
  * A utility class helping with file-based operations.
@@ -212,19 +212,16 @@ public class DefaultFileProcessor
         throws IOException
     {
         long total = 0L;
-
-        ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
-        byte[] array = buffer.array();
-
+        byte[] buffer = new byte[ 1024 * 32 ];
         while ( true )
         {
-            int bytes = is.read( array );
+            int bytes = is.read( buffer );
             if ( bytes < 0 )
             {
                 break;
             }
 
-            os.write( array, 0, bytes );
+            os.write( buffer, 0, bytes );
 
             total += bytes;
 
@@ -232,9 +229,7 @@ public class DefaultFileProcessor
             {
                 try
                 {
-                    ( (Buffer) buffer ).rewind();
-                    ( (Buffer) buffer ).limit( bytes );
-                    listener.progressed( buffer );
+                    listener.progressed( ByteBuffer.wrap( buffer, 0, bytes ) );
                 }
                 catch ( Exception e )
                 {
@@ -259,4 +254,17 @@ public class DefaultFileProcessor
         }
     }
 
+    @Override
+    public String readChecksum( final File checksumFile ) throws IOException
+    {
+        // for now do exactly same as happened before, but FileProcessor is a component and can be replaced
+        return ChecksumUtils.read( checksumFile );
+    }
+
+    @Override
+    public void writeChecksum( final File checksumFile, final String checksum ) throws IOException
+    {
+        // for now do exactly same as happened before, but FileProcessor is a component and can be replaced
+        write( checksumFile, checksum );
+    }
 }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
index c19c006..70d36c6 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
@@ -34,6 +34,7 @@ final class FailChecksumPolicy
         super( resource );
     }
 
+    @Override
     public boolean onTransferChecksumFailure( ChecksumFailureException error )
     {
         return false;
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
index 82933e4..3715196 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactory.java
@@ -21,18 +21,24 @@ package org.eclipse.aether.internal.impl;
 
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.stream.Collectors;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector;
 import org.eclipse.aether.metadata.Metadata;
 import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
 import org.eclipse.aether.transfer.NoRepositoryLayoutException;
@@ -46,7 +52,7 @@ import static java.util.Objects.requireNonNull;
 @Singleton
 @Named( "maven2" )
 public final class Maven2RepositoryLayoutFactory
-    implements RepositoryLayoutFactory
+        implements RepositoryLayoutFactory
 {
 
     static final String CONFIG_PROP_SIGNATURE_CHECKSUMS = "aether.checksums.forSignature";
@@ -56,12 +62,29 @@ public final class Maven2RepositoryLayoutFactory
 
     private float priority;
 
+    private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
+
     public float getPriority()
     {
         return priority;
     }
 
     /**
+     * Service locator ctor.
+     */
+    @Deprecated
+    public Maven2RepositoryLayoutFactory()
+    {
+        this( new DefaultChecksumAlgorithmFactorySelector() );
+    }
+
+    @Inject
+    public Maven2RepositoryLayoutFactory( ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector )
+    {
+        this.checksumAlgorithmFactorySelector = requireNonNull( checksumAlgorithmFactorySelector );
+    }
+
+    /**
      * Sets the priority of this component.
      *
      * @param priority The priority.
@@ -74,7 +97,7 @@ public final class Maven2RepositoryLayoutFactory
     }
 
     public RepositoryLayout newInstance( RepositorySystemSession session, RemoteRepository repository )
-        throws NoRepositoryLayoutException
+            throws NoRepositoryLayoutException
     {
         requireNonNull( session, "session cannot be null" );
         requireNonNull( repository, "repository cannot be null" );
@@ -83,8 +106,18 @@ public final class Maven2RepositoryLayoutFactory
             throw new NoRepositoryLayoutException( repository );
         }
         boolean forSignature = ConfigUtils.getBoolean( session, false, CONFIG_PROP_SIGNATURE_CHECKSUMS );
-        List<String> checksumsAlgorithms = Arrays.asList( ConfigUtils.getString( session,
-                DEFAULT_CHECKSUMS_ALGORITHMS, CONFIG_PROP_CHECKSUMS_ALGORITHMS ).split( "," ) );
+        // ensure order of (potentially user set) algorithm list is kept and is unique
+        LinkedHashSet<String> checksumsAlgorithmNames = new LinkedHashSet<>( Arrays.asList(
+                ConfigUtils.getString(
+                        session, DEFAULT_CHECKSUMS_ALGORITHMS, CONFIG_PROP_CHECKSUMS_ALGORITHMS
+                ).split( "," ) )
+        );
+
+        List<ChecksumAlgorithmFactory> checksumsAlgorithms = new ArrayList<>( checksumsAlgorithmNames.size() );
+        for ( String checksumsAlgorithmName : checksumsAlgorithmNames )
+        {
+            checksumsAlgorithms.add( checksumAlgorithmFactorySelector.select( checksumsAlgorithmName ) );
+        }
 
         return forSignature
                 ? new Maven2RepositoryLayout( checksumsAlgorithms )
@@ -92,14 +125,14 @@ public final class Maven2RepositoryLayoutFactory
     }
 
     private static class Maven2RepositoryLayout
-        implements RepositoryLayout
+            implements RepositoryLayout
     {
 
-        private final List<String> checksumsAlgorithms;
+        private final List<ChecksumAlgorithmFactory> checksumAlgorithms;
 
-        protected Maven2RepositoryLayout( List<String> checksumsAlgorithms )
+        protected Maven2RepositoryLayout( List<ChecksumAlgorithmFactory> checksumAlgorithms )
         {
-            this.checksumsAlgorithms = checksumsAlgorithms;
+            this.checksumAlgorithms = Collections.unmodifiableList( checksumAlgorithms );
         }
 
         private URI toUri( String path )
@@ -114,6 +147,13 @@ public final class Maven2RepositoryLayoutFactory
             }
         }
 
+        @Override
+        public List<String> getChecksumAlgorithmNames()
+        {
+            return checksumAlgorithms.stream().map( ChecksumAlgorithmFactory::getName ).collect( Collectors.toList() );
+        }
+
+        @Override
         public URI getLocation( Artifact artifact, boolean upload )
         {
             StringBuilder path = new StringBuilder( 128 );
@@ -139,6 +179,7 @@ public final class Maven2RepositoryLayoutFactory
             return toUri( path.toString() );
         }
 
+        @Override
         public URI getLocation( Metadata metadata, boolean upload )
         {
             StringBuilder path = new StringBuilder( 128 );
@@ -163,45 +204,47 @@ public final class Maven2RepositoryLayoutFactory
             return toUri( path.toString() );
         }
 
-        public List<Checksum> getChecksums( Artifact artifact, boolean upload, URI location )
+        @Override
+        public List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location )
         {
-            return getChecksums( location );
+            return getChecksumLocations( location );
         }
 
-        public List<Checksum> getChecksums( Metadata metadata, boolean upload, URI location )
+        @Override
+        public List<ChecksumLocation> getChecksumLocations( Metadata metadata, boolean upload, URI location )
         {
-            return getChecksums( location );
+            return getChecksumLocations( location );
         }
 
-        private List<Checksum> getChecksums( URI location )
+        private List<ChecksumLocation> getChecksumLocations( URI location )
         {
-            List<Checksum> checksums = new ArrayList<>( checksumsAlgorithms.size() );
-            for ( String algorithm : checksumsAlgorithms )
+            List<ChecksumLocation> checksumLocations = new ArrayList<>( checksumAlgorithms.size() );
+            for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithms )
             {
-                checksums.add( Checksum.forLocation( location, algorithm ) );
+                checksumLocations.add( ChecksumLocation.forLocation( location, checksumAlgorithmFactory ) );
             }
-            return checksums;
+            return checksumLocations;
         }
 
     }
 
     private static class Maven2RepositoryLayoutEx
-        extends Maven2RepositoryLayout
+            extends Maven2RepositoryLayout
     {
 
-        protected Maven2RepositoryLayoutEx( List<String> checksumsAlgorithms )
+        protected Maven2RepositoryLayoutEx( List<ChecksumAlgorithmFactory> checksumAlgorithms )
         {
-            super( checksumsAlgorithms );
+            super( checksumAlgorithms );
         }
 
         @Override
-        public List<Checksum> getChecksums( Artifact artifact, boolean upload, URI location )
+        public List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location )
         {
             if ( isSignature( artifact.getExtension() ) )
             {
                 return Collections.emptyList();
             }
-            return super.getChecksums( artifact, upload, location );
+            return super.getChecksumLocations( artifact, upload, location );
         }
 
         private boolean isSignature( String extension )
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/WarnChecksumPolicy.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/WarnChecksumPolicy.java
index fca0671..ff8ac4f 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/WarnChecksumPolicy.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/WarnChecksumPolicy.java
@@ -34,6 +34,7 @@ final class WarnChecksumPolicy
         super( resource );
     }
 
+    @Override
     public boolean onTransferChecksumFailure( ChecksumFailureException exception )
     {
         logger.warn( "Could not validate integrity of download from {}{}", resource.getRepositoryUrl(),
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/DefaultChecksumAlgorithmFactorySelector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/DefaultChecksumAlgorithmFactorySelector.java
new file mode 100644
index 0000000..3e6d657
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/DefaultChecksumAlgorithmFactorySelector.java
@@ -0,0 +1,87 @@
+package org.eclipse.aether.internal.impl.checksum;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Default implementation.
+ *
+ * @since 1.8.0
+ */
+@Singleton
+@Named
+public class DefaultChecksumAlgorithmFactorySelector
+        implements ChecksumAlgorithmFactorySelector
+{
+    private final Map<String, ChecksumAlgorithmFactory> factories;
+
+    /**
+     * Default ctor for SL.
+     */
+    @Deprecated
+    public DefaultChecksumAlgorithmFactorySelector()
+    {
+        this.factories = new HashMap<>();
+        this.factories.put( Sha512ChecksumAlgorithmFactory.NAME, new Sha512ChecksumAlgorithmFactory() );
+        this.factories.put( Sha256ChecksumAlgorithmFactory.NAME, new Sha256ChecksumAlgorithmFactory() );
+        this.factories.put( Sha1ChecksumAlgorithmFactory.NAME, new Sha1ChecksumAlgorithmFactory() );
+        this.factories.put( Md5ChecksumAlgorithmFactory.NAME, new Md5ChecksumAlgorithmFactory() );
+    }
+
+    @Inject
+    public DefaultChecksumAlgorithmFactorySelector( Map<String, ChecksumAlgorithmFactory> factories )
+    {
+        this.factories = requireNonNull( factories );
+    }
+
+    @Override
+    public ChecksumAlgorithmFactory select( String algorithmName )
+    {
+        requireNonNull( algorithmName, "algorithmMame must not be null" );
+        ChecksumAlgorithmFactory factory =  factories.get( algorithmName );
+        if ( factory == null )
+        {
+            throw new IllegalArgumentException(
+                    String.format( "Unsupported checksum algorithm %s, supported ones are %s",
+                            algorithmName, getChecksumAlgorithmNames() )
+            );
+        }
+        return factory;
+    }
+
+    @Override
+    public Set<String> getChecksumAlgorithmNames()
+    {
+        return new HashSet<>( factories.keySet() );
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Md5ChecksumAlgorithmFactory.java
similarity index 61%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Md5ChecksumAlgorithmFactory.java
index c19c006..4c6faf8 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Md5ChecksumAlgorithmFactory.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl;
+package org.eclipse.aether.internal.impl.checksum;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -8,9 +8,9 @@ package org.eclipse.aether.internal.impl;
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,24 +19,25 @@ package org.eclipse.aether.internal.impl;
  * under the License.
  */
 
-import org.eclipse.aether.transfer.ChecksumFailureException;
-import org.eclipse.aether.transfer.TransferResource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
 
 /**
- * Implements {@link org.eclipse.aether.repository.RepositoryPolicy#CHECKSUM_POLICY_FAIL}.
+ * The MD5 checksum type.
+ *
+ * @since 1.8.0
  */
-final class FailChecksumPolicy
-    extends AbstractChecksumPolicy
+@Singleton
+@Named( Md5ChecksumAlgorithmFactory.NAME )
+public class Md5ChecksumAlgorithmFactory
+        extends MessageDigestChecksumAlgorithmFactorySupport
 {
+    public static final String NAME = "MD5";
 
-    FailChecksumPolicy( TransferResource resource )
+    @Inject
+    public Md5ChecksumAlgorithmFactory()
     {
-        super( resource );
+        super( NAME, "md5" );
     }
-
-    public boolean onTransferChecksumFailure( ChecksumFailureException error )
-    {
-        return false;
-    }
-
 }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/MessageDigestChecksumAlgorithmFactorySupport.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/MessageDigestChecksumAlgorithmFactorySupport.java
new file mode 100644
index 0000000..a285f5e
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/MessageDigestChecksumAlgorithmFactorySupport.java
@@ -0,0 +1,71 @@
+package org.eclipse.aether.internal.impl.checksum;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySupport;
+import org.eclipse.aether.util.ChecksumUtils;
+
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Support class to implement {@link ChecksumAlgorithmFactory} based on Java {@link MessageDigest}.
+ *
+ * @since 1.8.0
+ */
+public abstract class MessageDigestChecksumAlgorithmFactorySupport
+        extends ChecksumAlgorithmFactorySupport
+{
+    public MessageDigestChecksumAlgorithmFactorySupport( String name, String extension )
+    {
+        super( name, extension );
+    }
+
+    @Override
+    public ChecksumAlgorithm getAlgorithm()
+    {
+        try
+        {
+            MessageDigest messageDigest = MessageDigest.getInstance( getName() );
+            return new ChecksumAlgorithm()
+            {
+                @Override
+                public void update( final ByteBuffer input )
+                {
+                    messageDigest.update( input );
+                }
+
+                @Override
+                public String checksum()
+                {
+                    return ChecksumUtils.toHexString( messageDigest.digest() );
+                }
+            };
+        }
+        catch ( NoSuchAlgorithmException e )
+        {
+            throw new IllegalStateException(
+                    "MessageDigest algorithm " + getName() + " not supported, but is required by resolver.", e );
+        }
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha1ChecksumAlgorithmFactory.java
similarity index 61%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha1ChecksumAlgorithmFactory.java
index c19c006..aef57fe 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha1ChecksumAlgorithmFactory.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl;
+package org.eclipse.aether.internal.impl.checksum;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -8,9 +8,9 @@ package org.eclipse.aether.internal.impl;
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,24 +19,25 @@ package org.eclipse.aether.internal.impl;
  * under the License.
  */
 
-import org.eclipse.aether.transfer.ChecksumFailureException;
-import org.eclipse.aether.transfer.TransferResource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
 
 /**
- * Implements {@link org.eclipse.aether.repository.RepositoryPolicy#CHECKSUM_POLICY_FAIL}.
+ * The SHA-1 checksum type.
+ *
+ * @since 1.8.0
  */
-final class FailChecksumPolicy
-    extends AbstractChecksumPolicy
+@Singleton
+@Named( Sha1ChecksumAlgorithmFactory.NAME )
+public class Sha1ChecksumAlgorithmFactory
+        extends MessageDigestChecksumAlgorithmFactorySupport
 {
+    public static final String NAME = "SHA-1";
 
-    FailChecksumPolicy( TransferResource resource )
+    @Inject
+    public Sha1ChecksumAlgorithmFactory()
     {
-        super( resource );
+        super( NAME, "sha1" );
     }
-
-    public boolean onTransferChecksumFailure( ChecksumFailureException error )
-    {
-        return false;
-    }
-
 }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha256ChecksumAlgorithmFactory.java
similarity index 61%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha256ChecksumAlgorithmFactory.java
index c19c006..35d00cf 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha256ChecksumAlgorithmFactory.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl;
+package org.eclipse.aether.internal.impl.checksum;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -8,9 +8,9 @@ package org.eclipse.aether.internal.impl;
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,24 +19,25 @@ package org.eclipse.aether.internal.impl;
  * under the License.
  */
 
-import org.eclipse.aether.transfer.ChecksumFailureException;
-import org.eclipse.aether.transfer.TransferResource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
 
 /**
- * Implements {@link org.eclipse.aether.repository.RepositoryPolicy#CHECKSUM_POLICY_FAIL}.
+ * The SHA-256 checksum type.
+ *
+ * @since 1.8.0
  */
-final class FailChecksumPolicy
-    extends AbstractChecksumPolicy
+@Singleton
+@Named( Sha256ChecksumAlgorithmFactory.NAME )
+public class Sha256ChecksumAlgorithmFactory
+        extends MessageDigestChecksumAlgorithmFactorySupport
 {
+    public static final String NAME = "SHA-256";
 
-    FailChecksumPolicy( TransferResource resource )
+    @Inject
+    public Sha256ChecksumAlgorithmFactory()
     {
-        super( resource );
+        super( NAME, "sha256" );
     }
-
-    public boolean onTransferChecksumFailure( ChecksumFailureException error )
-    {
-        return false;
-    }
-
 }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha512ChecksumAlgorithmFactory.java
similarity index 61%
copy from maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
copy to maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha512ChecksumAlgorithmFactory.java
index c19c006..0c60862 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/FailChecksumPolicy.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/checksum/Sha512ChecksumAlgorithmFactory.java
@@ -1,4 +1,4 @@
-package org.eclipse.aether.internal.impl;
+package org.eclipse.aether.internal.impl.checksum;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -8,9 +8,9 @@ package org.eclipse.aether.internal.impl;
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,24 +19,25 @@ package org.eclipse.aether.internal.impl;
  * under the License.
  */
 
-import org.eclipse.aether.transfer.ChecksumFailureException;
-import org.eclipse.aether.transfer.TransferResource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
 
 /**
- * Implements {@link org.eclipse.aether.repository.RepositoryPolicy#CHECKSUM_POLICY_FAIL}.
+ * The SHA-512 checksum type.
+ *
+ * @since 1.8.0
  */
-final class FailChecksumPolicy
-    extends AbstractChecksumPolicy
+@Singleton
+@Named( Sha512ChecksumAlgorithmFactory.NAME )
+public class Sha512ChecksumAlgorithmFactory
+        extends MessageDigestChecksumAlgorithmFactorySupport
 {
+    public static final String NAME = "SHA-512";
 
-    FailChecksumPolicy( TransferResource resource )
+    @Inject
+    public Sha512ChecksumAlgorithmFactory()
     {
-        super( resource );
+        super( NAME, "sha512" );
     }
-
-    public boolean onTransferChecksumFailure( ChecksumFailureException error )
-    {
-        return false;
-    }
-
 }
diff --git a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java
index 3720509..311607c 100644
--- a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java
+++ b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/Maven2RepositoryLayoutFactoryTest.java
@@ -22,8 +22,8 @@ package org.eclipse.aether.internal.impl;
 import static org.junit.Assert.*;
 
 import java.net.URI;
+import java.util.Arrays;
 import java.util.List;
-import java.util.Locale;
 
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.artifact.DefaultArtifact;
@@ -31,14 +31,44 @@ import org.eclipse.aether.internal.test.util.TestUtils;
 import org.eclipse.aether.metadata.DefaultMetadata;
 import org.eclipse.aether.metadata.Metadata;
 import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySupport;
 import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
-import org.eclipse.aether.spi.connector.layout.RepositoryLayout.Checksum;
+import org.eclipse.aether.spi.connector.layout.RepositoryLayout.ChecksumLocation;
 import org.eclipse.aether.transfer.NoRepositoryLayoutException;
 import org.junit.Before;
 import org.junit.Test;
 
 public class Maven2RepositoryLayoutFactoryTest
 {
+    private final ChecksumAlgorithmFactory SHA512 = new ChecksumAlgorithmFactorySupport("SHA-512", "sha512") {
+        @Override
+        public ChecksumAlgorithm getAlgorithm() {
+            throw new RuntimeException("this should not happen");
+        }
+    };
+
+    private final ChecksumAlgorithmFactory SHA256 = new ChecksumAlgorithmFactorySupport("SHA-256", "sha256") {
+        @Override
+        public ChecksumAlgorithm getAlgorithm() {
+            throw new RuntimeException("this should not happen");
+        }
+    };
+
+    private final ChecksumAlgorithmFactory SHA1 = new ChecksumAlgorithmFactorySupport("SHA-1", "sha1") {
+        @Override
+        public ChecksumAlgorithm getAlgorithm() {
+            throw new RuntimeException("this should not happen");
+        }
+    };
+
+    private final ChecksumAlgorithmFactory MD5 = new ChecksumAlgorithmFactorySupport("MD5", "md5") {
+        @Override
+        public ChecksumAlgorithm getAlgorithm() {
+            throw new RuntimeException("this should not happen");
+        }
+    };
 
     private DefaultRepositorySystemSession session;
 
@@ -51,18 +81,23 @@ public class Maven2RepositoryLayoutFactoryTest
         return new RemoteRepository.Builder( "test", type, "classpath:/nil" ).build();
     }
 
-    private void assertChecksum( Checksum actual, String expectedUri, String expectedAlgo )
+    private void assertChecksum( ChecksumLocation actual, String expectedUri, String expectedAlgo )
     {
         assertEquals( expectedUri, actual.getLocation().toString() );
-        assertEquals( expectedAlgo, actual.getAlgorithm() );
+        assertEquals( expectedAlgo, actual.getChecksumAlgorithmFactory().getName() );
+    }
+
+    private void assertChecksum( ChecksumLocation actual, String expectedUri, ChecksumAlgorithmFactory expectedAlgorithmFactory )
+    {
+        assertChecksum( actual, expectedUri, expectedAlgorithmFactory.getName() );
     }
 
-    private void assertChecksums( List<Checksum> actual, String baseUri, String... algos )
+    private void assertChecksums( List<ChecksumLocation> actual, String baseUri, ChecksumAlgorithmFactory... algos )
     {
         assertEquals( algos.length, actual.size() );
         for ( int i = 0; i < algos.length; i++ )
         {
-            String uri = baseUri + '.' + algos[i].replace( "-", "" ).toLowerCase( Locale.ENGLISH );
+            String uri = baseUri + '.' + algos[i].getFileExtension();
             assertChecksum( actual.get( i ), uri, algos[i] );
         }
     }
@@ -84,6 +119,12 @@ public class Maven2RepositoryLayoutFactoryTest
     }
 
     @Test
+    public void testChecksumAlgorithmNames()
+    {
+        assertEquals( Arrays.asList( "SHA-1", "MD5" ), layout.getChecksumAlgorithmNames() );
+    }
+
+    @Test
     public void testArtifactLocation_Release()
     {
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "ext", "1.0" );
@@ -153,10 +194,10 @@ public class Maven2RepositoryLayoutFactoryTest
     {
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "ext", "1.0" );
         URI uri = layout.getLocation( artifact, false );
-        List<Checksum> checksums = layout.getChecksums( artifact, false, uri );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( artifact, false, uri );
         assertEquals( 2, checksums.size() );
-        assertChecksum( checksums.get( 0 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha1", "SHA-1" );
-        assertChecksum( checksums.get( 1 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.md5", "MD5" );
+        assertChecksum( checksums.get( 0 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha1", SHA1 );
+        assertChecksum( checksums.get( 1 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.md5", MD5 );
     }
 
     @Test
@@ -166,10 +207,17 @@ public class Maven2RepositoryLayoutFactoryTest
         layout = factory.newInstance( session, newRepo( "default" ) );
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "ext", "1.0" );
         URI uri = layout.getLocation( artifact, false );
-        List<Checksum> checksums = layout.getChecksums( artifact, false, uri );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( artifact, false, uri );
         assertEquals( 2, checksums.size() );
-        assertChecksum( checksums.get( 0 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha256", "SHA-256" );
-        assertChecksum( checksums.get( 1 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha1", "SHA-1" );
+        assertChecksum( checksums.get( 0 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha256", SHA256 );
+        assertChecksum( checksums.get( 1 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha1", SHA1 );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void testArtifactChecksums_DownloadWithUnsupportedAlgorithms() throws NoRepositoryLayoutException
+    {
+        session.setConfigProperty( Maven2RepositoryLayoutFactory.CONFIG_PROP_CHECKSUMS_ALGORITHMS, "FOO,SHA-1");
+        layout = factory.newInstance( session, newRepo( "default" ) );
     }
 
     @Test
@@ -177,10 +225,10 @@ public class Maven2RepositoryLayoutFactoryTest
     {
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "ext", "1.0" );
         URI uri = layout.getLocation( artifact, true );
-        List<Checksum> checksums = layout.getChecksums( artifact, true, uri );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( artifact, true, uri );
         assertEquals( 2, checksums.size() );
-        assertChecksum( checksums.get( 0 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha1", "SHA-1" );
-        assertChecksum( checksums.get( 1 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.md5", "MD5" );
+        assertChecksum( checksums.get( 0 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha1", SHA1 );
+        assertChecksum( checksums.get( 1 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.md5", MD5 );
     }
 
     @Test
@@ -190,10 +238,10 @@ public class Maven2RepositoryLayoutFactoryTest
         layout = factory.newInstance( session, newRepo( "default" ) );
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "ext", "1.0" );
         URI uri = layout.getLocation( artifact, true );
-        List<Checksum> checksums = layout.getChecksums( artifact, true, uri );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( artifact, true, uri );
         assertEquals( 2, checksums.size() );
-        assertChecksum( checksums.get( 0 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha512", "SHA-512" );
-        assertChecksum( checksums.get( 1 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.md5", "MD5" );
+        assertChecksum( checksums.get( 0 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.sha512", SHA512 );
+        assertChecksum( checksums.get( 1 ), "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.ext.md5", MD5 );
     }
 
     @Test
@@ -203,11 +251,12 @@ public class Maven2RepositoryLayoutFactoryTest
             new DefaultMetadata( "org.apache.maven.plugins", "maven-jar-plugin", "maven-metadata.xml",
                                  Metadata.Nature.RELEASE_OR_SNAPSHOT );
         URI uri = layout.getLocation( metadata, false );
-        List<Checksum> checksums = layout.getChecksums( metadata, false, uri );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( metadata, false, uri );
         assertEquals( 2, checksums.size() );
         assertChecksum( checksums.get( 0 ), "org/apache/maven/plugins/maven-jar-plugin/maven-metadata.xml.sha1",
-                        "SHA-1" );
-        assertChecksum( checksums.get( 1 ), "org/apache/maven/plugins/maven-jar-plugin/maven-metadata.xml.md5", "MD5" );
+                        SHA1 );
+        assertChecksum( checksums.get( 1 ), "org/apache/maven/plugins/maven-jar-plugin/maven-metadata.xml.md5",
+                        MD5 );
     }
 
     @Test
@@ -217,11 +266,12 @@ public class Maven2RepositoryLayoutFactoryTest
             new DefaultMetadata( "org.apache.maven.plugins", "maven-jar-plugin", "maven-metadata.xml",
                                  Metadata.Nature.RELEASE_OR_SNAPSHOT );
         URI uri = layout.getLocation( metadata, true );
-        List<Checksum> checksums = layout.getChecksums( metadata, true, uri );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( metadata, true, uri );
         assertEquals( 2, checksums.size() );
         assertChecksum( checksums.get( 0 ), "org/apache/maven/plugins/maven-jar-plugin/maven-metadata.xml.sha1",
-                        "SHA-1" );
-        assertChecksum( checksums.get( 1 ), "org/apache/maven/plugins/maven-jar-plugin/maven-metadata.xml.md5", "MD5" );
+                        SHA1 );
+        assertChecksum( checksums.get( 1 ), "org/apache/maven/plugins/maven-jar-plugin/maven-metadata.xml.md5",
+                        MD5 );
     }
 
     @Test
@@ -229,12 +279,12 @@ public class Maven2RepositoryLayoutFactoryTest
     {
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "asc", "1.0" );
         URI uri = layout.getLocation( artifact, false );
-        List<Checksum> checksums = layout.getChecksums( artifact, false, uri );
-        assertChecksums( checksums, "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.asc", "SHA-1", "MD5" );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( artifact, false, uri );
+        assertChecksums( checksums, "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.asc", SHA1, MD5 );
 
         artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "jar.asc", "1.0" );
         uri = layout.getLocation( artifact, false );
-        checksums = layout.getChecksums( artifact, false, uri );
+        checksums = layout.getChecksumLocations( artifact, false, uri );
         assertEquals( 0, checksums.size() );
     }
 
@@ -243,12 +293,12 @@ public class Maven2RepositoryLayoutFactoryTest
     {
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "asc", "1.0" );
         URI uri = layout.getLocation( artifact, true );
-        List<Checksum> checksums = layout.getChecksums( artifact, true, uri );
-        assertChecksums( checksums, "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.asc", "SHA-1", "MD5" );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( artifact, true, uri );
+        assertChecksums( checksums, "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.asc", SHA1, MD5 );
 
         artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "jar.asc", "1.0" );
         uri = layout.getLocation( artifact, true );
-        checksums = layout.getChecksums( artifact, true, uri );
+        checksums = layout.getChecksumLocations( artifact, true, uri );
         assertEquals( 0, checksums.size() );
     }
 
@@ -260,8 +310,8 @@ public class Maven2RepositoryLayoutFactoryTest
         layout = factory.newInstance( session, newRepo( "default" ) );
         DefaultArtifact artifact = new DefaultArtifact( "g.i.d", "a-i.d", "cls", "jar.asc", "1.0" );
         URI uri = layout.getLocation( artifact, true );
-        List<Checksum> checksums = layout.getChecksums( artifact, true, uri );
-        assertChecksums( checksums, "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.jar.asc", "SHA-1", "MD5" );
+        List<ChecksumLocation> checksums = layout.getChecksumLocations( artifact, true, uri );
+        assertChecksums( checksums, "g/i/d/a-i.d/1.0/a-i.d-1.0-cls.jar.asc", SHA1, MD5 );
     }
 
 }
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithm.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithm.java
new file mode 100644
index 0000000..ade3c62
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithm.java
@@ -0,0 +1,46 @@
+package org.eclipse.aether.spi.connector.checksum;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.nio.ByteBuffer;
+
+/**
+ * Implementation performing checksum calculation for specific algorithm. Instances of this interface are stateful,
+ * non-thread safe, and should not be reused.
+ *
+ * @since 1.8.0
+ */
+public interface ChecksumAlgorithm
+{
+    /**
+     * Updates the checksum algorithm inner state with input.
+     */
+    void update( ByteBuffer input );
+
+    /**
+     * Returns the algorithm end result as string, never {@code null}. After invoking this method, this instance should
+     * be discarded and not reused. For new checksum calculation you have to get new instance.
+     *
+     * Values returned by this method are handled as "opaque strings", and are used for simple equality checks (matches
+     * or not matches the checksum), and are also persisted in this form (locally to file system but also uploaded as
+     * checksum files). Resolver itself never tries to "decode" or "interpret" this string in any other way.
+     */
+    String checksum();
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactory.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactory.java
new file mode 100644
index 0000000..f8196d0
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactory.java
@@ -0,0 +1,48 @@
+package org.eclipse.aether.spi.connector.checksum;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * A component representing a checksum factory: provides {@link ChecksumAlgorithm} instances, name and extension to be
+ * used with this algorithm. While directly injecting components of this type is possible, it is not recommended. To
+ * obtain factory instances use {@link ChecksumAlgorithmFactorySelector} instead.
+ *
+ * @since 1.8.0
+ */
+public interface ChecksumAlgorithmFactory
+{
+    /**
+     * Returns the algorithm name, usually used as key, never {@code null} value. The name is a standard name of
+     * algorithm (if applicable) or any other designator that is algorithm commonly referred with. Example: "SHA-1".
+     */
+    String getName();
+
+    /**
+     * Returns the file extension to be used for given checksum file (without leading dot), never {@code null}. The
+     * extension should be file and URL path friendly, and may differ from value returned by {@link #getName()}.
+     * Example: "sha1".
+     */
+    String getFileExtension();
+
+    /**
+     * Each invocation of this method returns a new instance of algorithm, never {@code null} value.
+     */
+    ChecksumAlgorithm getAlgorithm();
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java
new file mode 100644
index 0000000..eece32e
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySelector.java
@@ -0,0 +1,44 @@
+package org.eclipse.aether.spi.connector.checksum;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Set;
+
+/**
+ * Component performing selection of {@link ChecksumAlgorithmFactory} based on known factory names.
+ *
+ * @since 1.8.0
+ */
+public interface ChecksumAlgorithmFactorySelector
+{
+    /**
+     * Returns factory for given algorithm name, or throws if algorithm not supported.
+     *
+     * @throws IllegalArgumentException if asked algorithm name is not supported.
+     */
+    ChecksumAlgorithmFactory select( String algorithmName );
+
+    /**
+     * Returns a set of supported algorithm names. This set represents ALL the algorithms supported by Resolver, and is
+     * NOT in any relation to given repository layout used checksums, returned by method {@link
+     * org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmNames()} (is super set of it).
+     */
+    Set<String> getChecksumAlgorithmNames();
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySupport.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySupport.java
new file mode 100644
index 0000000..7fd398e
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmFactorySupport.java
@@ -0,0 +1,59 @@
+package org.eclipse.aether.spi.connector.checksum;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Support class for {@link ChecksumAlgorithmFactory} implementations.
+ *
+ * @since 1.8.0
+ */
+public abstract class ChecksumAlgorithmFactorySupport
+    implements ChecksumAlgorithmFactory
+{
+    private final String name;
+
+    private final String fileExtension;
+
+    public ChecksumAlgorithmFactorySupport( String name, String fileExtension )
+    {
+        this.name = requireNonNull( name );
+        this.fileExtension = requireNonNull( fileExtension );
+    }
+
+    /**
+     * Returns the algorithm name, usually used as key, never {@code null} value.
+     */
+    @Override
+    public String getName()
+    {
+        return name;
+    }
+
+    /**
+     * Returns the file extension to be used for given checksum algorithm (without leading dot), never {@code null}.
+     */
+    @Override
+    public String getFileExtension()
+    {
+        return fileExtension;
+    }
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmHelper.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmHelper.java
new file mode 100644
index 0000000..5238dbb
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumAlgorithmHelper.java
@@ -0,0 +1,103 @@
+package org.eclipse.aether.spi.connector.checksum;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper for checksum operations.
+ *
+ * @since 1.8.0
+ */
+public final class ChecksumAlgorithmHelper
+{
+    private ChecksumAlgorithmHelper()
+    {
+        // nop
+    }
+
+    /**
+     * Calculates checksums for specified data.
+     *
+     * @param data        The content for which to calculate checksums, must not be {@code null}.
+     * @param factories   The checksum algorithm factories to use, must not be {@code null}.
+     * @return The calculated checksums, indexed by algorithm name, or the exception that occurred while trying to
+     * calculate it, never {@code null}.
+     * @throws IOException In case of any problem.
+     */
+    public static Map<String, String> calculate( byte[] data, List<ChecksumAlgorithmFactory> factories )
+            throws IOException
+    {
+        try ( InputStream inputStream = new ByteArrayInputStream( data ) )
+        {
+            return calculate( inputStream, factories );
+        }
+    }
+
+    /**
+     * Calculates checksums for specified file.
+     *
+     * @param file        The file for which to calculate checksums, must not be {@code null}.
+     * @param factories   The checksum algorithm factories to use, must not be {@code null}.
+     * @return The calculated checksums, indexed by algorithm name, or the exception that occurred while trying to
+     * calculate it, never {@code null}.
+     * @throws IOException In case of any problem.
+     */
+    public static Map<String, String> calculate( File file, List<ChecksumAlgorithmFactory> factories )
+            throws IOException
+    {
+        try ( InputStream inputStream = new BufferedInputStream( new FileInputStream( file ) ) )
+        {
+            return calculate( inputStream, factories );
+        }
+    }
+
+    private static Map<String, String> calculate( InputStream inputStream, List<ChecksumAlgorithmFactory> factories )
+            throws IOException
+    {
+        LinkedHashMap<String, ChecksumAlgorithm> algorithms = new LinkedHashMap<>();
+        factories.forEach( f -> algorithms.put( f.getName(), f.getAlgorithm() ) );
+        final byte[] buffer = new byte[ 1024 * 32 ];
+        for ( ; ; )
+        {
+            int read = inputStream.read( buffer );
+            if ( read < 0 )
+            {
+                break;
+            }
+            for ( ChecksumAlgorithm checksumAlgorithm : algorithms.values() )
+            {
+                checksumAlgorithm.update( ByteBuffer.wrap( buffer, 0, read ) );
+            }
+        }
+        LinkedHashMap<String, String> result = new LinkedHashMap<>();
+        algorithms.forEach( ( k, v ) -> result.put( k, v.checksum() ) );
+        return result;
+    }
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumPolicy.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumPolicy.java
index eb1716d..f7d7a37 100644
--- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumPolicy.java
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/checksum/ChecksumPolicy.java
@@ -8,9 +8,9 @@ package org.eclipse.aether.spi.connector.checksum;
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -26,7 +26,7 @@ import org.eclipse.aether.transfer.ChecksumFailureException;
  * downloaded file, a checksum policy instance is obtained and presented with the available checksums to conclude
  * whether the download is valid or not. The following pseudo-code illustrates the usage of a checksum policy by a
  * repository connector in some more detail (the retry logic has been omitted for the sake of brevity):
- * 
+ *
  * <pre>
  * void validateChecksums() throws ChecksumFailureException {
  *   for (checksum : checksums) {
@@ -46,7 +46,7 @@ import org.eclipse.aether.transfer.ChecksumFailureException;
  *   }
  *   policy.onNoMoreChecksums();
  * }
- * 
+ *
  * void downloadFile() throws Exception {
  *   ...
  *   policy = newChecksumPolicy();
@@ -59,7 +59,7 @@ import org.eclipse.aether.transfer.ChecksumFailureException;
  *   }
  * }
  * </pre>
- * 
+ * <p>
  * Checksum policies might be stateful and are generally not thread-safe.
  */
 public interface ChecksumPolicy
@@ -73,12 +73,12 @@ public interface ChecksumPolicy
     /**
      * Signals a match between the locally computed checksum value and the checksum value declared by the remote
      * repository.
-     * 
+     *
      * @param algorithm The name of the checksum algorithm being used, must not be {@code null}.
-     * @param kind A bit field providing further details about the checksum. See the {@code KIND_*} constants in this
-     *            interface for possible bit flags.
+     * @param kind      A bit field providing further details about the checksum. See the {@code KIND_*} constants in
+     *                  this interface for possible bit flags.
      * @return {@code true} to accept the download as valid and stop further validation, {@code false} to continue
-     *         validation with the next checksum.
+     * validation with the next checksum.
      */
     boolean onChecksumMatch( String algorithm, int kind );
 
@@ -86,39 +86,41 @@ public interface ChecksumPolicy
      * Signals a mismatch between the locally computed checksum value and the checksum value declared by the remote
      * repository. A simple policy would just rethrow the provided exception. More sophisticated policies could update
      * their internal state and defer a conclusion until all available checksums have been processed.
-     * 
+     *
      * @param algorithm The name of the checksum algorithm being used, must not be {@code null}.
-     * @param kind A bit field providing further details about the checksum. See the {@code KIND_*} constants in this
-     *            interface for possible bit flags.
+     * @param kind      A bit field providing further details about the checksum. See the {@code KIND_*} constants in
+     *                  this
+     *                  interface for possible bit flags.
      * @param exception The exception describing the checksum mismatch, must not be {@code null}.
      * @throws ChecksumFailureException If the checksum validation is to be failed. If the method returns normally,
-     *             validation continues with the next checksum.
+     *                                  validation continues with the next checksum.
      */
     void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception )
-        throws ChecksumFailureException;
+            throws ChecksumFailureException;
 
     /**
      * Signals an error while computing the local checksum value or retrieving the checksum value from the remote
      * repository.
-     * 
+     *
      * @param algorithm The name of the checksum algorithm being used, must not be {@code null}.
-     * @param kind A bit field providing further details about the checksum. See the {@code KIND_*} constants in this
-     *            interface for possible bit flags.
+     * @param kind      A bit field providing further details about the checksum. See the {@code KIND_*} constants in
+     *                  this
+     *                  interface for possible bit flags.
      * @param exception The exception describing the checksum error, must not be {@code null}.
      * @throws ChecksumFailureException If the checksum validation is to be failed. If the method returns normally,
-     *             validation continues with the next checksum.
+     *                                  validation continues with the next checksum.
      */
     void onChecksumError( String algorithm, int kind, ChecksumFailureException exception )
-        throws ChecksumFailureException;
+            throws ChecksumFailureException;
 
     /**
      * Signals that all available checksums have been processed.
-     * 
+     *
      * @throws ChecksumFailureException If the checksum validation is to be failed. If the method returns normally, the
-     *             download is assumed to be valid.
+     *                                  download is assumed to be valid.
      */
     void onNoMoreChecksums()
-        throws ChecksumFailureException;
+            throws ChecksumFailureException;
 
     /**
      * Signals that the download is being retried after a previously thrown {@link ChecksumFailureException} that is
@@ -130,12 +132,13 @@ public interface ChecksumPolicy
     /**
      * Signals that (even after a potential retry) checksum validation has failed. A policy could opt to merely log this
      * issue or insist on rejecting the downloaded file as unusable.
-     * 
+     *
      * @param exception The exception that was thrown from a prior call to
-     *            {@link #onChecksumMismatch(String, int, ChecksumFailureException)},
-     *            {@link #onChecksumError(String, int, ChecksumFailureException)} or {@link #onNoMoreChecksums()}.
+     *                  {@link #onChecksumMismatch(String, int, ChecksumFailureException)},
+     *                  {@link #onChecksumError(String, int, ChecksumFailureException)} or {@link
+     *                  #onNoMoreChecksums()}.
      * @return {@code true} to accept the download nevertheless and let artifact resolution succeed, {@code false} to
-     *         reject the transferred file as unusable.
+     * reject the transferred file as unusable.
      */
     boolean onTransferChecksumFailure( ChecksumFailureException exception );
 
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java
index a7382b9..8bc88a7 100644
--- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/layout/RepositoryLayout.java
@@ -8,9 +8,9 @@ package org.eclipse.aether.spi.connector.layout;
  * to you under the Apache License, Version 2.0 (the
  * "License"); you may not use this file except in compliance
  * with the License.  You may obtain a copy of the License at
- * 
+ *
  *  http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing,
  * software distributed under the License is distributed on an
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,11 +21,12 @@ package org.eclipse.aether.spi.connector.layout;
 
 import java.net.URI;
 import java.util.List;
-import java.util.Locale;
+
 import static java.util.Objects.requireNonNull;
 
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.metadata.Metadata;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
 
 /**
  * The layout for a remote repository whose artifacts/metadata can be addressed via URIs.
@@ -36,45 +37,44 @@ public interface RepositoryLayout
 {
 
     /**
-     * A descriptor for a checksum file. This descriptor simply associates the location of a checksum file with the
-     * underlying algorithm used to calculate/verify it. Checksum algorithms are denoted by names as used with
-     * {@link java.security.MessageDigest#getInstance(String)}, e.g. {@code "SHA-1"} or {@code "MD5"}.
+     * A descriptor for a checksum location. This descriptor simply associates the location of a checksum file with the
+     * underlying checksum algorithm used to calculate/verify it.
      */
-    final class Checksum
+    final class ChecksumLocation
     {
-
-        private final String algorithm;
-
         private final URI location;
 
+        private final ChecksumAlgorithmFactory checksumAlgorithmFactory;
+
         /**
          * Creates a new checksum file descriptor with the specified algorithm and location. The method
-         * {@link #forLocation(URI, String)} is usually more convenient though.
-         * 
-         * @param algorithm The algorithm used to calculate the checksum, must not be {@code null}.
-         * @param location The relative URI to the checksum file within a repository, must not be {@code null}.
+         * {@link #forLocation(URI, ChecksumAlgorithmFactory)} is usually more convenient though.
+         *
+         * @param location                 The relative URI to the checksum file within a repository, must not be {@code
+         *                                 null}.
+         * @param checksumAlgorithmFactory The checksum type used to calculate the checksum, must not be {@code null}.
          */
-        public Checksum( String algorithm, URI location )
+        public ChecksumLocation( URI location, ChecksumAlgorithmFactory checksumAlgorithmFactory )
         {
-            verify( algorithm, location );
-            this.algorithm = algorithm;
+            verify( location, checksumAlgorithmFactory );
             this.location = location;
+            this.checksumAlgorithmFactory = checksumAlgorithmFactory;
         }
 
         /**
-         * Creates a checksum file descriptor for the specified artifact/metadata location and algorithm. The location
+         * Creates a checksum descriptor for the specified artifact/metadata location and algorithm. The location
          * of the checksum file itself is derived from the supplied resource URI by appending the file extension
-         * corresponding to the algorithm. The file extension in turn is derived from the algorithm name by stripping
-         * out any hyphen ('-') characters and lower-casing the name, e.g. "SHA-1" is mapped to ".sha1".
-         * 
-         * @param location The relative URI to the artifact/metadata whose checksum file is being obtained, must not be
-         *            {@code null} and must not have a query or fragment part.
-         * @param algorithm The algorithm used to calculate the checksum, must not be {@code null}.
+         * specified by the algorithm factory. See {@link ChecksumAlgorithmFactory#getFileExtension()}.
+         *
+         * @param location                 The relative URI to the artifact/metadata whose checksum file is being
+         *                                 obtained, must not be
+         *                                 {@code null} and must not have a query or fragment part.
+         * @param checksumAlgorithmFactory The algorithm used to calculate the checksum, must not be {@code null}.
          * @return The checksum file descriptor, never {@code null}.
          */
-        public static Checksum forLocation( URI location, String algorithm )
+        public static ChecksumLocation forLocation( URI location, ChecksumAlgorithmFactory checksumAlgorithmFactory )
         {
-            verify( algorithm, location );
+            verify( location, checksumAlgorithmFactory );
             if ( location.getRawQuery() != null )
             {
                 throw new IllegalArgumentException( "resource location must not have query parameters: " + location );
@@ -83,39 +83,34 @@ public interface RepositoryLayout
             {
                 throw new IllegalArgumentException( "resource location must not have a fragment: " + location );
             }
-            String extension = '.' + algorithm.replace( "-", "" ).toLowerCase( Locale.ENGLISH );
-            return new Checksum( algorithm, URI.create( location.toString() + extension ) );
+            return new ChecksumLocation( URI.create( location + "." + checksumAlgorithmFactory.getFileExtension() ),
+                    checksumAlgorithmFactory );
         }
 
-        private static void verify( String algorithm, URI location )
+        private static void verify( URI location, ChecksumAlgorithmFactory checksumAlgorithmFactory )
         {
-            requireNonNull( algorithm, "checksum algorithm cannot be null" );
-            if ( algorithm.length() == 0 )
-            {
-                throw new IllegalArgumentException( "checksum algorithm cannot be empty" );
-            }
             requireNonNull( location, "checksum location cannot be null" );
             if ( location.isAbsolute() )
             {
                 throw new IllegalArgumentException( "checksum location must be relative" );
             }
+            requireNonNull( checksumAlgorithmFactory, "checksum algorithm factory cannot be null" );
         }
 
         /**
-         * Gets the name of the algorithm that is used to calculate the checksum.
-         * 
-         * @return The algorithm name, never {@code null}.
-         * @see java.security.MessageDigest#getInstance(String)
+         * Gets the {@link ChecksumAlgorithmFactory} that is used to calculate the checksum.
+         *
+         * @return The checksum factory, never {@code null}.
          */
-        public String getAlgorithm()
+        public ChecksumAlgorithmFactory getChecksumAlgorithmFactory()
         {
-            return algorithm;
+            return checksumAlgorithmFactory;
         }
 
         /**
          * Gets the location of the checksum file with a remote repository. The URI is relative to the root directory of
          * the repository.
-         * 
+         *
          * @return The relative URI to the checksum file, never {@code null}.
          */
         public URI getLocation()
@@ -126,17 +121,25 @@ public interface RepositoryLayout
         @Override
         public String toString()
         {
-            return location + " (" + algorithm + ")";
+            return location + " (" + checksumAlgorithmFactory.getName() + ")";
         }
-
     }
 
     /**
+     * Returns the list of checksum names this instance of layout uses, never {@code null}. The checksum order
+     * represents the order how checksums are validated (hence retrieved).
+     *
+     * @since 1.8.0
+     */
+    List<String> getChecksumAlgorithmNames();
+
+    /**
      * Gets the location within a remote repository where the specified artifact resides. The URI is relative to the
      * root directory of the repository.
-     * 
+     *
      * @param artifact The artifact to get the URI for, must not be {@code null}.
-     * @param upload {@code false} if the artifact is being downloaded, {@code true} if the artifact is being uploaded.
+     * @param upload   {@code false} if the artifact is being downloaded, {@code true} if the artifact is being
+     *                 uploaded.
      * @return The relative URI to the artifact, never {@code null}.
      */
     URI getLocation( Artifact artifact, boolean upload );
@@ -144,9 +147,10 @@ public interface RepositoryLayout
     /**
      * Gets the location within a remote repository where the specified metadata resides. The URI is relative to the
      * root directory of the repository.
-     * 
+     *
      * @param metadata The metadata to get the URI for, must not be {@code null}.
-     * @param upload {@code false} if the metadata is being downloaded, {@code true} if the metadata is being uploaded.
+     * @param upload   {@code false} if the metadata is being downloaded, {@code true} if the metadata is being
+     *                 uploaded.
      * @return The relative URI to the metadata, never {@code null}.
      */
     URI getLocation( Metadata metadata, boolean upload );
@@ -154,27 +158,27 @@ public interface RepositoryLayout
     /**
      * Gets the checksums files that a remote repository keeps to help detect data corruption during transfers of the
      * specified artifact.
-     * 
+     *
      * @param artifact The artifact to get the checksum files for, must not be {@code null}.
-     * @param upload {@code false} if the checksums are being downloaded/verified, {@code true} if the checksums are
-     *            being uploaded/created.
+     * @param upload   {@code false} if the checksums are being downloaded/verified, {@code true} if the checksums are
+     *                 being uploaded/created.
      * @param location The relative URI to the artifact within the repository as previously obtained from
-     *            {@link #getLocation(Artifact, boolean)}, must not be {@code null}.
+     *                 {@link #getLocation(Artifact, boolean)}, must not be {@code null}.
      * @return The checksum files for the given artifact, possibly empty but never {@code null}.
      */
-    List<Checksum> getChecksums( Artifact artifact, boolean upload, URI location );
+    List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location );
 
     /**
      * Gets the checksums files that a remote repository keeps to help detect data corruption during transfers of the
      * specified metadata.
-     * 
+     *
      * @param metadata The metadata to get the checksum files for, must not be {@code null}.
-     * @param upload {@code false} if the checksums are being downloaded/verified, {@code true} if the checksums are
-     *            being uploaded/created.
+     * @param upload   {@code false} if the checksums are being downloaded/verified, {@code true} if the checksums are
+     *                 being uploaded/created.
      * @param location The relative URI to the metadata within the repository as previously obtained from
-     *            {@link #getLocation(Metadata, boolean)}, must not be {@code null}.
+     *                 {@link #getLocation(Metadata, boolean)}, must not be {@code null}.
      * @return The checksum files for the given metadata, possibly empty but never {@code null}.
      */
-    List<Checksum> getChecksums( Metadata metadata, boolean upload, URI location );
+    List<ChecksumLocation> getChecksumLocations( Metadata metadata, boolean upload, URI location );
 
 }
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java
index 125d84d..45389a6 100644
--- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/transport/AbstractTransporter.java
@@ -22,7 +22,6 @@ package org.eclipse.aether.spi.connector.transport;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -254,14 +253,11 @@ public abstract class AbstractTransporter
     private static void copy( OutputStream os, InputStream is, TransportListener listener )
         throws IOException, TransferCancelledException
     {
-        ByteBuffer buffer = ByteBuffer.allocate( 1024 * 32 );
-        byte[] array = buffer.array();
-        for ( int read = is.read( array ); read >= 0; read = is.read( array ) )
+        byte[] buffer = new byte[ 1024 * 32 ];
+        for ( int read = is.read( buffer ); read >= 0; read = is.read( buffer ) )
         {
-            os.write( array, 0, read );
-            ( (Buffer) buffer ).rewind();
-            ( (Buffer) buffer ).limit( read );
-            listener.transportProgressed( buffer );
+            os.write( buffer, 0, read );
+            listener.transportProgressed( ByteBuffer.wrap( buffer, 0, read ) );
         }
     }
 
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/FileProcessor.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/FileProcessor.java
index 9e0f098..0a84eb1 100644
--- a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/FileProcessor.java
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/io/FileProcessor.java
@@ -112,4 +112,20 @@ public interface FileProcessor
 
     }
 
+    /**
+     * Reads checksum from specified file.
+     *
+     * @throws IOException in case of any IO error.
+     * @since 1.8.0
+     */
+    String readChecksum( File checksumFile ) throws IOException;
+
+    /**
+     * Writes checksum to specified file.
+     *
+     * @throws IOException in case of any IO error.
+     * @since 1.8.0
+     */
+    void writeChecksum( File checksumFile, String checksum ) throws IOException;
+
 }
diff --git a/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/layout/ChecksumLocationTest.java b/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/layout/ChecksumLocationTest.java
new file mode 100644
index 0000000..c8b53fa
--- /dev/null
+++ b/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/layout/ChecksumLocationTest.java
@@ -0,0 +1,95 @@
+package org.eclipse.aether.spi.connector.layout;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.net.URI;
+
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithm;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
+import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySupport;
+import org.junit.Test;
+
+import org.eclipse.aether.spi.connector.layout.RepositoryLayout.ChecksumLocation;
+
+import static org.junit.Assert.assertEquals;
+
+public class ChecksumLocationTest
+{
+    private ChecksumAlgorithmFactory SHA512 = new ChecksumAlgorithmFactorySupport("SHA-512", "sha512") {
+        @Override
+        public ChecksumAlgorithm getAlgorithm() {
+            throw new RuntimeException("this should not happen");
+        }
+    };
+
+    private ChecksumAlgorithmFactory SHA256 = new ChecksumAlgorithmFactorySupport("SHA-256", "sha256") {
+        @Override
+        public ChecksumAlgorithm getAlgorithm() {
+            throw new RuntimeException("this should not happen");
+        }
+    };
+
+    private ChecksumAlgorithmFactory SHA1 = new ChecksumAlgorithmFactorySupport("SHA-1", "sha1") {
+        @Override
+        public ChecksumAlgorithm getAlgorithm() {
+            throw new RuntimeException("this should not happen");
+        }
+    };
+
+    private ChecksumAlgorithmFactory MD5 = new ChecksumAlgorithmFactorySupport("MD5", "md5") {
+        @Override
+        public ChecksumAlgorithm getAlgorithm() {
+            throw new RuntimeException("this should not happen");
+        }
+    };
+
+    @Test
+    public void testForLocation()
+    {
+        ChecksumLocation cs = ChecksumLocation.forLocation( URI.create( "dir/sub%20dir/file.txt" ), SHA512 );
+        assertEquals( SHA512, cs.getChecksumAlgorithmFactory() );
+        assertEquals( "dir/sub%20dir/file.txt.sha512", cs.getLocation().toString() );
+
+        cs = ChecksumLocation.forLocation( URI.create( "dir/sub%20dir/file.txt" ), SHA256 );
+        assertEquals( SHA256, cs.getChecksumAlgorithmFactory() );
+        assertEquals( "dir/sub%20dir/file.txt.sha256", cs.getLocation().toString() );
+
+        cs = ChecksumLocation.forLocation( URI.create( "dir/sub%20dir/file.txt" ), SHA1 );
+        assertEquals( SHA1, cs.getChecksumAlgorithmFactory() );
+        assertEquals( "dir/sub%20dir/file.txt.sha1", cs.getLocation().toString() );
+
+        cs = ChecksumLocation.forLocation( URI.create( "dir/sub%20dir/file.txt" ), MD5 );
+        assertEquals( MD5, cs.getChecksumAlgorithmFactory() );
+        assertEquals( "dir/sub%20dir/file.txt.md5", cs.getLocation().toString() );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void testForLocation_WithQueryParams()
+    {
+        ChecksumLocation.forLocation( URI.create( "file.php?param=1" ), SHA1 );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void testForLocation_WithFragment()
+    {
+        ChecksumLocation.forLocation( URI.create( "file.html#fragment" ), SHA1 );
+    }
+
+}
diff --git a/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/layout/ChecksumTest.java b/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/layout/ChecksumTest.java
deleted file mode 100644
index 4d701b5..0000000
--- a/maven-resolver-spi/src/test/java/org/eclipse/aether/spi/connector/layout/ChecksumTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.eclipse.aether.spi.connector.layout;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import static org.junit.Assert.*;
-
-import java.net.URI;
-
-import org.junit.Test;
-
-import org.eclipse.aether.spi.connector.layout.RepositoryLayout.Checksum;
-
-public class ChecksumTest
-{
-
-    @Test
-    public void testForLocation()
-    {
-        Checksum cs = Checksum.forLocation( URI.create( "dir/sub%20dir/file.txt" ), "SHA-512" );
-        assertEquals( "SHA-512", cs.getAlgorithm() );
-        assertEquals( "dir/sub%20dir/file.txt.sha512", cs.getLocation().toString() );
-
-        cs = Checksum.forLocation( URI.create( "dir/sub%20dir/file.txt" ), "SHA-256" );
-        assertEquals( "SHA-256", cs.getAlgorithm() );
-        assertEquals( "dir/sub%20dir/file.txt.sha256", cs.getLocation().toString() );
-
-        cs = Checksum.forLocation( URI.create( "dir/sub%20dir/file.txt" ), "SHA-1" );
-        assertEquals( "SHA-1", cs.getAlgorithm() );
-        assertEquals( "dir/sub%20dir/file.txt.sha1", cs.getLocation().toString() );
-
-        cs = Checksum.forLocation( URI.create( "dir/sub%20dir/file.txt" ), "MD5" );
-        assertEquals( "MD5", cs.getAlgorithm() );
-        assertEquals( "dir/sub%20dir/file.txt.md5", cs.getLocation().toString() );
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void testForLocation_WithQueryParams()
-    {
-        Checksum.forLocation( URI.create( "file.php?param=1" ), "SHA-1" );
-    }
-
-    @Test( expected = IllegalArgumentException.class )
-    public void testForLocation_WithFragment()
-    {
-        Checksum.forLocation( URI.create( "file.html#fragment" ), "SHA-1" );
-    }
-
-}
diff --git a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestFileProcessor.java b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestFileProcessor.java
index 00089a7..ae5d9f8 100644
--- a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestFileProcessor.java
+++ b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/TestFileProcessor.java
@@ -29,6 +29,7 @@ import java.io.OutputStream;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 
 import org.eclipse.aether.spi.io.FileProcessor;
 
@@ -248,4 +249,15 @@ public class TestFileProcessor
         }
     }
 
+    @Override
+    public String readChecksum( final File checksumFile ) throws IOException
+    {
+        return new String( Files.readAllBytes( checksumFile.toPath() ), StandardCharsets.UTF_8 );
+    }
+
+    @Override
+    public void writeChecksum( final File checksumFile, final String checksum ) throws IOException
+    {
+        write( checksumFile, checksum );
+    }
 }
diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java
index 0ac354f..bf6ed3d 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/ChecksumUtils.java
@@ -50,7 +50,9 @@ public final class ChecksumUtils
      * @param checksumFile The path to the checksum file, must not be {@code null}.
      * @return The checksum stored in the file, never {@code null}.
      * @throws IOException If the checksum does not exist or could not be read for other reasons.
+     * @deprecated Use SPI FileProcessor to read and write checksum files.
      */
+    @Deprecated
     public static String read( File checksumFile )
         throws IOException
     {
@@ -101,21 +103,25 @@ public final class ChecksumUtils
      * @return The calculated checksums, indexed by algorithm name, or the exception that occurred while trying to
      *         calculate it, never {@code null}.
      * @throws IOException If the data file could not be read.
+     * @deprecated Use SPI checksum selector instead.
      */
+    @Deprecated
     public static Map<String, Object> calc( File dataFile, Collection<String> algos )
                     throws IOException
     {
        return calc( new FileInputStream( dataFile ), algos );
     }
 
-    
+    /**
+     * @deprecated Use SPI checksum selector instead.
+     */
+    @Deprecated
     public static Map<String, Object> calc( byte[] dataBytes, Collection<String> algos )
                     throws IOException
     {
         return calc( new ByteArrayInputStream( dataBytes ), algos );
     }
 
-    
     private static Map<String, Object> calc( InputStream data, Collection<String> algos )
         throws IOException
     {
diff --git a/src/site/markdown/about-checksums.md b/src/site/markdown/about-checksums.md
new file mode 100644
index 0000000..5e55f30
--- /dev/null
+++ b/src/site/markdown/about-checksums.md
@@ -0,0 +1,73 @@
+# About Checksums
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+
+Maven Resolver uses checksums to verify the integrity of downloaded artifacts and
+metadata. Checksums are usually laid out in repositories next to the file in question, with file
+extension telling the checksum algorithm that produced the given checksum file content. Currently,
+most Maven repositories contain SHA-1 and MD5 checksums by default (they are produced by Resolver by default).
+
+Historically, Maven Resolver used `java.security.MessageDigest` to implement checksums. So to say, secure one-way
+hashes provided by Java Cryptography Architecture were (mis)used to implement checksums for transport integrity
+validation. There is no misunderstanding here, secure hashes MAY be used as checksums, as there is quite some
+overlap between checksums and hashes in general. But this simplicity comes at a price: cryptographically safe
+algorithms require way more CPU cycles to calculate checksum, while all their purpose is just
+integrity validation, nothing more. There is no security, trust or whatever else implied or expected from
+them.
+
+If you are interested in trust in your artifacts, it is signatures (for example
+[GPG Signatures](https://maven.apache.org/plugins/maven-gpg-plugin/)) that you should look for.
+
+Hence, the usual argument that "XXX algorithm is unsafe, deprecated, not secure anymore" does not stand in use case
+of Maven Resolver: there is nothing secure being involved with checksums. Moreover, this is true not only for SHA-1
+algorithm, but even for its "elder brother" MD5. Both algorithms are still widely used today as "transport integrity
+validation" or "error detection" (aka "bit-rot detection").
+
+## Checksum Changes
+
+From a technical perspective, the above written facts infer following consequences: as checksum algorithms are exposed
+to the user, so one can set them via configuration, users are not prevented to ask for SHA-256 or even SHA-512, even if
+these algorithms are not part of standard Maven process. Moreover, nothing prevent users (integrating
+Maven Resolver) registering with Java an alternate Java Cryptography Provider and use even broader (or exotic) set
+of message digests for checksums. While this is not wrong or even mistake in any case, we do consider this as
+wrong use case. The notion of transport validation and secure hashes are being constantly mixed up due historical
+reasons explained above.
+
+Hence, Maven Resolver team decided to make supported set of checksums limited. Instead of directly exposing
+`MessageDigest` algorithms, we introduced an API around checksums. This not only prevents wrong use cases (not
+exposing all supported algorithms of `MessageDigest` to users), but also makes possible to introduce real checksum
+algorithms. Finally, the set of supported checksum algorithms remains extensible: if some required algorithm is
+not provided by Resolver, it can be easily added by creating a factory component for it.
+
+Resolver out of the box supports the following checksum algorithms:
+
+* MD5
+* SHA-1
+* SHA-256
+* SHA-512
+
+We are aware that users started using "better SHA" algorithms, and we do not want to break them. Nothing for them
+changes (configuration and everything basically remains the same). But, we do want to prevent any possible further
+proliferation of non-standard checksums.
+
+Links:
+
+* [SHA-1](https://en.wikipedia.org/wiki/SHA-1) (see "Data Integrity" section)
+* [MD5](https://en.wikipedia.org/wiki/MD5) (see "Applications" section, especially about error checking functionality)
+
diff --git a/src/site/markdown/configuration.md b/src/site/markdown/configuration.md
index e5f356e..169f5b0 100644
--- a/src/site/markdown/configuration.md
+++ b/src/site/markdown/configuration.md
@@ -22,7 +22,7 @@ Option | Type | Description | Default Value | Supports Repo ID Suffix
 --- | --- | --- | --- | ---
 `aether.artifactResolver.snapshotNormalization` | boolean | It replaces the timestamped snapshot file name with a filename containing the `SNAPSHOT` qualifier only. This only affects resolving/retrieving artifacts but not uploading those. | `true` | no
 `aether.checksums.forSignature` | boolean | Flag indicating if signature artifacts (`.asc`) should have checksums. | `false` | no
-`aether.checksums.algorithms` | String | List of [algorithms](https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#MessageDigest) passed to [`MessageDigest`](https://docs.oracle.com/javase/7/docs/api/java/security/MessageDigest.html) with which checksums are validated (downloaded) and generated (uploaded). | `"SHA-1,MD5"` | no
+`aether.checksums.algorithms` | String | Comma separated list of checksum algorithms with which checksums are validated (downloaded) and generated (uploaded). Resolver by default supports following algorithms: `MD5`, `SHA-1`, `SHA-256` and `SHA-512`. New algorithms can be added by implementing `ChecksumAlgorithmFactory` component. | `"SHA-1,MD5"` | no
 `aether.conflictResolver.verbose` | boolean | Flag controlling the conflict resolver's verbose mode. | `false` | no
 `aether.connector.basic.threads` or `maven.artifact.threads` | int | Number of threads to use for uploading/downloading. | `5` | no
 `aether.connector.classpath.loader` | ClassLoader | `ClassLoader` from which resources should be retrieved which start with the `classpath:` protocol. | `Thread.currentThread().getContextClassLoader()` | no
diff --git a/src/site/site.xml b/src/site/site.xml
index a18b855..db7af75 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -27,6 +27,7 @@ under the License.
     <menu name="Overview">
       <item name="Introduction" href="index.html"/>
       <item name="Configuration" href="configuration.html"/>
+      <item name="About Checksums" href="about-checksums.html"/>
       <item name="Maven 3.8.x" href="maven-3.8.x.html"/>
       <item name="JavaDocs" href="apidocs/index.html"/>
       <item name="Source Xref" href="xref/index.html"/>