/* * Demoiselle Framework * Copyright (C) 2010 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 versão 3 como publicada pela Fundação * do Software Livre (FSF). * * Este programa é distribuído na esperança que possa ser útil, mas SEM NENHUMA * GARANTIA; sem uma garantia implícita de ADEQUAÇÃO a qualquer MERCADO ou * APLICAÇÃO EM PARTICULAR. Veja a Licença Pública Geral GNU/LGPL em português * para maiores detalhes. * * Você deve ter recebido uma cópia da GNU LGPL versão 3, sob o título * "LICENCA.txt", junto com esse programa. Se não, acesse <http://www.gnu.org/licenses/> * ou escreva para a Fundação do Software Livre (FSF) Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02111-1301, USA. */ package br.gov.frameworkdemoiselle.certificate.signer.pkcs7.bc.policies; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; import java.security.cert.CertStore; import java.security.cert.CertStoreException; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; import java.text.ParseException; import java.util.Collection; import java.util.Date; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.DERUTCTime; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.CMSAttributes; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSProcessable; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.jce.provider.BouncyCastleProvider; import br.gov.frameworkdemoiselle.certificate.criptography.Digest; import br.gov.frameworkdemoiselle.certificate.criptography.DigestAlgorithmEnum; import br.gov.frameworkdemoiselle.certificate.criptography.factory.DigestFactory; import br.gov.frameworkdemoiselle.certificate.signer.SignerAlgorithmEnum; import br.gov.frameworkdemoiselle.certificate.signer.SignerException; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.SignaturePolicy; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.SignaturePolicyException; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.attribute.SigPolicyQualifierInfoURL; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.attribute.SignaturePolicyId; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.attribute.SigningCertificate; import br.gov.frameworkdemoiselle.certificate.signer.util.ValidadorUtil; /** * Implementa a Política ICP-Brasil * * POLÍTICA ICP-BRASIL PARA ASSINATURA DIGITAL COM REFERÊNCIA BÁSICA NO FORMATO * CMS versão 1.0 * * definina no documento: REQUISITOS DAS POLÍTICAS DE ASSINATURA DIGITAL NA * ICP-BRASIL - DOC-ICP-15.03 - Versão 2.0 - 05 de abril de 2010 * */ public class ADRBCMS_1_0 implements SignaturePolicy { private final int keySize = 1024; @Override public SignaturePolicyId getSignaturePolicyId() { SignaturePolicyId signaturePolicyId = new SignaturePolicyId(); signaturePolicyId.setHash(new byte[]{ 32, -42, 120, -109, 37, 81, 59, -68, -116, 41, 98, 78, 31, 64, -74, 24, 19, -20, 92, -25 }); signaturePolicyId.setHashAlgorithm(SignerAlgorithmEnum.SHA1withDSA.getOIDAlgorithmHash()); signaturePolicyId.setSigPolicyId(OIDICPBrasil.POLICY_ID_AD_RB_CMS_V_1_0); signaturePolicyId.addSigPolicyQualifiers(new SigPolicyQualifierInfoURL("http://politicas.icpbrasil.gov.br/PA_AD_RB.der")); return signaturePolicyId; } @Override public void validate(byte[] content, byte[] contentSigned) { if (contentSigned == null || contentSigned.length == 0) { throw new SignaturePolicyException("Content signed is null"); } X509Certificate certificate = null; PublicKey publicKey = null; /* * Validando a integridade do arquivo */ CMSSignedData signedData = null; try { if (content == null) { signedData = new CMSSignedData(contentSigned); } else { signedData = new CMSSignedData(new CMSProcessableByteArray(content), contentSigned); } } catch (CMSException exception) { throw new SignerException("Invalid bytes for a package PKCS7", exception); } /* * Validando as informações da assinatura */ SignerInformationStore signerInformationStore = signedData.getSignerInfos(); SignerInformation signerInformation = (SignerInformation) signerInformationStore.getSigners().iterator().next(); /* * Retirando o Certificado Digital e a chave Pública da assinatura */ try { CertStore certs; try { Security.addProvider(new BouncyCastleProvider()); certs = signedData.getCertificatesAndCRLs("Collection", "BC"); Collection<? extends Certificate> collCertificados = certs.getCertificates(signerInformation.getSID()); if (!collCertificados.isEmpty()) { certificate = (X509Certificate) collCertificados.iterator().next(); publicKey = certificate.getPublicKey(); } } catch (NoSuchAlgorithmException exception) { throw new SignerException(exception); } catch (NoSuchProviderException exception) { throw new SignerException(exception); } catch (CMSException exception) { throw new SignerException(exception); } catch (CertStoreException exception) { throw new SignerException(exception); } } catch (SignerException exception) { throw new SignerException("Error on get information about certificates and public keys from a package PKCS7", exception); } /* * Validando os atributos assinados */ AttributeTable signedAttributesTable = signerInformation.getSignedAttributes(); /* * Validando o atributo ContentType */ org.bouncycastle.asn1.cms.Attribute attributeContentType = signedAttributesTable.get(CMSAttributes.contentType); if (attributeContentType == null) { throw new SignerException("Package PKCS7 without attribute ContentType"); } if (!attributeContentType.getAttrValues().getObjectAt(0).equals(ContentInfo.data)) { throw new SignerException("ContentType isn't a DATA type"); } /* * Com o atributo ContentType válido, extrair o conteúdo assinado, caso * possua o conteúdo atached */ try { CMSProcessable contentProcessable = signedData.getSignedContent(); if (contentProcessable != null) { content = (byte[]) contentProcessable.getContent(); } } catch (Exception exception) { throw new SignerException(exception); } /* * Validando o atributo MessageDigest */ org.bouncycastle.asn1.cms.Attribute attributeMessageDigest = signedAttributesTable.get(CMSAttributes.messageDigest); if (attributeMessageDigest == null) { throw new SignerException("Package PKCS7 without attribute MessageDigest"); } Object der = attributeMessageDigest.getAttrValues().getObjectAt(0).getDERObject(); ASN1OctetString octeto = ASN1OctetString.getInstance(der); byte[] hashContentSigned = octeto.getOctets(); String algorithm = SignerAlgorithmEnum.getSignerOIDAlgorithmHashEnum(signerInformation.getDigestAlgorithmID().getObjectId().toString()).getAlgorithmHash(); if (!algorithm.equals(DigestAlgorithmEnum.SHA_1.getAlgorithm())) { throw new SignerException("Algoritmo de resumo inválido para esta política"); } Digest digest = DigestFactory.getInstance().factoryDefault(); digest.setAlgorithm(DigestAlgorithmEnum.SHA_1.getAlgorithm()); byte[] hashContent = digest.digest(content); if (!MessageDigest.isEqual(hashContentSigned, hashContent)) { throw new SignerException("Hash not equal"); } try { signerInformation.verify(publicKey, "BC"); } catch (NoSuchAlgorithmException e) { throw new SignerException(e); } catch (NoSuchProviderException e) { throw new SignerException(e); } catch (CMSException e) { throw new SignerException("Invalid signature", e); } // Valida a cadeia de certificação de um arquivo assinado //ValidadorUtil.validate(contentSigned, OIDICPBrasil.POLICY_ID_AD_RB_CMS_V_1_0, CertPathEncoding.PKCS7); Date dataSigner = null; try { org.bouncycastle.asn1.cms.Attribute attributeSigningTime = signedAttributesTable.get(CMSAttributes.signingTime); ASN1Set valorDateSigner = attributeSigningTime.getAttrValues(); DERSet derSet = (DERSet) valorDateSigner.getDERObject(); DERUTCTime time = (DERUTCTime) derSet.getObjectAt(0); dataSigner = time.getAdjustedDate(); } catch (ParseException ex) { throw new SignerException("SigningTime error", ex); } //Para a versão 1.0, o período para assinatura desta PA é de 31/10/2008 a 31/12/2014. // Calendar calendar = GregorianCalendar.getInstance(); // calendar.set(2008, Calendar.OCTOBER, 31, 0, 0, 0); // Date firstDate = calendar.getTime(); // // calendar.set(2014, Calendar.DECEMBER, 31, 23, 59, 59); // Date lastDate = calendar.getTime(); // // if (dataSigner != null) { // if (dataSigner.before(firstDate)) { // throw new SignerException("Invalid signing time. Not valid before 10/31/2008"); // } // if (dataSigner.after(lastDate)) { // throw new SignerException("Invalid signing time. Not valid after 12/31/2014"); // } // } else { // throw new SignerException("There is SigningTime attribute on Package PKCS7, but it is null"); // } } /** * Efetua a validação da politica para um determinado certificado no momento * da assinatura * * @param certificate * @param privateKey */ @Override public void validate(X509Certificate certificate, PrivateKey privateKey) { /* * O tamanho mínimo de chaves para criação de assinaturas segundo esta * PA é de : * * a) para a versão 1.0: 1024 bits; b) para a versão 1.1: 1024 bits; c) * para a versão 2.0: 2048 bits. */ if (((RSAPublicKey) certificate.getPublicKey()).getModulus().bitLength() < keySize) { throw new SignerException("O tamanho mínimo da chave privada deve ser de " + keySize + " bits"); } /* * Assinaturas digitais geradas segundo esta Política de Assinatura * deverão ser criadas com chave privada associada ao certificado * ICP-Brasil * tipo A1 (do OID 2.16.76.1.2.1.1 ao OID * 2.16.76.1.2.1.100), tipo A2 (do OID 2.16.76.1.2.2.1 ao OID * 2.16.76.1.2.2.100), do tipo A3 (do OID 2.16.76.1.2.3.1 ao OID * 2.16.76.1.2.3.100) e do tipo A4 (do OID 2.16.76.1.2.4.1 ao OID * 2.16.76.1.2.4.100), conforme definido em DOC-ICP-04. */ ValidadorUtil.validate(certificate); // TODO Implementar a validação do caminho de certificação para o // certificado digital a ser utilizado na assinatura } @Override public SignerAlgorithmEnum getSignerAlgorithm() { return SignerAlgorithmEnum.SHA1withRSA; } @Override public SigningCertificate getSigningCertificateAttribute(X509Certificate certificate) { return new SigningCertificate(certificate); } }