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

org.tomitribe.PEM Maven / Gradle / Ivy

There is a newer version: 0.0.4
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */
package org.tomitribe;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;

/**
 * A PEM utility that can be used to read keys from PEM. With this PEM utility,
 * private keys in either PKCS#1 or PKCS#8 PEM encoded format can be read
 * without the need to depend on the Bouncy Castle library.
 * 

* Some background information: *

    *
  • Interestingly, the creation of a CloudFront Key Pair via the AWS console * would result in a private key in PKCS#1 PEM format.
  • *
  • Unfortunately, the JDK doesn't provide a means to load PEM key encoded in * PKCS#1 without adding the Bouncy Castle to the classpath. The JDK can only * load PEM key encoded in PKCS#8 encoding.
  • *
  • One the other hand, one can use openssl to convert a PEM file from PKCS#1 * to PKCS#8. Example: * *
     * openssl pkcs8 -topk8 -in pk-APKAJM22QV32R3I2XVIQ.pem -inform pem -out pk-APKAJM22QV32R3I2XVIQ_pk8.pem  -outform pem -nocrypt
     * 
    * *
  • *
*/ public enum PEM { ; private static final String BEGIN_MARKER = "-----BEGIN "; /** * Returns the first private key that is found from the input stream of a * PEM file. * * @throws java.security.spec.InvalidKeySpecException * if failed to convert the DER bytes into a private key. * @throws IllegalArgumentException * if no private key is found. */ public static PrivateKey readPrivateKey(final InputStream is) throws InvalidKeySpecException, IOException { final List objects = readPEMObjects(is); for (final PEMObject object : objects) { switch (object.getPEMObjectType()) { case PRIVATE_KEY_PKCS1: return RSA.privateKeyFromPKCS1(object.getDerBytes()); case PRIVATE_EC_KEY_PKCS8: return EC.privateKeyFromPKCS8(object.getDerBytes()); case PRIVATE_KEY_PKCS8: try { return RSA.privateKeyFromPKCS8(object.getDerBytes()); } catch (InvalidKeySpecException e) { return EC.privateKeyFromPKCS8(object.getDerBytes()); } default: break; } } throw new IllegalArgumentException("Found no private key"); } /** * Returns the first public key that is found from the input stream of a PEM * file. * * @throws java.security.spec.InvalidKeySpecException * if failed to convert the DER bytes into a public key. * @throws IllegalArgumentException * if no public key is found. */ public static PublicKey readPublicKey(final InputStream is) throws InvalidKeySpecException, IOException { for (final PEMObject object : readPEMObjects(is)) { switch (object.getPEMObjectType()) { case PUBLIC_KEY_X509: try { return RSA.publicKeyFrom(object.getDerBytes()); } catch (InvalidKeySpecException e) { return EC.publicKeyFrom(object.getDerBytes()); } default: break; } } throw new IllegalArgumentException("Found no public key"); } public static Certificate[] readCertificates(final InputStream is) throws IOException, CertificateException { CertificateFactory cf = CertificateFactory.getInstance("X.509"); final List result = new ArrayList(); for (final PEMObject object : readPEMObjects(is)) { switch (object.getPEMObjectType()) { case CERTIFICATE_X509: try { result.add(cf.generateCertificate(new ByteArrayInputStream(object.getDerBytes()))); } catch (CertificateException e) { // try and continue } break; default: break; } } return result.toArray(new Certificate[0]); } /** * A lower level API used to returns all PEM objects that can be read off * from the input stream of a PEM file. *

* This method can be useful if more than one PEM object of different types * are embedded in the same PEM file. */ private static List readPEMObjects(final InputStream is) throws IOException { final BufferedReader reader = new BufferedReader(new InputStreamReader(is)); try { final List pemContents = new ArrayList(); /* * State of reading: set to true if reading content between a * begin-marker and end-marker; false otherwise. */ boolean readingContent = false; String beginMarker = null; String endMarker = null; StringBuffer sb = null; String line; while ((line = reader.readLine()) != null) { if (readingContent) { if (line.contains(endMarker)) { pemContents.add( // completed reading one PEM object new PEMObject(beginMarker, Base64.decodeBase64(sb.toString().getBytes("UTF-8")))); readingContent = false; } else { sb.append(line.trim()); } } else { if (line.contains(BEGIN_MARKER)) { readingContent = true; beginMarker = line.trim(); endMarker = beginMarker.replace("BEGIN", "END"); sb = new StringBuffer(); } } } return pemContents; } finally { try { reader.close(); } catch (IOException ignore) { //ignore } } } /** * A PEM object in a PEM file. *

* A PEM file can contain one or multiple PEM objects, each with a beginning * and ending marker. */ static class PEMObject { private final String beginMarker; private final byte[] derBytes; public PEMObject(final String beginMarker, final byte[] derBytes) { this.beginMarker = beginMarker; this.derBytes = derBytes.clone(); } public String getBeginMarker() { return beginMarker; } public byte[] getDerBytes() { return derBytes.clone(); } public PEMObjectType getPEMObjectType() { return PEMObjectType.fromBeginMarker(beginMarker); } } /** * The type of a specific PEM object in a PEM file. *

* A PEM file can contain one or multiple PEM objects, each with a beginning * and ending marker. */ static enum PEMObjectType { PRIVATE_KEY_PKCS1("-----BEGIN RSA PRIVATE KEY-----"), PRIVATE_EC_KEY_PKCS8("-----BEGIN EC PRIVATE KEY-----"), // RFC-5915 PRIVATE_KEY_PKCS8("-----BEGIN PRIVATE KEY-----"), PUBLIC_KEY_X509("-----BEGIN PUBLIC KEY-----"), CERTIFICATE_X509("-----BEGIN CERTIFICATE-----"); private final String beginMarker; public String getBeginMarker() { return beginMarker; } PEMObjectType(final String beginMarker) { this.beginMarker = beginMarker; } public static PEMObjectType fromBeginMarker(final String beginMarker) { for (PEMObjectType e : PEMObjectType.values()) { if (e.getBeginMarker().equals(beginMarker)){ return e; } } return null; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy