All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.identityx.auth.impl.JoseResponseVerifier Maven / Gradle / Ivy

Go to download

Client library used for adding authentication to http messages as required by IdentityX Rest Services

There is a newer version: 5.6.0.2
Show newest version
/*
* Copyright Daon.
*
* 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.identityx.auth.impl;

import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXParameters;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.identityx.auth.def.IApiKey;
import com.identityx.auth.def.IResponse;
import com.identityx.auth.def.IResponseAuthenticator;
import com.identityx.auth.def.IResponseVerifier;
import com.identityx.auth.impl.keys.PublicApiKey;
import com.identityx.auth.support.DigestVerificationFailedException;
import com.identityx.auth.util.CertValidationResultCache;
import com.identityx.auth.util.CertsUtil;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;

public class JoseResponseVerifier implements IResponseVerifier {

	private String authenticationScheme = JoseRequestAuthenticator.AUTHENTICATION_SCHEME;
	private static final Log logger = LogFactory.getLog(JoseResponseVerifier.class);
		
	@Override
	public void verify(IResponse response, IResponseAuthenticator responseAuthenticator, IApiKey apiKey, String nonce) throws DigestVerificationFailedException {

		if (!response.getHeaders().containsKey(DigestAuthenticator.DATE_HEADER) || !response.getHeaders().containsKey(AuthSettings.AUTHORIZATION_HEADER)) {
			throw new DigestVerificationFailedException("The required headers were not present in the response message");
		}
		if (apiKey == null || !(apiKey instanceof PublicApiKey)) {
			throw new IllegalArgumentException("apiKey parameter null or wrong type");
		}
				
		try {
			String authHeader = response.getHeaders().getFirst(AuthSettings.AUTHORIZATION_HEADER);			
			JWSPayload jwsPayload = ((JoseResponseAuthenticator)responseAuthenticator).getJWSPayloadFromHeader(authHeader);
			
	    	String canonicalResponse = responseAuthenticator.buildCanonicalResponse(response);
	        String canonicalResponseHashHex = DigestAuthenticator.toHex(DigestAuthenticator.hash(canonicalResponse));
				    	
	    	if (!jwsPayload.getCanonicalRequestHashHex().equals(canonicalResponseHashHex)) {
	    		String message = "The canonical response in the signature does not match the actual canonical response"; 
	    		logger.error(message);
	    		throw new DigestVerificationFailedException("Failed to verify the signature of the response object: " + message);
	    	}

	    	PublicApiKey publicApiKey = (PublicApiKey)apiKey;
	    		    	
	    	if (publicApiKey.getCert() == null && jwsPayload.getSigningCerts() == null) {
	    		String msg = "Certs were expected in the server response.";
    			logger.error(msg);
				throw new DigestVerificationFailedException("Failed to verify the signature of the response object: " + msg);	    			    		
	    	}
	    	
	    	//////////////////////////////////////////////////////////////////

	    	List certs = null;
	    	if (jwsPayload.getSigningCerts() == null || jwsPayload.getSigningCerts().isEmpty()) {
	    		// no certs received from the server
	    		certs = new ArrayList();
	    		certs.add(publicApiKey.getCert());
	    		certs.addAll(publicApiKey.getIntermediateCerts());
	    		X509Certificate eeCert = publicApiKey.getCert();
	    		
	    		checkServerCertDistinguishedName(eeCert, publicApiKey);
	    		
	    		String key = eeCert.getIssuerDN().toString() + eeCert.getSerialNumber().toString();
	    		if (!CertValidationResultCache.getInstance(key).isCached()) {
		    		performCertPathValidation(publicApiKey, certs);
		    		// no exception so far so mark it valid in the cache
		    		CertValidationResultCache.getInstance(key).markPassedValidation();
	    		}
	    	}
	    	else {
	    		// server provides new certs
	    		String signingCerts = jwsPayload.getSigningCerts();
	    		certs = CertsUtil.certsFromString(signingCerts);
	    		
	    		X509Certificate eeCert = certs.get(0);
	    		checkServerCertDistinguishedName(eeCert, publicApiKey);
	    		
	    		performCertPathValidation(publicApiKey, certs);
	    		publicApiKey.setCert(eeCert);
	    		publicApiKey.getIntermediateCerts().clear();
	    		publicApiKey.getIntermediateCerts().addAll(certs.subList(1, certs.size()));
	    	}
	    		    	

	    	X509Certificate serverCert = certs.get(0);
	    	
	    	serverCert.checkValidity();
	    	
    		JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey)serverCert.getPublicKey());
    		
    		JWSObject jwsObject = ((JoseResponseAuthenticator)responseAuthenticator).getJWSObjectFromHeader(authHeader);
    		logger.debug("Verifying the JSW token's signature");
    		if (!jwsObject.verify(verifier)) {
    			logger.error("Failed to verify the signature of the jws token");
				throw new DigestVerificationFailedException("Failed to verify the signature of the response object");
			}
			
		} catch (CertPathValidatorException e) {
			// make sure after a failure the result is still cached
			//CertValidationResultCache.getInstance().resetCache();
			
			throw new DigestVerificationFailedException("Failed to validate the signature of the response object", e);
		}
		catch (Exception e) {
			throw new DigestVerificationFailedException("An error has occurred while verifying the signature of the response object", e);
		}				
	}
	
	public void checkServerCertDistinguishedName(X509Certificate serverCert, PublicApiKey publicApiKey) throws DigestVerificationFailedException {
    	String incomingDN = serverCert.getSubjectDN().toString();
    	
    	if (!incomingDN.equals(publicApiKey.getServerCertDN())) {
    		String message = "The incoming server certificate's DistinguishedName does not match the expected DN: got " + incomingDN + ", expecting " + publicApiKey.getServerCertDN(); 
			logger.error(message);
			throw new DigestVerificationFailedException(message);
    	}

	}
	

	protected void performCertPathValidation(PublicApiKey publicApiKey, List certs) throws CertificateException, DigestVerificationFailedException, NoSuchAlgorithmException, CertPathValidatorException, InvalidAlgorithmParameterException, NoSuchProviderException {
		
    	// 1. cert should be in date or is that included in the path validation ?
    	// 2. cert SAN should be the same as the one we store
    	// 3. Full path validation

		
		if (certs == null || certs.isEmpty()) {
			return;
		}
		// only work with certs that are not self signed
		List pathCerts = new ArrayList();
		for (X509Certificate icert : certs) {
			if (!CertsUtil.isSelfSigned(icert)) {
				pathCerts.add(icert);
			}
		}
		
		if (pathCerts.isEmpty()) {
			return;
		}
				
		CertificateFactory fact = CertificateFactory.getInstance("X.509");
    	CertPath certPath = fact.generateCertPath(pathCerts);
		    	
		PKIXParameters params = null;
		TrustAnchor anchor = new TrustAnchor(publicApiKey.getCaCert(), null);
        params = new PKIXParameters(Collections.singleton(anchor));	  
        params.setRevocationEnabled(publicApiKey.isRevocationEnabled());
        
        if (publicApiKey.isRevocationEnabled()) {
        	Properties props = System.getProperties();
        	props.setProperty("com.sun.security.enableCRLDP", "true");
        }        
        
        if (publicApiKey.isOcspEnabled()) {
        	Security.setProperty("ocsp.enable", "true");
        }
        else {
        	Security.setProperty("ocsp.enable", "false");
        }
        
        CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
        cpv.validate(certPath, params);	    	
	}
	
	public String getAuthenticationScheme() {
		return authenticationScheme;
	}

	public void setAuthenticationScheme(String authenticationScheme) {
		this.authenticationScheme = authenticationScheme;
	}



}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy