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 javax.annotation.ParametersAreNonnullByDefault;
027import javax.annotation.WillClose;
028
029import org.bouncycastle.jce.ECNamedCurveTable;
030import org.bouncycastle.jce.spec.ECParameterSpec;
031import org.bouncycastle.openssl.PEMException;
032import org.bouncycastle.openssl.PEMKeyPair;
033import org.bouncycastle.openssl.PEMParser;
034import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
035import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
036
037/**
038 * Utility class offering convenience methods for {@link KeyPair}.
039 * <p>
040 * Requires {@code Bouncy Castle}. This class is part of the {@code acme4j-utils} module.
041 */
042@ParametersAreNonnullByDefault
043public class KeyPairUtils {
044
045    private KeyPairUtils() {
046        // utility class without constructor
047    }
048
049    /**
050     * Creates a new RSA {@link KeyPair}.
051     *
052     * @param keysize
053     *            Key size
054     * @return Generated {@link KeyPair}
055     */
056    public static KeyPair createKeyPair(int keysize) {
057        try {
058            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
059            keyGen.initialize(keysize);
060            return keyGen.generateKeyPair();
061        } catch (NoSuchAlgorithmException ex) {
062            throw new IllegalStateException(ex);
063        }
064    }
065
066    /**
067     * Creates a new elliptic curve {@link KeyPair}.
068     *
069     * @param name
070     *            ECDSA curve name (e.g. "secp256r1")
071     * @return Generated {@link KeyPair}
072     */
073    public static KeyPair createECKeyPair(String name) {
074        try {
075            ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(name);
076            KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
077            g.initialize(ecSpec, new SecureRandom());
078            return g.generateKeyPair();
079        } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException ex) {
080            throw new IllegalArgumentException("Invalid curve name " + name, ex);
081        } catch (NoSuchProviderException ex) {
082            throw new IllegalStateException(ex);
083        }
084    }
085
086    /**
087     * Reads a {@link KeyPair} from a PEM file.
088     *
089     * @param r
090     *            {@link Reader} to read the PEM file from. The {@link Reader} is closed
091     *            after use.
092     * @return {@link KeyPair} read
093     */
094    public static KeyPair readKeyPair(@WillClose Reader r) throws IOException {
095        try (PEMParser parser = new PEMParser(r)) {
096            PEMKeyPair keyPair = (PEMKeyPair) parser.readObject();
097            return new JcaPEMKeyConverter().getKeyPair(keyPair);
098        } catch (PEMException ex) {
099            throw new IOException("Invalid PEM file", ex);
100        }
101    }
102
103    /**
104     * Writes a {@link KeyPair} PEM file.
105     *
106     * @param keypair
107     *            {@link KeyPair} to write
108     * @param w
109     *            {@link Writer} to write the PEM file to. The {@link Writer} is closed
110     *            after use.
111     */
112    public static void writeKeyPair(KeyPair keypair, @WillClose Writer w) throws IOException {
113        try (JcaPEMWriter jw = new JcaPEMWriter(w)) {
114            jw.writeObject(keypair);
115        }
116    }
117
118}