/* * * Copyright 2016 Netflix, Inc. * * Licensed 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 com.netflix.genie.web.security.oauth2.pingfederate; import com.netflix.spectator.api.Registry; import org.apache.commons.lang3.StringUtils; import org.jose4j.jwt.consumer.JwtConsumer; import org.jose4j.jwt.consumer.JwtConsumerBuilder; import org.jose4j.keys.RsaKeyUtil; import org.jose4j.lang.JoseException; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.PublicKey; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; /** * Beans needed to support OAuth2 authentication via JWT tokens returned from Ping Federate. * * @author tgianos * @since 3.0.0 */ @Configuration @Conditional(PingFederateSecurityConditions.PingFederateJWTEnabled.class) public class PingFederateJWTConfig { /** * A validator which checks the validity of the JWT tokens sent in from ping federate against expected * Genie required fields. * * @param registry The metrics registry to use * @return The validator */ @Bean public PingFederateValidator pingFederateValidator(final Registry registry) { return new PingFederateValidator(registry); } /** * The public key used to verify the signatures of JWT tokens. * * @param keyValue The string of the public key to use in either RSA or X.509 format * @return A public key object to use when validating JWT tokens * @throws IOException On reading or closing byte array input stream * @throws JoseException When trying to create the key using jose library * @throws InvalidKeySpecException When the cert has an invalid spec * @throws CertificateException When trying to create a X.509 specification object */ @Bean public PublicKey jwtPublicKey(@Value("${genie.security.oauth2.pingfederate.jwt.keyValue}") final String keyValue) throws IOException, JoseException, InvalidKeySpecException, CertificateException { final String certBegin = "-----BEGIN CERTIFICATE-----"; final String rsaBegin = "-----BEGIN PUBLIC KEY-----"; if (StringUtils.isEmpty(keyValue)) { // In future try a key resolver to pull the key from the server throw new IllegalArgumentException("No value set for security.oauth2.resource.jwt.keyValue"); } if (keyValue.startsWith(certBegin)) { // X.509 cert try (final ByteArrayInputStream bis = new ByteArrayInputStream(keyValue.getBytes("UTF-8"))) { final CertificateFactory fact = CertificateFactory.getInstance("X.509"); final X509Certificate cer = (X509Certificate) fact.generateCertificate(bis); return cer.getPublicKey(); } } else if (keyValue.startsWith(rsaBegin)) { // RSA Public Key return new RsaKeyUtil().fromPemEncoded(keyValue); } else { throw new IllegalArgumentException( "Only support X.509 pem certs or Public RSA Keys for security.oauth2.resource.jwt.keyValue" ); } } /** * The jwtConsumer class which will be used to verify and parse the JWT token from ping federate. * * @param jwtPublicKey The public key used to verify the signature on the JWT token. * @param pingFederateValidator The validator to add to the validation chain specifically for Ping Federate * @return The consumer to use */ @Bean public JwtConsumer jwtConsumer( @Qualifier("jwtPublicKey") final PublicKey jwtPublicKey, final PingFederateValidator pingFederateValidator ) { return new JwtConsumerBuilder() .setVerificationKey(jwtPublicKey) .setRequireExpirationTime() .registerValidator(pingFederateValidator) .build(); } /** * The token services class used to take a JWT token and produce a Spring Security Authentication object. * * @param jwtConsumer The JWT consumer used to verify and parse the JWT tokens * @param registry The metrics registry to use for collecting metrics * @return The Token services class */ @Bean @Primary public PingFederateJWTTokenServices pingFederateJWTTokenServices( final JwtConsumer jwtConsumer, final Registry registry ) { return new PingFederateJWTTokenServices(jwtConsumer, registry); } }