Java tutorial
/* * Demoiselle Framework * Copyright (C) 2016 SERPRO * ---------------------------------------------------------------------------- * This file is part of Demoiselle Framework. * * Demoiselle Framework is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License version 3 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License version 3 * along with this program; if not, see <http://www.gnu.org/licenses/> * or write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301, USA. * ---------------------------------------------------------------------------- * Este arquivo parte do Framework Demoiselle. * * O Framework Demoiselle um software livre; voc pode redistribu-lo e/ou * modific-lo dentro dos termos da GNU LGPL verso 3 como publicada pela Fundao * do Software Livre (FSF). * * Este programa distribudo na esperana que possa ser til, mas SEM NENHUMA * GARANTIA; sem uma garantia implcita de ADEQUAO a qualquer MERCADO ou * APLICAO EM PARTICULAR. Veja a Licena Pblica Geral GNU/LGPL em portugus * para maiores detalhes. * * Voc deve ter recebido uma cpia da GNU LGPL verso 3, sob o ttulo * "LICENCA.txt", junto com esse programa. Se no, acesse <http://www.gnu.org/licenses/> * ou escreva para a Fundao do Software Livre (FSF) Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA. */ package org.demoiselle.signer.policy.impl.cades.pkcs7.impl; import java.io.IOException; import java.security.Security; import java.security.cert.X509Certificate; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1UTCTime; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.CMSAttributes; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSProcessable; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignerDigestMismatchException; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.tsp.TSPException; import org.bouncycastle.tsp.TimeStampToken; import org.bouncycastle.util.Store; import org.demoiselle.signer.core.CertificateManager; import org.demoiselle.signer.core.ca.manager.CAManager; import org.demoiselle.signer.core.exception.CertificateCoreException; import org.demoiselle.signer.core.exception.CertificateRevocationException; import org.demoiselle.signer.core.exception.CertificateValidatorCRLException; import org.demoiselle.signer.core.exception.CertificateValidatorException; import org.demoiselle.signer.core.util.MessagesBundle; import org.demoiselle.signer.core.validator.CRLValidator; import org.demoiselle.signer.core.validator.PeriodValidator; import org.demoiselle.signer.policy.engine.asn1.etsi.ObjectIdentifier; import org.demoiselle.signer.policy.engine.asn1.etsi.SignaturePolicy; import org.demoiselle.signer.policy.engine.factory.PolicyFactory; import org.demoiselle.signer.policy.impl.cades.SignatureInformations; import org.demoiselle.signer.policy.impl.cades.SignerException; import org.demoiselle.signer.policy.impl.cades.pkcs7.PKCS7Checker; import org.demoiselle.signer.timestamp.Timestamp; import org.demoiselle.signer.timestamp.connector.TimeStampOperator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * Basic Implementation for digital signatures in PKCS7 Format. * */ public class CAdESChecker implements PKCS7Checker { private static final Logger logger = LoggerFactory.getLogger(CAdESChecker.class); private SignaturePolicy signaturePolicy = null; private static MessagesBundle cadesMessagesBundle = new MessagesBundle(); private byte[] hash = null; private Map<String, byte[]> hashes = new HashMap<String, byte[]>(); private boolean checkHash = false; private List<SignatureInformations> signaturesInfo = new ArrayList<SignatureInformations>(); private String policyName; private CertificateManager certificateManager; public CAdESChecker() { super(); } /** * Validation is done only on digital signatures with a single signer. Valid * only with content of type DATA.: OID ContentType 1.2.840.113549.1.9.3 = * OID Data 1.2.840.113549.1.7.1 * * @param content Is only necessary to inform if the PKCS7 package is NOT * ATTACHED type. If it is of type attached, this parameter will be * replaced by the contents of the PKCS7 package. * @param signedData Value in bytes of the PKCS7 package, such as the * contents of a ".p7s" file. It is not only signature as in the * case of PKCS1. */ // TODO: Implementar validao de co-assinaturas public boolean check(byte[] content, byte[] signedData) throws SignerException { Security.addProvider(new BouncyCastleProvider()); CMSSignedData cmsSignedData = null; try { if (content == null) { if (this.checkHash) { cmsSignedData = new CMSSignedData(this.hashes, signedData); this.checkHash = false; } else { cmsSignedData = new CMSSignedData(signedData); } } else { if (this.getAttached(signedData, false) != null) { cmsSignedData = new CMSSignedData(signedData); } else { cmsSignedData = new CMSSignedData(new CMSProcessableByteArray(content), signedData); } } } catch (CMSException ex) { throw new SignerException(cadesMessagesBundle.getString("error.invalid.bytes.pkcs7"), ex); } // Quantidade inicial de assinaturas validadas int verified = 0; Store<?> certStore = cmsSignedData.getCertificates(); SignerInformationStore signers = cmsSignedData.getSignerInfos(); Iterator<?> it = signers.getSigners().iterator(); // Realizao da verificao bsica de todas as assinaturas while (it.hasNext()) { SignatureInformations signatureInfo = new SignatureInformations(); try { SignerInformation signerInfo = (SignerInformation) it.next(); SignerInformationStore signerInfoStore = signerInfo.getCounterSignatures(); logger.info("Foi(ram) encontrada(s) " + signerInfoStore.size() + " contra-assinatura(s)."); @SuppressWarnings("unchecked") Collection<?> certCollection = certStore.getMatches(signerInfo.getSID()); Iterator<?> certIt = certCollection.iterator(); X509CertificateHolder certificateHolder = (X509CertificateHolder) certIt.next(); X509Certificate varCert = new JcaX509CertificateConverter().getCertificate(certificateHolder); CRLValidator cV = new CRLValidator(); try { cV.validate(varCert); } catch (CertificateValidatorCRLException cvce) { signatureInfo.getValidatorErrors().add(cvce.getMessage()); logger.info(cvce.getMessage()); } catch (CertificateRevocationException cre) { signatureInfo.getValidatorErrors().add(cre.getMessage()); logger.info("certificado revogado"); } PeriodValidator pV = new PeriodValidator(); try { pV.validate(varCert); } catch (CertificateValidatorException cve) { signatureInfo.getValidatorErrors().add(cve.getMessage()); } if (signerInfo.verify( new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certificateHolder))) { verified++; logger.info(cadesMessagesBundle.getString("info.signature.valid.seq", verified)); } // recupera atributos assinados logger.info(cadesMessagesBundle.getString("info.signed.attribute")); String varOIDPolicy = PKCSObjectIdentifiers.id_aa_ets_sigPolicyId.getId(); AttributeTable signedAttributes = signerInfo.getSignedAttributes(); if ((signedAttributes == null) || (signedAttributes != null && signedAttributes.size() == 0)) { signatureInfo.getValidatorErrors() .add(cadesMessagesBundle.getString("error.signed.attribute.table.not.found")); logger.info(cadesMessagesBundle.getString("error.signed.attribute.table.not.found")); //throw new SignerException(cadesMessagesBundle.getString("error.signed.attribute.table.not.found")); } else { //Validando atributos assinados de acordo com a politica Attribute idSigningPolicy = null; idSigningPolicy = signedAttributes.get(new ASN1ObjectIdentifier(varOIDPolicy)); if (idSigningPolicy == null) { signatureInfo.getValidatorErrors().add( cadesMessagesBundle.getString("error.pcks7.attribute.not.found", varOIDPolicy)); } else { for (Enumeration<?> p = idSigningPolicy.getAttrValues().getObjects(); p .hasMoreElements();) { String policyOnSignature = p.nextElement().toString(); for (PolicyFactory.Policies pv : PolicyFactory.Policies.values()) { if (policyOnSignature.contains(pv.getUrl())) { setSignaturePolicy(pv); break; } } } } } Date dataHora = null; if (signedAttributes != null) { // Valida o atributo ContentType Attribute attributeContentType = signedAttributes.get(CMSAttributes.contentType); if (attributeContentType == null) { signatureInfo.getValidatorErrors().add( cadesMessagesBundle.getString("error.pcks7.attribute.not.found", "ContentType")); //throw new SignerException(cadesMessagesBundle.getString("error.pcks7.attribute.not.found", "ContentType")); logger.info( cadesMessagesBundle.getString("error.pcks7.attribute.not.found", "ContentType")); } if (!attributeContentType.getAttrValues().getObjectAt(0).equals(ContentInfo.data)) { signatureInfo.getValidatorErrors() .add(cadesMessagesBundle.getString("error.content.not.data")); //throw new SignerException(cadesMessagesBundle.getString("error.content.not.data")); logger.info(cadesMessagesBundle.getString("error.content.not.data")); } // Validando o atributo MessageDigest Attribute attributeMessageDigest = signedAttributes.get(CMSAttributes.messageDigest); if (attributeMessageDigest == null) { throw new SignerException( cadesMessagesBundle.getString("error.pcks7.attribute.not.found", "MessageDigest")); } // Mostra data e hora da assinatura, no carimbo de tempo Attribute timeAttribute = signedAttributes.get(CMSAttributes.signingTime); if (timeAttribute != null) { dataHora = (((ASN1UTCTime) timeAttribute.getAttrValues().getObjectAt(0)).getDate()); logger.info(cadesMessagesBundle.getString("info.date.utc", dataHora)); } else { logger.info(cadesMessagesBundle.getString("info.date.utc", "N/D")); } } if (signaturePolicy == null) { signatureInfo.getValidatorErrors().add( cadesMessagesBundle.getString("error.policy.on.component.not.found", varOIDPolicy)); logger.info(cadesMessagesBundle.getString("error.policy.on.component.not.found")); } else { if (signaturePolicy.getSignPolicyInfo().getSignatureValidationPolicy().getCommonRules() .getSignerAndVeriferRules().getSignerRules().getMandatedSignedAttr() .getObjectIdentifiers() != null) { for (ObjectIdentifier objectIdentifier : signaturePolicy.getSignPolicyInfo() .getSignatureValidationPolicy().getCommonRules().getSignerAndVeriferRules() .getSignerRules().getMandatedSignedAttr().getObjectIdentifiers()) { String oi = objectIdentifier.getValue(); Attribute signedAtt = signedAttributes.get(new ASN1ObjectIdentifier(oi)); logger.info(oi); if (signedAtt == null) { signatureInfo.getValidatorErrors().add(cadesMessagesBundle.getString( "error.signed.attribute.not.found", oi, signaturePolicy.getSignPolicyInfo().getSignPolicyIdentifier().getValue())); } } } } // recupera os atributos NO assinados logger.info(cadesMessagesBundle.getString("info.unsigned.attribute")); AttributeTable unsignedAttributes = signerInfo.getUnsignedAttributes(); if ((unsignedAttributes == null) || (unsignedAttributes != null && unsignedAttributes.size() == 0)) { // Apenas info pois a RB no tem atributos no assinados logger.info(cadesMessagesBundle.getString("error.unsigned.attribute.table.not.found")); } if (signaturePolicy != null) { // Validando atributos NO assinados de acordo com a politica if (signaturePolicy.getSignPolicyInfo().getSignatureValidationPolicy().getCommonRules() .getSignerAndVeriferRules().getSignerRules().getMandatedUnsignedAttr() .getObjectIdentifiers() != null) { for (ObjectIdentifier objectIdentifier : signaturePolicy.getSignPolicyInfo() .getSignatureValidationPolicy().getCommonRules().getSignerAndVeriferRules() .getSignerRules().getMandatedUnsignedAttr().getObjectIdentifiers()) { String oi = objectIdentifier.getValue(); Attribute unSignedAtt = unsignedAttributes.get(new ASN1ObjectIdentifier(oi)); logger.info(oi); if (unSignedAtt == null) { signatureInfo.getValidatorErrors().add(cadesMessagesBundle.getString( "error.unsigned.attribute.not.found", oi, signaturePolicy.getSignPolicyInfo().getSignPolicyIdentifier().getValue())); } if (oi.equalsIgnoreCase(PKCSObjectIdentifiers.id_aa_signatureTimeStampToken.getId())) { //Verificando timeStamp try { byte[] varSignature = signerInfo.getSignature(); Timestamp varTimeStampSigner = validateTimestamp(unSignedAtt, varSignature); signatureInfo.setTimeStampSigner(varTimeStampSigner); } catch (Exception ex) { signatureInfo.getValidatorErrors().add(ex.getMessage()); // nas assinaturas feitas na applet o unsignedAttributes.get gera exceo. } } if (oi.equalsIgnoreCase("1.2.840.113549.1.9.16.2.25")) { logger.info("++++++++++ EscTimeStamp ++++++++++++"); } } } } LinkedList<X509Certificate> varChain = (LinkedList<X509Certificate>) CAManager.getInstance() .getCertificateChain(varCert); if (varChain.size() < 3) { signatureInfo.getValidatorErrors() .add(cadesMessagesBundle.getString("error.no.ca", varCert.getIssuerDN())); logger.info(cadesMessagesBundle.getString("error.no.ca", varCert.getIssuerDN())); } signatureInfo.setSignDate(dataHora); signatureInfo.setChain(varChain); signatureInfo.setSignaturePolicy(signaturePolicy); this.getSignaturesInfo().add(signatureInfo); } catch (OperatorCreationException | java.security.cert.CertificateException ex) { signatureInfo.getValidatorErrors().add(ex.getMessage()); logger.info(ex.getMessage()); } catch (CMSException ex) { // When file is mismatch with sign if (ex instanceof CMSSignerDigestMismatchException) { signatureInfo.getValidatorErrors() .add(cadesMessagesBundle.getString("error.signature.mismatch")); logger.info(cadesMessagesBundle.getString("error.signature.mismatch")); throw new SignerException(cadesMessagesBundle.getString("error.signature.mismatch"), ex); } else { signatureInfo.getValidatorErrors() .add(cadesMessagesBundle.getString("error.signature.invalid")); logger.info(cadesMessagesBundle.getString("error.signature.invalid")); throw new SignerException(cadesMessagesBundle.getString("error.signature.invalid"), ex); } } catch (ParseException e) { signatureInfo.getValidatorErrors().add(e.getMessage()); logger.info(e.getMessage()); } } logger.info(cadesMessagesBundle.getString("info.signature.verified", verified)); // TODO Efetuar o parsing da estrutura CMS return true; } /** * validade a timestampo on signature * @param attributeTimeStamp * @param varSignature * @return */ private Timestamp validateTimestamp(Attribute attributeTimeStamp, byte[] varSignature) { try { TimeStampOperator timeStampOperator = new TimeStampOperator(); byte[] varTimeStamp = attributeTimeStamp.getAttrValues().getObjectAt(0).toASN1Primitive().getEncoded(); TimeStampToken timeStampToken = new TimeStampToken(new CMSSignedData(varTimeStamp)); Timestamp timeStampSigner = new Timestamp(timeStampToken); timeStampOperator.validate(varSignature, varTimeStamp, null); return timeStampSigner; } catch (CertificateCoreException | IOException | TSPException | CMSException e) { throw new SignerException(e); } } /** * Return the signed file content attached to the signature. * * @param signed * Signature and signed content. * @return content for attached signature */ public byte[] getAttached(byte[] signed) { return this.getAttached(signed, true); } /** * Extracts the signed content from the digital signature structure, if it * is a signature with attached content. * * @param signed * Signature and signed content. * @param validateOnExtract * TRUE (to execute validation) or FALSE (not execute validation) * * @return content for attached signature */ @Override public byte[] getAttached(byte[] signed, boolean validateOnExtract) { byte[] result = null; if (validateOnExtract) { this.check(null, signed); } CMSSignedData signedData = null; try { signedData = new CMSSignedData(signed); } catch (CMSException exception) { throw new SignerException(cadesMessagesBundle.getString("error.invalid.bytes.pkcs7"), exception); } try { CMSProcessable contentProcessable = signedData.getSignedContent(); if (contentProcessable != null) { result = (byte[]) contentProcessable.getContent(); } else { logger.info(cadesMessagesBundle.getString("error.get.content.empty")); } } catch (Exception exception) { throw new SignerException(cadesMessagesBundle.getString("error.get.content.pkcs7"), exception); } return result; } @Override public List<SignatureInformations> checkAttachedSignature(byte[] signedData) { if (this.check(null, signedData)) { return this.getSignaturesInfo(); } else { return null; } } @Override @Deprecated public List<SignatureInformations> checkDetattachedSignature(byte[] content, byte[] signedData) { if (this.check(content, signedData)) { return this.getSignaturesInfo(); } else { return null; } } @Override public List<SignatureInformations> checkDetachedSignature(byte[] content, byte[] signedData) { if (this.check(content, signedData)) { return this.getSignaturesInfo(); } else { return null; } } @Override public List<SignatureInformations> checkSignatureByHash(String digestAlgorithmOID, byte[] calculatedHashContent, byte[] signedData) throws SignerException { this.checkHash = true; this.hashes.put(digestAlgorithmOID, calculatedHashContent); this.setHash(calculatedHashContent); if (this.check(null, signedData)) { return this.getSignaturesInfo(); } else { return null; } } private void setSignaturePolicy(PolicyFactory.Policies signaturePolicy) { this.setPolicyName(signaturePolicy.name()); PolicyFactory policyFactory = PolicyFactory.getInstance(); org.demoiselle.signer.policy.engine.asn1.etsi.SignaturePolicy sp = policyFactory .loadPolicy(signaturePolicy); this.signaturePolicy = sp; } @Override public List<SignatureInformations> getSignaturesInfo() { return signaturesInfo; } public void setSignaturesInfo(List<SignatureInformations> signatureInfo) { this.signaturesInfo = signatureInfo; } public String getPolicyName() { return policyName; } public void setPolicyName(String policyName) { this.policyName = policyName; } public CertificateManager getCertificateManager() { return certificateManager; } public void setCertificateManager(CertificateManager certificateManager) { this.certificateManager = certificateManager; } public byte[] getHash() { return hash; } public void setHash(byte[] hash) { this.hash = hash; } }