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.PublicKey; 025import java.security.SecureRandom; 026 027import org.bouncycastle.jce.ECNamedCurveTable; 028import org.bouncycastle.jce.provider.BouncyCastleProvider; 029import org.bouncycastle.openssl.PEMException; 030import org.bouncycastle.openssl.PEMKeyPair; 031import org.bouncycastle.openssl.PEMParser; 032import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; 033import org.bouncycastle.openssl.jcajce.JcaPEMWriter; 034 035/** 036 * Utility class offering convenience methods for {@link KeyPair}. 037 * <p> 038 * Requires {@code Bouncy Castle}. 039 */ 040public class KeyPairUtils { 041 042 private KeyPairUtils() { 043 // utility class without constructor 044 } 045 046 /** 047 * Creates a new standard {@link KeyPair}. 048 * <p> 049 * This method can be used if no specific key type is required. It returns a 050 * "secp384r1" ECDSA key pair. 051 * 052 * @return Generated {@link KeyPair} 053 * @since 2.8 054 */ 055 public static KeyPair createKeyPair() { 056 return createECKeyPair("secp384r1"); 057 } 058 059 /** 060 * Creates a new RSA {@link KeyPair}. 061 * 062 * @param keysize 063 * Key size 064 * @return Generated {@link KeyPair} 065 */ 066 public static KeyPair createKeyPair(int keysize) { 067 try { 068 var keyGen = KeyPairGenerator.getInstance("RSA"); 069 keyGen.initialize(keysize); 070 return keyGen.generateKeyPair(); 071 } catch (NoSuchAlgorithmException ex) { 072 throw new IllegalStateException(ex); 073 } 074 } 075 076 /** 077 * Creates a new elliptic curve {@link KeyPair}. 078 * 079 * @param name 080 * ECDSA curve name (e.g. "secp256r1") 081 * @return Generated {@link KeyPair} 082 */ 083 public static KeyPair createECKeyPair(String name) { 084 try { 085 var ecSpec = ECNamedCurveTable.getParameterSpec(name); 086 var g = KeyPairGenerator.getInstance("ECDSA", BouncyCastleProvider.PROVIDER_NAME); 087 g.initialize(ecSpec, new SecureRandom()); 088 return g.generateKeyPair(); 089 } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException ex) { 090 throw new IllegalArgumentException("Invalid curve name " + name, ex); 091 } catch (NoSuchProviderException ex) { 092 throw new IllegalStateException(ex); 093 } 094 } 095 096 /** 097 * Reads a {@link KeyPair} from a PEM file. 098 * 099 * @param r 100 * {@link Reader} to read the PEM file from. The {@link Reader} is closed 101 * after use. 102 * @return {@link KeyPair} read 103 */ 104 public static KeyPair readKeyPair(Reader r) throws IOException { 105 try (var parser = new PEMParser(r)) { 106 var keyPair = (PEMKeyPair) parser.readObject(); 107 return new JcaPEMKeyConverter().getKeyPair(keyPair); 108 } catch (PEMException ex) { 109 throw new IOException("Invalid PEM file", ex); 110 } 111 } 112 113 /** 114 * Writes a {@link KeyPair} PEM file. 115 * 116 * @param keypair 117 * {@link KeyPair} to write 118 * @param w 119 * {@link Writer} to write the PEM file to. The {@link Writer} is closed 120 * after use. 121 */ 122 public static void writeKeyPair(KeyPair keypair, Writer w) throws IOException { 123 try (var jw = new JcaPEMWriter(w)) { 124 jw.writeObject(keypair); 125 } 126 } 127 128 /** 129 * Writes a {@link PublicKey} as PEM file. 130 * 131 * @param key 132 * {@link PublicKey} 133 * @param w 134 * {@link Writer} to write the PEM file to. The {@link Writer} is closed 135 * after use. 136 * @since 3.0.0 137 */ 138 public static void writePublicKey(PublicKey key, Writer w) throws IOException { 139 try (var jw = new JcaPEMWriter(w)) { 140 jw.writeObject(key); 141 } 142 } 143 144}