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 static org.assertj.core.api.Assertions.assertThat;
017
018import java.io.IOException;
019import java.io.StringReader;
020import java.io.StringWriter;
021import java.lang.reflect.Modifier;
022import java.security.KeyPair;
023import java.security.Security;
024import java.security.interfaces.ECPublicKey;
025import java.security.interfaces.RSAPublicKey;
026
027import org.bouncycastle.jce.provider.BouncyCastleProvider;
028import org.junit.jupiter.api.BeforeAll;
029import org.junit.jupiter.api.Test;
030
031/**
032 * Unit tests for {@link KeyPairUtils}.
033 */
034public class KeyPairUtilsTest {
035    private static final int KEY_SIZE = 2048;
036    private static final String EC_CURVE = "secp256r1";
037
038    @BeforeAll
039    public static void setup() {
040        Security.addProvider(new BouncyCastleProvider());
041    }
042
043    /**
044     * Test that standard keypair generates a secure key pair.
045     */
046    @Test
047    public void testCreateStandardKeyPair() {
048        var pair = KeyPairUtils.createKeyPair();
049        assertThat(pair).isNotNull();
050        assertThat(pair.getPublic()).isInstanceOf(ECPublicKey.class);
051        var pk = (ECPublicKey) pair.getPublic();
052        assertThat(pk.getAlgorithm()).isEqualTo("ECDSA");
053        assertThat(pk.getParams().getCurve().getField().getFieldSize()).isEqualTo(384);
054    }
055
056    /**
057     * Test that RSA keypairs of the correct size are generated.
058     */
059    @Test
060    public void testCreateKeyPair() {
061        var pair = KeyPairUtils.createKeyPair(KEY_SIZE);
062        assertThat(pair).isNotNull();
063        assertThat(pair.getPublic()).isInstanceOf(RSAPublicKey.class);
064
065        var pub = (RSAPublicKey) pair.getPublic();
066        assertThat(pub.getModulus().bitLength()).isEqualTo(KEY_SIZE);
067    }
068
069    /**
070     * Test that reading and writing keypairs work correctly.
071     */
072    @Test
073    public void testWriteAndRead() throws IOException {
074        // Generate a test keypair
075        var pair = KeyPairUtils.createKeyPair(KEY_SIZE);
076
077        // Write keypair to PEM
078        String pem;
079        try (var out = new StringWriter()) {
080            KeyPairUtils.writeKeyPair(pair, out);
081            pem = out.toString();
082        }
083
084        // Make sure PEM file is properly formatted
085        assertThat(pem).matches(
086                  "-----BEGIN RSA PRIVATE KEY-----[\\r\\n]+"
087                + "([a-zA-Z0-9/+=]+[\\r\\n]+)+"
088                + "-----END RSA PRIVATE KEY-----[\\r\\n]*");
089
090        // Read keypair from PEM
091        KeyPair readPair;
092        try (var in = new StringReader(pem)) {
093            readPair = KeyPairUtils.readKeyPair(in);
094        }
095
096        // Verify that both keypairs are the same
097        assertThat(pair).isNotSameAs(readPair);
098        assertThat(pair.getPublic().getEncoded()).isEqualTo(readPair.getPublic().getEncoded());
099        assertThat(pair.getPrivate().getEncoded()).isEqualTo(readPair.getPrivate().getEncoded());
100    }
101
102    /**
103     * Test that ECDSA keypairs are generated.
104     */
105    @Test
106    public void testCreateECCKeyPair() {
107        var pair = KeyPairUtils.createECKeyPair(EC_CURVE);
108        assertThat(pair).isNotNull();
109        assertThat(pair.getPublic()).isInstanceOf(ECPublicKey.class);
110    }
111
112    /**
113     * Test that reading and writing ECDSA keypairs work correctly.
114     */
115    @Test
116    public void testWriteAndReadEC() throws IOException {
117        // Generate a test keypair
118        var pair = KeyPairUtils.createECKeyPair(EC_CURVE);
119
120        // Write keypair to PEM
121        String pem;
122        try (var out = new StringWriter()) {
123            KeyPairUtils.writeKeyPair(pair, out);
124            pem = out.toString();
125        }
126
127        // Make sure PEM file is properly formatted
128        assertThat(pem).matches(
129                  "-----BEGIN EC PRIVATE KEY-----[\\r\\n]+"
130                + "([a-zA-Z0-9/+=]+[\\r\\n]+)+"
131                + "-----END EC PRIVATE KEY-----[\\r\\n]*");
132
133        // Read keypair from PEM
134        KeyPair readPair;
135        try (var in = new StringReader(pem)) {
136            readPair = KeyPairUtils.readKeyPair(in);
137        }
138
139        // Verify that both keypairs are the same
140        assertThat(pair).isNotSameAs(readPair);
141        assertThat(pair.getPublic().getEncoded()).isEqualTo(readPair.getPublic().getEncoded());
142        assertThat(pair.getPrivate().getEncoded()).isEqualTo(readPair.getPrivate().getEncoded());
143
144        // Write Public Key
145        String publicPem;
146        try (var out = new StringWriter()) {
147            KeyPairUtils.writePublicKey(pair.getPublic(), out);
148            publicPem = out.toString();
149        }
150
151        // Make sure PEM file is properly formatted
152        assertThat(publicPem).matches(
153                  "-----BEGIN PUBLIC KEY-----[\\r\\n]+"
154                + "([a-zA-Z0-9/+=]+[\\r\\n]+)+"
155                + "-----END PUBLIC KEY-----[\\r\\n]*");
156    }
157
158    /**
159     * Test that constructor is private.
160     */
161    @Test
162    public void testPrivateConstructor() throws Exception {
163        var constructor = KeyPairUtils.class.getDeclaredConstructor();
164        assertThat(Modifier.isPrivate(constructor.getModifiers())).isTrue();
165        constructor.setAccessible(true);
166        constructor.newInstance();
167    }
168
169}