/*******************************************************************************
* 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);
}
}
}