Java tutorial
/* * 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 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 br.gov.frameworkdemoiselle.certificate.signer.pkcs7.bc; import br.gov.frameworkdemoiselle.certificate.CertificateException; import br.gov.frameworkdemoiselle.certificate.CertificateManager; import br.gov.frameworkdemoiselle.certificate.CertificateValidatorException; import br.gov.frameworkdemoiselle.certificate.IValidator; import br.gov.frameworkdemoiselle.certificate.ca.manager.CAManager; import br.gov.frameworkdemoiselle.certificate.extension.BasicCertificate; import br.gov.frameworkdemoiselle.certificate.signer.SignerAlgorithmEnum; import br.gov.frameworkdemoiselle.certificate.signer.SignerException; import br.gov.frameworkdemoiselle.certificate.signer.factory.PKCS1Factory; import br.gov.frameworkdemoiselle.certificate.signer.pkcs1.PKCS1Signer; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.PKCS7Signer; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.SignaturePolicy; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.SignaturePolicyFactory; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.attribute.Attribute; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.attribute.SignaturePolicyIdentifier; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.attribute.SignedAttribute; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.attribute.SigningCertificate; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.attribute.UnsignedAttribute; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.bc.attribute.BCAdapter; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.bc.attribute.BCAttribute; import br.gov.frameworkdemoiselle.certificate.signer.pkcs7.bc.policies.ADRBCMS_1_0; import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.Provider; 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.CollectionCertStoreParameters; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSProcessable; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * Assinatura de dados no formato PKCS#7 Implementalo baseada na RFC5126 - * CAdES (http://tools.ietf.org/html/rfc5126) e voltada para uso no mbito * ICP-Brasil. */ public class CAdESSigner implements PKCS7Signer { private static final Logger LOGGER = Logger.getLogger(CAdESSigner.class.getName()); private final PKCS1Signer pkcs1 = PKCS1Factory.getInstance().factoryDefault(); private X509Certificate certificate; private Certificate certificateChain[]; private boolean attached = false; private SignaturePolicy signaturePolicy = null; private Map<Class<? extends Attribute>, Collection<Attribute>> attributes; private Collection<IValidator> certificateValidators = null; private boolean defaultCertificateValidators = true; public CAdESSigner() { this.pkcs1.setAlgorithm((String) null); this.setSignaturePolicy(new ADRBCMS_1_0()); } @Override public void addAttribute(Attribute attribute) { if (this.attributes == null) { this.attributes = new HashMap<Class<? extends Attribute>, Collection<Attribute>>(); } if (attribute != null) { Class<? extends Attribute> clazz = getTypeAttribute(attribute); Collection<Attribute> collection = this.attributes.get(clazz); if (collection == null) { collection = new HashSet<Attribute>(); } collection.add(attribute); this.attributes.put(clazz, collection); } } @Override public void addAttributes(Collection<Attribute> attributes) { for (Attribute attribute : attributes) { this.addAttribute(attribute); } } public void addCertificateValidator(IValidator validator) { if (this.certificateValidators == null) { this.certificateValidators = new ArrayList<IValidator>(); } if (!this.certificateValidators.contains(validator)) { this.certificateValidators.add(validator); } } /** * A validao se basea apenas em assinaturas com um assinante apenas. * Valida apenas com o contedo do tipo DATA: OID ContentType * 1.2.840.113549.1.9.3 = OID Data 1.2.840.113549.1.7.1 * * @param content * @param signed * @return * @params content Necessrio informar apenas se o pacote PKCS7 NO for do * tipo ATTACHED. Caso seja do tipo attached, este parmetro ser * substituido pelo contedo do pacote PKCS7. * @params signed Valor em bytes do pacote PKCS7, como por exemplo o * contedo de um arquivo ".p7s". No a assinatura pura como no caso do * PKCS1. TODO: Implementar validao de co-assinaturas */ @Override public boolean check(byte[] content, byte[] signed) { CMSSignedData signedData = null; PublicKey publicKey = null; try { if (content == null) { signedData = new CMSSignedData(signed); } else { signedData = new CMSSignedData(new CMSProcessableByteArray(content), signed); } } catch (CMSException exception) { throw new SignerException("Invalid bytes for a PKCS7 package", exception); } SignerInformationStore signerInformationStore = signedData.getSignerInfos(); SignerInformation signerInformation = (SignerInformation) signerInformationStore.getSigners().iterator() .next(); /* * Retirando o Certificado Digital e a chave Pblica 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 ex) { throw new SignerException( "Error on get information about certificates and public keys from a package PKCS7", ex); } 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); } AttributeTable signedAttributes = signerInformation.getSignedAttributes(); if (signedAttributes == null) { throw new SignerException("Package PKCS7 without signed attributes"); } // Validar a poltica org.bouncycastle.asn1.cms.Attribute signaturePolicyIdentifierAttribute = signedAttributes .get(new DERObjectIdentifier((new SignaturePolicyIdentifier()).getOID())); if (signaturePolicyIdentifierAttribute != null) { ASN1Set valueAttribute = signaturePolicyIdentifierAttribute.getAttrValues(); for (Enumeration<DERSequence> iterator = valueAttribute.getObjects(); iterator.hasMoreElements();) { DERSequence sequence = iterator.nextElement(); DERObjectIdentifier policyIdentifier = (DERObjectIdentifier) sequence.getObjectAt(0); String policyOID = policyIdentifier.getId(); SignaturePolicy policy = SignaturePolicyFactory.getInstance().factory(policyOID); if (policy != null) { policy.validate(content, signed); } else { LOGGER.log(Level.WARNING, "N\u00e3o existe validador para a pol\u00edtica {0}", policyOID); } } } else { throw new SignerException("ICP-Brasil invalid format. There is not policy signature."); } return true; } private CertStore generatedCertStore() { CertStore result = null; try { List<Certificate> certificates = new ArrayList<Certificate>(); // TODO Avaliar se pega todos os certificados for (Certificate certChain : certificateChain) { certificates.add(certChain); } CollectionCertStoreParameters cert = new CollectionCertStoreParameters(certificates); result = CertStore.getInstance("Collection", cert, "BC"); } catch (InvalidAlgorithmParameterException exception) { throw new SignerException(exception); } catch (NoSuchAlgorithmException exception) { throw new SignerException(exception); } catch (NoSuchProviderException exception) { throw new SignerException(exception); } return result; } @Override public String getAlgorithm() { return this.signaturePolicy.getSignerAlgorithm().getAlgorithm(); } public byte[] getAttached(byte[] signed) { return this.getAttached(signed, true); } @Override public byte[] getAttached(byte[] signed, boolean validate) { byte[] result = null; if (validate) { this.check(null, signed); } CMSSignedData signedData = null; try { signedData = new CMSSignedData(signed); } catch (CMSException exception) { throw new SignerException("Invalid bytes for a package PKCS7", exception); } try { CMSProcessable contentProcessable = signedData.getSignedContent(); if (contentProcessable != null) { result = (byte[]) contentProcessable.getContent(); } } catch (Exception exception) { throw new SignerException("Error on get content from PKCS7", exception); } return result; } @Override public Collection<Attribute> getAttributes() { Collection<Attribute> result = new ArrayList<Attribute>(); Set<Class<? extends Attribute>> keys = this.attributes.keySet(); for (Class<? extends Attribute> key : keys) { result.addAll(this.attributes.get(key)); } return result; } @Override public PrivateKey getPrivateKey() { return this.pkcs1.getPrivateKey(); } @Override public Provider getProvider() { return this.pkcs1.getProvider(); } private String getProviderName() { if (this.pkcs1.getProvider() != null) { return this.pkcs1.getProvider().getName(); } return null; } @Override public PublicKey getPublicKey() { return this.pkcs1.getPublicKey(); } private Class<? extends Attribute> getTypeAttribute(Attribute attribute) { if (attribute instanceof UnsignedAttribute) { return UnsignedAttribute.class; } else if (attribute instanceof SignedAttribute) { return SignedAttribute.class; } throw new SignerException("Attribute invalid. Attribute should be SignedAttribute or UnsignedAttribute"); } public boolean isDefaultCertificateValidators() { return this.defaultCertificateValidators; } private AttributeTable mountAttributeTable(Collection<Attribute> collection) { if (collection == null || collection.isEmpty()) { return null; } AttributeTable table = null; Hashtable<DERObjectIdentifier, org.bouncycastle.asn1.cms.Attribute> attributes = new Hashtable<DERObjectIdentifier, org.bouncycastle.asn1.cms.Attribute>(); for (Attribute attribute : collection) { org.bouncycastle.asn1.cms.Attribute bcAttribute = this.transformAttribute(attribute); attributes.put(bcAttribute.getAttrType(), bcAttribute); } if (attributes.size() > 0) { table = new AttributeTable(attributes); } return table; } private AttributeTable mountSignedTable() { if (this.attributes != null && this.attributes.size() > 0) { return this.mountAttributeTable(this.attributes.get(SignedAttribute.class)); } return null; } private AttributeTable mountUnsignedTable() { if (this.attributes != null && this.attributes.size() > 0) { return this.mountAttributeTable(this.attributes.get(UnsignedAttribute.class)); } return null; } @Override public void setAlgorithm(SignerAlgorithmEnum algorithm) { this.pkcs1.setAlgorithm(algorithm); } @Override public void setAlgorithm(String algorithm) { this.pkcs1.setAlgorithm(algorithm); } @Override public void setAttached(boolean attached) { this.attached = attached; } @Override public void setCertificate(X509Certificate certificate) { this.certificate = certificate; } @Override public void setCertificates(Certificate[] certificates) { this.certificateChain = certificates; } public void setDefaultCertificateValidators(boolean defaultCertificateValidators) { this.defaultCertificateValidators = defaultCertificateValidators; } @Override public void setPrivateKey(PrivateKey privateKey) { this.pkcs1.setPrivateKey(privateKey); } @Override public void setProvider(Provider provider) { this.pkcs1.setProvider(provider); } @Override public void setPublicKey(PublicKey publicKey) { this.pkcs1.setPublicKey(publicKey); } @Override public void setSignaturePolicy(SignaturePolicy signaturePolicy) { if (signaturePolicy == null) { return; } this.signaturePolicy = signaturePolicy; } /** * Mtodo de assinatura de dados e gerao do pacote PKCS7 Assina apenas com * o contedo do tipo DATA: OID ContentType 1.2.840.113549.1.9.3 = OID Data * 1.2.840.113549.1.7.1 Utiliza o algoritmo da propriedade algorithm. Caso * essa propriedade no esteja setada, o algoritmo do enum * {@link SignerAlgorithmEnum.DEFAULT} ser usado. Para este mtodo * necessrio informar o contedo, a chave privada e um certificado digital * padro ICP-Brasil. * * @param content Contedo a ser assinado. TODO: Implementar co-assinaturas, * informar a poltica de assinatura * @return */ @Override public byte[] signer(byte[] content) { Security.addProvider(new BouncyCastleProvider()); if (this.certificate == null && this.certificateChain != null && this.certificateChain.length > 0) { this.certificate = (X509Certificate) this.certificateChain[0]; } this.validateForSigner(content); if (this.certificateChain == null || this.certificateChain.length <= 1) { this.certificateChain = CAManager.getInstance().getCertificateChainArray(this.certificate); } //Adiciona o atributo de identificacao da politica SignaturePolicyIdentifier signaturePolicyIdentifier = new SignaturePolicyIdentifier(); signaturePolicyIdentifier.setSignaturePolicyId(this.signaturePolicy.getSignaturePolicyId()); this.addAttribute(signaturePolicyIdentifier); //Adiciona o astributo certificado de assinatura boolean addSigningCertificateAttribute = true; for (Attribute attribute : this.getAttributes()) { if (attribute instanceof SigningCertificate) { addSigningCertificateAttribute = false; break; } } if (addSigningCertificateAttribute) { SigningCertificate signingCertificateAttribute = this.signaturePolicy .getSigningCertificateAttribute(this.certificate); this.addAttribute(signingCertificateAttribute); } this.setCertificate((X509Certificate) certificateChain[0]); if (certificateChain.length == 1) { throw new SignerException("Impossivel extrair a cadeia de confianca do certificado"); } String algorithmHashOID = null; String algorithmEncryptationOID = null; if (this.pkcs1 != null && this.pkcs1.getAlgorithm() != null && this.pkcs1.getAlgorithm().trim().length() > 0) { algorithmHashOID = SignerAlgorithmEnum.valueOf(this.pkcs1.getAlgorithm()).getOIDAlgorithmHash(); algorithmEncryptationOID = SignerAlgorithmEnum.valueOf(this.pkcs1.getAlgorithm()) .getOIDAlgorithmCipher(); } else { algorithmHashOID = this.signaturePolicy.getSignerAlgorithm().getOIDAlgorithmHash(); algorithmEncryptationOID = this.signaturePolicy.getSignerAlgorithm().getOIDAlgorithmCipher(); } byte[] result = null; CMSSignedDataGenerator signedDataGenerator = new CMSSignedDataGenerator(); try { signedDataGenerator.addCertificatesAndCRLs(this.generatedCertStore()); } catch (CertStoreException e) { throw new SignerException(e); } catch (CMSException e) { throw new SignerException(e); } // Valida o certificado usando a politica de certificacao this.signaturePolicy.validate(this.certificate, this.pkcs1.getPrivateKey()); //Recupera o(s) certificado(s) de confianca para validacao Collection<X509Certificate> trustedCas = CAManager.getInstance() .getSignaturePolicyRootCAs(signaturePolicy.getSignaturePolicyId().getSigPolicyId()); //Efetua a validacao das cadeias do certificado baseado na politica CAManager.getInstance().validateRootCAs(trustedCas, certificate); AttributeTable signedTable = this.mountSignedTable(); AttributeTable unsignedTable = this.mountUnsignedTable(); signedDataGenerator.addSigner(this.pkcs1.getPrivateKey(), this.certificate, algorithmEncryptationOID, algorithmHashOID, signedTable, unsignedTable); try { CMSProcessable processable = null; if (content == null) { processable = new CMSAbsentContent(); } else { processable = new CMSProcessableByteArray(content); } CMSSignedData signedData = signedDataGenerator.generate(CMSSignedDataGenerator.DATA, processable, this.attached, this.getProviderName(), true); result = signedData.getEncoded(); } catch (IOException e) { throw new SignerException(e); } catch (NoSuchAlgorithmException e) { throw new SignerException(e); } catch (NoSuchProviderException e) { throw new SignerException(e); } catch (CMSException e) { throw new SignerException(e); } return result; } private org.bouncycastle.asn1.cms.Attribute transformAttribute(Attribute attribute) { BCAttribute adapter = BCAdapter.factoryBCAttribute(attribute); return new org.bouncycastle.asn1.cms.Attribute(adapter.getObjectIdentifier(), adapter.getValue()); } private void validateForSigner(byte... content) { if (this.pkcs1 == null) { throw new SignerException("Please enter the required properties"); } if (this.pkcs1.getPrivateKey() == null) { throw new SignerException("Private Key is null"); } if (this.certificate == null) { throw new SignerException("Certificate is null"); } else { BasicCertificate basicCertificate = new BasicCertificate(this.certificate); try { List<String> listLCRs = basicCertificate.getCRLDistributionPoint(); if (listLCRs == null || listLCRs.isEmpty()) { throw new SignerException("Blank LCR distribuition point for certificate."); } } catch (IOException error) { throw new SignerException("Error on read CRL distribuition point from Certificate"); } try { if (this.certificateValidators == null || this.certificateValidators.isEmpty()) { new CertificateManager(this.certificate, this.defaultCertificateValidators); } else { new CertificateManager(this.certificate, this.defaultCertificateValidators, this.certificateValidators.toArray(new IValidator[] {})); } } catch (Throwable exception) { if (exception instanceof CertificateException) { throw (CertificateException) exception; } if (exception instanceof CertificateValidatorException) { throw (CertificateValidatorException) exception; } throw new SignerException("Certificate is not valid", exception); } } } }