/******************************************************************************* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. ******************************************************************************/ /* * Copyright 2003 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egovernment.moa.id.auth.validator; import java.security.InvalidKeyException; import java.security.PublicKey; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; import at.gv.egovernment.moa.id.auth.data.VerifyXMLSignatureResponse; import at.gv.egovernment.moa.id.auth.exception.ValidateException; import at.gv.egovernment.moa.id.commons.MOAIDAuthConstants; import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; import at.gv.egovernment.moa.id.commons.api.data.IVerifiyXMLSignatureResponse; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.commons.utils.MOAIDMessageProvider; import at.gv.egovernment.moa.logging.Logger; import iaik.asn1.structures.Name; import iaik.security.ec.common.ECPublicKey; import iaik.utils.RFC2253NameParserException; import iaik.x509.X509Certificate; import iaik.x509.X509ExtensionInitException; /** * This class is used to validate an {@link VerifyXMLSignatureResponse} * returned by MOA-SPSS * * @author Stefan Knirsch * @version $Id$ */ public class VerifyXMLSignatureResponseValidator { /** Identification string for checking identity link */ public static final String CHECK_IDENTITY_LINK = "IdentityLink"; /** Identification string for checking authentication block */ public static final String CHECK_AUTH_BLOCK = "AuthBlock"; /** Singleton instance. null, if none has been created. */ private static VerifyXMLSignatureResponseValidator instance; /** * Constructor for a singleton VerifyXMLSignatureResponseValidator. */ public static synchronized VerifyXMLSignatureResponseValidator getInstance() throws ValidateException { if (instance == null) { instance = new VerifyXMLSignatureResponseValidator(); } return instance; } /** * Validates a {@link VerifyXMLSignatureResponse} returned by MOA-SPSS. * * @param verifyXMLSignatureResponse the <VerifyXMLSignatureResponse> * @param identityLinkSignersSubjectDNNames subject names configured * @param whatToCheck is used to identify whether the identityLink or the Auth-Block is validated * @param oaParam specifies whether the validation result of the * manifest has to be ignored (identityLink validation if * the OA is a business service) or not * @throws ValidateException on any validation error * @throws ConfigurationException */ public void validate(IVerifiyXMLSignatureResponse verifyXMLSignatureResponse, List identityLinkSignersSubjectDNNames, String whatToCheck, IOAAuthParameters oaParam, AuthConfiguration authConfig) throws ValidateException, ConfigurationException { if (verifyXMLSignatureResponse.getSignatureCheckCode() != 0) throw new ValidateException("validator.06", new Object[] {whatToCheck}); if (verifyXMLSignatureResponse.getCertificateCheckCode() != 0) { String checkFailedReason =""; if (verifyXMLSignatureResponse.getCertificateCheckCode() == 1) checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.21", null); if (verifyXMLSignatureResponse.getCertificateCheckCode() == 2) checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.22", null); if (verifyXMLSignatureResponse.getCertificateCheckCode() == 3) checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.23", null); if (verifyXMLSignatureResponse.getCertificateCheckCode() == 4) checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.24", null); if (verifyXMLSignatureResponse.getCertificateCheckCode() == 5) checkFailedReason = MOAIDMessageProvider.getInstance().getMessage("validator.25", null); // TEST CARDS if (whatToCheck.equals(CHECK_IDENTITY_LINK)) throw new ValidateException("validator.07", new Object[] { checkFailedReason } ); else throw new ValidateException("validator.19", new Object[] { checkFailedReason } ); } //check QC if (authConfig.isCertifiacteQCActive() && !whatToCheck.equals(CHECK_IDENTITY_LINK) && !verifyXMLSignatureResponse.isQualifiedCertificate()) { //check if testcards are active and certificate has an extension for test credentials if (oaParam.isTestCredentialEnabled()) { boolean foundTestCredentialOID = false; try { X509Certificate signerCert = verifyXMLSignatureResponse.getX509certificate(); List validOIDs = new ArrayList(); if (oaParam.getTestCredentialOIDs() != null) validOIDs.addAll(oaParam.getTestCredentialOIDs()); else validOIDs.add(MOAIDAuthConstants.TESTCREDENTIALROOTOID); Set extentsions = signerCert.getCriticalExtensionOIDs(); extentsions.addAll(signerCert.getNonCriticalExtensionOIDs()); Iterator extit = extentsions.iterator(); while(extit.hasNext()) { String certOID = extit.next(); for (String el : validOIDs) { if (certOID.startsWith(el)) foundTestCredentialOID = true; } } } catch (Exception e) { Logger.warn("Test credential OID extraction FAILED.", e); } //throw Exception if not TestCredentialOID is found if (!foundTestCredentialOID) throw new ValidateException("validator.72", null); } else throw new ValidateException("validator.71", null); } // if OA is type is business service the manifest validation result has // to be ignored boolean ignoreManifestValidationResult = false; if (whatToCheck.equals(CHECK_IDENTITY_LINK)) ignoreManifestValidationResult = (oaParam.hasBaseIdInternalProcessingRestriction()) ? true : false; if (ignoreManifestValidationResult) { Logger.debug("OA type is business service, thus ignoring DSIG manifest validation result"); } else { if (verifyXMLSignatureResponse.isXmlDSIGManigest()) if (verifyXMLSignatureResponse.getXmlDSIGManifestCheckCode() != 0) throw new ValidateException("validator.08", null); } // Check the signature manifest only when verifying the signed AUTHBlock if (whatToCheck.equals(CHECK_AUTH_BLOCK)) { if (verifyXMLSignatureResponse.getSignatureManifestCheckCode() > 0) { throw new ValidateException("validator.50", null); } } //Check whether the returned X509 SubjectName is in the MOA-ID configuration or not if (identityLinkSignersSubjectDNNames != null) { String subjectDN = ""; X509Certificate x509Cert = verifyXMLSignatureResponse.getX509certificate(); try { subjectDN = ((Name) x509Cert.getSubjectDN()).getRFC2253String(); } catch (RFC2253NameParserException e) { throw new ValidateException("validator.17", null); } //System.out.println("subjectDN: " + subjectDN); // check the authorisation to sign the identity link if (!identityLinkSignersSubjectDNNames.contains(subjectDN)) { // subject DN check failed, try OID check: try { if (x509Cert.getExtension(MOAIDAuthConstants.IDENTITY_LINK_SIGNER_OID) == null) { throw new ValidateException("validator.18", new Object[] { subjectDN }); } else { Logger.debug("Identity link signer cert accepted for signing identity link: " + "subjectDN check failed, but OID check successfully passed."); } } catch (X509ExtensionInitException e) { throw new ValidateException("validator.49", null); } } else { Logger.debug("Identity link signer cert accepted for signing identity link: " + "subjectDN check successfully passed."); } } } /** * Method validateCertificate. * @param verifyXMLSignatureResponse The VerifyXMLSignatureResponse * @param idl The Identitylink * @throws ValidateException */ public void validateCertificate( IVerifiyXMLSignatureResponse verifyXMLSignatureResponse, IIdentityLink idl) throws ValidateException { X509Certificate x509Response = verifyXMLSignatureResponse.getX509certificate(); PublicKey[] pubKeysIdentityLink = (PublicKey[]) idl.getPublicKey(); PublicKey pubKeySignature = x509Response.getPublicKey(); checkIDLAgainstSignatureCertificate(pubKeysIdentityLink, pubKeySignature); } public void checkIDLAgainstSignatureCertificate( PublicKey[] pubKeysIdentityLink, PublicKey pubKeySignature) throws ValidateException { boolean found = false; for (int i = 0; i < pubKeysIdentityLink.length; i++) { PublicKey idlPubKey = pubKeysIdentityLink[i]; //compare RSAPublicKeys if ((idlPubKey instanceof java.security.interfaces.RSAPublicKey) && (pubKeySignature instanceof java.security.interfaces.RSAPublicKey)) { RSAPublicKey rsaPubKeySignature = (RSAPublicKey) pubKeySignature; RSAPublicKey rsakey = (RSAPublicKey) pubKeysIdentityLink[i]; if (rsakey.getModulus().equals(rsaPubKeySignature.getModulus()) && rsakey.getPublicExponent().equals(rsaPubKeySignature.getPublicExponent())) found = true; } //compare ECDSAPublicKeys if( ( (idlPubKey instanceof java.security.interfaces.ECPublicKey) || (idlPubKey instanceof ECPublicKey)) && ( (pubKeySignature instanceof java.security.interfaces.ECPublicKey) || (pubKeySignature instanceof ECPublicKey) ) ) { try { ECPublicKey ecdsaPubKeySignature = new ECPublicKey(pubKeySignature.getEncoded()); ECPublicKey ecdsakey = new ECPublicKey(pubKeysIdentityLink[i].getEncoded()); if(ecdsakey.equals(ecdsaPubKeySignature)) found = true; } catch (InvalidKeyException e) { Logger.warn("ECPublicKey can not parsed into a iaik.ECPublicKey", e); throw new ValidateException("validator.09", null); } } // Logger.debug("IDL-Pubkey=" + idl.getPublicKey()[i].getClass().getName() // + " Resp-Pubkey=" + pubKeySignature.getClass().getName()); } if (!found) { throw new ValidateException("validator.09", null); } } }