package com.fp.firma.common; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.List; import org.bouncycastle.jce.provider.BouncyCastleProvider; import com.fp.firma.dto.TfirmDatosFirmante; import com.fp.firma.keystore.KeyStoreProvider; import com.fp.firma.keystore.LinuxKeyStoreProvider; import com.fp.firma.keystore.WindowsJDK6KeyStoreProvider; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Font; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.AcroFields; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.PdfSignatureAppearance; import com.itextpdf.text.pdf.PdfStamper; import com.itextpdf.text.pdf.security.BouncyCastleDigest; import com.itextpdf.text.pdf.security.CertificateInfo; import com.itextpdf.text.pdf.security.CertificateInfo.X500Name; import com.itextpdf.text.pdf.security.DigestAlgorithms; import com.itextpdf.text.pdf.security.ExternalDigest; import com.itextpdf.text.pdf.security.ExternalSignature; import com.itextpdf.text.pdf.security.MakeSignature; import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard; import com.itextpdf.text.pdf.security.OcspClient; import com.itextpdf.text.pdf.security.OcspClientBouncyCastle; import com.itextpdf.text.pdf.security.PrivateKeySignature; import com.itextpdf.text.pdf.security.TSAClient; //import javax.persistence.NoResultException; //import javax.persistence.Query; /** * Clase que contiene métodos utilitarios para manejar certificados * * @author dcruz * */ public class CertificateUtils { /* * CONSTANTES DESIGNADAS SEGUN LA NUEVA ESTRUCTURA DEL BCE */ // OIDs de Campos del Certificado: public static final String OID_CEDULA_PASAPORTE = FirmMessages.getString("oid.cedula_pasaporte"); public static final String OID_NOMBRES = FirmMessages.getString("oid.nombres_persona"); public static final String OID_APELLIDO_1 = FirmMessages.getString("oid.apellido_persona1"); public static final String OID_APELLIDO_2 = FirmMessages.getString("oid.apellido_persona2"); public static final String OID_CARGO = FirmMessages.getString("oid.cargo"); public static final String OID_INSTITUCION = FirmMessages.getString("oid.institucion"); public static final String OID_DIRECCION = FirmMessages.getString("oid.direccion"); public static final String OID_TELEFONO = FirmMessages.getString("oid.telefono"); public static final String OID_CIUDAD = FirmMessages.getString("oid.ciudad"); public static final String OID_RAZON_SOCIAL = FirmMessages.getString("oid.razon_social"); public static final String OID_RUC = FirmMessages.getString("oid.ruc"); public static final String X509 = "X.509"; static { BouncyCastleProvider provider = new BouncyCastleProvider(); Security.addProvider(provider); } /** * Retorna el certificado del usuario contenido en la firma * * @param store el store en el que se va a buscar los certificados * @param aliases los alias que estan dentro del keystore * @return el {@link java.security.Certificate} * @throws KeyStoreException */ public synchronized static Certificate obtainCertificateInAlias(KeyStore store, Enumeration aliases) throws KeyStoreException { Certificate certs[] = null; synchronized (aliases) { int i = 0; while (aliases.hasMoreElements()) { String alias = aliases.nextElement(); System.out.println("Se imprime los alias: " + i + " " + alias); certs = store.getCertificateChain(alias); for (int j = 0; j < certs.length; j++) { X509Certificate certificate = (X509Certificate) certs[j]; boolean[] usages = certificate.getKeyUsage(); System.out.println("Certificado en primera posicion usado: " + j + " " + usages[0]); if (usages[0]) { return certificate; } } i++; } } return null; } /** * Se devuelve todos los datos de la firma del usuario en base al certificado enviado * * @param certificate el certificado enviado * @return Los datos del certificado de dicho usuario */ public static TfirmDatosFirmante obtainDataForCertificate(Certificate certificate) { X509Certificate signedCertificate = (X509Certificate) certificate; TfirmDatosFirmante datosFirmante = new TfirmDatosFirmante(); if (signedCertificate.getExtensionValue(OID_CEDULA_PASAPORTE) != null) { datosFirmante.setIdentificacion(new String(signedCertificate.getExtensionValue(OID_CEDULA_PASAPORTE)).trim()); } if (signedCertificate.getExtensionValue(OID_NOMBRES) != null) { datosFirmante.setNombre(new String(signedCertificate.getExtensionValue(OID_NOMBRES)).trim()); } if (signedCertificate.getExtensionValue(OID_APELLIDO_1) != null) { datosFirmante.setApellido1(new String(signedCertificate.getExtensionValue(OID_APELLIDO_1)).trim()); if (signedCertificate.getExtensionValue(OID_APELLIDO_2) != null) { datosFirmante.setApellido2(new String(signedCertificate.getExtensionValue(OID_APELLIDO_2)).trim()); } else { datosFirmante.setApellido2(""); } } if (signedCertificate.getExtensionValue(OID_TELEFONO) != null) { datosFirmante.setTelefono(new String(signedCertificate.getExtensionValue(OID_TELEFONO)).trim()); } if (signedCertificate.getExtensionValue(OID_CARGO) != null) { datosFirmante.setCargo(new String(signedCertificate.getExtensionValue(OID_CARGO)).trim()); } if (signedCertificate.getExtensionValue(OID_CIUDAD) != null) { datosFirmante.setCiudad(new String(signedCertificate.getExtensionValue(OID_CIUDAD)).trim()); } if (signedCertificate.getExtensionValue(OID_DIRECCION) != null) { datosFirmante.setDireccion(new String(signedCertificate.getExtensionValue(OID_DIRECCION)).trim()); } if (signedCertificate.getExtensionValue(OID_INSTITUCION) != null) { datosFirmante.setInstitucion(new String(signedCertificate.getExtensionValue(OID_INSTITUCION)).trim()); } if (signedCertificate.getExtensionValue(OID_RAZON_SOCIAL) != null) { datosFirmante.setInstitucion(new String(signedCertificate.getExtensionValue(OID_RAZON_SOCIAL)).trim()); } if (signedCertificate.getExtensionValue(OID_RUC) != null) { datosFirmante.setRuc(new String(signedCertificate.getExtensionValue(OID_RUC)).trim()); } datosFirmante.setFechaInicioVigencia(signedCertificate.getNotBefore()); datosFirmante.setFechaVigencia(signedCertificate.getNotAfter()); return datosFirmante; } /** * Valida que el certificado del usuario sea un certificado valido contra el servicio OCSP del proveedor * * @param certificateUser * @throws FileNotFoundException * @throws CertificateException */ public static void validateOcsCertificate(Certificate certificateUser) throws FileNotFoundException, CertificateException { System.out.println(((X509Certificate) certificateUser).getIssuerDN().getName()); CertificateFactory cf = CertificateFactory.getInstance(X509); InputStream subordStream = new FileInputStream(FirmMessages.getString("dir.ruta.base.repositorio") + "/" + FirmMessages.getString("nombre.certificado.subordinado")); X509Certificate subordCertificate = (X509Certificate) cf.generateCertificate(subordStream); check((X509Certificate) certificateUser, subordCertificate); } /** * Valida que los certificados enviados sean validos por la entidad certificadora * * @param issuerCert certificado del usuario * @param x509Cert certificado que firmo el certificado expedido */ public static void check(X509Certificate issuerCert, X509Certificate x509Cert) { // try { // // BigInteger serialNumber = issuerCert.getSerialNumber(); // X509CertificateHolder holder; // // try { // holder = new X509CertificateHolder(x509Cert.getEncoded()); // } catch (IOException e) { // throw new RuntimeException(e); // } // // CertificateID id = new CertificateID(new JcaDigestCalculatorProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build() // .get(CertificateID.HASH_SHA1), holder, serialNumber); // // OCSPReqBuilder ocspGen = new OCSPReqBuilder(); // ocspGen.addRequest(id); // OCSPReq ocspReq = ocspGen.build(); // // // Ir al OCSP // String ocspUrl = CertificateUtil.getOCSPURL(issuerCert); // // if (ocspUrl == null) { // System.out.println("URL de OCSP is null"); // return; // } // // URL url; // // try { // url = new URL(ocspUrl); // } catch (MalformedURLException e) { // throw new RuntimeException(e); // } // // HttpURLConnection con; // OCSPResp ocspResponse; // DataOutputStream dataOut = null; // try { // con = (HttpURLConnection) url.openConnection(); // // con.setRequestProperty("Content-Type", "application/ocsp-request"); // con.setRequestProperty("Accept", "application/ocsp-response"); // con.setDoOutput(true); // // OutputStream out = con.getOutputStream(); // dataOut = new DataOutputStream(new BufferedOutputStream(out)); // dataOut.write(ocspReq.getEncoded()); // // System.out.println("Estado de respuesta de la peticion=" + con.getResponseCode()); // // /* // * Se parsea la respuesta y se obtiene el estado del certificado retornado por el OCSP // */ // InputStream in = (InputStream) con.getContent(); // byte[] resp = read(in); // Read the reponse // ocspResponse = new OCSPResp(resp); // } catch (IOException e) { // throw new FirmasException("ERROR AL ESTABLECER CONEXI\u00d3N AL SERVICIO: "+url); // } finally{ // if(dataOut != null){ // try { // dataOut.flush(); // dataOut.close(); // } catch (IOException e) {} // } // } // // int status = ocspResponse.getStatus(); // System.out.println("status=" + status); // // BasicOCSPResp basicResponse = (BasicOCSPResp) ocspResponse.getResponseObject(); // // if (basicResponse != null) { // SingleResp[] responses = basicResponse.getResponses(); // SingleResp response = responses[0]; // CertificateStatus certStatus = (CertificateStatus) response.getCertStatus(); // // if (certStatus instanceof RevokedStatus) { // System.out.println("REVOKED"); // RevokedStatus revokedStatus = (RevokedStatus) certStatus; // System.out.println("Reason: " + revokedStatus.getRevocationReason()); // System.out.println("Date: " + revokedStatus.getRevocationTime()); // // throw new FirmasException("ERROR, CERTIFICADO REVOCADO POR "+revokedStatus.getRevocationReason()+" CON FECHA "+ revokedStatus.getRevocationTime()); // } // } // } catch (OCSPException e) { // throw new RuntimeException(e); // } catch (CertificateEncodingException e) { // throw new RuntimeException(e); // } } /** * Devuelve los nombres de firmas de un documento PDF * * @param docStream el documento a verificar * @return un lista de nombres de firmas */ public static List obtainNameSigns(InputStream docStream) { try { PdfReader docPdf = new PdfReader(docStream); AcroFields af = docPdf.getAcroFields(); return af.getSignatureNames(); } catch (IOException e) { System.err.println("Error al obtener los nombres del documentoss"); return null; } finally{ } } /** * Método para firmar documentos * @param fileSrc * @param usuario * @param password * @param compania * @param reason * @param location * @param rectangle * @param numPage * @param nameSign * @return * @throws Exception */ public static byte[] sign(InputStream fileSrc, String usuario, String password, Integer compania, String reason, String location, Rectangle rectangle, int numPage, String nameSign, String pathCertificate) throws Exception { String path = pathCertificate; FileInputStream ceritificateInputStream = new FileInputStream(path); return sign(fileSrc, ceritificateInputStream, password, reason, location, false, true, rectangle, numPage, nameSign, null); } /** * Metodo para firmar documentos * @param fileSrc * @param usuario * @param password * @param compania * @param reason * @param location * @param rectangle * @param numPage * @param nameSign * @return * @throws Exception */ public static byte[] sign(InputStream fileSrc, String usuario, String password, Integer compania, String reason, String location, Rectangle rectangle, int numPage, String nameSign) throws Exception { String path = ""; FileInputStream ceritificateInputStream = new FileInputStream(path); return sign(fileSrc, ceritificateInputStream, password, reason, location, false, true, rectangle, numPage, nameSign, null); } /** * Método que firma un documento en base los parámetros indicados * * @param fileSrc archivo origen * @param fileCert archivo firma en formato digital si no es null el firmado es con archivo caso contrario es token * @param password contrasena * @param reason razón o motivo de la firma. * @param location localización coemtario adicional de la firma. * @param withTS tiene TS estamapado de tiempo. * @param withOCSP tiene OCSP verificacion de la firma por el la entidad certificadora BCE. * @param rectangle lugar localizaci´n firma * @param numPage número pagina * @param nameSign nombre firma * @param tipoFirma es el tipo de token que se usará(Token que tipo?) * @return el documento firmado */ public static byte[] sign(InputStream fileSrc, InputStream fileCert, String password, String reason, String location, boolean withTS, boolean withOCSP, Rectangle rectangle, int numPage, String nameSign, String tipoFirma) { OutputStream outputStream = null; System.out.println("Ingreso a firmar"); try { PdfReader pdfReader = new PdfReader(fileSrc); outputStream = new ByteArrayOutputStream(512); PdfStamper stamper = PdfStamper.createSignature(pdfReader, outputStream, '\0'); PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); KeyStore store; String alias; // PrivateKey privateKey = null; ExternalSignature signature = null; if(fileCert != null){ store = KeyStore.getInstance("PKCS12"); store.load(fileCert, password.toCharArray()); alias = store.aliases().nextElement(); signature = new PrivateKeySignature((PrivateKey) store.getKey(alias, password.toCharArray()), DigestAlgorithms.SHA256, "SunRsaSign"); } else{//firma por token KeyStoreProvider provider = getKeyStoreProvider(tipoFirma); store = provider.getKeystore(password.toCharArray()); alias = store.aliases().nextElement(); // privateKey = (PrivateKey) store.getKey(alias, null); signature = new PrivateKeySignature((PrivateKey) store.getKey(alias, null), DigestAlgorithms.SHA1, null); } // appearance.setCrypto(privateKey, store.getCertificateChain(alias), null, null); appearance.setLayer2Text(generateSignText(store.getCertificateChain(alias), reason, location)); appearance.setLayer2Font(new Font(Font.FontFamily.UNDEFINED, 8F)); appearance.setVisibleSignature(rectangle, numPage, nameSign); TSAClient tsc = null; if (withTS) { } OcspClient ocsp = null; if (withOCSP) { ocsp = new OcspClientBouncyCastle(); } // stamper.close(); ExternalDigest externalDigest = new BouncyCastleDigest(); MakeSignature.signDetached(appearance, externalDigest, signature, store.getCertificateChain(alias), null, ocsp, tsc, 15000, CryptoStandard.CADES); return ((ByteArrayOutputStream) outputStream).toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } catch (DocumentException e) { throw new RuntimeException(e); } catch (KeyStoreException e) { throw new RuntimeException(e); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } catch (CertificateException e) { throw new RuntimeException(e); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } catch (FirmasException e) { throw new RuntimeException(FirmMessages.getString("FIR-0006")); } catch (Throwable e) { throw new RuntimeException(e); } finally{ try { if(fileCert != null){ fileCert.close(); } } catch (IOException e) {} try { fileSrc.close(); } catch (IOException e) {} try { if(outputStream != null){ outputStream.close(); } } catch (IOException e) {} } } /** * Arma documento el texto a mostrar de la plantilla * @param chains certificados * @param reason razón * @param location lugar * @return una layout de la firma */ private static String generateSignText(Certificate[] chains, String reason, String location){ StringBuffer textLayout = new StringBuffer("Firmado digitalmente por: "); String name = null; X500Name x500name = CertificateInfo.getSubjectFields((X509Certificate)chains[0]); if (x500name != null) { name = x500name.getField("CN"); if (name == null) name = x500name.getField("E"); } if(name == null){ name = ""; } textLayout.append(name).append("\n"); if(reason != null){ textLayout.append("Raz\u00f3n: ").append(reason).append("\n"); } if(location != null){ textLayout.append("Lugar: ").append(location).append("\n"); } SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); textLayout.append("Fecha: ").append(format.format(new Date(System.currentTimeMillis()))); return textLayout.toString(); } /** * Retorna el keystore del token especificado * @param tipoToken * @return * @throws KeyStoreException */ private static KeyStoreProvider getKeyStoreProvider(String tipoToken) { String osName = System.getProperty("os.name"); KeyStoreProvider provider = null; if(tipoToken == null){ return provider; } if (osName.toUpperCase().indexOf("WINDOWS") == 0) { provider = new WindowsJDK6KeyStoreProvider(); if (tipoToken.equals("2")) { provider = new WindowsJDK6KeyStoreProvider(); // trabaja con librerias sera para eToken }else if (tipoToken.equals("1")) { provider = new WindowsJDK6KeyStoreProvider(); // trabaja con librerias sera para iKey }else if (tipoToken.equals("3")) { provider = new WindowsJDK6KeyStoreProvider(); // trabaja con librerias sera para iKey } } else { provider = new LinuxKeyStoreProvider(); } return provider; } private static byte[] read(InputStream in) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); int next = in.read(); while (next > -1) { bos.write(next); next = in.read(); } bos.flush(); return bos.toByteArray(); } /** * Entrega el path en el que se encuentra el certificado digital. * * @param cusuario Codigo de usuario. * @param ccompania Codigo de compania. * @return String * @throws Exception */ // private static String getPath(String cusuario, Integer ccompania) throws Exception { // TfirmCertificado tfirmcertificado = getTfirmCertificado(cusuario); // TgeneParametersKey tgeneParametersKey = new TgeneParametersKey("PATH_CERTIFICADO_BCE", ccompania); // TgeneParameters tgeneParameters = TgeneParameters.find(PersistenceHelper.getEntityManager(), tgeneParametersKey); // String path = tgeneParameters.getTextvalue() + "/" + tfirmcertificado.getPk().getIdcertificado(); // return path; // } /** * Sentencia que entrega la defincion vigente de un certificado digital para un usuario. */ // private static final String JPQL = "from TfirmCertificado where t.codigousuario = :cusuario"; /** * Entrega defincion de certificados digitales vigente. * * @param cusuario Codigo de usuario. * @return TfirmCertificado. * @throws Exception */ // private static TfirmCertificado getTfirmCertificado(String cusuario) throws Exception { // TfirmCertificado tfirmCertificado = null; // Query qry = PersistenceHelper.getEntityManager().createQuery(JPQL); // qry.setParameter("cusuario", cusuario); // try { // tfirmCertificado = (TfirmCertificado) qry.getSingleResult(); // } catch (NoResultException e) { // throw new FirmasException("FIR-0001", "PARAMETROS DEL CERTIFICADO DIGITAL NO DEFINIDO PARA EL USUARIO: {0}", cusuario); // } // return tfirmCertificado; // } /* public static boolean validatePassword(InputStream fileSrc, String usuario, String password, Integer compania, String reason, String location, Rectangle rectangle, int numPage, String nameSign) throws Exception { String path = getPath(usuario, compania); FileInputStream ceritificateInputStream = new FileInputStream(path); return pass(fileSrc, ceritificateInputStream, password, reason, location, false, true, rectangle, numPage, nameSign); } private static boolean pass(InputStream fileSrc, InputStream fileCert, String password, String reason, String location, boolean withTS, boolean withOCSP, Rectangle rectangle, int numPage, String nameSign) { try { PdfReader pdfReader = new PdfReader(fileSrc); OutputStream outputStream = new ByteArrayOutputStream(512); PdfStamper stamper = PdfStamper.createSignature(pdfReader, outputStream, '\0'); PdfSignatureAppearance appearance = stamper.getSignatureAppearance(); KeyStore store = KeyStore.getInstance("PKCS12"); store.load(fileCert, password.toCharArray()); String alias = store.aliases().nextElement(); ExternalSignature signature = new PrivateKeySignature((PrivateKey) store.getKey(alias, password.toCharArray()), DigestAlgorithms.SHA256, "SunRsaSign"); return true; } catch (Exception e) { return false; } } */ }