/******************************************************************************* * 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.MOAIDConstants; 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 { final X509Certificate signerCert = verifyXMLSignatureResponse.getX509certificate(); final List validOIDs = new ArrayList<>(); if (oaParam.getTestCredentialOIDs() != null) { validOIDs.addAll(oaParam.getTestCredentialOIDs()); } else { validOIDs.add(MOAIDConstants.TESTCREDENTIALROOTOID); } final Set extentsions = signerCert.getCriticalExtensionOIDs(); extentsions.addAll(signerCert.getNonCriticalExtensionOIDs()); final Iterator extit = extentsions.iterator(); while (extit.hasNext()) { final String certOID = extit.next(); for (final String el : validOIDs) { if (certOID.startsWith(el)) { foundTestCredentialOID = true; } } } } catch (final 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 = ""; final X509Certificate x509Cert = verifyXMLSignatureResponse.getX509certificate(); try { subjectDN = ((Name) x509Cert.getSubjectDN()).getRFC2253String(); } catch (final 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 (final 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 { final X509Certificate x509Response = verifyXMLSignatureResponse.getX509certificate(); final PublicKey[] pubKeysIdentityLink = idl.getPublicKey(); final PublicKey pubKeySignature = x509Response.getPublicKey(); checkIDLAgainstSignatureCertificate(pubKeysIdentityLink, pubKeySignature); } public void checkIDLAgainstSignatureCertificate(PublicKey[] pubKeysIdentityLink, PublicKey pubKeySignature) throws ValidateException { boolean found = false; for (final PublicKey idlPubKey : pubKeysIdentityLink) { // compare RSAPublicKeys if (idlPubKey instanceof java.security.interfaces.RSAPublicKey && pubKeySignature instanceof java.security.interfaces.RSAPublicKey) { final RSAPublicKey rsaPubKeySignature = (RSAPublicKey) pubKeySignature; final RSAPublicKey rsakey = (RSAPublicKey) idlPubKey; 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 { final ECPublicKey ecdsaPubKeySignature = new ECPublicKey(pubKeySignature.getEncoded()); final ECPublicKey ecdsakey = new ECPublicKey(idlPubKey.getEncoded()); if (ecdsakey.equals(ecdsaPubKeySignature)) { found = true; } } catch (final 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); } } }