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

martin_s pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/archiva-redback-core.git


The following commit(s) were added to refs/heads/master by this push:
     new 3ff31ad  Fix and further implementation of JWT authenticator
3ff31ad is described below

commit 3ff31adf4c773874d6c8fc6117c12113eb401507
Author: Martin Stockhammer <[email protected]>
AuthorDate: Wed Jul 8 18:26:01 2020 +0200

    Fix and further implementation of JWT authenticator
---
 pom.xml                                            |  1 -
 .../authentication/jwt/JwtAuthenticator.java       | 93 ++++++++++++++++++++--
 .../archiva/redback/config-defaults.properties     |  8 +-
 3 files changed, 92 insertions(+), 10 deletions(-)

diff --git a/pom.xml b/pom.xml
index ae2f5c4..d3a2f06 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,7 +55,6 @@
     <module>redback-keys</module>
     <module>redback-users</module>
     <module>redback-integrations</module>
-    <module>redback-authentication-jwt</module>
   </modules>
 
   <scm>
diff --git 
a/redback-authentication/redback-authentication-providers/redback-authentication-jwt/src/main/java/org/apache/archiva/redback/authentication/jwt/JwtAuthenticator.java
 
b/redback-authentication/redback-authentication-providers/redback-authentication-jwt/src/main/java/org/apache/archiva/redback/authentication/jwt/JwtAuthenticator.java
index b78ab6a..8ebe45f 100644
--- 
a/redback-authentication/redback-authentication-providers/redback-authentication-jwt/src/main/java/org/apache/archiva/redback/authentication/jwt/JwtAuthenticator.java
+++ 
b/redback-authentication/redback-authentication-providers/redback-authentication-jwt/src/main/java/org/apache/archiva/redback/authentication/jwt/JwtAuthenticator.java
@@ -19,7 +19,6 @@ package org.apache.archiva.redback.authentication.jwt;
  * under the License.
  */
 
-import io.jsonwebtoken.Jwts;
 import io.jsonwebtoken.SignatureAlgorithm;
 import io.jsonwebtoken.security.Keys;
 import org.apache.archiva.redback.authentication.AbstractAuthenticator;
@@ -32,6 +31,8 @@ import 
org.apache.archiva.redback.configuration.UserConfiguration;
 import org.apache.archiva.redback.configuration.UserConfigurationKeys;
 import org.apache.archiva.redback.policy.AccountLockedException;
 import org.apache.archiva.redback.policy.MustChangePasswordException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.PostConstruct;
@@ -41,11 +42,19 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.attribute.PosixFilePermissions;
 import java.security.Key;
 import java.security.KeyFactory;
 import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
 import java.util.Base64;
 import java.util.Properties;
 
@@ -53,8 +62,13 @@ import java.util.Properties;
 @Service("authenticator#jwt")
 public class JwtAuthenticator extends AbstractAuthenticator implements 
Authenticator
 {
+    private static final Logger log = LoggerFactory.getLogger( 
JwtAuthenticator.class );
+
     public static final String ID = "JwtAuthenticator";
-    public static final String PROP_ALG = "algorithm";
+    public static final String PROP_PRIV_ALG = "privateAlgorithm";
+    public static final String PROP_PRIV_FORMAT = "privateFormat";
+    public static final String PROP_PUB_ALG = "publicAlgorithm";
+    public static final String PROP_PUB_FORMAT = "publicFormat";
     public static final String PROP_PRIVATEKEY = "privateKey";
     public static final String PROP_PUBLICKEY = "publicKey";
 
@@ -65,7 +79,7 @@ public class JwtAuthenticator extends AbstractAuthenticator 
implements Authentic
 
     boolean symmetricAlg = true;
     Key key;
-    KeyPair keyPair;
+    Key publicKey;
     String sigAlg;
     String keystoreType;
     Path keystoreFilePath;
@@ -92,8 +106,9 @@ public class JwtAuthenticator extends AbstractAuthenticator 
implements Authentic
             {
                 this.key = createNewSecretKey( this.sigAlg );
             } else {
-                this.keyPair = createNewKeyPair( this.sigAlg );
-                this.keyPair.getPublic();
+                KeyPair pair = createNewKeyPair( this.sigAlg );
+                this.key = pair.getPrivate( );
+                this.publicKey = pair.getPublic( );
             }
         }
     }
@@ -113,7 +128,7 @@ public class JwtAuthenticator extends AbstractAuthenticator 
implements Authentic
             try ( InputStream in = Files.newInputStream( filePath )) {
                 props.loadFromXML( in );
             }
-            String algorithm = props.getProperty( PROP_ALG ).trim( );
+            String algorithm = props.getProperty( PROP_PRIV_ALG ).trim( );
             String secretKey = props.getProperty( PROP_PRIVATEKEY ).trim( );
             byte[] keyData = Base64.getDecoder( ).decode( secretKey.getBytes() 
);
             return new SecretKeySpec(keyData, algorithm);
@@ -122,9 +137,71 @@ public class JwtAuthenticator extends 
AbstractAuthenticator implements Authentic
         }
     }
 
-    private KeyPair loadPairFromFile(Path filePath) throws IOException
+
+    private KeyPair loadPairFromFile(Path filePath) throws IOException, 
NoSuchAlgorithmException, InvalidKeySpecException
+    {
+        if (Files.exists( filePath )) {
+            Properties props = new Properties( );
+            try ( InputStream in = Files.newInputStream( filePath )) {
+                props.loadFromXML( in );
+            }
+            String algorithm = props.getProperty( PROP_PRIV_ALG ).trim( );
+            String secretKeyBase64 = props.getProperty( PROP_PRIVATEKEY 
).trim( );
+            String publicKeyBase64 = props.getProperty( PROP_PUBLICKEY ).trim( 
);
+            byte[] privateBytes = Base64.getDecoder( ).decode( secretKeyBase64 
);
+            byte[] publicBytes = Base64.getDecoder( ).decode( publicKeyBase64 
);
+
+            PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec( 
privateBytes );
+            X509EncodedKeySpec publicSpec = new X509EncodedKeySpec( 
publicBytes );
+            PrivateKey privateKey = KeyFactory.getInstance( algorithm 
).generatePrivate( privateSpec );
+            PublicKey publicKey = KeyFactory.getInstance( algorithm 
).generatePublic( publicSpec );
+
+            return new KeyPair( publicKey, privateKey );
+        } else {
+            throw new RuntimeException( "Could not load key file from " + 
filePath );
+        }
+    }
+
+    private void writeSecretKey(Path filePath, SecretKey key) throws 
IOException
     {
-        return null;
+        log.info( "Writing secret key algorithm=" + key.getAlgorithm( ) + ", 
format=" + key.getFormat( ) + " to file " + filePath );
+        Properties props = new Properties( );
+        props.setProperty( PROP_PRIV_ALG, key.getAlgorithm( ) );
+        if ( key.getFormat( ) != null )
+        {
+            props.setProperty( PROP_PRIV_FORMAT, key.getFormat( ) );
+        }
+        props.setProperty( PROP_PRIVATEKEY, String.valueOf( Base64.getEncoder( 
).encode( key.getEncoded( ) ) ) );
+        try ( OutputStream out = Files.newOutputStream( filePath ) )
+        {
+            props.storeToXML( out, "Key for JWT signing" );
+        }
+        try
+        {
+            Files.setPosixFilePermissions( filePath, 
PosixFilePermissions.fromString( "600" ) );
+        } catch (Exception e) {
+            log.error( "Could not set file permissions for " + filePath );
+        }
+    }
+
+    private void writeKeyPair(Path filePath, PrivateKey privateKey, PublicKey 
publicKey) {
+        log.info( "Writing private key algorithm=" + privateKey.getAlgorithm( 
) + ", format=" + privateKey.getFormat( ) + " to file " + filePath );
+        log.info( "Writing public key algorithm=" + publicKey.getAlgorithm( ) 
+ ", format=" + publicKey.getFormat( ) + " to file " + filePath );
+        Properties props = new Properties( );
+        props.setProperty( PROP_PRIV_ALG, privateKey.getAlgorithm( ) );
+        if (privateKey.getFormat()!=null) {
+            props.setProperty( PROP_PRIV_FORMAT, privateKey.getFormat( ) );
+        }
+        props.setProperty( PROP_PUB_ALG, publicKey.getAlgorithm( ) );
+        if (publicKey.getFormat()!=null) {
+            props.setProperty( PROP_PUB_FORMAT, publicKey.getFormat( ) );
+        }
+        PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec( 
privateKey.getEncoded( ) );
+        X509EncodedKeySpec publicSpec = new X509EncodedKeySpec( 
publicKey.getEncoded( ) );
+        props.setProperty( PROP_PRIVATEKEY, Base64.getEncoder( 
).encodeToString( privateSpec.getEncoded( ) ) );
+        props.setProperty( PROP_PUBLICKEY, Base64.getEncoder( 
).encodeToString( publicSpec.getEncoded( ) ) );
+
+
     }
 
     @Override
diff --git 
a/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
 
b/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
index 40c94fd..3cdd1d1 100644
--- 
a/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
+++ 
b/redback-configuration/src/main/resources/org/apache/archiva/redback/config-defaults.properties
@@ -150,4 +150,10 @@ rest.csrffilter.absentorigin.deny=true
 # Enable/Disable the token validation only.
 # If true, the validation of the CSRF tokens will be disabled.
 # Possible values: true, false
-rest.csrffilter.disableTokenValidation=false
\ No newline at end of file
+rest.csrffilter.disableTokenValidation=false
+
+
+# Configuration for JWT authentication
+authentication.jwt.keystoreType=memory
+authentication.jwt.signatureAlgorithm=HS384
+authentication.jwt.keyfile=jwt-key.xml
\ No newline at end of file

Reply via email to