From b5dd73bb34724851435701cd0c2b2563b23b99e7 Mon Sep 17 00:00:00 2001 From: Garry Date: Fri, 3 Jun 2016 12:20:50 -0400 Subject: [PATCH] Added a test for PEM.. tricky one.. it lead to a little refactor of JwtGenerator --- .../authenticator/JwtAuthenticator.java | 2 +- .../org/pac4j/jwt/profile/JwtGenerator.java | 26 +++++++- .../src/test/java/org/pac4j/jwt/JwtTests.java | 59 +++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/pac4j-jwt/src/main/java/org/pac4j/jwt/credentials/authenticator/JwtAuthenticator.java b/pac4j-jwt/src/main/java/org/pac4j/jwt/credentials/authenticator/JwtAuthenticator.java index 82a08bdef5..50b3de6d14 100644 --- a/pac4j-jwt/src/main/java/org/pac4j/jwt/credentials/authenticator/JwtAuthenticator.java +++ b/pac4j-jwt/src/main/java/org/pac4j/jwt/credentials/authenticator/JwtAuthenticator.java @@ -212,7 +212,7 @@ public void setSigningPem(final String publicKeyPEM, final String algorithm) thr // 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.replaceAll("-----BEGIN PUBLIC KEY-----[\\r\\n]+", ""); publicKeyPEMToUse = publicKeyPEMToUse.replace("-----END PUBLIC KEY-----", ""); byte[] publicKeyBytes = javax.xml.bind.DatatypeConverter.parseBase64Binary(publicKeyPEMToUse); diff --git a/pac4j-jwt/src/main/java/org/pac4j/jwt/profile/JwtGenerator.java b/pac4j-jwt/src/main/java/org/pac4j/jwt/profile/JwtGenerator.java index 265c862cd5..a6fba3dbab 100644 --- a/pac4j-jwt/src/main/java/org/pac4j/jwt/profile/JwtGenerator.java +++ b/pac4j-jwt/src/main/java/org/pac4j/jwt/profile/JwtGenerator.java @@ -67,7 +67,7 @@ public JwtGenerator(final String signingSecret, final String encryptionSecret) { this.signingSecret = signingSecret; this.encryptionSecret = encryptionSecret; } - + /** * Generates a JWT from a user profile. * @@ -75,14 +75,30 @@ public JwtGenerator(final String signingSecret, final String encryptionSecret) { * @return the created JWT */ public String generate(final U profile) { + try { + // Create HMAC signer + final JWSSigner signer = new MACSigner(this.signingSecret); + return generate(profile, signer, this.jwsAlgorithm); + } catch (JOSEException e) { + throw new RuntimeException(e); + } + } + + /** + * Generates a JWT from a user profile. + * + * @param profile the given user profile + * @return the created JWT + */ + public String generate(final U profile, JWSSigner signer, JWSAlgorithm jwsAlgorithm) { verifyProfile(profile); try { final JWTClaimsSet claims = buildJwtClaimsSet(profile); if (CommonHelper.isNotBlank(this.signingSecret)) { - CommonHelper.assertNotNull("jwsAlgorithm", this.jwsAlgorithm); + CommonHelper.assertNotNull("jwsAlgorithm", jwsAlgorithm); - final SignedJWT signedJWT = signJwt(claims); + final SignedJWT signedJWT = signJwt(claims, signer, jwsAlgorithm); if (CommonHelper.isNotBlank(this.encryptionSecret)) { CommonHelper.assertNotNull("jweAlgorithm", jweAlgorithm); @@ -116,6 +132,10 @@ protected String encryptJwt(final SignedJWT signedJWT) throws Exception { protected SignedJWT signJwt(final JWTClaimsSet claims) throws JOSEException { // Create HMAC signer final JWSSigner signer = new MACSigner(this.signingSecret); + return signJwt(claims, signer, this.jwsAlgorithm); + } + + protected SignedJWT signJwt(final JWTClaimsSet claims, JWSSigner signer, JWSAlgorithm jwsAlgorithm) throws JOSEException { final SignedJWT signedJWT = new SignedJWT(new JWSHeader(jwsAlgorithm), claims); // Apply the HMAC signedJWT.sign(signer); diff --git a/pac4j-jwt/src/test/java/org/pac4j/jwt/JwtTests.java b/pac4j-jwt/src/test/java/org/pac4j/jwt/JwtTests.java index 8326521638..51193f5ede 100644 --- a/pac4j-jwt/src/test/java/org/pac4j/jwt/JwtTests.java +++ b/pac4j-jwt/src/test/java/org/pac4j/jwt/JwtTests.java @@ -1,5 +1,8 @@ package org.pac4j.jwt; +import org.bouncycastle.jce.provider.PEMUtil; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemWriter; import org.junit.Test; import org.pac4j.core.exception.HttpAction; import org.pac4j.core.exception.TechnicalException; @@ -12,6 +15,22 @@ import org.pac4j.oauth.profile.facebook.FacebookAttributesDefinition; import org.pac4j.oauth.profile.facebook.FacebookProfile; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.crypto.ECDSASigner; +import com.nimbusds.jose.jwk.ECKey; + +import java.io.IOException; +import java.io.StringWriter; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.EllipticCurve; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -69,6 +88,46 @@ public void testPlainJwt() throws HttpAction { final String token = generator.generate(profile); assertToken(profile, token); } + + @Test + public void testPemJwt() throws Exception { + final JwtGenerator generator = new JwtGenerator<>(null, null); + final FacebookProfile profile = createProfile(); + KeyPair keyPair = createECKeyPair(EC256SPEC); + + ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic(); + ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate(); + final String token = generator.generate(profile, new ECDSASigner(privateKey), JWSAlgorithm.ES256); + assertToken(profile, token, new JwtAuthenticator(getPem("PUBLIC KEY", publicKey.getEncoded()), "EC", null)); + } + private static final int COFACTOR = 1; + private static final ECParameterSpec EC256SPEC = new ECParameterSpec( + new EllipticCurve( + new ECFieldFp(new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951")), + new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"), + new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291")), + new ECPoint( + new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"), + new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109")), + new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"), + COFACTOR); + + private static KeyPair createECKeyPair(final AlgorithmParameterSpec spec) + throws Exception { + + // Create the public and private keys + KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("EC"); + keyGenerator.initialize(spec); + return keyGenerator.generateKeyPair(); + } + private String getPem(String keyTitle, byte[] encodedKey) throws IOException { + StringWriter writer = new StringWriter(); + PemWriter pemWriter = new PemWriter(writer); + pemWriter.writeObject(new PemObject("PUBLIC KEY", encodedKey)); + pemWriter.flush(); + pemWriter.close(); + return writer.toString(); + } @Test public void testGenerateAuthenticate() throws HttpAction {