All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.signatures.SignUtils Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2024 Apryse Group NV
    Authors: Apryse Software.

    This program is offered under a commercial and under the AGPL license.
    For commercial licensing, contact us at https://itextpdf.com/sales.  For AGPL licensing, see below.

    AGPL licensing:
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see .
 */
package com.itextpdf.signatures;

import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
import com.itextpdf.commons.bouncycastle.asn1.IASN1ObjectIdentifier;
import com.itextpdf.commons.bouncycastle.asn1.IASN1Sequence;
import com.itextpdf.commons.bouncycastle.asn1.IDEROctetString;
import com.itextpdf.commons.bouncycastle.asn1.tsp.ITSTInfo;
import com.itextpdf.commons.bouncycastle.asn1.x509.IAlgorithmIdentifier;
import com.itextpdf.commons.bouncycastle.asn1.x509.IExtension;
import com.itextpdf.commons.bouncycastle.cert.IX509CertificateHolder;
import com.itextpdf.commons.bouncycastle.cert.jcajce.IJcaX509CertificateConverter;
import com.itextpdf.commons.bouncycastle.cert.ocsp.AbstractOCSPException;
import com.itextpdf.commons.bouncycastle.cert.ocsp.IBasicOCSPResp;
import com.itextpdf.commons.bouncycastle.cert.ocsp.ICertificateID;
import com.itextpdf.commons.bouncycastle.cert.ocsp.IOCSPReq;
import com.itextpdf.commons.bouncycastle.cert.ocsp.IOCSPReqBuilder;
import com.itextpdf.commons.bouncycastle.cms.ISignerInformationVerifier;
import com.itextpdf.commons.bouncycastle.operator.AbstractOperatorCreationException;
import com.itextpdf.commons.bouncycastle.tsp.AbstractTSPException;
import com.itextpdf.commons.bouncycastle.tsp.ITimeStampToken;
import com.itextpdf.commons.utils.Base64;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.pdf.PdfEncryption;
import com.itextpdf.signatures.exceptions.SignExceptionMessageConstant;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CRL;
import java.security.cert.CRLException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.cert.X509CRL;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import javax.security.auth.x500.X500Principal;

final class SignUtils {
    private static final IBouncyCastleFactory FACTORY = BouncyCastleFactoryCreator.getFactory();

    static String getPrivateKeyAlgorithm(PrivateKey pk) {
        String algorithm = pk.getAlgorithm();
        if (algorithm.equals("EC")) {
            algorithm = "ECDSA";
        }
        return algorithm;
    }

    /**
     * Parses a CRL from an InputStream.
     *
     * @param input                     The InputStream holding the unparsed CRL.
     * @return the parsed CRL object
     * @throws CertificateException     thrown when no provider has been found for X509
     * @throws CRLException             thrown during parsing the CRL
     */
    static CRL parseCrlFromStream(InputStream input) throws CertificateException, CRLException {
        return CertificateFactory.getInstance("X.509").generateCRL(input);
    }

    static byte[] getExtensionValueByOid(X509Certificate certificate, String oid) {
        return certificate.getExtensionValue(oid);
    }

    static byte[] getExtensionValueByOid(CRL crl, String oid) {
        return ((X509CRL) crl).getExtensionValue(oid);
    }

    static MessageDigest getMessageDigest(String hashAlgorithm) throws GeneralSecurityException {
        return new BouncyCastleDigest().getMessageDigest(hashAlgorithm);
    }

    static MessageDigest getMessageDigest(String hashAlgorithm, IExternalDigest externalDigest) throws GeneralSecurityException {
        return externalDigest.getMessageDigest(hashAlgorithm);
    }

    static MessageDigest getMessageDigest(String hashAlgorithm, String provider)
            throws NoSuchAlgorithmException, NoSuchProviderException {
        if (provider == null || provider.startsWith("SunPKCS11") || provider.startsWith("SunMSCAPI")) {
            return MessageDigest.getInstance(DigestAlgorithms.normalizeDigestName(hashAlgorithm));
        } else {
            return MessageDigest.getInstance(hashAlgorithm, provider);
        }
    }

    static InputStream getHttpResponse(URL urlt) throws IOException {
        HttpURLConnection con = (HttpURLConnection) urlt.openConnection();
        if (con.getResponseCode() / 100 != 2) {
            throw new PdfException(SignExceptionMessageConstant.INVALID_HTTP_RESPONSE)
                    .setMessageParams(con.getResponseCode());
        }
        return (InputStream) con.getContent();
    }

    static ICertificateID generateCertificateId(X509Certificate issuerCert, BigInteger serialNumber,
            IAlgorithmIdentifier digestAlgorithmIdentifier)
            throws AbstractOperatorCreationException, CertificateEncodingException, AbstractOCSPException {
        return FACTORY.createCertificateID(
                FACTORY.createJcaDigestCalculatorProviderBuilder().build().get(digestAlgorithmIdentifier),
                FACTORY.createJcaX509CertificateHolder(issuerCert),
                serialNumber);
    }

    static ICertificateID generateCertificateId(X509Certificate issuerCert, BigInteger serialNumber,
            IASN1ObjectIdentifier identifier)
            throws AbstractOperatorCreationException, CertificateEncodingException, AbstractOCSPException {
        return FACTORY.createCertificateID(
                FACTORY.createJcaDigestCalculatorProviderBuilder().build().get(
                        FACTORY.createAlgorithmIdentifier(identifier, FACTORY.createDERNull())),
                FACTORY.createJcaX509CertificateHolder(issuerCert), serialNumber);
    }

    static IOCSPReq generateOcspRequestWithNonce(ICertificateID id) throws IOException, AbstractOCSPException {
        IOCSPReqBuilder gen = FACTORY.createOCSPReqBuilder();
        gen.addRequest(id);

        IDEROctetString derOctetString = FACTORY.createDEROctetString(
                FACTORY.createDEROctetString(PdfEncryption.generateNewDocumentId()).getEncoded());
        IExtension ext = FACTORY.createExtension(
                FACTORY.createOCSPObjectIdentifiers().getIdPkixOcspNonce(), false, derOctetString);
        gen.setRequestExtensions(FACTORY.createExtensions(ext));
        return gen.build();
    }

    static InputStream getHttpResponseForOcspRequest(byte[] request, URL urlt) throws IOException {
        HttpURLConnection con = (HttpURLConnection) urlt.openConnection();
        con.setRequestProperty("Content-Type", "application/ocsp-request");
        con.setRequestProperty("Accept", "application/ocsp-response");
        con.setDoOutput(true);
        OutputStream out = con.getOutputStream();
        DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(out));
        dataOut.write(request);
        dataOut.flush();
        dataOut.close();
        if (con.getResponseCode() / 100 != 2) {
            throw new PdfException(SignExceptionMessageConstant.INVALID_HTTP_RESPONSE)
                    .setMessageParams(con.getResponseCode());
        }
        //Get Response
        return (InputStream) con.getContent();
    }

    static boolean isSignatureValid(IBasicOCSPResp validator, Certificate certStoreX509, String provider)
            throws AbstractOperatorCreationException, AbstractOCSPException {
        if (provider == null) {
            provider = FACTORY.getProviderName();
        }
        return validator.isSignatureValid(FACTORY.createJcaContentVerifierProviderBuilder()
                .setProvider(provider).build(certStoreX509.getPublicKey()));
    }

    static void isSignatureValid(ITimeStampToken validator, X509Certificate certStoreX509, String provider)
            throws AbstractOperatorCreationException, AbstractTSPException {
        if (provider == null) {
            provider = FACTORY.getProviderName();
        }
        ISignerInformationVerifier verifier = FACTORY.createJcaSimpleSignerInfoVerifierBuilder().setProvider(provider)
                .build(certStoreX509);
        validator.validate(verifier);
    }

    static boolean checkIfIssuersMatch(ICertificateID certID, X509Certificate issuerCert)
            throws CertificateEncodingException, IOException, AbstractOCSPException, AbstractOperatorCreationException {
        return certID.matchesIssuer(
                FACTORY.createX509CertificateHolder(issuerCert.getEncoded()),
                FACTORY.createJcaDigestCalculatorProviderBuilder().build());
    }

    static Date add180Sec(Date date) {
        return new Date(date.getTime() + 180000L);
    }

    static Iterable getCertsFromOcspResponse(IBasicOCSPResp ocspResp) {
        List certs = new ArrayList<>();
        IX509CertificateHolder[] certHolders = ocspResp.getCerts();
        IJcaX509CertificateConverter converter = FACTORY.createJcaX509CertificateConverter();
        for (IX509CertificateHolder certHolder : certHolders) {
            try {
                certs.add(converter.getCertificate(certHolder));
            } catch (Exception ex) {
                // do nothing
            }
        }
        return certs;
    }

    static Collection readAllCerts(byte[] contentsKey) throws CertificateException {
        return SignUtils.readAllCerts(new ByteArrayInputStream(contentsKey), FACTORY.getProvider());
    }

    static Collection readAllCerts(InputStream contentsKey, Provider provider)
            throws CertificateException {
        final CertificateFactory factory = provider == null ? CertificateFactory.getInstance("X509") :
                CertificateFactory.getInstance("X509", provider);
        return new ArrayList<>(factory.generateCertificates(contentsKey));
    }

    static Certificate generateCertificate(InputStream data, Provider provider) throws CertificateException {
        final CertificateFactory factory = provider == null ? CertificateFactory.getInstance("X509") :
                CertificateFactory.getInstance("X509", provider);
        return factory.generateCertificate(data);
    }

    static Collection readAllCRLs(byte[] contentsKey) throws CertificateException, CRLException {
        final CertificateFactory factory = CertificateFactory.getInstance("X509", FACTORY.getProvider());
        return new ArrayList<>(factory.generateCRLs(new ByteArrayInputStream(contentsKey)));
    }

    static  T getFirstElement(Iterable iterable) {
        return iterable.iterator().next();
    }

    static X500Principal getIssuerX500Principal(IASN1Sequence issuerAndSerialNumber) throws IOException {
        return new X500Principal(issuerAndSerialNumber.getObjectAt(0).toASN1Primitive().getEncoded());
    }

    static class TsaResponse {
        String encoding;
        InputStream tsaResponseStream;
    }

    static TsaResponse getTsaResponseForUserRequest(String tsaUrl, byte[] requestBytes, String tsaUsername,
            String tsaPassword) throws IOException {
        URL url = new URL(tsaUrl);
        URLConnection tsaConnection;
        try {
            tsaConnection = url.openConnection();
        } catch (IOException ioe) {
            throw new PdfException(SignExceptionMessageConstant.FAILED_TO_GET_TSA_RESPONSE).setMessageParams(tsaUrl);
        }
        tsaConnection.setDoInput(true);
        tsaConnection.setDoOutput(true);
        tsaConnection.setUseCaches(false);
        tsaConnection.setRequestProperty("Content-Type", "application/timestamp-query");
        //tsaConnection.setRequestProperty("Content-Transfer-Encoding", "base64");
        tsaConnection.setRequestProperty("Content-Transfer-Encoding", "binary");

        if ((tsaUsername != null) && !tsaUsername.equals("")) {
            String userPassword = tsaUsername + ":" + tsaPassword;
            tsaConnection.setRequestProperty("Authorization", "Basic " +
                    Base64.encodeBytes(userPassword.getBytes(StandardCharsets.UTF_8), Base64.DONT_BREAK_LINES));
        }
        OutputStream out = tsaConnection.getOutputStream();
        out.write(requestBytes);
        out.close();

        TsaResponse response = new TsaResponse();
        response.tsaResponseStream = tsaConnection.getInputStream();
        response.encoding = tsaConnection.getContentEncoding();
        return response;
    }

    /**
     * Check if the provided certificate has a critical extension that iText doesn't support.
     *
     * @param cert X509Certificate instance to check
     * @return true if there are unsupported critical extensions, false if there are none
     * @deprecated this behavior is different in Java and .NET, because in Java we use this
     * two-step check: first via #hasUnsupportedCriticalExtension method, and then additionally allowing
     * standard critical extensions; in .NET there's only second step. However, removing
     * first step in Java can be a breaking change for some users and moreover we don't
     * have any means of providing customization for unsupported extensions check as of right now.
     * 

* During major release I'd suggest changing java unsupported extensions check logic to the same as in .NET, * but only if it is possible to customize this logic. */ // TODO DEVSIX-2634 @Deprecated static boolean hasUnsupportedCriticalExtension(X509Certificate cert) { if (cert == null) { throw new IllegalArgumentException("X509Certificate can't be null."); } if (cert.hasUnsupportedCriticalExtension()) { for (String oid : cert.getCriticalExtensionOIDs()) { if (OID.X509Extensions.SUPPORTED_CRITICAL_EXTENSIONS.contains(oid)) { continue; } return true; } } return false; } static Calendar getTimeStampDate(ITSTInfo timeStampTokenInfo) { GregorianCalendar calendar = new GregorianCalendar(); try { calendar.setTime(timeStampTokenInfo.getGenTime()); } catch (ParseException ignored) { // Do nothing. } return calendar; } static Signature getSignatureHelper(String algorithm, String provider) throws NoSuchProviderException, NoSuchAlgorithmException { return provider == null ? Signature.getInstance(algorithm) : Signature.getInstance(algorithm, provider); } static void setRSASSAPSSParamsWithMGF1(Signature signature, String digestAlgoName, int saltLen, int trailerField) throws InvalidAlgorithmParameterException { MGF1ParameterSpec mgf1Spec = new MGF1ParameterSpec(digestAlgoName); PSSParameterSpec spec = new PSSParameterSpec(digestAlgoName, "MGF1", mgf1Spec, saltLen, trailerField); signature.setParameter(spec); } public static void updateVerifier(Signature signature, byte[] attr) throws SignatureException { signature.update(attr); } static boolean verifyCertificateSignature(X509Certificate certificate, PublicKey issuerPublicKey, String provider) { boolean res = false; try { if (provider == null) { certificate.verify(issuerPublicKey); } else { certificate.verify(issuerPublicKey, provider); } res = true; } catch (Exception ignored) { } return res; } static Iterable getCertificates(final KeyStore keyStore) throws KeyStoreException { final Enumeration keyStoreAliases = keyStore.aliases(); return new Iterable() { @Override public Iterator iterator() { return new Iterator() { private X509Certificate nextCert; @Override public boolean hasNext() { if (nextCert == null) { tryToGetNextCertificate(); } return nextCert != null; } @Override public X509Certificate next() { if (!hasNext()) { throw new NoSuchElementException(); } X509Certificate cert = nextCert; nextCert = null; return cert; } private void tryToGetNextCertificate() { while (keyStoreAliases.hasMoreElements()) { try { String alias = keyStoreAliases.nextElement(); if (keyStore.isCertificateEntry(alias) || keyStore.isKeyEntry(alias)) { nextCert = (X509Certificate) keyStore.getCertificate(alias); break; } } catch (KeyStoreException e) { // do nothing and continue } } } @Override public void remove() { throw new UnsupportedOperationException("remove"); } }; } }; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy