Skip to content

Commit

Permalink
Merge pull request #4 from alvarolobato/BC_1_54
Browse files Browse the repository at this point in the history
[JENKINS-35291] New version for BC 1.54
  • Loading branch information
alvarolobato committed Jun 8, 2016
2 parents 5e55fa4 + c89b51c commit 97f7458
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 63 deletions.
6 changes: 3 additions & 3 deletions pom.xml
Expand Up @@ -33,7 +33,7 @@
</parent>

<artifactId>bouncycastle-api</artifactId>
<version>1.1-SNAPSHOT</version>
<version>1.648-SNAPSHOT</version>
<packaging>hpi</packaging>

<name>bouncycastle API Plugin</name>
Expand Down Expand Up @@ -65,7 +65,7 @@
</scm>

<properties>
<jenkins.version>1.609</jenkins.version>
<jenkins.version>1.648</jenkins.version>
</properties>

<repositories>
Expand All @@ -86,7 +86,7 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.47</version>
<version>1.54</version>
<scope>provided</scope>
</dependency>
</dependencies>
Expand Down
88 changes: 67 additions & 21 deletions src/main/java/jenkins/bouncycastle/api/PEMEncodable.java
Expand Up @@ -37,17 +37,32 @@
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.openssl.PasswordException;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.util.encoders.Base64;

/**
Expand Down Expand Up @@ -128,23 +143,51 @@ public static PEMEncodable decode(@Nonnull String pem) throws IOException, Unrec
@Nonnull
public static PEMEncodable decode(@Nonnull String pem, @Nullable final char[] passphrase)
throws IOException, UnrecoverableKeyException {
PasswordFinder pwf = null;
if (passphrase != null) {
pwf = new PasswordFinder() {
@Override
public char[] getPassword() {
return passphrase;
}
};
}

PEMReader parser = new PEMReader(new StringReader(pem), pwf);
try {
return new PEMEncodable(parser.readObject());
} catch (PasswordException pwE) {
try (PEMParser parser = new PEMParser(new StringReader(pem));) {

Object object = parser.readObject();

JcaPEMKeyConverter kConv = new JcaPEMKeyConverter().setProvider("BC");

// handle supported PEM formats.
if (object instanceof PEMEncryptedKeyPair) {
if (passphrase != null) {
PEMDecryptorProvider dp = new JcePEMDecryptorProviderBuilder().build(passphrase);
PEMEncryptedKeyPair ekp = (PEMEncryptedKeyPair) object;
return new PEMEncodable(kConv.getKeyPair(ekp.decryptKeyPair(dp)));
} else {
throw new UnrecoverableKeyException();
}
} else if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
if (passphrase != null) {
InputDecryptorProvider dp = new JceOpenSSLPKCS8DecryptorProviderBuilder().build(passphrase);
PKCS8EncryptedPrivateKeyInfo epk = (PKCS8EncryptedPrivateKeyInfo) object;
return new PEMEncodable(kConv.getPrivateKey(epk.decryptPrivateKeyInfo(dp)));
} else {
throw new UnrecoverableKeyException();
}
} else if (object instanceof PEMKeyPair) {
return new PEMEncodable(kConv.getKeyPair((PEMKeyPair) object));
} else if (object instanceof PrivateKeyInfo) {
return new PEMEncodable(kConv.getPrivateKey((PrivateKeyInfo) object));
} else if (object instanceof SubjectPublicKeyInfo) {
return new PEMEncodable(kConv.getPublicKey((SubjectPublicKeyInfo) object));
} else if (object instanceof X509CertificateHolder) {
JcaX509CertificateConverter cConv = new JcaX509CertificateConverter().setProvider("BC");
return new PEMEncodable(cConv.getCertificate((X509CertificateHolder) object));
} else {
throw new IOException(
"Could not parse PEM, only key pairs, private keys, public keys and certificates are supported. Received "
+ object.getClass().getName());
}
} catch (OperatorCreationException e) {
throw new IOException(e.getMessage(), e);
} catch (PKCSException e) {
LOGGER.log(Level.WARNING, "Could not read PEM encrypted information", e);
throw new UnrecoverableKeyException();
} finally {
parser.close();
} catch (CertificateException e) {
throw new IOException("Could not read certificate", e);
}
}

Expand All @@ -157,7 +200,7 @@ public char[] getPassword() {
@Nonnull
public String encode() throws IOException {
StringWriter sw = new StringWriter();
PEMWriter w = new PEMWriter(sw);
JcaPEMWriter w = new JcaPEMWriter(sw);
try {
w.writeObject(object);
} finally {
Expand Down Expand Up @@ -217,9 +260,10 @@ public void write(@Nonnull File pemFile) throws IOException {
*/
@CheckForNull
public KeyPair toKeyPair() {

if (object instanceof KeyPair) {
return (KeyPair) object;
} // We will need conversion here on BC 1.54
}
return null;
}

Expand Down Expand Up @@ -424,4 +468,6 @@ private static String encodeBase64(@Nonnull byte[] data) {
private static byte[] decodeBase64(@Nonnull String data) {
return Base64.decode(data);
}

private static final Logger LOGGER = Logger.getLogger(PEMEncodable.class.getName());
}
129 changes: 90 additions & 39 deletions src/test/java/jenkins/bouncycastle/EncodignDecodingTest.java
Expand Up @@ -25,12 +25,14 @@
package jenkins.bouncycastle;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.io.File;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.Arrays;

import org.apache.commons.io.FileUtils;
Expand All @@ -55,92 +57,130 @@ public static void setUpBC() {
private static File PRIVATE_KEY_PEM;
private static File PRIVATE_KEY_PW_PEM;
private static File PUBLIC_KEY_PEM;
private static File CERTIFICATE_PEM;
private static File CERTIFICATE_PUBLIC_KEY_PEM;
private static File CERTIFICATE_PW_PEM;
private static File CERTIFICATE_PUBLIC_KEY_PW_PEM;

private static String PRIVATE_KEY_PW = "test";

@Rule
public TemporaryFolder folder = new TemporaryFolder();

@BeforeClass
public static void setUpClass() throws URISyntaxException {
PRIVATE_KEY_PEM = new File(EncodignDecodingTest.class.getClassLoader().getResource("private-key.pem").toURI());
PRIVATE_KEY_PW_PEM = new File(
EncodignDecodingTest.class.getClassLoader().getResource("private-key-with-password.pem").toURI());
PUBLIC_KEY_PEM = new File(EncodignDecodingTest.class.getClassLoader().getResource("public-key.pem").toURI());
PRIVATE_KEY_PEM = getResourceFile("private-key.pem");
PRIVATE_KEY_PW_PEM = getResourceFile("private-key-with-password.pem");
PUBLIC_KEY_PEM = getResourceFile("public-key.pem");
CERTIFICATE_PEM = getResourceFile("test_cert_cert.pem");
CERTIFICATE_PUBLIC_KEY_PEM = getResourceFile("test_cert_key.pem");
CERTIFICATE_PW_PEM = getResourceFile("test_cert_cert_pass.pem");
CERTIFICATE_PUBLIC_KEY_PW_PEM = getResourceFile("test_cert_key_pass.pem");
}

private static File getResourceFile(String resource) throws URISyntaxException {
return new File(EncodignDecodingTest.class.getClassLoader().getResource(resource).toURI());
}

@Test
public void testReadPrivateKeyPEM() throws Exception {
PEMEncodable pemManager = PEMEncodable.read(new File(PRIVATE_KEY_PEM.toURI()));
PEMEncodable pemEnc = PEMEncodable.read(PRIVATE_KEY_PEM);

assertEquals(
new String(Base64.encode(pemManager.toKeyPair().getPrivate().getEncoded()), StandardCharsets.UTF_8),
new String(Base64.encode(pemManager.toPrivateKey().getEncoded()), StandardCharsets.UTF_8));
new String(Base64.encode(pemEnc.toKeyPair().getPrivate().getEncoded()), StandardCharsets.UTF_8),
new String(Base64.encode(pemEnc.toPrivateKey().getEncoded()), StandardCharsets.UTF_8));
assertEquals(PUBLIC_KEY,
new String(Base64.encode(pemManager.toKeyPair().getPublic().getEncoded()), StandardCharsets.UTF_8));
new String(Base64.encode(pemEnc.toKeyPair().getPublic().getEncoded()), StandardCharsets.UTF_8));
assertEquals(PUBLIC_KEY,
new String(Base64.encode(pemManager.toPublicKey().getEncoded()), StandardCharsets.UTF_8));
new String(Base64.encode(pemEnc.toPublicKey().getEncoded()), StandardCharsets.UTF_8));
}

@Test
public void testReadPrivateKeyWithPasswordPEM() throws Exception {
PEMEncodable pemManager = PEMEncodable.read(new File(PRIVATE_KEY_PW_PEM.toURI()), PRIVATE_KEY_PW.toCharArray());
PEMEncodable pemEnc = PEMEncodable.read(PRIVATE_KEY_PW_PEM, PRIVATE_KEY_PW.toCharArray());

assertEquals(
new String(Base64.encode(pemManager.toKeyPair().getPrivate().getEncoded()), StandardCharsets.UTF_8),
new String(Base64.encode(pemManager.toPrivateKey().getEncoded()), StandardCharsets.UTF_8));
new String(Base64.encode(pemEnc.toKeyPair().getPrivate().getEncoded()), StandardCharsets.UTF_8),
new String(Base64.encode(pemEnc.toPrivateKey().getEncoded()), StandardCharsets.UTF_8));
assertEquals(PUBLIC_KEY,
new String(Base64.encode(pemManager.toKeyPair().getPublic().getEncoded()), StandardCharsets.UTF_8));
new String(Base64.encode(pemEnc.toKeyPair().getPublic().getEncoded()), StandardCharsets.UTF_8));
assertEquals(PUBLIC_KEY,
new String(Base64.encode(pemManager.toPublicKey().getEncoded()), StandardCharsets.UTF_8));
new String(Base64.encode(pemEnc.toPublicKey().getEncoded()), StandardCharsets.UTF_8));
}

@Test
public void testReadOnlyPrivateKeyPEM() throws Exception {
File onlyPrivate = folder.newFile("from-private.prm");

PEMEncodable pemManager = PEMEncodable.read(PRIVATE_KEY_PEM);
PEMEncodable pemManagerOnlyPrivate = PEMEncodable.create(pemManager.toPrivateKey());
PEMEncodable pemEnc = PEMEncodable.read(PRIVATE_KEY_PEM);
PEMEncodable pemEncOnlyPrivate = PEMEncodable.create(pemEnc.toPrivateKey());

pemManagerOnlyPrivate.write(onlyPrivate);
assertEquals(true, Arrays.equals(pemManagerOnlyPrivate.toPrivateKey().getEncoded(),
pemManager.toPrivateKey().getEncoded()));
pemEncOnlyPrivate.write(onlyPrivate);
assertEquals(true, Arrays.equals(pemEncOnlyPrivate.toPrivateKey().getEncoded(),
pemEnc.toPrivateKey().getEncoded()));
assertEquals(FileUtils.readFileToString(PRIVATE_KEY_PEM), FileUtils.readFileToString(onlyPrivate));
}

@Test
public void testReadPublicKeyPEM() throws Exception {
PEMEncodable pemManager = PEMEncodable.read(PUBLIC_KEY_PEM);
PEMEncodable pemEnc = PEMEncodable.read(PUBLIC_KEY_PEM);

assertEquals(PUBLIC_KEY,
new String(Base64.encode(pemManager.toPublicKey().getEncoded()), StandardCharsets.UTF_8));
new String(Base64.encode(pemEnc.toPublicKey().getEncoded()), StandardCharsets.UTF_8));
}

@Test
public void testReadInexistentFromPublicKey() throws Exception {
PEMEncodable pemManager = PEMEncodable.read(PUBLIC_KEY_PEM);
assertEquals(null, pemManager.toPrivateKey());
assertEquals(null, pemManager.toKeyPair());
assertEquals(null, pemManager.toCertificate());
PEMEncodable pemEnc = PEMEncodable.read(PUBLIC_KEY_PEM);
assertEquals(null, pemEnc.toPrivateKey());
assertEquals(null, pemEnc.toKeyPair());
assertEquals(null, pemEnc.toCertificate());
}

@Test
public void testReadInexistentFromPrivateKey() throws Exception {
PEMEncodable pemManager = PEMEncodable.read(PRIVATE_KEY_PEM);
PEMEncodable pemEnc = PEMEncodable.read(PRIVATE_KEY_PEM);

PEMEncodable pemEncOnlyPrivate = PEMEncodable.create(pemEnc.toKeyPair().getPrivate());

PEMEncodable pemManagerOnlyPrivate = PEMEncodable.create(pemManager.toKeyPair().getPrivate());
assertEquals(null, pemEncOnlyPrivate.toPublicKey());
assertEquals(null, pemEncOnlyPrivate.toKeyPair());
assertEquals(null, pemEncOnlyPrivate.toCertificate());

assertEquals(null, pemManagerOnlyPrivate.toPublicKey());
assertEquals(null, pemManagerOnlyPrivate.toKeyPair());
assertEquals(null, pemManagerOnlyPrivate.toCertificate());
}

@Test
public void testReadCertificatePEM() throws Exception {
PEMEncodable pemEncCer = PEMEncodable.read(CERTIFICATE_PEM);
PEMEncodable pemEncKey = PEMEncodable.read(CERTIFICATE_PUBLIC_KEY_PEM);

Certificate certificate = pemEncCer.toCertificate();
PublicKey publicKey = pemEncKey.toPublicKey();
assertNotNull(certificate);
assertNotNull(publicKey);
assertEquals(new String(Base64.encode(certificate.getPublicKey().getEncoded()), StandardCharsets.UTF_8),
new String(Base64.encode(publicKey.getEncoded()), StandardCharsets.UTF_8));
}

@Test
public void testReadCertificateWithPasswordPEM() throws Exception {
PEMEncodable pemEncCer = PEMEncodable.read(CERTIFICATE_PW_PEM);
PEMEncodable pemEncKey = PEMEncodable.read(CERTIFICATE_PUBLIC_KEY_PW_PEM);

Certificate certificate = pemEncCer.toCertificate();
PublicKey publicKey = pemEncKey.toPublicKey();
assertNotNull(certificate);
assertNotNull(publicKey);
assertEquals(new String(Base64.encode(certificate.getPublicKey().getEncoded()), StandardCharsets.UTF_8),
new String(Base64.encode(publicKey.getEncoded()), StandardCharsets.UTF_8));
}

@Test
public void testWritePublicKeyPEM() throws Exception {
File pemFileNew = folder.newFile("public-key-test.pem");

PEMEncodable pemManager = PEMEncodable.read(PUBLIC_KEY_PEM);
pemManager.write(pemFileNew);
PEMEncodable pemEnc = PEMEncodable.read(PUBLIC_KEY_PEM);
pemEnc.write(pemFileNew);

assertEquals(FileUtils.readFileToString(PUBLIC_KEY_PEM), FileUtils.readFileToString(pemFileNew));
}
Expand All @@ -149,18 +189,28 @@ public void testWritePublicKeyPEM() throws Exception {
public void testWritePrivateKeyPEM() throws Exception {
File pemFileNew = folder.newFile("private-key-test.pem");

PEMEncodable pemManager = PEMEncodable.read(PRIVATE_KEY_PEM);
pemManager.write(pemFileNew);
PEMEncodable pemEnc = PEMEncodable.read(PRIVATE_KEY_PEM);
pemEnc.write(pemFileNew);

assertEquals(FileUtils.readFileToString(PRIVATE_KEY_PEM), FileUtils.readFileToString(pemFileNew));
}

@Test
public void testWriteCertificatePEM() throws Exception {
File pemFileNew = folder.newFile("certificate-test.pem");

PEMEncodable pemEnc = PEMEncodable.read(CERTIFICATE_PW_PEM);
pemEnc.write(pemFileNew);

assertEquals(FileUtils.readFileToString(CERTIFICATE_PW_PEM), FileUtils.readFileToString(pemFileNew));
}

@Test
public void testCreationFromObjectPublicKeyPEM() throws Exception {
File pemFileNew = folder.newFile("public-key-test.pem");

PEMEncodable pemManager = PEMEncodable.read(PUBLIC_KEY_PEM);
PEMEncodable.create(pemManager.toPublicKey()).write(pemFileNew);
PEMEncodable pemEnc = PEMEncodable.read(PUBLIC_KEY_PEM);
PEMEncodable.create(pemEnc.toPublicKey()).write(pemFileNew);

assertEquals(FileUtils.readFileToString(PUBLIC_KEY_PEM), FileUtils.readFileToString(pemFileNew));
}
Expand All @@ -169,9 +219,10 @@ public void testCreationFromObjectPublicKeyPEM() throws Exception {
public void testCreationFromObjectPrivateKeyPEM() throws Exception {
File pemFileNew = folder.newFile("private-key-test.pem");

PEMEncodable pemManager = PEMEncodable.read(PRIVATE_KEY_PEM);
PEMEncodable.create(pemManager.toKeyPair()).write(pemFileNew);
PEMEncodable pemEnc = PEMEncodable.read(PRIVATE_KEY_PEM);
PEMEncodable.create(pemEnc.toKeyPair()).write(pemFileNew);

assertEquals(FileUtils.readFileToString(PRIVATE_KEY_PEM), FileUtils.readFileToString(pemFileNew));
}

}

0 comments on commit 97f7458

Please sign in to comment.