diff --git a/pom.xml b/pom.xml
index 17e8de0..4c7c850 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,7 +41,7 @@
3.3.9
1.8
- 1.78
+ 0.6.0
5.9.0
1.2.0
@@ -68,14 +68,9 @@
- org.bouncycastle
- bcprov-jdk18on
- ${bouncycastle.version}
-
-
- org.bouncycastle
- bcpkix-jdk18on
- ${bouncycastle.version}
+ com.hierynomus
+ asn-one
+ ${asn.one.version}
diff --git a/src/main/java/de/dentrassi/crypto/pem/PemReader.java b/src/main/java/de/dentrassi/crypto/pem/PemReader.java
new file mode 100644
index 0000000..b75aaa6
--- /dev/null
+++ b/src/main/java/de/dentrassi/crypto/pem/PemReader.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2024 Red Hat Inc and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Domenico Francesco Bruscino - initial PemReader implementation
+ */
+
+package de.dentrassi.crypto.pem;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.interfaces.ECPrivateKey;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.List;
+
+import com.hierynomus.asn1.ASN1InputStream;
+import com.hierynomus.asn1.ASN1OutputStream;
+import com.hierynomus.asn1.encodingrules.der.DERDecoder;
+import com.hierynomus.asn1.encodingrules.der.DEREncoder;
+import com.hierynomus.asn1.types.ASN1Object;
+import com.hierynomus.asn1.types.constructed.ASN1Sequence;
+import com.hierynomus.asn1.types.constructed.ASN1TaggedObject;
+import com.hierynomus.asn1.types.primitive.ASN1Integer;
+import com.hierynomus.asn1.types.primitive.ASN1ObjectIdentifier;
+import com.hierynomus.asn1.types.string.ASN1BitString;
+import com.hierynomus.asn1.types.string.ASN1OctetString;
+
+public class PemReader extends BufferedReader {
+
+ private static final String BEGIN = "-----BEGIN ";
+ private static final String CERTIFICATE = "CERTIFICATE";
+ private static final String X509_CERTIFICATE = "X509 CERTIFICATE";
+ private static final String EC_PRIVATE_KEY = "EC PRIVATE KEY";
+ private static final String EC_PUBLIC_KEY_OBJ_ID = "1.2.840.10045.2.1";
+ private static final String DSA_PRIVATE_KEY = "DSA PRIVATE KEY";
+ private static final String RSA_PRIVATE_KEY = "RSA PRIVATE KEY";
+ private static final String PRIVATE_KEY = "PRIVATE KEY";
+ private static final String END = "-----END ";
+ private static final List KEY_ALGORITHMS = Arrays.asList("RSA", "DSA", "EC");
+
+ private static List keyFactories;
+
+ private static List getKeyFactories() {
+ if (keyFactories == null) {
+ keyFactories = new ArrayList<>();
+
+ KEY_ALGORITHMS.forEach(s -> {
+ try {
+ keyFactories.add(KeyFactory.getInstance(s));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+
+ return keyFactories;
+ }
+
+ public PemReader(Reader in) {
+ super(in);
+ }
+
+ public Object readObject() throws CertificateException, IOException {
+ byte[] objectContent = null;
+ String objectType = null;
+
+ String line = readLine();
+
+ while (line != null && !line.startsWith(BEGIN)) {
+ line = readLine();
+ }
+
+ if (line != null) {
+ line = line.substring(BEGIN.length()).trim();
+ int index = line.indexOf('-');
+
+ if (index > 0 && line.endsWith("-----") && (line.length() - index) == 5) {
+ objectType = line.substring(0, index);
+
+ StringBuffer buffer = new StringBuffer();
+ String endMarker = END + objectType + "-----";
+ while ((line = readLine()) != null && line.indexOf(endMarker) != 0) {
+ if (line.indexOf(':') < 0) {
+ buffer.append(line.trim());
+ }
+ }
+ objectContent = Base64.getDecoder().decode(buffer.toString());
+ }
+ }
+
+ if (objectContent != null) {
+ if (CERTIFICATE.equals(objectType) || X509_CERTIFICATE.equals(objectType)) {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ try (ByteArrayInputStream contentInputStream = new ByteArrayInputStream(objectContent)) {
+ return certificateFactory.generateCertificate(contentInputStream);
+ }
+ } else if (EC_PRIVATE_KEY.equals(objectType)) {
+ /*
+ https://datatracker.ietf.org/doc/html/rfc5915
+ ECPrivateKey ::= SEQUENCE {
+ version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ privateKey OCTET STRING,
+ parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
+ publicKey [1] BIT STRING OPTIONAL
+ }
+ */
+ try (ASN1InputStream asn1In = new ASN1InputStream(new DERDecoder(), objectContent)) {
+ ASN1Sequence pkcs1Sequence = asn1In.readObject();
+ try (ByteArrayOutputStream pkcs8Out = new ByteArrayOutputStream()) {
+ try (ASN1OutputStream asn1Out = new ASN1OutputStream(new DEREncoder(), pkcs8Out)) {
+ List outObjects = new ArrayList<>();
+ outObjects.add(new ASN1Integer(0));
+ outObjects.add(new ASN1Sequence(Arrays.asList(new ASN1ObjectIdentifier(EC_PUBLIC_KEY_OBJ_ID), ((ASN1TaggedObject) pkcs1Sequence.get(2)).getObject())));
+ outObjects.add(new ASN1OctetString(objectContent));
+ asn1Out.writeObject(new ASN1Sequence(outObjects));
+ }
+
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8Out.toByteArray());
+ try {
+ KeyFactory ecKeyFactory = KeyFactory.getInstance("EC");
+
+ PublicKey ecPublicKey = null;
+ ECPrivateKey ecPrivateKey = (ECPrivateKey) ecKeyFactory.generatePrivate(keySpec);
+
+ if (pkcs1Sequence.size() > 3) {
+ byte[] ecPointBytes = ((ASN1BitString) ((ASN1TaggedObject) pkcs1Sequence.get(3)).getObject()).getValueBytes();
+ if (ecPointBytes[0] == 4) {
+ byte[] ecPointXBytes = new byte[32];
+ byte[] ecPointYBytes = new byte[32];
+ System.arraycopy(ecPointBytes, 1, ecPointXBytes, 0, 32);
+ System.arraycopy(ecPointBytes, 33, ecPointYBytes, 0, 32);
+ ECPoint ecPoint = new ECPoint(new BigInteger(1, ecPointXBytes), new BigInteger(1, ecPointYBytes));
+ ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(ecPoint, ecPrivateKey.getParams());
+ ecPublicKey = ecKeyFactory.generatePublic(publicKeySpec);
+ }
+ }
+
+ return new KeyPair(ecPublicKey, ecPrivateKey);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+ } else if (DSA_PRIVATE_KEY.equals(objectType)) {
+ /*
+ https://datatracker.ietf.org/doc/html/draft-woodhouse-cert-best-practice-01
+ DSAPrivateKey ::= SEQUENCE {
+ version INTEGER, -- should be zero
+ p INTEGER,
+ q INTEGER,
+ g INTEGER,
+ pub INTEGER, -- public
+ priv INTEGER, -- private
+ }
+ */
+ try (ASN1InputStream asn1InputStream = new ASN1InputStream(new DERDecoder(), objectContent)) {
+ ASN1Sequence pkcs1Sequence = asn1InputStream.readObject();
+
+ //BigInteger y, BigInteger p, BigInteger q, BigInteger g)
+ DSAPublicKeySpec publicKeySpec = new DSAPublicKeySpec(((ASN1Integer) pkcs1Sequence.get(4)).getValue(), ((ASN1Integer) pkcs1Sequence.get(1)).getValue(), ((ASN1Integer) pkcs1Sequence.get(2)).getValue(), ((ASN1Integer) pkcs1Sequence.get(3)).getValue());
+
+ //BigInteger x, BigInteger p, BigInteger q, BigInteger g
+ DSAPrivateKeySpec privateKeySpec = new DSAPrivateKeySpec(((ASN1Integer) pkcs1Sequence.get(5)).getValue(), ((ASN1Integer) pkcs1Sequence.get(1)).getValue(), ((ASN1Integer) pkcs1Sequence.get(2)).getValue(), ((ASN1Integer) pkcs1Sequence.get(3)).getValue());
+
+ try {
+ KeyFactory dsaKeyFactory = KeyFactory.getInstance("DSA");
+ PublicKey dsaPublicKey = dsaKeyFactory.generatePublic(publicKeySpec);
+ PrivateKey dsaPrivateKey = dsaKeyFactory.generatePrivate(privateKeySpec);
+ return new KeyPair(dsaPublicKey, dsaPrivateKey);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new IOException(e);
+ }
+ }
+ } else if (RSA_PRIVATE_KEY.equals(objectType)) {
+ /*
+ https://datatracker.ietf.org/doc/html/rfc8017
+ RSAPrivateKey ::= SEQUENCE {
+ version Version,
+ modulus INTEGER, -- n
+ publicExponent INTEGER, -- e
+ privateExponent INTEGER, -- d
+ prime1 INTEGER, -- p
+ prime2 INTEGER, -- q
+ exponent1 INTEGER, -- d mod (p-1)
+ exponent2 INTEGER, -- d mod (q-1)
+ coefficient INTEGER, -- (inverse of q) mod p
+ otherPrimeInfos OtherPrimeInfos OPTIONAL
+ }
+ */
+ try (ASN1InputStream asn1InputStream = new ASN1InputStream(new DERDecoder(), objectContent)) {
+ ASN1Sequence pkcs1Sequence = asn1InputStream.readObject();
+
+ //BigInteger modulus, BigInteger publicExponent
+ RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(((ASN1Integer) pkcs1Sequence.get(1)).getValue(), ((ASN1Integer) pkcs1Sequence.get(2)).getValue());
+
+ //BigInteger modulus, BigInteger publicExponent, BigInteger privateExponent, BigInteger primeP, BigInteger primeQ, BigInteger primeExponentP, BigInteger primeExponentQ, BigInteger crtCoefficient
+ RSAPrivateCrtKeySpec privateKeySpec = new RSAPrivateCrtKeySpec(((ASN1Integer) pkcs1Sequence.get(1)).getValue(), ((ASN1Integer) pkcs1Sequence.get(2)).getValue(), ((ASN1Integer) pkcs1Sequence.get(3)).getValue(), ((ASN1Integer) pkcs1Sequence.get(4)).getValue(), ((ASN1Integer) pkcs1Sequence.get(5)).getValue(), ((ASN1Integer) pkcs1Sequence.get(6)).getValue(), ((ASN1Integer) pkcs1Sequence.get(7)).getValue(), ((ASN1Integer) pkcs1Sequence.get(8)).getValue());
+
+ try {
+ KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
+ PublicKey rsaPublicKey = rsaKeyFactory.generatePublic(publicKeySpec);
+ PrivateKey rsaPrivateKey = rsaKeyFactory.generatePrivate(privateKeySpec);
+ return new KeyPair(rsaPublicKey, rsaPrivateKey);
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
+ throw new IOException(e);
+ }
+ }
+ } else if (PRIVATE_KEY.equals(objectType)) {
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(objectContent);
+
+ InvalidKeySpecException firstException = null;
+ for (KeyFactory factory : getKeyFactories()) {
+ try {
+ return factory.generatePrivate(keySpec);
+ } catch (InvalidKeySpecException e) {
+ if (firstException == null)
+ firstException = e;
+ }
+ }
+ throw new IOException("Private key could not be loaded", firstException);
+ } else {
+ throw new IOException("Invalid object: " + objectType);
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/de/dentrassi/crypto/pem/PemUtils.java b/src/main/java/de/dentrassi/crypto/pem/PemUtils.java
index 93406cc..a3b46fa 100644
--- a/src/main/java/de/dentrassi/crypto/pem/PemUtils.java
+++ b/src/main/java/de/dentrassi/crypto/pem/PemUtils.java
@@ -11,31 +11,23 @@
package de.dentrassi.crypto.pem;
-import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.Key;
+import java.security.KeyPair;
+import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMKeyPair;
-import org.bouncycastle.openssl.PEMParser;
-import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
-
import de.dentrassi.crypto.pem.AbstractPemKeyStore.Entry;
public class PemUtils {
@@ -85,27 +77,18 @@ private static InputStream openResource(final String uri) throws IOException {
private static void loadFrom(final Map result, final String alias, final boolean chained,
final InputStream stream) throws CertificateException, IOException {
- final CertificateFactory factory = CertificateFactory.getInstance("X.509");
- final JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(new BouncyCastleProvider());
-
- @SuppressWarnings("resource")
- final PEMParser reader = new PEMParser(new InputStreamReader(stream, StandardCharsets.UTF_8));
-
final List chain = new ArrayList<>();
Key key = null;
int counter = 0;
Object object;
- while ((object = reader.readObject()) != null) {
-
- if (object instanceof X509CertificateHolder) {
+ try (PemReader pemReader = new PemReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) {
+ while ((object = pemReader.readObject()) != null) {
- final X509CertificateHolder certHolder = (X509CertificateHolder) object;
+ if (object instanceof Certificate) {
- final Collection extends Certificate> certs = factory
- .generateCertificates(new ByteArrayInputStream(certHolder.getEncoded()));
+ final Certificate cert = (Certificate)object;
- for (final Certificate cert : certs) {
if (chained) {
if (cert instanceof X509Certificate) {
chain.add(cert);
@@ -113,16 +96,16 @@ private static void loadFrom(final Map result, final String alias
} else {
result.put(alias + "-" + counter++, new Entry(null, new Certificate[] { cert }));
}
- }
- } else if (object instanceof PEMKeyPair) {
+ } else if (object instanceof KeyPair) {
- key = converter.getKeyPair((PEMKeyPair) object).getPrivate();
+ key = ((KeyPair)object).getPrivate();
- } else if (object instanceof PrivateKeyInfo) {
+ } else if (object instanceof PrivateKey) {
- key = converter.getPrivateKey((PrivateKeyInfo) object);
+ key = (PrivateKey)object;
+ }
}
}
@@ -140,5 +123,4 @@ private static void loadFrom(final Map result, final String alias
});
}
-
}
diff --git a/src/test/java/de/dentrassi/crypto/pem/PemReaderTest.java b/src/test/java/de/dentrassi/crypto/pem/PemReaderTest.java
new file mode 100644
index 0000000..f03953b
--- /dev/null
+++ b/src/test/java/de/dentrassi/crypto/pem/PemReaderTest.java
@@ -0,0 +1,127 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Red Hat Inc and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Domenico Francesco Bruscino - initial PemReader implementation
+ *******************************************************************************/
+
+package de.dentrassi.crypto.pem;
+
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXCertPathValidatorResult;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Assumptions;
+import org.junit.jupiter.api.Test;
+
+public class PemReaderTest {
+
+ private static final String TEST_TEXT = "TEST";
+
+ @Test
+ public void testCertificate() throws Exception {
+ try (PemReader pemReader = new PemReader(new InputStreamReader(PemReaderTest.class.getResourceAsStream("/test1.crt")))) {
+ Certificate certificate = (Certificate) pemReader.readObject();
+ Assertions.assertTrue(certificate instanceof X509Certificate);
+ Assertions.assertEquals("CN=Test 1", ((X509Certificate)certificate).getSubjectX500Principal().getName());
+ }
+ }
+
+ @Test
+ public void testCertificateChain() throws Exception {
+ try (PemReader pemReader = new PemReader(new InputStreamReader(PemReaderTest.class.getResourceAsStream("/tls.crt")))) {
+ Certificate cert1 = (Certificate) pemReader.readObject();
+ Assertions.assertTrue(cert1 instanceof X509Certificate);
+ Assertions.assertEquals("CN=Test 1", ((X509Certificate)cert1).getSubjectX500Principal().getName());
+
+ Certificate cert2 = (Certificate) pemReader.readObject();
+ Assertions.assertTrue(cert2 instanceof X509Certificate);
+ Assertions.assertEquals("CN=Intermediate", ((X509Certificate)cert2).getSubjectX500Principal().getName());
+
+ Certificate cert3 = (Certificate) pemReader.readObject();
+ Assertions.assertTrue(cert3 instanceof X509Certificate);
+ Assertions.assertEquals("CN=CA", ((X509Certificate)cert3).getSubjectX500Principal().getName());
+
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ CertPath certPath = certificateFactory.generateCertPath(Arrays.asList(cert1, cert2));
+ TrustAnchor anchor = new TrustAnchor((X509Certificate)cert3, null);
+
+ CertPathValidator validator = CertPathValidator.getInstance("PKIX");
+ PKIXParameters params = new PKIXParameters(Collections.singleton(anchor));
+ params.setRevocationEnabled(false);
+ PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(certPath, params);
+ Assertions.assertEquals(cert3, result.getTrustAnchor().getTrustedCert());
+ }
+ }
+
+ @Test
+ public void testECPrivateKey() throws Exception {
+ try (PemReader pemReader = new PemReader(new InputStreamReader(PemReaderTest.class.getResourceAsStream("/ec-private-key.pem")))) {
+ KeyPair keyPair = (KeyPair) pemReader.readObject();
+ testSignature("SHA512withECDSA", keyPair);
+ }
+ }
+
+ @Test
+ public void testDSAPrivateKey() throws Exception {
+ try (PemReader pemReader = new PemReader(new InputStreamReader(PemReaderTest.class.getResourceAsStream("/dsa-private-key.pem")))) {
+ KeyPair keyPair = (KeyPair) pemReader.readObject();
+ testSignature("SHA512WithDSA", keyPair);
+ }
+ }
+
+ @Test
+ public void testRSAPrivateKey() throws Exception {
+ try (PemReader pemReader = new PemReader(new InputStreamReader(PemReaderTest.class.getResourceAsStream("/privkey1.pem")))) {
+ KeyPair keyPair = (KeyPair) pemReader.readObject();
+ testSignature("SHA512WithRSA", keyPair);
+ }
+ }
+
+ @Test
+ public void testPrivateKey() throws Exception {
+ try (PemReader pemReader = new PemReader(new InputStreamReader(PemReaderTest.class.getResourceAsStream("/private-key.pem")))) {
+ Assertions.assertTrue(pemReader.readObject() instanceof PrivateKey);
+ }
+ }
+
+ private void testSignature(String algorithm, KeyPair keyPair) throws Exception {
+ Assumptions.assumeTrue(() -> {
+ try {
+ Signature.getInstance(algorithm);
+ } catch (Throwable t) {
+ return t == null;
+ }
+ return true;
+ }, "Cannot find any provider supporting " + algorithm);
+
+ byte[] messageBytes = TEST_TEXT.getBytes(StandardCharsets.UTF_8);
+
+ Signature signer = Signature.getInstance(algorithm);
+ signer.initSign(keyPair.getPrivate());
+ signer.update(messageBytes);
+ byte[] signature = signer.sign();
+
+ Signature verifier = Signature.getInstance(algorithm);
+ verifier.initVerify(keyPair.getPublic());
+ verifier.update(messageBytes);
+ Assertions.assertTrue(verifier.verify(signature));
+ }
+}
diff --git a/src/test/resources/dsa-private-key.pem b/src/test/resources/dsa-private-key.pem
new file mode 100644
index 0000000..96fc204
--- /dev/null
+++ b/src/test/resources/dsa-private-key.pem
@@ -0,0 +1,20 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIDTgIBAAKCAQEAkhV3fPm+yC3TRzGbnCTqKvpNWcQ+ZHx9aBv9Iw8wtH+z5POC
+p09AQt+SK6MHm9PrLsfeSB80guAGni55LTFFlEWscrBoE9pHtTdqz7I/mvTt7caz
+9YlJrByJ1pXj1Dunaswbf9gTLsPjIZDKItaQagKQl8QV3kAXVcvp7yI4aaml9b2V
+3xpvLuUIL21mVvjyidYo1UWGXAxc3AJLGkCUA+cr9F7BqCGkKKBxTGrzy8WT/ki9
+CEKeE52kND35DKml7x5B0eU0JjwVTwaIytCooEibwEnirnarOsasBFPssmD+21a6
+2wG/pdgbfydMIEXmquZetddCTTZQvki61cJaDQIdAPdQXDy4ZxLD8023NFPW/yw6
+uP5NGrhUTyc/lNUCggEAJOYkLEMD3MK7PNFoKh4fwiTpmRieSyPH33EiskEFFBxy
+mD8EjcP89qiKZFxItAZV2A9JHVGHWnt95+simIMN1rS563HlXzOWUg1u2K2TTQF9
+H4MsFKTHgl1YEs1MwjsBSn/k9cipOjR99o/xTXwouqtx8TrpCEndNOyJmmg98QG1
+cEKcgELr8QdEek9DJCX0PDHEDu+BScS/Sp6CmMeFH+3GuxzJ1vbGLb83I+ELq2dr
+kuid8LsKvqPVLrdVGaniLxWlb7jiqcXg1g6U1xF8lO33T9fT7zRFwagzvyEghsHW
+Gx2ia6dLm4xiAT/n82kyUTkzRyH4PZAM6TwcIclP+gKCAQAE55vvsLs+jxj77QIA
+Bi5Qx857q+P2aejyOsdkMYmbqTbbs6VBSWD2G0BnodXsxj907oNBmEoQJJtktmpe
+9acXlAuHKhxXkan7HpUylc9oV3MYPEVnEfG/kSvHLs7Uk/Ca/ZNQ8iTBbAujpDKZ
+8wuQ6Kl72ryIDPKgM5DsuRcBDhzu08tZ3how3HwGVKqwnaEDQXvImeeitwH87nqr
+M4YJ3T+8wiR+u/ZBIoiJHEWpFDcE5rhduafhLv38qm0vA632mD8sJMeMd47VSK/c
+UnbOpmbAexSdj6ZfoiWH4Yp+ReQwXtsiK10GYsQDq+QtGMiLUlfyJOHFWSTHNdnA
+b4eIAh0A9X6697lJYHsw4cJKr7LtcBIWQsVa+VDjH+hvzQ==
+-----END DSA PRIVATE KEY-----
diff --git a/src/test/resources/ec-private-key.pem b/src/test/resources/ec-private-key.pem
new file mode 100644
index 0000000..bf2bbd0
--- /dev/null
+++ b/src/test/resources/ec-private-key.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPeF+duprCstov+goD+vp+YEjEBpGmmhamtwAqFigMWvoAoGCCqGSM49
+AwEHoUQDQgAErq7/AhOEzWTkBzJO5zoccBNBMof0q4z/CD8QkCqzRABUPVZmy7GG
++g16Btr7WEEe1WyxzaQod6mB8Om4VauFlg==
+-----END EC PRIVATE KEY-----
diff --git a/src/test/resources/private-key.pem b/src/test/resources/private-key.pem
new file mode 100644
index 0000000..7d875f0
--- /dev/null
+++ b/src/test/resources/private-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrPo05c9I1Qla1
+SQ+C3/w3WGPtLTXMUzAs5TCGVs/CMuXJZ3Hv/G1U1UPNCpS76xx6esY57pNBubRO
+KuMosU+uz5ckgJJsr/mvbGioh6CTLQnZzHCFYJPEGCACcFXSL7cQ21ze+2Z7fq7E
+dOHuLJIj8OFaLsE5DZMeUAkNmrw18cTCUeVNF633B46HZMiL/WSUcI6Y835kYbd0
+XMTF3G3gWEI374v/w67P7dmJSQcTPaBFqvBX6to8b9dSW/+jl/bVVkHvnD47unp1
+Q534Si7O5RKF7SDZP7US+VKicmSoo8njhjzzznE8zzHY32KzwB991TUkHhxknvkQ
+97ZLmhV3AgMBAAECggEACso6fYSJUNASIEvTpGDgD83VIFM9zESnUd8nFrkFmRoV
+nstuHruQxt7FyXemETtziMePpDrVjjd9ll4oSXobzKaSwpZT7LpotpZl0vHNX4HN
+j6rMofGtVLs2IkN9xNoWiN7OJmat+ZBawix8WjAbDTlEmcuGrCDE1uhoG+lWoOUi
+6rbMzh2RVVJGksjv4QWVyKnmIJqhyELIFjfTV/QcucBjGUDEZZJvUy/n6PQyJe1y
+60gk3KoBPlU885WkgDLLgzsiCvfUioLGPNYh58Smz1U/db9pTn6G6ymZfIyl8J49
+Ojlty+ckYCTcRIiN8VMOfJ6W8hu2nfV7AKTOYeNtcQKBgQDvr8o/KAOUpZ6MBFmR
+0OQrfw2l1yqOPWdTsHQEPzzP1+rNDZrfQ7VIuhMfUfDpvU7ZSNj97h3O64Gdr0Ik
+7uEgdlpyjKJbdIf9sjQA6oHbVOMJu0F6ICNtFOAGuWXbF4LYQae7PNtr7cNabWQ6
+YY/yN5L62seyKCW9jKCtIOj86wKBgQC25j+Nm/pKWC7UmyC6lf0LoqVsj4FESOp2
+VkGJeJx1MjajPYoeHArWCN2ZhMJkg9DKYOayeVELS5G4o3Qu51hSv0hI8RwaAKVp
+0N8fox5EYuzD+D5+fGbsS8od0B53tHjO0OiVeF2dkzLRruEwGrildOrzBVnpnCsq
+qiH+bOC2pQKBgQDcZ9FaHEiWo2nVJZbcALQgz1fUfbTTUeG3UpaM5T3dfpaa8vzM
+e+2zL/Cvp2Ea/4sHQfbQIuvkQCpTvzrazZPVjyADIBGYeeMnxnwNr5e0Ai4436oJ
+TI5nG4Aajtf8DXWzuUQtaHv5lo1ClT9KdDazLKmK5i4mRMbXs/541b+J+wKBgC4a
+jBqeHtFuuR9Om0ltVYQBU8GxytvzpWZ/B7YneQjxx2QOtyov12tsgK+aD4ZW0+Kv
+2ndSrWMzgWARk8/e8RyqqwX4ASVs1EWAKT5IV/DVHumnQdmQckFOOXyaYZO7Ili/
+wGEtHiwCVuUUOB9wHOtYNYFc7/U7MIZorHj96QchAoGBALEa1MaxarxluZa5lM7x
+R5GE4btBEgwXO3V01tN2swlJQHv9mvRfV5YM3CrNRNdlkWE7Aj6UTJkw39e/TVxL
++BHqW3Ui1/7yuoLAWIznItHQhCrMN1nNt7gKxI1gB/3zP8idLM/5QC1Gq+14zu/B
+zZ3Z6ERLM4huRkX4mNu8XHI4
+-----END PRIVATE KEY-----