001/*
002 * acme4j - Java ACME client
003 *
004 * Copyright (C) 2015 Richard "Shred" Körber
005 *   http://acme4j.shredzone.org
006 *
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
013 */
014package org.shredzone.acme4j.util;
015
016import java.io.IOException;
017import java.io.Reader;
018import java.io.Writer;
019import java.security.InvalidAlgorithmParameterException;
020import java.security.KeyPair;
021import java.security.KeyPairGenerator;
022import java.security.NoSuchAlgorithmException;
023import java.security.NoSuchProviderException;
024import java.security.SecureRandom;
025
026import org.bouncycastle.jce.ECNamedCurveTable;
027import org.bouncycastle.jce.spec.ECParameterSpec;
028import org.bouncycastle.openssl.PEMException;
029import org.bouncycastle.openssl.PEMKeyPair;
030import org.bouncycastle.openssl.PEMParser;
031import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
032import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
033
034/**
035 * Utility class offering convenience methods for {@link KeyPair}.
036 * <p>
037 * Requires {@code Bouncy Castle}. This class is part of the {@code acme4j-utils} module.
038 */
039public class KeyPairUtils {
040
041    private KeyPairUtils() {
042        // utility class without constructor
043    }
044
045    /**
046     * Creates a new standard {@link KeyPair}.
047     * <p>
048     * This method can be used if no specific key type is required. It returns a
049     * "secp384r1" ECDSA key pair.
050     *
051     * @return Generated {@link KeyPair}
052     * @since 2.8
053     */
054    public static KeyPair createKeyPair() {
055        return createECKeyPair("secp384r1");
056    }
057
058    /**
059     * Creates a new RSA {@link KeyPair}.
060     *
061     * @param keysize
062     *            Key size
063     * @return Generated {@link KeyPair}
064     */
065    public static KeyPair createKeyPair(int keysize) {
066        try {
067            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
068            keyGen.initialize(keysize);
069            return keyGen.generateKeyPair();
070        } catch (NoSuchAlgorithmException ex) {
071            throw new IllegalStateException(ex);
072        }
073    }
074
075    /**
076     * Creates a new elliptic curve {@link KeyPair}.
077     *
078     * @param name
079     *            ECDSA curve name (e.g. "secp256r1")
080     * @return Generated {@link KeyPair}
081     */
082    public static KeyPair createECKeyPair(String name) {
083        try {
084            ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(name);
085            KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
086            g.initialize(ecSpec, new SecureRandom());
087            return g.generateKeyPair();
088        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException ex) {
089            throw new IllegalArgumentException("Invalid curve name " + name, ex);
090        } catch (NoSuchProviderException ex) {
091            throw new IllegalStateException(ex);
092        }
093    }
094
095    /**
096     * Reads a {@link KeyPair} from a PEM file.
097     *
098     * @param r
099     *            {@link Reader} to read the PEM file from. The {@link Reader} is closed
100     *            after use.
101     * @return {@link KeyPair} read
102     */
103    public static KeyPair readKeyPair(Reader r) throws IOException {
104        try (PEMParser parser = new PEMParser(r)) {
105            PEMKeyPair keyPair = (PEMKeyPair) parser.readObject();
106            return new JcaPEMKeyConverter().getKeyPair(keyPair);
107        } catch (PEMException ex) {
108            throw new IOException("Invalid PEM file", ex);
109        }
110    }
111
112    /**
113     * Writes a {@link KeyPair} PEM file.
114     *
115     * @param keypair
116     *            {@link KeyPair} to write
117     * @param w
118     *            {@link Writer} to write the PEM file to. The {@link Writer} is closed
119     *            after use.
120     */
121    public static void writeKeyPair(KeyPair keypair, Writer w) throws IOException {
122        try (JcaPEMWriter jw = new JcaPEMWriter(w)) {
123            jw.writeObject(keypair);
124        }
125    }
126
127}