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

com.unboundid.util.ssl.cert.PKCS8PEMFileReader Maven / Gradle / Ivy

/*
 * Copyright 2021-2023 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2021-2023 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2021-2023 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.util.ssl.cert;



import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import com.unboundid.util.Base64;
import com.unboundid.util.Debug;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;

import static com.unboundid.util.ssl.cert.CertMessages.*;



/**
 * This class provides a mechanism for reading a PEM-encoded PKCS #8 private key
 * from a specified file.  While it is generally expected that a private key
 * file will contain only a single key, it is possible to read multiple keys
 * from the same file.  Each private key should consist of the following:
 * 
    *
  • A line containing only the string "-----BEGIN PRIVATE KEY-----" or * ""-----BEGIN RSA PRIVATE KEY-----.
  • *
  • One or more lines representing the base64-encoded representation of the * bytes that comprise the PKCS #8 private key.
  • *
  • A line containing only the string "-----END PRIVATE KEY-----" or * ""-----END RSA PRIVATE KEY-----.
  • *
*

* Any spaces that appear at the beginning or end of each line will be ignored. * Empty lines and lines that start with the octothorpe (#) character will also * be ignored. */ @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class PKCS8PEMFileReader implements Closeable { /** * The header string that should appear on a line by itself before the * base64-encoded representation of the bytes that comprise an encrypted * PKCS #8 private key. */ @NotNull public static final String BEGIN_ENCRYPTED_PRIVATE_KEY_HEADER = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; /** * The header string that should appear on a line by itself before the * base64-encoded representation of the bytes that comprise a PKCS #8 private * key. */ @NotNull public static final String BEGIN_PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----"; /** * An alternative begin header string that may appear on a line by itself for * cases in which the certificate uses an RSA key pair. */ @NotNull public static final String BEGIN_RSA_PRIVATE_KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----"; /** * The footer string that should appear on a line by itself after the * base64-encoded representation of the bytes that comprise an encrypted * PKCS #8 private key. */ @NotNull public static final String END_ENCRYPTED_PRIVATE_KEY_FOOTER = "-----END ENCRYPTED PRIVATE KEY-----"; /** * The footer string that should appear on a line by itself after the * base64-encoded representation of the bytes that comprise a PKCS #8 private * key. */ @NotNull public static final String END_PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----"; /** * An alternative end footer string that may appear on a line by itself for * cases in which the certificate uses an RSA key pair. */ @NotNull public static final String END_RSA_PRIVATE_KEY_FOOTER = "-----END RSA PRIVATE KEY-----"; // The reader that will be used to consume data from the PEM file. @NotNull private final BufferedReader reader; /** * Creates a new PKCS #8 PEM file reader that will read private key * information from the specified file. * * @param pemFilePath The path to the PEM file from which the private key * should be read. This must not be {@code null} and the * file must exist. * * @throws IOException If a problem occurs while attempting to open the file * for reading. */ public PKCS8PEMFileReader(@NotNull final String pemFilePath) throws IOException { this(new File(pemFilePath)); } /** * Creates a new PKCS #8 PEM file reader that will read private key * information from the specified file. * * @param pemFile The PEM file from which the private key should be read. * This must not be {@code null} and the file must * exist. * * @throws IOException If a problem occurs while attempting to open the file * for reading. */ public PKCS8PEMFileReader(@NotNull final File pemFile) throws IOException { this(new FileInputStream(pemFile)); } /** * Creates a new PKCS #8 PEM file reader that will read private key * information from the provided input stream. * * @param inputStream The input stream from which the private key should * be read. This must not be {@code null} and it must be * open for reading. */ public PKCS8PEMFileReader(@NotNull final InputStream inputStream) { reader = new BufferedReader(new InputStreamReader(inputStream)); } /** * Reads the next private key from the PEM file. The private key must be * unencrypted. * * @return The private key that was read, or {@code null} if the end of the * file has been reached. * * @throws IOException If a problem occurs while trying to read data from * the PEM file. * * @throws CertException If a problem occurs while trying to interpret data * read from the PEM file as a PKCS #8 private key. */ @Nullable() public PKCS8PrivateKey readPrivateKey() throws IOException, CertException { return readPrivateKey(null); } /** * Reads the next private key from the PEM file. The private key may * optionally be encrypted. * * @param encryptionPassword The password used to encrypt the private key. * It must not be {@code null} if the private key * is encrypted. It may be {@code null} if the * private key is not encrypted. * * @return The private key that was read, or {@code null} if the end of the * file has been reached. * * @throws IOException If a problem occurs while trying to read data from * the PEM file. * * @throws CertException If a problem occurs while trying to interpret data * read from the PEM file as a PKCS #8 private key. */ @Nullable() public PKCS8PrivateKey readPrivateKey( @Nullable final char[] encryptionPassword) throws IOException, CertException { boolean isEncrypted = false; String beginLine = null; final StringBuilder base64Buffer = new StringBuilder(); while (true) { final String line = reader.readLine(); if (line == null) { // We hit the end of the file. If we read a begin header, then that's // an error. if (beginLine != null) { throw new CertException(ERR_PKCS8_PEM_READER_EOF_WITHOUT_END.get( END_PRIVATE_KEY_FOOTER, beginLine)); } return null; } final String trimmedLine = line.trim(); if (trimmedLine.isEmpty() || trimmedLine.startsWith("#")) { continue; } final String upperLine = StaticUtils.toUpperCase(trimmedLine); if (BEGIN_ENCRYPTED_PRIVATE_KEY_HEADER.equals(upperLine) || BEGIN_PRIVATE_KEY_HEADER.equals(upperLine) || BEGIN_RSA_PRIVATE_KEY_HEADER.equals(upperLine)) { if (beginLine != null) { throw new CertException(ERR_PKCS8_PEM_READER_REPEATED_BEGIN.get( upperLine)); } else { beginLine = upperLine; if (BEGIN_ENCRYPTED_PRIVATE_KEY_HEADER.equals(upperLine)) { isEncrypted = true; if (encryptionPassword == null) { throw new CertException( ERR_PKCS8_PEM_READER_NO_PW_FOR_ENCRYPTED_KEY.get()); } } } } else if (END_ENCRYPTED_PRIVATE_KEY_FOOTER.equals(upperLine) || END_PRIVATE_KEY_FOOTER.equals(upperLine) || END_RSA_PRIVATE_KEY_FOOTER.equals(upperLine)) { if (beginLine == null) { throw new CertException(ERR_PKCS8_PEM_READER_END_WITHOUT_BEGIN.get( upperLine, beginLine)); } else if (base64Buffer.length() == 0) { throw new CertException(ERR_PKCS8_PEM_READER_END_WITHOUT_DATA.get( upperLine, beginLine)); } else { final byte[] pkcs8Bytes; if (isEncrypted) { final byte[] encryptedKeyBytes; try { encryptedKeyBytes = Base64.decode(base64Buffer.toString()); } catch (final Exception e) { Debug.debugException(e); throw new CertException( ERR_PKCS8_PEM_READER_CANNOT_BASE64_DECODE.get(), e); } return PKCS8EncryptionHandler.decryptPrivateKey( encryptedKeyBytes, encryptionPassword); } else { try { pkcs8Bytes = Base64.decode(base64Buffer.toString()); } catch (final Exception e) { Debug.debugException(e); throw new CertException( ERR_PKCS8_PEM_READER_CANNOT_BASE64_DECODE.get(), e); } } return new PKCS8PrivateKey(pkcs8Bytes); } } else { if (beginLine == null) { throw new CertException(ERR_PKCS8_PEM_READER_DATA_WITHOUT_BEGIN.get( BEGIN_PRIVATE_KEY_HEADER)); } base64Buffer.append(trimmedLine); } } } /** * Closes this PKCS #8 PEM file reader. * * @throws IOException If a problem is encountered while attempting to close * the reader. */ @Override() public void close() throws IOException { reader.close(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy