/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.gateway.services.token.impl; import java.security.KeyStoreException; import java.security.Principal; import java.security.PublicKey; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.util.Map; import java.util.List; import java.util.ArrayList; import javax.security.auth.Subject; import org.apache.hadoop.gateway.config.GatewayConfig; import org.apache.hadoop.gateway.services.Service; import org.apache.hadoop.gateway.services.ServiceLifecycleException; import org.apache.hadoop.gateway.services.security.AliasService; import org.apache.hadoop.gateway.services.security.AliasServiceException; import org.apache.hadoop.gateway.services.security.KeystoreService; import org.apache.hadoop.gateway.services.security.KeystoreServiceException; import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority; import org.apache.hadoop.gateway.services.security.token.TokenServiceException; import org.apache.hadoop.gateway.services.security.token.impl.JWTToken; import com.nimbusds.jose.JWSSigner; import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jose.crypto.RSASSAVerifier; public class DefaultTokenAuthorityService implements JWTokenAuthority, Service { private static final String SIGNING_KEY_PASSPHRASE = "signing.key.passphrase"; private AliasService as = null; private KeystoreService ks = null; String signingKeyAlias = null; public void setKeystoreService(KeystoreService ks) { this.ks = ks; } public void setAliasService(AliasService as) { this.as = as; } /* (non-Javadoc) * @see org.apache.hadoop.gateway.provider.federation.jwt.JWTokenAuthority#issueToken(javax.security.auth.Subject, java.lang.String) */ @Override public JWTToken issueToken(Subject subject, String algorithm) throws TokenServiceException { Principal p = (Principal) subject.getPrincipals().toArray()[0]; return issueToken(p, algorithm); } /* (non-Javadoc) * @see org.apache.hadoop.gateway.provider.federation.jwt.JWTokenAuthority#issueToken(java.security.Principal, java.lang.String) */ @Override public JWTToken issueToken(Principal p, String algorithm) throws TokenServiceException { return issueToken(p, null, algorithm); } /* (non-Javadoc) * @see org.apache.hadoop.gateway.provider.federation.jwt.JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, long expires) */ @Override public JWTToken issueToken(Principal p, String algorithm, long expires) throws TokenServiceException { return issueToken(p, (String)null, algorithm, expires); } public JWTToken issueToken(Principal p, String audience, String algorithm) throws TokenServiceException { return issueToken(p, audience, algorithm, -1); } /* (non-Javadoc) * @see org.apache.hadoop.gateway.provider.federation.jwt.JWTokenAuthority#issueToken(java.security.Principal, java.lang.String, java.lang.String) */ @Override public JWTToken issueToken(Principal p, String audience, String algorithm, long expires) throws TokenServiceException { ArrayList<String> audiences = null; if (audience != null) { audiences = new ArrayList<String>(); audiences.add(audience); } return issueToken(p, audiences, algorithm, expires); } @Override public JWTToken issueToken(Principal p, List<String> audiences, String algorithm, long expires) throws TokenServiceException { String[] claimArray = new String[4]; claimArray[0] = "KNOXSSO"; claimArray[1] = p.getName(); claimArray[2] = null; if (expires == -1) { claimArray[3] = null; } else { claimArray[3] = String.valueOf(expires); } JWTToken token = null; if ("RS256".equals(algorithm)) { token = new JWTToken("RS256", claimArray, audiences); RSAPrivateKey key; char[] passphrase = null; try { passphrase = getSigningKeyPassphrase(); } catch (AliasServiceException e) { throw new TokenServiceException(e); } try { key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(), passphrase); JWSSigner signer = new RSASSASigner(key); token.sign(signer); } catch (KeystoreServiceException e) { throw new TokenServiceException(e); } } else { throw new TokenServiceException("Cannot issue token - Unsupported algorithm"); } return token; } private char[] getSigningKeyPassphrase() throws AliasServiceException { char[] phrase = as.getPasswordFromAliasForGateway(SIGNING_KEY_PASSPHRASE); if (phrase == null) { phrase = as.getGatewayIdentityPassphrase(); } return phrase; } private String getSigningKeyAlias() { if (signingKeyAlias == null) { return "gateway-identity"; } return signingKeyAlias; } @Override public boolean verifyToken(JWTToken token) throws TokenServiceException { boolean rc = false; PublicKey key; try { key = ks.getSigningKeystore().getCertificate(getSigningKeyAlias()).getPublicKey(); JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) key); // TODO: interrogate the token for issuer claim in order to determine the public key to use for verification // consider jwk for specifying the key too rc = token.verify(verifier); } catch (KeyStoreException e) { throw new TokenServiceException("Cannot verify token.", e); } catch (KeystoreServiceException e) { throw new TokenServiceException("Cannot verify token.", e); } return rc; } @Override public void init(GatewayConfig config, Map<String, String> options) throws ServiceLifecycleException { if (as == null || ks == null) { throw new ServiceLifecycleException("Alias or Keystore service is not set"); } signingKeyAlias = config.getSigningKeyAlias(); @SuppressWarnings("unused") RSAPrivateKey key; char[] passphrase = null; try { passphrase = as.getPasswordFromAliasForGateway(SIGNING_KEY_PASSPHRASE); if (passphrase != null) { key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(), passphrase); if (key == null) { throw new ServiceLifecycleException("Provisioned passphrase cannot be used to acquire signing key."); } } } catch (AliasServiceException e) { throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e); } catch (KeystoreServiceException e) { throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e); } } @Override public void start() throws ServiceLifecycleException { } @Override public void stop() throws ServiceLifecycleException { } }