Example usage for com.lowagie.text.pdf PdfSignatureAppearance SignatureRenderDescription

List of usage examples for com.lowagie.text.pdf PdfSignatureAppearance SignatureRenderDescription

Introduction

In this page you can find the example usage for com.lowagie.text.pdf PdfSignatureAppearance SignatureRenderDescription.

Prototype

int SignatureRenderDescription

To view the source code for com.lowagie.text.pdf PdfSignatureAppearance SignatureRenderDescription.

Click Source Link

Document

The rendering mode is just the description

Usage

From source file:es.gob.afirma.signers.pades.PAdESSigner.java

License:Open Source License

@SuppressWarnings("boxing")
static byte[] signPDF(final PrivateKey key, final java.security.cert.Certificate[] certChain,
        final byte[] inPDF, final Properties extraParams, final String algorithm)
        throws IOException, AOException, DocumentException, NoSuchAlgorithmException, CertificateException {

    // *********************************************************************************************************************
    // **************** LECTURA PARAMETROS ADICIONALES *********************************************************************
    // *********************************************************************************************************************

    // Imagen de la rubrica
    final Image rubric = getRubricImage(extraParams.getProperty("signatureRubricImage")); //$NON-NLS-1$

    // Usar hora y fecha del sistema
    final boolean useSystemDateTime = Boolean
            .parseBoolean(extraParams.getProperty("applySystemDate", Boolean.TRUE.toString())); //$NON-NLS-1$

    // Motivo de la firma
    final String reason = extraParams.getProperty("signReason"); //$NON-NLS-1$

    // Nombre del campo de firma preexistente en el PDF a usar
    final String signatureField = extraParams.getProperty("signatureField"); //$NON-NLS-1$

    // Lugar de realizacion de la firma
    final String signatureProductionCity = extraParams.getProperty("signatureProductionCity"); //$NON-NLS-1$

    // Datos de contacto (correo electronico) del firmante
    final String signerContact = extraParams.getProperty("signerContact"); //$NON-NLS-1$

    // Pagina donde situar la firma visible
    int page = LAST_PAGE;
    try {//from  w w w.j ava 2s .c o m
        page = Integer.parseInt(extraParams.getProperty("signaturePage")); //$NON-NLS-1$
    } catch (final Exception e) {
        /* Se deja la pagina tal y como esta */
    }

    // Nombre del subfiltro de firma en el diccionario PDF
    final String signatureSubFilter = extraParams.getProperty("signatureSubFilter"); //$NON-NLS-1$

    // ******************
    // ** Adjuntos ******

    // Contenido a adjuntar (en Base64)
    final String b64Attachment = extraParams.getProperty("attach"); //$NON-NLS-1$

    // Nombre que se pondra al fichero adjunto en el PDF
    final String attachmentFileName = extraParams.getProperty("attachFileName"); //$NON-NLS-1$

    // Descripcion del adjunto
    final String attachmentDescription = extraParams.getProperty("attachDescription"); //$NON-NLS-1$

    // ** Fin Adjuntos **
    // ******************

    // Nivel de certificacion del PDF
    int certificationLevel;
    try {
        certificationLevel = extraParams.getProperty("certificationLevel") != null ? //$NON-NLS-1$
                Integer.parseInt(extraParams.getProperty("certificationLevel")) : //$NON-NLS-1$
                -1;
    } catch (final Exception e) {
        certificationLevel = UNDEFINED;
    }

    // *****************************
    // **** Texto firma visible ****

    // Texto en capa 4
    final String layer4Text = extraParams.getProperty("layer4Text"); //$NON-NLS-1$

    // Texto en capa 2
    final String layer2Text = extraParams.getProperty("layer2Text"); //$NON-NLS-1$

    // Tipo de letra en capa 2
    int layer2FontFamily;
    try {
        layer2FontFamily = extraParams.getProperty("layer2FontFamily") != null ? //$NON-NLS-1$
                Integer.parseInt(extraParams.getProperty("layer2FontFamily")) : //$NON-NLS-1$
                -1;
    } catch (final Exception e) {
        layer2FontFamily = UNDEFINED;
    }

    // Tamano del tipo de letra en capa 2
    int layer2FontSize;
    try {
        layer2FontSize = extraParams.getProperty("layer2FontSize") != null ? //$NON-NLS-1$
                Integer.parseInt(extraParams.getProperty("layer2FontSize")) : //$NON-NLS-1$
                -1;
    } catch (final Exception e) {
        layer2FontSize = UNDEFINED;
    }

    // Estilo del tipo de letra en capa 2
    int layer2FontStyle;
    try {
        layer2FontStyle = extraParams.getProperty("layer2FontStyle") != null ? //$NON-NLS-1$
                Integer.parseInt(extraParams.getProperty("layer2FontStyle")) : //$NON-NLS-1$
                -1;
    } catch (final Exception e) {
        layer2FontStyle = UNDEFINED;
    }

    // Color del tipo de letra en capa 2
    final String layer2FontColor = extraParams.getProperty("layer2FontColor"); //$NON-NLS-1$

    // ** Fin texto firma visible **
    // *****************************

    // Contrasena del propietario del PDF
    String ownerPassword = extraParams.getProperty("ownerPassword"); //$NON-NLS-1$

    // Contrasena del usuario del PDF
    final String userPassword = extraParams.getProperty("userPassword"); //$NON-NLS-1$

    // *********************************************************************************************************************
    // **************** FIN LECTURA PARAMETROS ADICIONALES *****************************************************************
    // *********************************************************************************************************************

    PdfReader pdfReader;
    try {
        if (ownerPassword != null) {
            pdfReader = new PdfReader(inPDF, ownerPassword.getBytes());
        } else if (userPassword != null) {
            pdfReader = new PdfReader(inPDF, userPassword.getBytes());
        } else {
            pdfReader = new PdfReader(inPDF);
        }
    } catch (final BadPasswordException e) {
        // Comprobamos que el signer esta en modo interactivo, y si no lo
        // esta no pedimos contrasena por dialogo, principalmente para no interrumpir un firmado por lotes
        // desatendido
        if (Boolean.TRUE.toString().equalsIgnoreCase(extraParams.getProperty("headLess"))) { //$NON-NLS-1$
            throw new BadPdfPasswordException(e);
        }
        // La contrasena que nos han proporcionada no es buena o no nos
        // proporcionaron ninguna
        ownerPassword = new String(AOUIFactory.getPassword(
                ownerPassword == null ? PDFMessages.getString("AOPDFSigner.0") //$NON-NLS-1$
                        : PDFMessages.getString("AOPDFSigner.1"), //$NON-NLS-1$
                null));
        try {
            pdfReader = new PdfReader(inPDF, ownerPassword.getBytes());
        } catch (final BadPasswordException e2) {
            throw new BadPdfPasswordException(e2);
        }
    } catch (final IOException e) {
        throw new InvalidPdfException(e);
    }

    if (pdfReader.getCertificationLevel() == PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED
            && !Boolean.parseBoolean(extraParams.getProperty("allowSigningCertifiedPdfs"))) { //$NON-NLS-1$
        // Si no permitimos dialogos graficos o directamente hemos indicado que no permitimos firmar PDF certificados lanzamos
        // una excepcion
        if (Boolean.parseBoolean(extraParams.getProperty("headLess")) //$NON-NLS-1$
                || "false".equalsIgnoreCase(extraParams.getProperty("allowSigningCertifiedPdfs"))) { //$NON-NLS-1$ //$NON-NLS-2$
            throw new PdfIsCertifiedException();
        }
        // En otro caso, perguntamos al usuario
        if (AOUIFactory.NO_OPTION == AOUIFactory.showConfirmDialog(null, PDFMessages.getString("AOPDFSigner.8"), //$NON-NLS-1$
                PDFMessages.getString("AOPDFSigner.9"), //$NON-NLS-1$
                AOUIFactory.YES_NO_OPTION, AOUIFactory.WARNING_MESSAGE)) {
            throw new AOCancelledOperationException(
                    "El usuario no ha permitido la firma de un PDF certificado"); //$NON-NLS-1$
        }
    }

    // Los derechos van firmados por Adobe, y como desde iText se invalidan
    // es mejor quitarlos
    pdfReader.removeUsageRights();

    final ByteArrayOutputStream baos = new ByteArrayOutputStream();

    // Activar el atributo de "agregar firma" (cuarto parametro del metodo
    // "PdfStamper.createSignature") hace que se cree una nueva revision del
    // documento y evita que las firmas previas queden invalidadas. Sin embargo, este
    // exige que el PDF no incorpore ningun error, asi que lo mantendremos desactivado
    // para la primera firma y activado para las subsiguientes. Un error incorporado
    // en un PDF erroneo puede quedar subsanado en su version firmada, haciendo
    // posible incorporar nuevas firmas agregando revisiones del documento.
    final PdfStamper stp;
    try {
        stp = PdfStamper.createSignature(pdfReader, // PDF de entrada
                baos, // Salida
                '\0', // Mantener version
                null, // No crear temporal
                pdfReader.getAcroFields().getSignatureNames().size() > 0 // Si hay mas firmas, creo una revision
        );
    } catch (final BadPasswordException e) {
        throw new PdfIsPasswordProtectedException(e);
    }

    // Aplicamos todos los atributos de firma
    final PdfSignatureAppearance sap = stp.getSignatureAppearance();
    stp.setFullCompression();
    sap.setAcro6Layers(true);

    // PAdES parte 3 seccion 4.7 - Habilitacion para LTV
    stp.getWriter().addDeveloperExtension(new PdfDeveloperExtension(new PdfName("ESIC"), //$NON-NLS-1$
            PdfWriter.PDF_VERSION_1_7, 1));

    // Adjuntos
    if (b64Attachment != null && attachmentFileName != null) {
        byte[] attachment = null;
        try {
            attachment = Base64.decode(b64Attachment);
        } catch (final IOException e) {
            LOGGER.warning("Se ha indicado un adjunto, pero no estaba en formato Base64, se ignorara : " + e); //$NON-NLS-1$
        }
        if (attachment != null) {
            stp.getWriter().addFileAttachment(attachmentDescription, attachment, null, attachmentFileName);
        }
    }

    // iText antiguo
    sap.setRender(PdfSignatureAppearance.SignatureRenderDescription);
    // En iText nuevo seria "sap.setRenderingMode(PdfSignatureAppearance.RenderingMode.NAME_AND_DESCRIPTION);"

    // Razon de firma
    if (reason != null) {
        sap.setReason(reason);
    }

    // Establecer fecha local del equipo
    if (useSystemDateTime) {
        sap.setSignDate(new GregorianCalendar());
    }

    // Gestion de los cifrados
    if (pdfReader.isEncrypted() && (ownerPassword != null || userPassword != null)) {
        if (Boolean.TRUE.toString().equalsIgnoreCase(extraParams.getProperty("avoidEncryptingSignedPdfs"))) { //$NON-NLS-1$
            LOGGER.info(
                    "Aunque el PDF original estaba encriptado no se encriptara el PDF firmado (se establecio el indicativo 'avoidEncryptingSignedPdfs')" //$NON-NLS-1$
            );
        } else {
            LOGGER.info("El PDF original estaba encriptado, se intentara encriptar tambien el PDF firmado" //$NON-NLS-1$
            );
            try {
                stp.setEncryption(ownerPassword != null ? ownerPassword.getBytes() : null,
                        userPassword != null ? userPassword.getBytes() : null, pdfReader.getPermissions(),
                        pdfReader.getCryptoMode());
            } catch (final DocumentException de) {
                LOGGER.warning("No se ha podido cifrar el PDF destino, se escribira sin contrasena: " + de //$NON-NLS-1$
                );
            }
        }
    }

    // Pagina en donde se imprime la firma
    if (page == LAST_PAGE) {
        page = pdfReader.getNumberOfPages();
    }

    // Posicion de la firma
    final Rectangle signaturePositionOnPage = getSignaturePositionOnPage(extraParams);
    if (signaturePositionOnPage != null && signatureField == null) {
        sap.setVisibleSignature(signaturePositionOnPage, page, null);
    } else if (signatureField != null) {
        sap.setVisibleSignature(signatureField);
    }

    // Localizacion en donde se produce la firma
    if (signatureProductionCity != null) {
        sap.setLocation(signatureProductionCity);
    }

    // Contacto del firmante
    if (signerContact != null) {
        sap.setContact(signerContact);
    }

    // Rubrica de la firma
    if (rubric != null) {
        sap.setImage(rubric);
        sap.setLayer2Text(""); //$NON-NLS-1$
        sap.setLayer4Text(""); //$NON-NLS-1$
    }

    // **************************
    // ** Texto en las capas ****
    // **************************

    // Capa 2
    if (layer2Text != null) {

        sap.setLayer2Text(layer2Text);

        final int layer2FontColorR;
        final int layer2FontColorG;
        final int layer2FontColorB;
        if ("black".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 0;
            layer2FontColorG = 0;
            layer2FontColorB = 0;
        } else if ("white".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 255;
            layer2FontColorG = 255;
            layer2FontColorB = 255;
        } else if ("lightGray".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 192;
            layer2FontColorG = 192;
            layer2FontColorB = 192;
        } else if ("gray".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 128;
            layer2FontColorG = 128;
            layer2FontColorB = 128;
        } else if ("darkGray".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 64;
            layer2FontColorG = 64;
            layer2FontColorB = 64;
        } else if ("red".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 255;
            layer2FontColorG = 0;
            layer2FontColorB = 0;
        } else if ("pink".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 255;
            layer2FontColorG = 175;
            layer2FontColorB = 175;
        } else if (layer2FontColor == null) {
            layer2FontColorR = 0;
            layer2FontColorG = 0;
            layer2FontColorB = 0;
        } else {
            LOGGER.warning("No se soporta el color '" + layer2FontColor //$NON-NLS-1$
                    + "' para el texto de la capa 4, se usara negro"); //$NON-NLS-1$
            layer2FontColorR = 0;
            layer2FontColorG = 0;
            layer2FontColorB = 0;
        }

        com.lowagie.text.Font font;
        try {
            Class<?> colorClass;
            if (Platform.getOS() == OS.ANDROID) {
                colorClass = Class.forName("harmony.java.awt.Color"); //$NON-NLS-1$
            } else {
                colorClass = Class.forName("java.awt.Color"); //$NON-NLS-1$
            }
            final Object color = colorClass.getConstructor(Integer.TYPE, Integer.TYPE, Integer.TYPE)
                    .newInstance(layer2FontColorR, layer2FontColorG, layer2FontColorB);

            font = com.lowagie.text.Font.class
                    .getConstructor(Integer.TYPE, Integer.TYPE, Integer.TYPE, colorClass).newInstance(
                            // Family (COURIER = 0, HELVETICA = 1, TIMES_ROMAN = 2, SYMBOL = 3, ZAPFDINGBATS = 4)
                            layer2FontFamily == UNDEFINED ? COURIER : layer2FontFamily,
                            // Size (DEFAULTSIZE = 12)
                            layer2FontSize == UNDEFINED ? DEFAULT_LAYER_2_FONT_SIZE : layer2FontSize,
                            // Style (NORMAL = 0, BOLD = 1, ITALIC = 2, BOLDITALIC = 3, UNDERLINE = 4, STRIKETHRU = 8)
                            layer2FontStyle == UNDEFINED ? com.lowagie.text.Font.NORMAL : layer2FontStyle,
                            // Color
                            color);

        } catch (Exception e) {
            font = new com.lowagie.text.Font(
                    // Family (COURIER = 0, HELVETICA = 1, TIMES_ROMAN = 2, SYMBOL = 3, ZAPFDINGBATS = 4)
                    layer2FontFamily == UNDEFINED ? COURIER : layer2FontFamily,
                    // Size (DEFAULTSIZE = 12)
                    layer2FontSize == UNDEFINED ? DEFAULT_LAYER_2_FONT_SIZE : layer2FontSize,
                    // Style (NORMAL = 0, BOLD = 1, ITALIC = 2, BOLDITALIC = 3, UNDERLINE = 4, STRIKETHRU = 8)
                    layer2FontStyle == UNDEFINED ? com.lowagie.text.Font.NORMAL : layer2FontStyle,
                    // Color
                    null);
        }
        sap.setLayer2Font(font);
    }

    // Capa 4
    if (layer4Text != null) {
        sap.setLayer4Text(layer4Text);
    }

    // ***************************
    // ** Fin texto en las capas *
    // ***************************

    sap.setCrypto(null, certChain, null, null);

    final PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE,
            signatureSubFilter != null && !"".equals(signatureSubFilter) ? new PdfName(signatureSubFilter) //$NON-NLS-1$
                    : PdfName.ADBE_PKCS7_DETACHED);

    // Fecha de firma
    if (sap.getSignDate() != null) {
        dic.setDate(new PdfDate(sap.getSignDate()));
    }

    dic.setName(PdfPKCS7.getSubjectFields((X509Certificate) certChain[0]).getField("CN")); //$NON-NLS-1$
    if (sap.getReason() != null) {
        dic.setReason(sap.getReason());
    }

    // Lugar de la firma
    if (sap.getLocation() != null) {
        dic.setLocation(sap.getLocation());
    }

    // Contacto del firmante
    if (sap.getContact() != null) {
        dic.setContact(sap.getContact());
    }

    sap.setCryptoDictionary(dic);

    // Certificacion del PDF (NOT_CERTIFIED = 0, CERTIFIED_NO_CHANGES_ALLOWED = 1,
    // CERTIFIED_FORM_FILLING = 2, CERTIFIED_FORM_FILLING_AND_ANNOTATIONS = 3)
    if (certificationLevel != -1) {
        sap.setCertificationLevel(certificationLevel);
    }

    // Reservamos el espacio necesario en el PDF para insertar la firma
    final HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
    exc.put(PdfName.CONTENTS, Integer.valueOf(CSIZE * 2 + 2));

    sap.preClose(exc);

    // ********************************************************************************
    // **************** CALCULO DEL SIGNED DATA ***************************************
    // ********************************************************************************

    // La norma PAdES establece que si el algoritmo de huella digital es SHA1 debe usarse SigningCertificateV2, y en cualquier
    // otro caso deberia usarse SigningCertificateV2
    boolean signingCertificateV2;
    if (extraParams.containsKey("signingCertificateV2")) { //$NON-NLS-1$
        signingCertificateV2 = Boolean.parseBoolean(extraParams.getProperty("signingCertificateV2")); //$NON-NLS-1$
    } else {
        signingCertificateV2 = !"SHA1".equals(AOSignConstants.getDigestAlgorithmName(algorithm)); //$NON-NLS-1$
    }

    byte[] completeCAdESSignature = GenCAdESEPESSignedData.generateSignedData(
            new P7ContentSignerParameters(inPDF, algorithm), true, // omitContent
            new AdESPolicy(extraParams), signingCertificateV2, key, certChain,
            MessageDigest.getInstance(AOSignConstants.getDigestAlgorithmName(algorithm))
                    .digest(AOUtil.getDataFromInputStream(sap.getRangeStream())),
            AOSignConstants.getDigestAlgorithmName(algorithm), true, // Modo PAdES
            PDF_OID,
            extraParams.getProperty("contentDescription") != null //$NON-NLS-1$
                    ? extraParams.getProperty("contentDescription") //$NON-NLS-1$
                    : PDF_DESC);

    //***************** SELLO DE TIEMPO ****************
    final String tsa = extraParams.getProperty("tsaURL"); //$NON-NLS-1$
    URI tsaURL;
    if (tsa != null) {
        try {
            tsaURL = new URI(tsa);
        } catch (final Exception e) {
            LOGGER.warning("Se ha indicado una URL de TSA invalida (" + tsa //$NON-NLS-1$
                    + "), no se anadira sello de tiempo: " + e); //$NON-NLS-1$
            tsaURL = null;
        }
        if (tsaURL != null) {
            final String tsaPolicy = extraParams.getProperty("tsaPolicy"); //$NON-NLS-1$
            if (tsaPolicy == null) {
                LOGGER.warning(
                        "Se ha indicado una URL de TSA pero no una politica, no se anadira sello de tiempo"); //$NON-NLS-1$
            } else {
                final String tsaHashAlgorithm = extraParams.getProperty("tsaHashAlgorithm"); //$NON-NLS-1$
                completeCAdESSignature = new CMSTimestamper(
                        !Boolean.FALSE.toString().equalsIgnoreCase(extraParams.getProperty("tsaRequireCert")), //$NON-NLS-1$
                        tsaPolicy, tsaURL, extraParams.getProperty("tsaUsr"), //$NON-NLS-1$
                        extraParams.getProperty("tsaPwd"), //$NON-NLS-1$
                        extraParams.getProperty("tsaExtensionOid") != null //$NON-NLS-1$
                                && extraParams.getProperty("tsaExtensionValueBase64") != null ? //$NON-NLS-1$
                                        new TsaRequestExtension[] { new TsaRequestExtension(
                                                extraParams.getProperty("tsaExtensionOid"), //$NON-NLS-1$
                                                Boolean.getBoolean(extraParams
                                                        .getProperty("tsaExtensionCritical", "false")), //$NON-NLS-1$ //$NON-NLS-2$
                                                Base64.decode(
                                                        extraParams.getProperty("tsaExtensionValueBase64")) //$NON-NLS-1$
                                        ) } : null).addTimestamp(completeCAdESSignature,
                                                AOAlgorithmID.getOID(AOSignConstants.getDigestAlgorithmName(
                                                        tsaHashAlgorithm != null ? tsaHashAlgorithm : "SHA1"))); //$NON-NLS-1$
            }
        }

    }
    //************** FIN SELLO DE TIEMPO ****************

    // ********************************************************************************
    // *************** FIN CALCULO DEL SIGNED DATA ************************************
    // ********************************************************************************

    final byte[] outc = new byte[CSIZE];
    if (outc.length < completeCAdESSignature.length) {
        throw new AOException("La firma generada tiene un tamano (" + completeCAdESSignature.length //$NON-NLS-1$
                + ") mayor que el permitido (" + outc.length + ")" //$NON-NLS-1$ //$NON-NLS-2$
        );
    }
    final PdfDictionary dic2 = new PdfDictionary();
    System.arraycopy(completeCAdESSignature, 0, outc, 0, completeCAdESSignature.length);
    dic2.put(PdfName.CONTENTS, new PdfString(outc).setHexWriting(true));

    sap.close(dic2);

    return baos.toByteArray();
}

From source file:es.gob.afirma.signers.pades.PdfSessionManager.java

License:Open Source License

/** Obtiene los datos PDF relevantes en cuanto a las firmas electr&oacute;nicas, consistentes en los datos
 * a ser firmados con CAdES o PKCS#7 y los metadatos necesarios para su correcta inserci&oacute;n en el PDF.
 * @param inPDF Documento PDF que se desea firmar
 * @param certChain Cadena de certificados del firmante
 * @param signTime Hora de la firma//from  w  w  w.  ja v  a  2 s .  com
 * @param extraParams Par&aacute;metros adicionales de la firma
 * @return Datos PDF relevantes en cuanto a las firmas electr&oacute;nicas
 * @throws AOException En caso de que ocurra cualquier otro tipo de error
 * @throws IOException En caso de errores de entrada / salida
 * @throws DocumentException Si ocurren errores en la estampaci&iacute;n de la firma PDF */
public static PdfTriPhaseSession getSessionData(final byte[] inPDF, final Certificate[] certChain,
        final Calendar signTime, final Properties extraParams)
        throws AOException, IOException, DocumentException {

    // *********************************************************************************************************************
    // **************** LECTURA PARAMETROS ADICIONALES *********************************************************************
    // *********************************************************************************************************************

    // Forzar la creacion de revisiones incluso en PDF no firmados
    final boolean alwaysCreateRevision = Boolean
            .parseBoolean(extraParams.getProperty("alwaysCreateRevision", "true")); //$NON-NLS-1$ //$NON-NLS-2$

    // Imagen de la rubrica
    final Image rubric = PdfPreProcessor.getImage(extraParams.getProperty("signatureRubricImage")); //$NON-NLS-1$

    // Motivo de la firma
    final String reason = extraParams.getProperty("signReason"); //$NON-NLS-1$

    // Nombre del campo de firma preexistente en el PDF a usar
    final String signatureField = extraParams.getProperty("signatureField"); //$NON-NLS-1$

    // Lugar de realizacion de la firma
    final String signatureProductionCity = extraParams.getProperty("signatureProductionCity"); //$NON-NLS-1$

    // Datos de contacto (correo electronico) del firmante
    final String signerContact = extraParams.getProperty("signerContact"); //$NON-NLS-1$

    // Pagina donde situar la firma visible
    int page = LAST_PAGE;
    try {
        page = Integer.parseInt(extraParams.getProperty("signaturePage")); //$NON-NLS-1$
    } catch (final Exception e) {
        /* Se deja la pagina tal y como esta */
    }

    // Nombre del subfiltro de firma en el diccionario PDF
    final String signatureSubFilter = extraParams.getProperty("signatureSubFilter"); //$NON-NLS-1$

    // Nivel de certificacion del PDF
    int certificationLevel;
    try {
        certificationLevel = extraParams.getProperty("certificationLevel") != null ? //$NON-NLS-1$
                Integer.parseInt(extraParams.getProperty("certificationLevel")) : //$NON-NLS-1$
                -1;
    } catch (final Exception e) {
        certificationLevel = UNDEFINED;
    }

    // *****************************
    // **** Texto firma visible ****

    // Texto en capa 4
    final String layer4Text = extraParams.getProperty("layer4Text"); //$NON-NLS-1$

    // Texto en capa 2
    final String layer2Text = extraParams.getProperty("layer2Text"); //$NON-NLS-1$

    // Tipo de letra en capa 2
    int layer2FontFamily;
    try {
        layer2FontFamily = extraParams.getProperty("layer2FontFamily") != null ? //$NON-NLS-1$
                Integer.parseInt(extraParams.getProperty("layer2FontFamily")) : //$NON-NLS-1$
                -1;
    } catch (final Exception e) {
        layer2FontFamily = UNDEFINED;
    }

    // Tamano del tipo de letra en capa 2
    int layer2FontSize;
    try {
        layer2FontSize = extraParams.getProperty("layer2FontSize") != null ? //$NON-NLS-1$
                Integer.parseInt(extraParams.getProperty("layer2FontSize")) : //$NON-NLS-1$
                -1;
    } catch (final Exception e) {
        layer2FontSize = UNDEFINED;
    }

    // Estilo del tipo de letra en capa 2
    int layer2FontStyle;
    try {
        layer2FontStyle = extraParams.getProperty("layer2FontStyle") != null ? //$NON-NLS-1$
                Integer.parseInt(extraParams.getProperty("layer2FontStyle")) : //$NON-NLS-1$
                -1;
    } catch (final Exception e) {
        layer2FontStyle = UNDEFINED;
    }

    // Color del tipo de letra en capa 2
    final String layer2FontColor = extraParams.getProperty("layer2FontColor"); //$NON-NLS-1$

    // ** Fin texto firma visible **
    // *****************************

    // *********************************************************************************************************************
    // **************** FIN LECTURA PARAMETROS ADICIONALES *****************************************************************
    // *********************************************************************************************************************

    final PdfReader pdfReader = PdfUtil.getPdfReader(inPDF, extraParams,
            Boolean.getBoolean(extraParams.getProperty("headLess")) //$NON-NLS-1$
    );

    //PdfUtil.checkPdfCertification(pdfReader.getCertificationLevel(), extraParams);
    //PdfUtil.checkUnregisteredSignatures(pdfReader, extraParams);

    // Los derechos van firmados por Adobe, y como desde iText se invalidan
    // es mejor quitarlos
    pdfReader.removeUsageRights();

    final ByteArrayOutputStream baos = new ByteArrayOutputStream();

    // Activar el atributo de "agregar firma" (quinto parametro del metodo
    // "PdfStamper.createSignature") hace que se cree una nueva revision del
    // documento y evita que las firmas previas queden invalidadas.
    // Sin embargo, este procedimiento no funciona cuando los PDF contienen informacion
    // despues de la ultima marca %%EOF, aspecto no permitido en PDF 1.7 (ISO 32000-1:2008)
    // pero si en PDF 1.3 (Adobe) y que se da con frecuencia en PDF generados con bibliotetcas
    // de software libre como QPDF.
    //
    //  Especificacion PDF 1.3
    //   3.4.4, "File Trailer"
    //     Acrobat viewers require only that the %%EOF marker appear somewhere within
    //     the last 1024 bytes of the file.
    //
    //  Especificacion PDF 1.7
    //   7.5.5. File Trailer
    //     The trailer of a PDF file enables a conforming reader to quickly find the
    //     cross-reference table and certain special objects. Conforming readers should read a
    //     PDF file from its end. The last line of the file shall contain only the end-of-file
    //     marker, %%EOF.
    //
    // Para aceptar al menos en algunos casos PDF 1.3 (son aun muy frecuentes, especialmente
    // en archivos, lo mantendremos desactivado para la primera firma y activado para las
    // subsiguientes.
    //
    // No obstante, el integrador puede siempre forzar la creacion de revisiones mediante
    // el parametro "alwaysCreateRevision".
    final PdfStamper stp;
    try {
        stp = PdfStamper.createSignature(
                // PDF de entrada
                pdfReader,
                // Salida
                baos,
                // Mantener version
                '\0',
                // No crear temporal
                null,
                // Si hay mas firmas o asi me lo indican, creo una revision
                alwaysCreateRevision || pdfReader.getAcroFields().getSignatureNames().size() > 0,
                // Momento de la firma
                signTime);
    } catch (final BadPasswordException e) {
        // Comprobamos que el signer esta en modo interactivo, y si no lo
        // esta no pedimos contrasena por dialogo, principalmente para no interrumpir un firmado por lotes
        // desatendido
        if (Boolean.getBoolean(extraParams.getProperty("headLess"))) { //$NON-NLS-1$
            throw new BadPdfPasswordException(e);
        }
        // La contrasena que nos han proporcionada no es buena o no nos
        // proporcionaron ninguna
        final String userPwd = new String(AOUIFactory.getPassword(
                extraParams.getProperty("userPassword") == null ? CommonPdfMessages.getString("AOPDFSigner.0") //$NON-NLS-1$//$NON-NLS-2$
                        : CommonPdfMessages.getString("AOPDFSigner.1"), //$NON-NLS-1$
                null));
        if ("".equals(userPwd)) { //$NON-NLS-1$
            throw new AOCancelledOperationException("Entrada de contrasena de PDF cancelada por el usuario", e //$NON-NLS-1$
            );
        }
        extraParams.put("userPassword", userPwd); //$NON-NLS-1$
        return getSessionData(inPDF, certChain, signTime, extraParams);
    }

    // Aplicamos todos los atributos de firma
    final PdfSignatureAppearance sap = stp.getSignatureAppearance();
    stp.setFullCompression();
    sap.setAcro6Layers(true);

    PdfUtil.enableLtv(stp);

    // Adjuntos
    PdfPreProcessor.attachFile(extraParams, stp);

    // Imagenes
    PdfPreProcessor.addImage(extraParams, stp);

    // Establecemos el render segun iText antiguo, varia en versiones modernas
    sap.setRender(PdfSignatureAppearance.SignatureRenderDescription);

    // Razon de firma
    if (reason != null) {
        sap.setReason(reason);
    }

    sap.setSignDate(signTime);

    // Gestion de los cifrados
    PdfUtil.managePdfEncryption(stp, pdfReader, extraParams);

    // Pagina en donde se imprime la firma
    if (page == LAST_PAGE) {
        page = pdfReader.getNumberOfPages();
    }

    // Posicion de la firma
    final Rectangle signaturePositionOnPage = getSignaturePositionOnPage(extraParams);
    if (signaturePositionOnPage != null && signatureField == null) {
        sap.setVisibleSignature(signaturePositionOnPage, page, null);
    } else if (signatureField != null) {
        sap.setVisibleSignature(signatureField);
    }

    // Localizacion en donde se produce la firma
    if (signatureProductionCity != null) {
        sap.setLocation(signatureProductionCity);
    }

    // Contacto del firmante
    if (signerContact != null) {
        sap.setContact(signerContact);
    }

    // Rubrica de la firma
    if (rubric != null) {
        sap.setImage(rubric);
        sap.setLayer2Text(""); //$NON-NLS-1$
        sap.setLayer4Text(""); //$NON-NLS-1$
    }

    // **************************
    // ** Texto en las capas ****
    // **************************

    // Capa 2
    if (layer2Text != null) {

        sap.setLayer2Text(layer2Text);

        final int layer2FontColorR;
        final int layer2FontColorG;
        final int layer2FontColorB;

        if ("black".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 0;
            layer2FontColorG = 0;
            layer2FontColorB = 0;
        } else if ("white".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 255;
            layer2FontColorG = 255;
            layer2FontColorB = 255;
        } else if ("lightGray".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 192;
            layer2FontColorG = 192;
            layer2FontColorB = 192;
        } else if ("gray".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 128;
            layer2FontColorG = 128;
            layer2FontColorB = 128;
        } else if ("darkGray".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 64;
            layer2FontColorG = 64;
            layer2FontColorB = 64;
        } else if ("red".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 255;
            layer2FontColorG = 0;
            layer2FontColorB = 0;
        } else if ("pink".equalsIgnoreCase(layer2FontColor)) { //$NON-NLS-1$
            layer2FontColorR = 255;
            layer2FontColorG = 175;
            layer2FontColorB = 175;
        } else if (layer2FontColor == null) {
            layer2FontColorR = 0;
            layer2FontColorG = 0;
            layer2FontColorB = 0;
        } else {
            LOGGER.warning("No se soporta el color '" + layer2FontColor //$NON-NLS-1$
                    + "' para el texto de la capa 4, se usara negro"); //$NON-NLS-1$
            layer2FontColorR = 0;
            layer2FontColorG = 0;
            layer2FontColorB = 0;
        }

        com.lowagie.text.Font font;
        try {
            Class<?> colorClass;
            if (Platform.getOS() == OS.ANDROID) {
                colorClass = Class.forName("harmony.java.awt.Color"); //$NON-NLS-1$
            } else {
                colorClass = Class.forName("java.awt.Color"); //$NON-NLS-1$
            }
            final Object color = colorClass.getConstructor(Integer.TYPE, Integer.TYPE, Integer.TYPE)
                    .newInstance(Integer.valueOf(layer2FontColorR), Integer.valueOf(layer2FontColorG),
                            Integer.valueOf(layer2FontColorB));

            font = com.lowagie.text.Font.class
                    .getConstructor(Integer.TYPE, Float.TYPE, Integer.TYPE, colorClass).newInstance(
                            // Family (COURIER = 0, HELVETICA = 1, TIMES_ROMAN = 2, SYMBOL = 3, ZAPFDINGBATS = 4)
                            Integer.valueOf(layer2FontFamily == UNDEFINED ? COURIER : layer2FontFamily),
                            // Size (DEFAULTSIZE = 12)
                            Float.valueOf(
                                    layer2FontSize == UNDEFINED ? DEFAULT_LAYER_2_FONT_SIZE : layer2FontSize),
                            // Style (NORMAL = 0, BOLD = 1, ITALIC = 2, BOLDITALIC = 3, UNDERLINE = 4, STRIKETHRU = 8)
                            Integer.valueOf(layer2FontStyle == UNDEFINED ? com.lowagie.text.Font.NORMAL
                                    : layer2FontStyle),
                            // Color
                            color);
        } catch (final Exception e) {
            font = new com.lowagie.text.Font(
                    // Family (COURIER = 0, HELVETICA = 1, TIMES_ROMAN = 2, SYMBOL = 3, ZAPFDINGBATS = 4)
                    layer2FontFamily == UNDEFINED ? COURIER : layer2FontFamily,
                    // Size (DEFAULTSIZE = 12)
                    layer2FontSize == UNDEFINED ? DEFAULT_LAYER_2_FONT_SIZE : layer2FontSize,
                    // Style (NORMAL = 0, BOLD = 1, ITALIC = 2, BOLDITALIC = 3, UNDERLINE = 4, STRIKETHRU = 8)
                    layer2FontStyle == UNDEFINED ? com.lowagie.text.Font.NORMAL : layer2FontStyle,
                    // Color
                    null);
        }
        sap.setLayer2Font(font);
    }

    // Capa 4
    if (layer4Text != null) {
        sap.setLayer4Text(layer4Text);
    }

    // ***************************
    // ** Fin texto en las capas *
    // ***************************

    sap.setCrypto(null, certChain, null, null);

    final PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE,
            signatureSubFilter != null && !"".equals(signatureSubFilter) ? new PdfName(signatureSubFilter) //$NON-NLS-1$
                    : PdfName.ADBE_PKCS7_DETACHED);

    // Fecha de firma
    if (sap.getSignDate() != null) {
        dic.setDate(new PdfDate(sap.getSignDate()));
    }

    dic.setName(PdfPKCS7.getSubjectFields((X509Certificate) certChain[0]).getField("CN")); //$NON-NLS-1$

    if (sap.getReason() != null) {
        dic.setReason(sap.getReason());
    }

    // Lugar de la firma
    if (sap.getLocation() != null) {
        dic.setLocation(sap.getLocation());
    }

    // Contacto del firmante
    if (sap.getContact() != null) {
        dic.setContact(sap.getContact());
    }

    sap.setCryptoDictionary(dic);

    // Certificacion del PDF (NOT_CERTIFIED = 0, CERTIFIED_NO_CHANGES_ALLOWED = 1,
    // CERTIFIED_FORM_FILLING = 2, CERTIFIED_FORM_FILLING_AND_ANNOTATIONS = 3)
    if (certificationLevel != -1) {
        sap.setCertificationLevel(certificationLevel);
    }

    // Reservamos el espacio necesario en el PDF para insertar la firma
    final HashMap<PdfName, Integer> exc = new HashMap<PdfName, Integer>();
    exc.put(PdfName.CONTENTS, Integer.valueOf(CSIZE * 2 + 2));

    sap.preClose(exc, signTime);

    final PdfObject pdfObject = ((com.lowagie.text.pdf.PdfStamperImp) stp.getWriter()).getFileID();

    return new PdfTriPhaseSession(sap, baos, new String(pdfObject.getBytes()));
}

From source file:eu.europa.ec.markt.dss.signature.pdf.itext.ITextPDFDocTimeSampService.java

License:Open Source License

@SuppressWarnings({ "unchecked", "rawtypes" })
private PdfStamper prepareStamper(InputStream pdfData, OutputStream output, SignatureParameters parameters)
        throws IOException {

    try {//from ww  w .jav  a 2  s  .c om
        PdfReader reader = new PdfReader(pdfData);
        PdfStamper stamper = PdfStamper.createSignature(reader, output, '\0', null, true);

        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
        appearance.setAcro6Layers(true);
        appearance.setRender(PdfSignatureAppearance.SignatureRenderDescription);

        PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("ETSI.RFC3161"));
        // defined in ETSI TS 102 778-4 A.2
        dic.put(PdfName.TYPE, new PdfName("DocTimeStamp"));

        Calendar cal = Calendar.getInstance();
        if (parameters.getSigningDate() != null) {
            cal.setTime(parameters.getSigningDate());
            appearance.setSignDate(cal);
        }
        dic.setDate(new PdfDate(cal));

        appearance.setCryptoDictionary(dic);

        int csize = getSignatureSize();
        HashMap exc = new HashMap();
        exc.put(PdfName.CONTENTS, new Integer(csize * 2 + 2));

        appearance.preClose(exc);

        return stamper;
    } catch (DocumentException e) {
        throw new IOException(e);
    }

}

From source file:eu.europa.ec.markt.dss.signature.pdf.itext.ITextPDFSignatureService.java

License:Open Source License

@SuppressWarnings({ "unchecked", "rawtypes" })
private PdfStamper prepareStamper(InputStream pdfData, OutputStream output, SignatureParameters parameters)
        throws IOException, DocumentException {

    PdfReader reader = new PdfReader(pdfData);
    PdfStamper stp = PdfStamper.createSignature(reader, output, '\0', null, true);

    PdfSignatureAppearance sap = stp.getSignatureAppearance();
    sap.setAcro6Layers(true);/* w w w.  j a va 2s  .c o  m*/
    sap.setRender(PdfSignatureAppearance.SignatureRenderDescription);

    PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("ETSI.CAdES.detached"));
    Calendar cal = Calendar.getInstance();
    cal.setTime(parameters.getSigningDate());
    sap.setSignDate(cal);
    dic.setDate(new PdfDate(cal));

    if (parameters.getReason() != null) {
        dic.setReason(parameters.getReason());
    }
    if (parameters.getLocation() != null) {
        dic.setLocation(parameters.getLocation());
    }
    if (parameters.getContactInfo() != null) {
        dic.setContact(parameters.getContactInfo());
    }

    sap.setCryptoDictionary(dic);

    int csize = getSignatureSize();
    HashMap exc = new HashMap();
    exc.put(PdfName.CONTENTS, new Integer(csize * 2 + 2));

    sap.preClose(exc);

    return stp;
}

From source file:eu.europa.ec.markt.dss.signature.pdf.ITextPDFDocTimeSampService.java

License:Open Source License

@SuppressWarnings({ "unchecked", "rawtypes" })
private PdfStamper prepareStamper(InputStream pdfData, OutputStream output, SignatureParameters parameters)
        throws IOException, DocumentException {

    PdfReader reader = new PdfReader(pdfData);
    PdfStamper stp = PdfStamper.createSignature(reader, output, '\0', null, true);

    PdfSignatureAppearance sap = stp.getSignatureAppearance();
    sap.setAcro6Layers(true);/*  w w  w .  j a v a 2  s  .  c o  m*/
    sap.setRender(PdfSignatureAppearance.SignatureRenderDescription);

    PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("ETSI.RFC3161"));
    // defined in ETSI TS 102 778-4 A.2
    dic.put(PdfName.TYPE, new PdfName("DocTimeStamp"));

    Calendar cal = Calendar.getInstance();
    if (parameters.getSigningDate() != null) {
        cal.setTime(parameters.getSigningDate());
        sap.setSignDate(cal);
    }
    dic.setDate(new PdfDate(cal));

    sap.setCryptoDictionary(dic);

    int csize = getSignatureSize();
    HashMap exc = new HashMap();
    exc.put(PdfName.CONTENTS, new Integer(csize * 2 + 2));

    sap.preClose(exc);

    return stp;
}

From source file:eu.europa.ec.markt.dss.signature.pdf.StatefulITextPDFSignatureService.java

License:Open Source License

@SuppressWarnings({ "unchecked", "rawtypes" })
private PdfStamper prepareStamper(InputStream pdfData, OutputStream output, SignatureParameters parameters)
        throws IOException, DocumentException {

    if (stp != null) {
        return stp;
    }//from w  ww. jav a2  s.  c  om

    PdfReader reader = new PdfReader(pdfData);
    stp = PdfStamper.createSignature(reader, output, '\0', null, true);

    PdfSignatureAppearance sap = stp.getSignatureAppearance();
    sap.setAcro6Layers(true);
    sap.setLayer2Text("");
    if (parameters.getSignatureAppearance() != null) {
        sap.setRender(PdfSignatureAppearance.SignatureRenderGraphic);
        sap.setImage(null);
        PdfReader stampReader = new PdfReader(parameters.getSignatureAppearance());
        PdfTemplate stamp = stp.getWriter().getImportedPage(stampReader, 1);
        //stamp.setBoundingBox(new Rectangle(200, 100));
        sap.setTemplate(stamp);
        sap.setSignatureGraphic(Image.getInstance(stamp));

        float[] pos = parameters.getSignaturePosition();
        Rectangle rect = new Rectangle(pos[0], pos[1], pos[2], pos[3]);
        sap.setVisibleSignature(rect, 1, parameters.getSignatureName());
    } else {
        sap.setRender(PdfSignatureAppearance.SignatureRenderDescription);
    }

    PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("ETSI.CAdES.detached"));
    Calendar cal = Calendar.getInstance();
    cal.setTime(parameters.getSigningDate());
    sap.setSignDate(cal);
    dic.setDate(new PdfDate(cal));

    if (parameters.getReason() != null) {
        dic.setReason(parameters.getReason());
    }
    if (parameters.getLocation() != null) {
        dic.setLocation(parameters.getLocation());
    }
    if (parameters.getContactInfo() != null) {
        dic.setContact(parameters.getContactInfo());
    }

    sap.setCryptoDictionary(dic);

    int csize = getSignatureSize();
    HashMap exc = new HashMap();
    exc.put(PdfName.CONTENTS, new Integer(csize * 2 + 2));

    sap.preClose(exc);

    return stp;
}

From source file:org.nuxeo.ecm.platform.signature.core.sign.SignatureServiceImpl.java

License:Open Source License

@Override
public Blob signPDF(Blob pdfBlob, DocumentModel user, String keyPassword, String reason)
        throws ClientException {
    CertService certService = Framework.getLocalService(CertService.class);
    CUserService cUserService = Framework.getLocalService(CUserService.class);
    try {// www.  jav a  2 s.  c om
        File outputFile = File.createTempFile("signed-", ".pdf");
        Blob blob = Blobs.createBlob(outputFile, MIME_TYPE_PDF);
        Framework.trackFile(outputFile, blob);

        PdfReader pdfReader = new PdfReader(pdfBlob.getStream());
        List<X509Certificate> pdfCertificates = getCertificates(pdfReader);

        // allows for multiple signatures
        PdfStamper pdfStamper = PdfStamper.createSignature(pdfReader, new FileOutputStream(outputFile), '\0',
                null, true);

        PdfSignatureAppearance pdfSignatureAppearance = pdfStamper.getSignatureAppearance();
        String userID = (String) user.getPropertyValue("user:username");
        AliasWrapper alias = new AliasWrapper(userID);
        KeyStore keystore = cUserService.getUserKeystore(userID, keyPassword);
        Certificate certificate = certService.getCertificate(keystore, alias.getId(AliasType.CERT));
        KeyPair keyPair = certService.getKeyPair(keystore, alias.getId(AliasType.KEY),
                alias.getId(AliasType.CERT), keyPassword);

        if (certificatePresentInPDF(certificate, pdfCertificates)) {
            X509Certificate userX509Certificate = (X509Certificate) certificate;
            String message = ALREADY_SIGNED_BY + userX509Certificate.getSubjectDN();
            log.debug(message);
            throw new AlreadySignedException(message);
        }

        List<Certificate> certificates = new ArrayList<Certificate>();
        certificates.add(certificate);

        Certificate[] certChain = certificates.toArray(new Certificate[0]);
        pdfSignatureAppearance.setCrypto(keyPair.getPrivate(), certChain, null,
                PdfSignatureAppearance.SELF_SIGNED);
        if (StringUtils.isBlank(reason)) {
            reason = getSigningReason();
        }
        pdfSignatureAppearance.setReason(reason);
        pdfSignatureAppearance.setAcro6Layers(true);
        Font layer2Font = FontFactory.getFont(FontFactory.TIMES, getSignatureLayout().getTextSize(),
                Font.NORMAL, new Color(0x00, 0x00, 0x00));
        pdfSignatureAppearance.setLayer2Font(layer2Font);
        pdfSignatureAppearance.setRender(PdfSignatureAppearance.SignatureRenderDescription);

        pdfSignatureAppearance.setVisibleSignature(getNextCertificatePosition(pdfReader, pdfCertificates), 1,
                null);

        pdfStamper.close(); // closes the file

        log.debug("File " + outputFile.getAbsolutePath() + " created and signed with " + reason);

        return blob;
    } catch (IOException e) {
        throw new SignException(e);
    } catch (DocumentException e) {
        // iText PDF stamping
        throw new SignException(e);
    } catch (IllegalArgumentException e) {
        if (String.valueOf(e.getMessage()).contains("PdfReader not opened with owner password")) {
            // iText PDF reading
            throw new SignException("PDF is password-protected");
        }
        throw new SignException(e);
    }
}