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.timestamp.connector; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.cms.SignerInformationVerifier; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.tsp.TSPAlgorithms; import org.bouncycastle.tsp.TSPException; import org.bouncycastle.tsp.TimeStampRequest; import org.bouncycastle.tsp.TimeStampRequestGenerator; import org.bouncycastle.tsp.TimeStampResponse; import org.bouncycastle.tsp.TimeStampToken; import org.bouncycastle.tsp.TimeStampTokenInfo; import org.bouncycastle.util.Store; import org.demoiselle.signer.core.exception.CertificateCoreException; import org.demoiselle.signer.core.keystore.loader.configuration.Configuration; import org.demoiselle.signer.core.util.MessagesBundle; import org.demoiselle.signer.cryptography.Digest; import org.demoiselle.signer.cryptography.DigestAlgorithmEnum; import org.demoiselle.signer.cryptography.factory.DigestFactory; import org.demoiselle.signer.timestamp.Timestamp; import org.demoiselle.signer.timestamp.enumeration.ConnectionType; import org.demoiselle.signer.timestamp.signer.RequestSigner; import org.demoiselle.signer.timestamp.utils.TimeStampConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * Performs all time stamp operations: from the connection with the time stamp authority to the stamp validation. * * @author 07721825741 * */ // TODO verificar os valores de algoritmos que esto sendo setados manualmente, provavelmente deve busca do que foi setado ou no que estiver na poltica. public class TimeStampOperator { private static final Logger logger = LoggerFactory.getLogger(TimeStampOperator.class); private static MessagesBundle timeStampMessagesBundle = new MessagesBundle(); private InputStream inputStream = null; private Timestamp timestamp; private TimeStampRequest timeStampRequest; private TimeStampResponse timeStampResponse; /** * Creates a time stamp request, signed with the users's certificate. * * @param privateKey private key to sign with * @param certificates certificate chain * @param content set null if signing only hash * @param hash set null if signing content * @return A time stamp request * @throws CertificateCoreException exception */ public byte[] createRequest(PrivateKey privateKey, Certificate[] certificates, byte[] content, byte[] hash) throws CertificateCoreException { try { logger.info(timeStampMessagesBundle.getString("info.timestamp.digest")); Digest digest = DigestFactory.getInstance().factoryDefault(); String varAlgoOid = null; String varAlgo = null; if (Configuration.getInstance().getSO().toLowerCase().indexOf("indows") > 0) { logger.info(timeStampMessagesBundle.getString("info.timestamp.winhash")); varAlgoOid = TSPAlgorithms.SHA256.getId(); varAlgo = "SHA256withRSA"; digest.setAlgorithm(DigestAlgorithmEnum.SHA_256); } else { logger.info(timeStampMessagesBundle.getString("info.timestamp.linuxhash")); varAlgoOid = TSPAlgorithms.SHA512.getId(); varAlgo = "SHA512withRSA"; digest.setAlgorithm(DigestAlgorithmEnum.SHA_512); } byte[] hashedMessage = null; if (content != null) { hashedMessage = digest.digest(content); //logger.info(Base64.toBase64String(hashedMessage)); } else { hashedMessage = hash; } logger.info(timeStampMessagesBundle.getString("info.timestamp.prepare.request")); TimeStampRequestGenerator timeStampRequestGenerator = new TimeStampRequestGenerator(); timeStampRequestGenerator .setReqPolicy(new ASN1ObjectIdentifier(TimeStampConfig.getInstance().getTSPOid())); timeStampRequestGenerator.setCertReq(true); BigInteger nonce = BigInteger.valueOf(100); timeStampRequest = timeStampRequestGenerator.generate(new ASN1ObjectIdentifier(varAlgoOid), hashedMessage, nonce); byte request[] = timeStampRequest.getEncoded(); logger.info(timeStampMessagesBundle.getString("info.timestamp.sign.request")); RequestSigner requestSigner = new RequestSigner(); byte[] signedRequest = requestSigner.signRequest(privateKey, certificates, request, varAlgo); return signedRequest; } catch (IOException ex) { throw new CertificateCoreException(ex.getMessage()); } } /** * * Creates a time stamp request using a certificate of type PKCS12 * * @param keystoreLocation key store location * @param pin personal identification number * @param alias alias * @param content content of the request * @return request as a byte[] * @throws CertificateCoreException exception */ public byte[] createRequest(String keystoreLocation, String pin, String alias, byte[] content, byte[] hash) throws CertificateCoreException { try { KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(new FileInputStream(keystoreLocation), pin.toCharArray()); PrivateKey pk = (PrivateKey) ks.getKey(alias, pin.toCharArray()); Certificate[] certs = ks.getCertificateChain(alias); return this.createRequest(pk, certs, content, hash); } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | UnrecoverableKeyException | IOException ex) { throw new CertificateCoreException(ex.getMessage()); } } /** * Sends the time stamp request {@link createRequest} to a time stamp server * * @param request request to be sent * @return The time stamp returned by the server */ public byte[] invoke(byte[] request) throws CertificateCoreException { try { logger.info(timeStampMessagesBundle.getString("info.timestamp.init.request")); Connector connector = ConnectorFactory.buildConnector(ConnectionType.SOCKET); connector.setHostname(TimeStampConfig.getInstance().getTspHostname()); connector.setPort(TimeStampConfig.getInstance().getTSPPort()); logger.info(timeStampMessagesBundle.getString("info.timestamp.response")); inputStream = connector.connect(request); long tempo; // Valor do timeout da verificacao de dados disponiveis para leitura int timeOut = 3500; // Verificando se os 4 bytes iniciais estao disponiveis para leitura for (tempo = System.currentTimeMillis() + timeOut; inputStream.available() < 4 && System.currentTimeMillis() < tempo;) { try { Thread.sleep(1L); } catch (InterruptedException e) { e.printStackTrace(); } } // Lendo tamanho total byte[] tamanhoRetorno = new byte[4]; inputStream.read(tamanhoRetorno, 0, 4); int tamanho = new BigInteger(tamanhoRetorno).intValue(); // Verificando se os bytes na quantidade "tamanho" estao disponiveis if (System.currentTimeMillis() < tempo) { while (inputStream.available() < tamanho && System.currentTimeMillis() < tempo) { try { Thread.sleep(1L); } catch (InterruptedException e) { e.printStackTrace(); } } if (System.currentTimeMillis() >= tempo) { logger.error(timeStampMessagesBundle.getString("info.timestamp.timeout")); } } else { logger.error(timeStampMessagesBundle.getString("info.timestamp.timeout")); } // Lendo flag byte[] retornoFlag = new byte[1]; inputStream.read(retornoFlag, 0, 1); // tamanho total menos o tamanho da flag tamanho -= 1; // Lendo dados carimbo byte[] retornoCarimboDeTempo = new byte[tamanho]; inputStream.read(retornoCarimboDeTempo, 0, tamanho); timeStampResponse = new TimeStampResponse(retornoCarimboDeTempo); logger.info(timeStampMessagesBundle.getString("info.timestamp.status", timeStampResponse.getStatus())); switch (timeStampResponse.getStatus()) { case 0: { logger.info(timeStampMessagesBundle.getString("info.pkistatus.granted")); break; } case 1: { logger.info(timeStampMessagesBundle.getString("info.pkistatus.grantedWithMods")); break; } case 2: { logger.info(timeStampMessagesBundle.getString("error.pkistatus.rejection")); throw new CertificateCoreException(timeStampMessagesBundle.getString("error.pkistatus.rejection")); } case 3: { logger.info(timeStampMessagesBundle.getString("error.pkistatus.waiting")); throw new CertificateCoreException(timeStampMessagesBundle.getString("error.pkistatus.waiting")); } case 4: { logger.info(timeStampMessagesBundle.getString("error.pkistatus.revocation.warn")); throw new CertificateCoreException( timeStampMessagesBundle.getString("error.pkistatus.revocation.warn")); } case 5: { logger.info(timeStampMessagesBundle.getString("error.pkistatus.revocation.notification")); throw new CertificateCoreException( timeStampMessagesBundle.getString("error.pkistatus.revocation.notification")); } default: { logger.info(timeStampMessagesBundle.getString("error.pkistatus.unknown")); throw new CertificateCoreException(timeStampMessagesBundle.getString("error.pkistatus.unknown")); } } // ok int failInfo = -1; if (timeStampResponse.getFailInfo() != null) { failInfo = Integer.parseInt(new String(timeStampResponse.getFailInfo().getBytes())); } logger.info(timeStampMessagesBundle.getString("info.timestamp.failinfo", failInfo)); switch (failInfo) { case 0: logger.info(timeStampMessagesBundle.getString("error.pkifailureinfo.badAlg")); break; case 2: logger.info(timeStampMessagesBundle.getString("error.pkifailureinfo.badRequest")); break; case 5: logger.info(timeStampMessagesBundle.getString("error.pkifailureinfo.badDataFormat")); break; case 14: logger.info(timeStampMessagesBundle.getString("error.pkifailureinfo.timeNotAvailable")); break; case 15: logger.info(timeStampMessagesBundle.getString("error.pkifailureinfo.unacceptedPolicy")); break; case 16: logger.info(timeStampMessagesBundle.getString("error.pkifailureinfo.unacceptedExtension")); break; case 17: logger.info(timeStampMessagesBundle.getString("error.pkifailureinfo.addInfoNotAvailable")); break; case 25: logger.info(timeStampMessagesBundle.getString("error.pkifailureinfo.systemFailure")); break; } timeStampResponse.validate(timeStampRequest); TimeStampToken timeStampToken = timeStampResponse.getTimeStampToken(); this.setTimestamp(new Timestamp(timeStampToken)); if (timeStampToken == null) { throw new CertificateCoreException(timeStampMessagesBundle.getString("error.timestamp.token.null")); } connector.close(); //Imprime os dados do carimbo de tempo logger.info(timestamp.toString()); //Retorna o carimbo de tempo gerado return timestamp.getEncoded(); } catch (CertificateCoreException | TSPException | IOException e) { throw new CertificateCoreException(e.getMessage()); } } /** * Validate a time stamp * * @param content if it is assigned, the parameter hash must to be null * @param timeStamp timestamp to be validated * @param hash if it is assigned, the parameter content must to be null * @throws CertificateCoreException validate exception */ @SuppressWarnings("unchecked") public void validate(byte[] content, byte[] timeStamp, byte[] hash) throws CertificateCoreException { try { TimeStampToken timeStampToken = new TimeStampToken(new CMSSignedData(timeStamp)); CMSSignedData s = timeStampToken.toCMSSignedData(); int verified = 0; Store<?> certStore = s.getCertificates(); SignerInformationStore signers = s.getSignerInfos(); Collection<SignerInformation> c = signers.getSigners(); Iterator<SignerInformation> it = c.iterator(); while (it.hasNext()) { SignerInformation signer = it.next(); Collection<?> certCollection = certStore.getMatches(signer.getSID()); Iterator<?> certIt = certCollection.iterator(); X509CertificateHolder cert = (X509CertificateHolder) certIt.next(); SignerInformationVerifier siv = new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC") .build(cert); if (signer.verify(siv)) { verified++; } cert.getExtension(new ASN1ObjectIdentifier("2.5.29.31")).getExtnValue(); timeStampToken.validate(siv); } logger.info(timeStampMessagesBundle.getString("info.signature.verified", verified)); //Valida o hash incluso no carimbo de tempo com hash do arquivo carimbado byte[] calculatedHash = null; if (content != null) { Digest digest = DigestFactory.getInstance().factoryDefault(); TimeStampTokenInfo info = timeStampToken.getTimeStampInfo(); ASN1ObjectIdentifier algOID = info.getMessageImprintAlgOID(); digest.setAlgorithm(algOID.toString()); calculatedHash = digest.digest(content); } else { calculatedHash = hash; } if (Arrays.equals(calculatedHash, timeStampToken.getTimeStampInfo().getMessageImprintDigest())) { logger.info(timeStampMessagesBundle.getString("info.timestamp.hash.ok")); } else { throw new CertificateCoreException(timeStampMessagesBundle.getString("info.timestamp.hash.nok")); } } catch (TSPException | IOException | CMSException | OperatorCreationException | CertificateException ex) { throw new CertificateCoreException(ex.getMessage()); } } public void setTimestamp(Timestamp timestamp) { this.timestamp = timestamp; } public Timestamp getTimestamp() { return timestamp; } }