PemFileHelper.java

/*
    This file is part of the iText (R) project.
    Copyright (c) 1998-2025 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 <https://www.gnu.org/licenses/>.
 */
package com.itextpdf.kernel.utils;

import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
import com.itextpdf.commons.bouncycastle.IBouncyCastleFactory;
import com.itextpdf.commons.bouncycastle.cert.IX509CertificateHolder;
import com.itextpdf.commons.bouncycastle.cert.jcajce.IJcaX509CertificateConverter;
import com.itextpdf.commons.bouncycastle.openssl.IPEMParser;
import com.itextpdf.commons.bouncycastle.openssl.jcajce.IJcaPEMKeyConverter;
import com.itextpdf.commons.bouncycastle.operator.AbstractOperatorCreationException;
import com.itextpdf.commons.bouncycastle.operator.IInputDecryptorProvider;
import com.itextpdf.commons.bouncycastle.pkcs.AbstractPKCSException;
import com.itextpdf.commons.bouncycastle.pkcs.IPKCS8EncryptedPrivateKeyInfo;

import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;

public final class PemFileHelper {

    private static final IBouncyCastleFactory BOUNCY_CASTLE_FACTORY = BouncyCastleFactoryCreator.getFactory();

    private PemFileHelper() {
        // Empty constructor.
    }
    
    public static PrivateKey readPrivateKeyFromPemFile(InputStream pemFile, char[] pkPassword)
            throws IOException, AbstractPKCSException, AbstractOperatorCreationException {
        IPKCS8EncryptedPrivateKeyInfo key = readPrivateKey(pemFile);
        if (key != null) {
            IInputDecryptorProvider decProv = BOUNCY_CASTLE_FACTORY.createJceOpenSSLPKCS8DecryptorProviderBuilder()
                    .setProvider(BOUNCY_CASTLE_FACTORY.getProvider()).build(pkPassword);
            IJcaPEMKeyConverter keyConverter = BOUNCY_CASTLE_FACTORY
                    .createJcaPEMKeyConverter().setProvider(BOUNCY_CASTLE_FACTORY.getProvider());
            return keyConverter.getPrivateKey(key.decryptPrivateKeyInfo(decProv));
        }
        return null;
    }

    public static Certificate[] readFirstChain(String pemFileName) throws IOException, CertificateException {
        List<IX509CertificateHolder> certificatesHolders = readCertificates(pemFileName);
        IJcaX509CertificateConverter converter =
                BOUNCY_CASTLE_FACTORY.createJcaX509CertificateConverter().setProvider(BOUNCY_CASTLE_FACTORY.getProvider());
        Certificate[] certificates = new Certificate[certificatesHolders.size()];
        for (int i = 0; i < certificatesHolders.size(); i++) {
            certificates[i] = converter.getCertificate(certificatesHolders.get(i));
        }
        return certificates;
    }

    private static List<IX509CertificateHolder> readCertificates(String pemFileName) throws IOException {
        try (IPEMParser parser = BOUNCY_CASTLE_FACTORY.createPEMParser(new FileReader(pemFileName))) {
            Object readObject = parser.readObject();
            List<IX509CertificateHolder> certificateHolders = new ArrayList<>();
            while (readObject != null) {
                if (readObject instanceof IX509CertificateHolder) {
                    certificateHolders.add((IX509CertificateHolder) readObject);
                }
                readObject = parser.readObject();
            }
            return certificateHolders;
        }
    }

    private static IPKCS8EncryptedPrivateKeyInfo readPrivateKey(InputStream pemFile) throws IOException {
        try (IPEMParser parser = BOUNCY_CASTLE_FACTORY.createPEMParser(new InputStreamReader(pemFile))) {
            Object readObject = parser.readObject();
            while (!(readObject instanceof IPKCS8EncryptedPrivateKeyInfo) && readObject != null) {
                readObject = parser.readObject();
            }
            return (IPKCS8EncryptedPrivateKeyInfo) readObject;
        }
    }
}