@Override public synchronized void onResourceUpdate(String name, KeyIdentifier instance) { LOG.debug("SharedResourceCache triggered update: leader={}, resource key={}", leader, name); if (currentKey == null || instance.getExpiration() > currentKey.getExpiration()) { currentKey = instance; LOG.debug("Set current key: leader={}, key={}", leader, currentKey.getKeyId()); } }
@Override protected void addKey(KeyIdentifier key) { allKeys.put(key.getKeyId(), key); } }
/** * Generates a new KeyIdentifier and sets that to be the current key being used. * @return A new KeyIdentifier. */ protected final KeyIdentifier generateKey() { Random rand = new Random(); int nextId; do { nextId = rand.nextInt(Integer.MAX_VALUE); } while (hasKey(nextId)); long now = System.currentTimeMillis(); SecretKey nextKey = keyGenerator.generateKey(); KeyIdentifier keyIdentifier = new KeyIdentifier(nextKey, nextId, keyExpirationPeriod > 0 ? (now + keyExpirationPeriod) : Long.MAX_VALUE); addKey(keyIdentifier); this.currentKey = keyIdentifier; LOG.info("Changed current key to {}", currentKey); return keyIdentifier; }
@Override public boolean equals(Object object) { if (object instanceof KeyIdentifier) { KeyIdentifier other = (KeyIdentifier) object; return Arrays.equals(encodedKey, other.encodedKey) && keyId == other.keyId && algorithm.equals(other.algorithm) && Objects.equal(getKey(), other.getKey()) && Objects.equal(expiration, other.expiration); } return false; }
@Override protected void addKey(KeyIdentifier key) { keyCache.put(Integer.toString(key.getKeyId()), key); }
@Test public void testKeyIdentifierSerialization() throws Exception { SecretKey nextKey = keyGenerator.generateKey(); Random random = new Random(); KeyIdentifier keyIdentifier = new KeyIdentifier(nextKey, random.nextInt(), Long.MAX_VALUE); byte[] encoded = keyIdentifierCodec.encode(keyIdentifier); KeyIdentifier decodedKeyIdentifier = keyIdentifierCodec.decode(encoded); assertEquals(keyIdentifier, decodedKeyIdentifier); }
/** * Computes a digest for the given input message, using the key identified by the given ID. * @param keyId Identifier of the secret key to use. * @param message The data over which we should generate a digest. * @return The computed digest. * @throws InvalidKeyException If the input {@code keyId} does not match a known key or the key is not accepted * by the internal {@code Mac} implementation. */ protected final byte[] generateMAC(int keyId, byte[] message) throws InvalidKeyException { KeyIdentifier key = getKey(keyId); if (key == null) { throw new InvalidKeyException("No key found for ID " + keyId); } return generateMAC(key.getKey(), message); }
@Override public synchronized void onUpdate() { LOG.debug("SharedResourceCache triggered update on key: leader={}", leader); for (KeyIdentifier keyEntry : keyCache.getResources()) { if (currentKey == null || keyEntry.getExpiration() > currentKey.getExpiration()) { currentKey = keyEntry; LOG.debug("Set current key: leader={}, key={}", leader, currentKey.getKeyId()); } } }
@Override public void doInit() throws IOException { File keyFile = new File(keyFilePath); String keyFileDirectory = keyFile.getParent(); File keyFileDir = new File(keyFileDirectory); // Create directory for keyfile if it doesn't exist already. if (!keyFileDir.exists() && !keyFileDir.mkdir()) { throw new IOException("Failed to create directory " + keyFileDirectory + " for keyfile storage."); } else { Preconditions.checkState(keyFileDir.isDirectory(), "Configured keyFile directory " + keyFileDirectory + " is not a directory!"); Preconditions.checkState(keyFileDir.canRead(), "Configured keyFile directory " + keyFileDirectory + " exists but is not readable!"); } // Read existing key from file. if (keyFile.exists()) { KeyIdentifier storedKey = keyIdentifierCodec.decode(Files.toByteArray(keyFile)); this.currentKey = storedKey; // the file-based key is considered valid forever allKeys.put(storedKey.getKeyId(), storedKey); } else { Preconditions.checkState(keyFileDir.canWrite(), "Configured keyFile directory " + keyFileDirectory + " exists but is not writable!"); // Create a new key and write to file. generateKey(); keyFile.createNewFile(); Files.write(keyIdentifierCodec.encode(currentKey), keyFile); } }
private synchronized void rotateKey() { long now = System.currentTimeMillis(); // create a new secret key generateKey(); // clear out any expired keys for (KeyIdentifier keyIdent : keyCache.getResources()) { // we can only remove keys that expired prior to the oldest non-expired token if (keyIdent.getExpiration() < (now - maxTokenExpiration)) { LOG.debug("Removing expired key: id={}, expiration={}", keyIdent.getKeyId(), keyIdent.getExpiration()); keyCache.remove(Integer.toString(keyIdent.getKeyId())); } } lastKeyUpdate = now; }
@Test public void testKeyDistribution() throws Exception { DistributedKeyManager manager1 = getKeyManager(injector1, true); DistributedKeyManager manager2 = getKeyManager(injector2, false); TimeUnit.MILLISECONDS.sleep(1000); TestingTokenManager tokenManager1 = new TestingTokenManager(manager1, injector1.getInstance(AccessTokenIdentifierCodec.class)); TestingTokenManager tokenManager2 = new TestingTokenManager(manager2, injector2.getInstance(AccessTokenIdentifierCodec.class)); tokenManager1.startAndWait(); tokenManager2.startAndWait(); long now = System.currentTimeMillis(); AccessTokenIdentifier ident1 = new AccessTokenIdentifier("testuser", Lists.newArrayList("users", "admins"), now, now + 60 * 60 * 1000); AccessToken token1 = tokenManager1.signIdentifier(ident1); // make sure the second token manager has the secret key required to validate the signature tokenManager2.waitForKey(tokenManager1.getCurrentKey().getKeyId(), 2000, TimeUnit.MILLISECONDS); tokenManager2.validateSecret(token1); tokenManager2.waitForCurrentKey(2000, TimeUnit.MILLISECONDS); AccessToken token2 = tokenManager2.signIdentifier(ident1); tokenManager1.validateSecret(token2); assertEquals(token1.getIdentifier().getUsername(), token2.getIdentifier().getUsername()); assertEquals(token1.getIdentifier().getGroups(), token2.getIdentifier().getGroups()); assertEquals(token1, token2); tokenManager1.stopAndWait(); tokenManager2.stopAndWait(); }