/** * Build an authenticator. * @param keystorePath the path to a keystore * @param keystorePassword the password for a keystore * @param keyids the key ids * @return an Authenticator, or null if there was an error */ public static Authenticator buildAuthenticatorWithTruststore(final String keystorePath, final char[] keystorePassword, final List<String> keyids) { return ofNullable(keystorePath).map(File::new).filter(File::exists).flatMap(file -> { try (final FileInputStream fis = new FileInputStream(file)) { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(fis, keystorePassword); final List<String> keyIds = filterKeyIds(ks, keyids); switch (keyIds.size()) { case 0: LOGGER.warn("No valid key ids provided! Skipping keystore: {}", keystorePath); return empty(); case 1: return of(new JwtAuthenticator(ks.getCertificate(keyIds.get(0)).getPublicKey())); default: return of(new FederatedJwtAuthenticator(ks, keyIds)); } } catch (final IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException ex) { LOGGER.error("Error reading keystore: {}", ex.getMessage()); LOGGER.warn("Ignoring JWT authenticator with keystore: {}", keystorePath); } return empty(); }).orElse(null); }
@Test public void testAuthenticateKeystoreEC() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final String token = buildEcToken(ks.getKey("trellis-ec", passphrase), "trellis-ec"); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("trellis-ec")); final Optional<Principal> result = authenticator.authenticate(token); assertTrue(result.isPresent(), "Missing principal!"); result.ifPresent(p -> assertEquals("https://people.apache.org/~acoburn/#i", p.getName(), "Incorrect webid!")); }
@Test public void testAuthenticateKeystoreAnotherNoMatch() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final String token = buildEcToken(ks.getKey("trellis-ec", passphrase), "foo"); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("foo")); assertThrows(SecurityException.class, () -> authenticator.authenticate(token), "Unexpected keystore entry!"); }
@Test public void testAuthenticateKeystoreNoMatch() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final String token = buildEcToken(ks.getKey("trellis-ec", passphrase), "trellis-ec"); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("trellis", "foo")); assertThrows(SecurityException.class, () -> authenticator.authenticate(token), "Unexpected keystore entry!"); }
@Test public void testKeyStoreException() throws Exception { final KeyStore mockKeyStore = mock(KeyStore.class, inv -> { throw new KeyStoreException("Expected"); }); final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final String token = buildEcToken(ks.getKey("trellis-ec", passphrase), "trellis-ec"); final Authenticator authenticator = new FederatedJwtAuthenticator(mockKeyStore, asList("trellis-ec")); assertThrows(SecurityException.class, () -> authenticator.authenticate(token), "Unexpectedly functional keystore!"); }
@Test public void testAuthenticateNoSub() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final Key privateKey = ks.getKey("trellis-ec", passphrase); final String token = Jwts.builder().setHeaderParam(JwsHeader.KEY_ID, "trellis-ec") .setIssuer("http://localhost").signWith(privateKey, SignatureAlgorithm.ES256).compact(); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("trellis-ec")); assertFalse(authenticator.authenticate(token).isPresent(), "Unexpected principal!"); }
@Test public void testAuthenticateKeystoreNoKeyId() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final Key privateKey = ks.getKey("trellis-ec", passphrase); final String token = Jwts.builder().setSubject("https://people.apache.org/~acoburn/#i") .signWith(privateKey, SignatureAlgorithm.ES256).compact(); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("trellis-ec")); assertThrows(JwtException.class, () -> authenticator.authenticate(token), "Unexpected key id field!"); }
@Test public void testAuthenticateSubNoWebIss() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final Key privateKey = ks.getKey("trellis-ec", passphrase); final String token = Jwts.builder().setHeaderParam(JwsHeader.KEY_ID, "trellis-ec") .setSubject("acoburn").setIssuer("some org") .signWith(privateKey, SignatureAlgorithm.ES256).compact(); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("trellis-ec")); assertFalse(authenticator.authenticate(token).isPresent(), "Unexpected principal!"); }
@Test public void testAuthenticateKeystore() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final Key privateKey = ks.getKey("trellis", passphrase); final String jwt = Jwts.builder().setHeaderParam(JwsHeader.KEY_ID, "trellis") .setSubject("https://people.apache.org/~acoburn/#me") .signWith(privateKey, SignatureAlgorithm.RS256).compact(); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("trellis", "foo")); final Optional<Principal> result = authenticator.authenticate(jwt); assertTrue(result.isPresent(), "Missing principal!"); result.ifPresent(p -> assertEquals("https://people.apache.org/~acoburn/#me", p.getName(), "Incorrect webid!")); }
@Test public void testAuthenticateKeystoreRSA() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final Key privateKey = ks.getKey("trellis", passphrase); final String token = Jwts.builder().setHeaderParam(JwsHeader.KEY_ID, "trellis-public") .setSubject("https://people.apache.org/~acoburn/#i") .signWith(privateKey, SignatureAlgorithm.RS256).compact(); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("trellis-public")); final Optional<Principal> result = authenticator.authenticate(token); assertTrue(result.isPresent(), "Missing principal!"); result.ifPresent(p -> assertEquals("https://people.apache.org/~acoburn/#i", p.getName(), "Incorrect webid!")); }
@Test public void testAuthenticateSubIss() throws Exception { final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(getClass().getResourceAsStream("/keystore.jks"), passphrase); final Key privateKey = ks.getKey("trellis-ec", passphrase); final String token = Jwts.builder().setHeaderParam(JwsHeader.KEY_ID, "trellis-ec") .setSubject("acoburn").setIssuer("http://localhost") .signWith(privateKey, SignatureAlgorithm.ES256).compact(); final Authenticator authenticator = new FederatedJwtAuthenticator(ks, asList("trellis-ec")); final Optional<Principal> result = authenticator.authenticate(token); assertTrue(result.isPresent(), "Missing principal!"); result.ifPresent(p -> assertEquals("http://localhost/acoburn", p.getName(), "Incorrect webid!")); }