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