RSAPSSAlgorithmTest.java
package com.auth0.jwt.algorithms;
import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.security.Provider;
import java.security.Security;
import java.security.interfaces.RSAKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import static com.auth0.jwt.PemUtils.readPrivateKeyFromFile;
import static com.auth0.jwt.PemUtils.readPublicKeyFromFile;
import static com.auth0.jwt.algorithms.CryptoTestHelper.asJWT;
import static com.auth0.jwt.algorithms.CryptoTestHelper.assertSignaturePresent;
/**
* Round-trip tests for the RSASSA-PSS (PS256/PS384/PS512) algorithms.
* <p>
* BouncyCastle is registered as a provider so these tests also pass on Java 8, whose built-in
* providers do not implement RSASSA-PSS. On Java 11+ the built-in provider would suffice, but
* registering BouncyCastle keeps the test behavior identical across the supported Java versions.
*/
public class RSAPSSAlgorithmTest {
private static final String PRIVATE_KEY_FILE = "src/test/resources/rsa-private.pem";
private static final String PUBLIC_KEY_FILE = "src/test/resources/rsa-public.pem";
private static final String INVALID_PUBLIC_KEY_FILE = "src/test/resources/rsa-public_invalid.pem";
private static final String PS256Header = "eyJhbGciOiJQUzI1NiJ9";
private static final String PS384Header = "eyJhbGciOiJQUzM4NCJ9";
private static final String PS512Header = "eyJhbGciOiJQUzUxMiJ9";
private static final String auth0IssPayload = "eyJpc3MiOiJhdXRoMCJ9";
@Rule
public ExpectedException exception = ExpectedException.none();
private static final Provider bcProvider = new BouncyCastleProvider();
@BeforeClass
public static void setUp() {
Security.insertProviderAt(bcProvider, 1);
}
@AfterClass
public static void tearDown() {
Security.removeProvider(bcProvider.getName());
}
@Test
public void shouldSignAndVerifyPS256WithBothKeys() throws Exception {
Algorithm algorithm = Algorithm.RSA256PSS(
(RSAPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE, "RSA"),
(RSAPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
String jwt = asJWT(algorithm, PS256Header, auth0IssPayload);
assertSignaturePresent(jwt);
algorithm.verify(JWT.decode(jwt));
}
@Test
public void shouldSignWithPrivateKeyAndVerifyWithPublicKeyPS256() throws Exception {
Algorithm signer = Algorithm.RSA256PSS((RSAKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
Algorithm verifier = Algorithm.RSA256PSS((RSAKey) readPublicKeyFromFile(PUBLIC_KEY_FILE, "RSA"));
String jwt = asJWT(signer, PS256Header, auth0IssPayload);
assertSignaturePresent(jwt);
verifier.verify(JWT.decode(jwt));
}
@Test
public void shouldSignAndVerifyPS384WithBothKeys() throws Exception {
Algorithm algorithm = Algorithm.RSA384PSS(
(RSAPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE, "RSA"),
(RSAPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
String jwt = asJWT(algorithm, PS384Header, auth0IssPayload);
assertSignaturePresent(jwt);
algorithm.verify(JWT.decode(jwt));
}
@Test
public void shouldSignAndVerifyPS512WithBothKeys() throws Exception {
Algorithm algorithm = Algorithm.RSA512PSS(
(RSAPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE, "RSA"),
(RSAPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
String jwt = asJWT(algorithm, PS512Header, auth0IssPayload);
assertSignaturePresent(jwt);
algorithm.verify(JWT.decode(jwt));
}
@Test
public void shouldProduceNonDeterministicSignatures() throws Exception {
Algorithm algorithm = Algorithm.RSA256PSS(
(RSAPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE, "RSA"),
(RSAPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
String firstJwt = asJWT(algorithm, PS256Header, auth0IssPayload);
String secondJwt = asJWT(algorithm, PS256Header, auth0IssPayload);
String firstSignature = firstJwt.substring(firstJwt.lastIndexOf('.') + 1);
String secondSignature = secondJwt.substring(secondJwt.lastIndexOf('.') + 1);
org.hamcrest.MatcherAssert.assertThat(firstSignature,
org.hamcrest.CoreMatchers.is(org.hamcrest.CoreMatchers.not(secondSignature)));
algorithm.verify(JWT.decode(firstJwt));
algorithm.verify(JWT.decode(secondJwt));
}
@Test
public void shouldFailPS256VerificationWithInvalidPublicKey() throws Exception {
exception.expect(SignatureVerificationException.class);
exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: RSASSA-PSS");
Algorithm signer = Algorithm.RSA256PSS((RSAKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
String jwt = asJWT(signer, PS256Header, auth0IssPayload);
Algorithm verifier = Algorithm.RSA256PSS((RSAKey) readPublicKeyFromFile(INVALID_PUBLIC_KEY_FILE, "RSA"));
verifier.verify(JWT.decode(jwt));
}
@Test
public void shouldRejectPS256TokenWhenVerifiedWithPS384() throws Exception {
exception.expect(SignatureVerificationException.class);
exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: RSASSA-PSS");
Algorithm signer = Algorithm.RSA256PSS((RSAKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
String jwt = asJWT(signer, PS256Header, auth0IssPayload);
Algorithm verifier = Algorithm.RSA384PSS((RSAKey) readPublicKeyFromFile(PUBLIC_KEY_FILE, "RSA"));
verifier.verify(JWT.decode(jwt));
}
@Test
public void shouldRejectPS256TokenWhenVerifiedWithPS512() throws Exception {
exception.expect(SignatureVerificationException.class);
exception.expectMessage("The Token's Signature resulted invalid when verified using the Algorithm: RSASSA-PSS");
Algorithm signer = Algorithm.RSA256PSS((RSAKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
String jwt = asJWT(signer, PS256Header, auth0IssPayload);
Algorithm verifier = Algorithm.RSA512PSS((RSAKey) readPublicKeyFromFile(PUBLIC_KEY_FILE, "RSA"));
verifier.verify(JWT.decode(jwt));
}
/**
* Exercises the JDK-native RSASSA-PSS implementation (SunRsaSign, Java 11+) rather than the
* BouncyCastle provider the rest of this class relies on. Skipped on Java 8, whose built-in
* providers do not implement RSASSA-PSS.
*/
@Test
public void shouldSignAndVerifyPS256WithJdkNativeProvider() throws Exception {
Assume.assumeFalse("Requires JDK-native RSASSA-PSS (Java 11+)",
System.getProperty("java.specification.version").equals("1.8"));
Security.removeProvider(bcProvider.getName());
try {
Algorithm algorithm = Algorithm.RSA256PSS(
(RSAPublicKey) readPublicKeyFromFile(PUBLIC_KEY_FILE, "RSA"),
(RSAPrivateKey) readPrivateKeyFromFile(PRIVATE_KEY_FILE, "RSA"));
String jwt = asJWT(algorithm, PS256Header, auth0IssPayload);
assertSignaturePresent(jwt);
algorithm.verify(JWT.decode(jwt));
} finally {
Security.insertProviderAt(bcProvider, 1);
}
}
}