Skip to content

Commit

Permalink
Use underlying library to decide which verifier to use based on header
Browse files Browse the repository at this point in the history
#454 On the authenticator side I think the better approach is to use the
underlying api to decide which verifier to use based on the header. On
the generator side you are in control so I don't think you need an
abstraction. Now except for the original MacVerifier I'm not sure how to
create a Key but I think that's up to whoever injects the key into the
authenticator.. I preserved existing functionality allowing secret key
to be converted to SecretKey.

Allow secret to be a PEM with associated algorithm
  • Loading branch information
Garry committed May 18, 2016
1 parent 8900736 commit c7c8813
Showing 1 changed file with 69 additions and 9 deletions.
@@ -1,8 +1,11 @@
package org.pac4j.jwt.credentials.authenticator;

import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.DirectDecrypter;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory;
import com.nimbusds.jose.proc.JWSVerifierFactory;
import com.nimbusds.jose.util.X509CertUtils;
import com.nimbusds.jwt.EncryptedJWT;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
Expand All @@ -25,11 +28,19 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.security.Key;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.crypto.spec.SecretKeySpec;

/**
* Authenticator for JWT. It creates the user profile and stores it in the credentials
* for the {@link AuthenticatorProfileCreator}.
Expand All @@ -41,8 +52,11 @@ public class JwtAuthenticator extends InitializableWebObject implements TokenAut

protected final Logger logger = LoggerFactory.getLogger(getClass());

private String signingSecret;
private String encryptionSecret;

private JWSVerifierFactory factory = new DefaultJWSVerifierFactory();

private Key key;

public JwtAuthenticator() {}

Expand All @@ -63,13 +77,23 @@ private void warning() {
* @since 1.8.2
*/
public JwtAuthenticator(final String signingSecret, final String encryptionSecret) {
this.signingSecret = signingSecret;
setSigningSecret(signingSecret);
this.encryptionSecret = encryptionSecret;
}

/**
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
* @since 1.8.2
*/
public JwtAuthenticator(final String publicKeyPEM, final String algorithm, final String encryptionSecret) throws NoSuchAlgorithmException, InvalidKeySpecException {
setSigningPem(publicKeyPEM, algorithm);
this.encryptionSecret = encryptionSecret;
}

@Override
protected void internalInit(final WebContext context) {
CommonHelper.assertNotBlank("signingSecret", signingSecret);
CommonHelper.assertNotNull("key", key);
}

/**
Expand Down Expand Up @@ -111,8 +135,10 @@ public void validate(final TokenCredentials credentials) throws RequiresHttpActi
} else {
throw new TechnicalException("unsupported unsecured jwt");
}

JWSVerifier verifier = factory.createJWSVerifier(signedJWT.getHeader(), key);

verified = signedJWT.verify(new MACVerifier(this.signingSecret));
verified = signedJWT.verify(verifier);
} catch (final Exception e) {
throw new TechnicalException("Cannot decrypt / verify JWT", e);
}
Expand All @@ -139,9 +165,11 @@ private static void createJwtProfile(final TokenCredentials credentials, final S

final Map<String, Object> attributes = new HashMap<>(claimSet.getClaims());
attributes.remove(JwtConstants.SUBJECT);
final List<String> roles = (List<String>) attributes.get(JwtGenerator.INTERNAL_ROLES);
@SuppressWarnings("unchecked")
final List<String> roles = (List<String>) attributes.get(JwtGenerator.INTERNAL_ROLES);
attributes.remove(JwtGenerator.INTERNAL_ROLES);
final List<String> permissions = (List<String>) attributes.get(JwtGenerator.INTERNAL_PERMISSIONS);
@SuppressWarnings("unchecked")
final List<String> permissions = (List<String>) attributes.get(JwtGenerator.INTERNAL_PERMISSIONS);
attributes.remove(JwtGenerator.INTERNAL_PERMISSIONS);
final CommonProfile profile = ProfileHelper.buildProfile(subject, attributes);
if (roles != null) {
Expand All @@ -154,11 +182,35 @@ private static void createJwtProfile(final TokenCredentials credentials, final S
}

public String getSigningSecret() {
return signingSecret;
return new String(key.getEncoded(), Charset.forName("UTF-8"));
}

public void setSigningSecret(final String signingSecret) {
this.signingSecret = signingSecret;
X509Certificate cert = X509CertUtils.parse(signingSecret);
if (cert == null) {
this.key = new SecretKeySpec(signingSecret.getBytes(Charset.forName("UTF-8")), "AES");
} else {
this.key = cert.getPublicKey();
}
}

/**
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @since 1.8.2
*/
public void setSigningPem(final String publicKeyPEM, final String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException {
// from http://stackoverflow.com/questions/35739932/how-do-i-decode-a-jwt-token-using-an-rsa-public-key-in-pem-format
// decode to its constituent bytes
String publicKeyPEMToUse = publicKeyPEM;
publicKeyPEMToUse = publicKeyPEMToUse.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEMToUse = publicKeyPEMToUse.replace("-----END PUBLIC KEY-----", "");
byte[] publicKeyBytes = javax.xml.bind.DatatypeConverter.parseBase64Binary(publicKeyPEMToUse);

java.security.spec.X509EncodedKeySpec keySpec = new java.security.spec.X509EncodedKeySpec(publicKeyBytes);

KeyFactory keyFactory = KeyFactory.getInstance(algorithm);
this.key = keyFactory.generatePublic(keySpec);
}

public String getEncryptionSecret() {
Expand All @@ -168,4 +220,12 @@ public String getEncryptionSecret() {
public void setEncryptionSecret(final String encryptionSecret) {
this.encryptionSecret = encryptionSecret;
}

public Key getKey() {
return key;
}

public void setKey(Key key) {
this.key = key;
}
}

0 comments on commit c7c8813

Please sign in to comment.