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/03/15 08:04:33 UTC

[maven-resolver] 02/19: Fix for MRESOLVER-242

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

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

commit 2e123bfc6300b19cde7174c8bc52190f85c29816
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Tue Feb 22 10:37:34 2022 +0100

    Fix for MRESOLVER-242
    
    Makes resolver aware of signatures in very same way as
    it became aware of checksums, but not implementing
    signign/verification services yet.
---
 .../aether/connector/basic/ChecksumValidator.java  |  9 ++-
 .../eclipse/aether/impl/guice/AetherModule.java    | 19 +++++
 .../impl/Maven2RepositoryLayoutFactory.java        | 62 ++++++++++++---
 .../DefaultSignatureAlgorithmFactorySelector.java  | 89 ++++++++++++++++++++++
 .../signature/GpgSignatureAlgorithmFactory.java    | 52 +++++++++++++
 .../spi/connector/layout/RepositoryLayout.java     | 14 +++-
 .../connector/signature/SignatureAlgorithm.java    | 38 +++++++++
 .../signature/SignatureAlgorithmFactory.java       | 49 ++++++++++++
 .../SignatureAlgorithmFactorySelector.java         | 45 +++++++++++
 .../SignatureAlgorithmFactorySupport.java          | 59 ++++++++++++++
 10 files changed, 422 insertions(+), 14 deletions(-)

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 87b6b3c..14ae450 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
@@ -120,11 +120,14 @@ final class ChecksumValidator
         {
             return;
         }
-        if ( validateExternalChecksums( actualChecksums ) )
+        if ( !checksumLocations.isEmpty() )
         {
-            return;
+            if ( validateExternalChecksums( actualChecksums ) )
+            {
+                return;
+            }
+            checksumPolicy.onNoMoreChecksums();
         }
-        checksumPolicy.onNoMoreChecksums();
     }
 
     private boolean validateChecksums( Map<String, ?> actualChecksums, ChecksumKind kind, Map<String, ?> checksums )
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 9629dc9..64a201b 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
@@ -48,6 +48,8 @@ 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.signature.DefaultSignatureAlgorithmFactorySelector;
+import org.eclipse.aether.internal.impl.signature.GpgSignatureAlgorithmFactory;
 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;
@@ -90,6 +92,8 @@ 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.signature.SignatureAlgorithmFactory;
+import org.eclipse.aether.spi.connector.signature.SignatureAlgorithmFactorySelector;
 import org.eclipse.aether.spi.connector.transport.TransporterProvider;
 import org.eclipse.aether.spi.io.FileProcessor;
 import org.eclipse.aether.spi.localrepo.LocalRepositoryManagerFactory;
@@ -184,6 +188,11 @@ public class AetherModule
         bind( ChecksumAlgorithmFactorySelector.class )
                 .to( DefaultChecksumAlgorithmFactorySelector.class ).in ( Singleton.class );
 
+        bind( SignatureAlgorithmFactory.class ).annotatedWith( Names.named( GpgSignatureAlgorithmFactory.NAME ) )
+                .to( GpgSignatureAlgorithmFactory.class );
+        bind( SignatureAlgorithmFactorySelector.class )
+                .to( DefaultSignatureAlgorithmFactorySelector.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 )
@@ -241,6 +250,16 @@ public class AetherModule
 
     @Provides
     @Singleton
+    Map<String, SignatureAlgorithmFactory> provideSignatureTypes(
+            @Named( GpgSignatureAlgorithmFactory.NAME ) SignatureAlgorithmFactory gpg )
+    {
+        Map<String, SignatureAlgorithmFactory> checksumTypes = new HashMap<>();
+        checksumTypes.put( GpgSignatureAlgorithmFactory.NAME, gpg );
+        return Collections.unmodifiableMap( checksumTypes );
+    }
+
+    @Provides
+    @Singleton
     Map<String, NameMapper> provideNameMappers(
             @Named( StaticNameMapper.NAME ) NameMapper staticNameMapper,
             @Named( GAVNameMapper.NAME ) NameMapper gavNameMapper,
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 d0b3de9..e95a011 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
@@ -34,12 +34,15 @@ 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.internal.impl.signature.DefaultSignatureAlgorithmFactorySelector;
 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.spi.connector.signature.SignatureAlgorithmFactory;
+import org.eclipse.aether.spi.connector.signature.SignatureAlgorithmFactorySelector;
 import org.eclipse.aether.transfer.NoRepositoryLayoutException;
 import org.eclipse.aether.util.ConfigUtils;
 
@@ -55,14 +58,21 @@ public final class Maven2RepositoryLayoutFactory
 {
 
     static final String CONFIG_PROP_SIGNATURE_CHECKSUMS = "aether.checksums.forSignature";
+
     static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = "aether.checksums.algorithms";
 
     static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
 
+    static final String CONFIG_PROP_SIGNATURE_ALGORITHMS = "aether.signatures.algorithms";
+
+    static final String DEFAULT_SIGNATURE_ALGORITHMS = "GPG";
+
     private float priority;
 
     private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
 
+    private final SignatureAlgorithmFactorySelector signatureAlgorithmFactorySelector;
+
     public float getPriority()
     {
         return priority;
@@ -74,13 +84,15 @@ public final class Maven2RepositoryLayoutFactory
     @Deprecated
     public Maven2RepositoryLayoutFactory()
     {
-        this( new DefaultChecksumAlgorithmFactorySelector() );
+        this( new DefaultChecksumAlgorithmFactorySelector(), new DefaultSignatureAlgorithmFactorySelector() );
     }
 
     @Inject
-    public Maven2RepositoryLayoutFactory( ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector )
+    public Maven2RepositoryLayoutFactory( ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector,
+                                          SignatureAlgorithmFactorySelector signatureAlgorithmFactorySelector )
     {
         this.checksumAlgorithmFactorySelector = requireNonNull( checksumAlgorithmFactorySelector );
+        this.signatureAlgorithmFactorySelector = requireNonNull( signatureAlgorithmFactorySelector );
     }
 
     /**
@@ -118,20 +130,37 @@ public final class Maven2RepositoryLayoutFactory
             checksumsAlgorithms.add( checksumAlgorithmFactorySelector.select( checksumsAlgorithmName ) );
         }
 
+        // no need for order here
+        List<String> signatureAlgorithmNames = Arrays.asList(
+                ConfigUtils.getString(
+                        session, DEFAULT_SIGNATURE_ALGORITHMS, CONFIG_PROP_SIGNATURE_ALGORITHMS
+                ).split( "," )
+        );
+
+        List<SignatureAlgorithmFactory> signatureAlgorithms = new ArrayList<>( signatureAlgorithmNames.size() );
+        for ( String signatureAlgorithmName : signatureAlgorithmNames )
+        {
+            signatureAlgorithms.add( signatureAlgorithmFactorySelector.select( signatureAlgorithmName ) );
+        }
+
         return forSignature
-                ? new Maven2RepositoryLayout( checksumsAlgorithms )
-                : new Maven2RepositoryLayoutEx( checksumsAlgorithms );
+                ? new Maven2RepositoryLayout( checksumsAlgorithms, signatureAlgorithms )
+                : new Maven2RepositoryLayoutEx( checksumsAlgorithms, signatureAlgorithms );
     }
 
     private static class Maven2RepositoryLayout
             implements RepositoryLayout
     {
 
-        private final List<ChecksumAlgorithmFactory> checksumAlgorithms;
+        protected final List<ChecksumAlgorithmFactory> checksumAlgorithms;
 
-        protected Maven2RepositoryLayout( List<ChecksumAlgorithmFactory> checksumAlgorithms )
+        protected final List<SignatureAlgorithmFactory> signatureAlgorithms;
+
+        protected Maven2RepositoryLayout( List<ChecksumAlgorithmFactory> checksumAlgorithms,
+                                          List<SignatureAlgorithmFactory> signatureAlgorithms )
         {
             this.checksumAlgorithms = Collections.unmodifiableList( checksumAlgorithms );
+            this.signatureAlgorithms = Collections.unmodifiableList( signatureAlgorithms );
         }
 
         private URI toUri( String path )
@@ -153,6 +182,12 @@ public final class Maven2RepositoryLayoutFactory
         }
 
         @Override
+        public List<SignatureAlgorithmFactory> getSignatureAlgorithmFactories()
+        {
+            return signatureAlgorithms;
+        }
+
+        @Override
         public URI getLocation( Artifact artifact, boolean upload )
         {
             StringBuilder path = new StringBuilder( 128 );
@@ -206,6 +241,10 @@ public final class Maven2RepositoryLayoutFactory
         @Override
         public List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location )
         {
+            if ( isChecksum( artifact.getExtension() ) )
+            {
+                return Collections.emptyList();
+            }
             return getChecksumLocations( location );
         }
 
@@ -225,15 +264,20 @@ public final class Maven2RepositoryLayoutFactory
             return checksumLocations;
         }
 
+        private boolean isChecksum( String extension )
+        {
+            return checksumAlgorithms.stream().anyMatch( a -> extension.endsWith( "." + a.getFileExtension() ) );
+        }
     }
 
     private static class Maven2RepositoryLayoutEx
             extends Maven2RepositoryLayout
     {
 
-        protected Maven2RepositoryLayoutEx( List<ChecksumAlgorithmFactory> checksumAlgorithms )
+        protected Maven2RepositoryLayoutEx( List<ChecksumAlgorithmFactory> checksumAlgorithms,
+                                            List<SignatureAlgorithmFactory> signatureAlgorithms )
         {
-            super( checksumAlgorithms );
+            super( checksumAlgorithms, signatureAlgorithms );
         }
 
         @Override
@@ -248,7 +292,7 @@ public final class Maven2RepositoryLayoutFactory
 
         private boolean isSignature( String extension )
         {
-            return extension.endsWith( ".asc" );
+            return signatureAlgorithms.stream().anyMatch( a -> extension.endsWith( "." + a.getFileExtension() ) );
         }
 
     }
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/signature/DefaultSignatureAlgorithmFactorySelector.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/signature/DefaultSignatureAlgorithmFactorySelector.java
new file mode 100644
index 0000000..2455d52
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/signature/DefaultSignatureAlgorithmFactorySelector.java
@@ -0,0 +1,89 @@
+package org.eclipse.aether.internal.impl.signature;
+
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.aether.spi.connector.signature.SignatureAlgorithmFactory;
+import org.eclipse.aether.spi.connector.signature.SignatureAlgorithmFactorySelector;
+
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.toList;
+
+/**
+ * Default implementation.
+ *
+ * @since 1.8.0
+ */
+@Singleton
+@Named
+public class DefaultSignatureAlgorithmFactorySelector
+        implements SignatureAlgorithmFactorySelector
+{
+    private final Map<String, SignatureAlgorithmFactory> factories;
+
+    /**
+     * Default ctor for SL.
+     */
+    @Deprecated
+    public DefaultSignatureAlgorithmFactorySelector()
+    {
+        this.factories = new HashMap<>();
+        this.factories.put( GpgSignatureAlgorithmFactory.NAME, new GpgSignatureAlgorithmFactory() );
+    }
+
+    @Inject
+    public DefaultSignatureAlgorithmFactorySelector( Map<String, SignatureAlgorithmFactory> factories )
+    {
+        this.factories = requireNonNull( factories );
+    }
+
+    @Override
+    public SignatureAlgorithmFactory select( String algorithmName )
+    {
+        requireNonNull( algorithmName, "algorithmMame must not be null" );
+        SignatureAlgorithmFactory factory = factories.get( algorithmName );
+        if ( factory == null )
+        {
+            throw new IllegalArgumentException(
+                    String.format( "Unsupported signature algorithm %s, supported ones are %s",
+                            algorithmName,
+                            getSignatureAlgorithmFactories().stream()
+                                    .map( SignatureAlgorithmFactory::getName )
+                                    .collect( toList() )
+                    )
+            );
+        }
+        return factory;
+    }
+
+    @Override
+    public List<SignatureAlgorithmFactory> getSignatureAlgorithmFactories()
+    {
+        return new ArrayList<>( factories.values() );
+    }
+}
diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/signature/GpgSignatureAlgorithmFactory.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/signature/GpgSignatureAlgorithmFactory.java
new file mode 100644
index 0000000..2f7ac67
--- /dev/null
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/signature/GpgSignatureAlgorithmFactory.java
@@ -0,0 +1,52 @@
+package org.eclipse.aether.internal.impl.signature;
+
+/*
+ * 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 org.eclipse.aether.spi.connector.signature.SignatureAlgorithm;
+import org.eclipse.aether.spi.connector.signature.SignatureAlgorithmFactorySupport;
+
+/**
+ * The GPG signature type.
+ *
+ * @since 1.8.0
+ */
+@Singleton
+@Named( GpgSignatureAlgorithmFactory.NAME )
+public class GpgSignatureAlgorithmFactory
+        extends SignatureAlgorithmFactorySupport
+{
+    public static final String NAME = "GPG";
+
+    @Inject
+    public GpgSignatureAlgorithmFactory()
+    {
+        super( NAME, "asc" );
+    }
+
+    @Override
+    public SignatureAlgorithm getAlgorithm()
+    {
+        throw new IllegalStateException( "not implemented" );
+    }
+}
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 4a02b06..f461560 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
@@ -27,6 +27,7 @@ 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;
+import org.eclipse.aether.spi.connector.signature.SignatureAlgorithmFactory;
 
 /**
  * The layout for a remote repository whose artifacts/metadata can be addressed via URIs.
@@ -134,6 +135,13 @@ public interface RepositoryLayout
     List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories();
 
     /**
+     * Returns immutable list of {@link SignatureAlgorithmFactory} this instance of layout uses, never {@code null}.
+     *
+     * @since 1.8.0
+     */
+    List<SignatureAlgorithmFactory> getSignatureAlgorithmFactories();
+
+    /**
      * Gets the location within a remote repository where the specified artifact resides. The URI is relative to the
      * root directory of the repository.
      *
@@ -164,7 +172,8 @@ public interface RepositoryLayout
      *                 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}.
-     * @return The checksum files for the given artifact, possibly empty but never {@code null}.
+     * @return The checksum files for the given artifact, possibly empty but never {@code null}. If empty, that means
+     * that this layout does not provide checksums for given artifact.
      */
     List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location );
 
@@ -177,7 +186,8 @@ public interface RepositoryLayout
      *                 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}.
-     * @return The checksum files for the given metadata, possibly empty but never {@code null}.
+     * @return The checksum files for the given metadata, possibly empty but never {@code null}. If empty, that means
+     * that this layout does not provide checksums for given artifact.
      */
     List<ChecksumLocation> getChecksumLocations( Metadata metadata, boolean upload, URI location );
 
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithm.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithm.java
new file mode 100644
index 0000000..4ab960f
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithm.java
@@ -0,0 +1,38 @@
+package org.eclipse.aether.spi.connector.signature;
+
+/*
+ * 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.Closeable;
+import java.nio.ByteBuffer;
+
+/**
+ * Implementation performing signature calculation for specific algorithm. Instances of this interface are stateful,
+ * non-thread safe, and should not be reused.
+ *
+ * @since 1.8.0
+ */
+public interface SignatureAlgorithm extends Closeable
+{
+    /**
+     * Updates the checksum algorithm inner state with input.
+     * TODO: figure out some API for this to suit signing.
+     */
+    void update( ByteBuffer input );
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactory.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactory.java
new file mode 100644
index 0000000..0c7bf7c
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactory.java
@@ -0,0 +1,49 @@
+package org.eclipse.aether.spi.connector.signature;
+
+/*
+ * 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 signature factory: provides {@link SignatureAlgorithm} 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 SignatureAlgorithmFactorySelector} instead.
+ *
+ * @since 1.8.0
+ */
+public interface SignatureAlgorithmFactory
+{
+    /**
+     * 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: "GPG".
+     */
+    String getName();
+
+    /**
+     * Returns the file extension to be used for given signature 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: "asc".
+     */
+    String getFileExtension();
+
+    /**
+     * Each invocation of this method returns a new instance of algorithm, never {@code null} value.
+     * TODO: figure out some API for this to suit signing.
+     */
+    SignatureAlgorithm getAlgorithm();
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactorySelector.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactorySelector.java
new file mode 100644
index 0000000..27b04d0
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactorySelector.java
@@ -0,0 +1,45 @@
+package org.eclipse.aether.spi.connector.signature;
+
+/*
+ * 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.Collection;
+
+/**
+ * Component performing selection of {@link SignatureAlgorithmFactory} based on known factory names.
+ *
+ * @since 1.8.0
+ */
+public interface SignatureAlgorithmFactorySelector
+{
+    /**
+     * Returns factory for given algorithm name, or throws if algorithm not supported.
+     *
+     * @throws IllegalArgumentException if asked algorithm name is not supported.
+     */
+    SignatureAlgorithmFactory select( String algorithmName );
+
+    /**
+     * Returns a collection of supported algorithm names. This set represents ALL the algorithms supported by Resolver,
+     * and is NOT in any relation to given repository layout used signatures, returned by method {@link
+     * org.eclipse.aether.spi.connector.layout.RepositoryLayout#getSignatureAlgorithmFactories()} (in fact, is super set
+     * of it).
+     */
+    Collection<SignatureAlgorithmFactory> getSignatureAlgorithmFactories();
+}
diff --git a/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactorySupport.java b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactorySupport.java
new file mode 100644
index 0000000..2c6c7e3
--- /dev/null
+++ b/maven-resolver-spi/src/main/java/org/eclipse/aether/spi/connector/signature/SignatureAlgorithmFactorySupport.java
@@ -0,0 +1,59 @@
+package org.eclipse.aether.spi.connector.signature;
+
+/*
+ * 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 SignatureAlgorithmFactory} implementations.
+ *
+ * @since 1.8.0
+ */
+public abstract class SignatureAlgorithmFactorySupport
+    implements SignatureAlgorithmFactory
+{
+    private final String name;
+
+    private final String fileExtension;
+
+    public SignatureAlgorithmFactorySupport( 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 signature algorithm (without leading dot), never {@code null}.
+     */
+    @Override
+    public String getFileExtension()
+    {
+        return fileExtension;
+    }
+}