Remove explicit dependency on bouncycastle for PEM parsing

This commit is contained in:
Adrian Cole 2012-12-28 23:11:42 -08:00
parent 80129a4817
commit 023b549e20
8 changed files with 168 additions and 309 deletions

View File

@ -48,11 +48,6 @@
</properties> </properties>
<dependencies> <dependencies>
<!-- Required for Pems.java to read and write public and private keys -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
<dependency> <dependency>
<groupId>javax.ws.rs</groupId> <groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId> <artifactId>jsr311-api</artifactId>

View File

@ -19,11 +19,21 @@
package org.jclouds.crypto; package org.jclouds.crypto;
import static com.google.common.base.Charsets.US_ASCII; import static com.google.common.base.Charsets.US_ASCII;
import static com.google.common.base.Joiner.on;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Splitter.fixedLength;
import static com.google.common.base.Throwables.propagate; import static com.google.common.base.Throwables.propagate;
import static com.google.common.base.Throwables.propagateIfInstanceOf;
import static com.google.common.io.BaseEncoding.base64;
import static com.google.common.io.ByteStreams.readBytes;
import static com.google.common.io.Closeables.closeQuietly; import static com.google.common.io.Closeables.closeQuietly;
import static org.jclouds.crypto.ASN1Codec.decodeRSAPrivateKey;
import static org.jclouds.crypto.ASN1Codec.decodeRSAPublicKey;
import static org.jclouds.crypto.ASN1Codec.encode;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -43,20 +53,14 @@ import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.Map; import java.util.Map;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.jclouds.crypto.Pems.PemProcessor.ResultParser;
import org.jclouds.crypto.pem.PKCS1EncodedPrivateKeySpec;
import org.jclouds.crypto.pem.PKCS1EncodedPublicKeySpec;
import org.jclouds.io.InputSuppliers; import org.jclouds.io.InputSuppliers;
import org.jclouds.javax.annotation.Nullable; import org.jclouds.javax.annotation.Nullable;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
import com.google.common.base.Joiner; import com.google.common.base.Function;
import com.google.common.base.Splitter; import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteProcessor;
import com.google.common.io.InputSupplier; import com.google.common.io.InputSupplier;
/** /**
@ -72,7 +76,7 @@ public class Pems {
public static final String PUBLIC_X509_MARKER = "-----BEGIN PUBLIC KEY-----"; public static final String PUBLIC_X509_MARKER = "-----BEGIN PUBLIC KEY-----";
public static final String PUBLIC_PKCS1_MARKER = "-----BEGIN RSA PUBLIC KEY-----"; public static final String PUBLIC_PKCS1_MARKER = "-----BEGIN RSA PUBLIC KEY-----";
public static class PemProcessor<T> implements com.google.common.io.ByteProcessor<T> { public static class PemProcessor<T> implements ByteProcessor<T> {
public interface ResultParser<T> { public interface ResultParser<T> {
T parseResult(byte[] bytes) throws IOException; T parseResult(byte[] bytes) throws IOException;
} }
@ -92,28 +96,94 @@ public class Pems {
@Override @Override
public T getResult() { public T getResult() {
PemReader reader = new PemReader(new StringReader(new String(out.toByteArray(), US_ASCII))); Pem pem = PemReader.INSTANCE.apply(new String(out.toByteArray(), US_ASCII));
String beginMarker = "-----BEGIN " + pem.type + "-----";
checkState(parsers.containsKey(beginMarker), "Invalid PEM: no parsers for marker %s in %s", beginMarker,
parsers.keySet());
try { try {
PemObject pem = reader.readPemObject(); return parsers.get(beginMarker).parseResult(pem.content);
byte[] bytes = pem.getContent();
// Bouncycastle removes the BEGIN and the markers when reading the PEM object
String beginMarker = "-----BEGIN " + pem.getType() + "-----";
checkState(parsers.containsKey(beginMarker), "Invalid PEM file: no parsers for marker %s in %s",
beginMarker, parsers.keySet());
return parsers.get(beginMarker).parseResult(bytes);
} catch (IOException e) { } catch (IOException e) {
throw propagate(e); throw new IllegalStateException("Invalid PEM : " + pem, e);
} finally {
closeQuietly(reader);
} }
} }
} }
/** /**
* Returns the object of generic type {@code T} that is pem encoded in the * Parsed PEM format
* supplier. *
* <pre>
* -----BEGIN RSA PRIVATE KEY-----
* Proc-Type: 4,ENCRYPTED
* DEK-Info: DES-EDE3-CBC,3F17F5316E2BAC89
*
* ...base64 encoded data...
* -----END RSA PRIVATE KEY-----
*
* </pre>
*
*/
private static enum PemReader implements Function<CharSequence, Pem> {
INSTANCE;
private static final String BEGIN = "-----BEGIN ";
private static final String END = "-----END ";
@Override
public Pem apply(CharSequence chars) {
checkNotNull(chars, "chars");
BufferedReader reader = null;
try {
reader = new BufferedReader(new StringReader(chars.toString()));
Optional<String> begin = skipUntilBegin(reader);
checkArgument(begin.isPresent(), "chars %s doesn't contain % line", chars, BEGIN);
String line = begin.get().substring(BEGIN.length());
String type = line.substring(0, line.indexOf('-'));
StringBuilder encoded = new StringBuilder();
boolean reachedEnd = false;
while ((line = reader.readLine()) != null) {
if (line.indexOf(':') >= 0) { // skip headers
continue;
}
if (line.indexOf(END + type) != -1) {
reachedEnd = true;
break;
}
encoded.append(line.trim());
}
checkArgument(reachedEnd, "chars %s doesn't contain % line", chars, END);
return new Pem(type, base64().decode(encoded.toString()));
} catch (IOException e) {
throw new IllegalStateException(String.format("io exception reading %s", chars), e);
} finally {
closeQuietly(reader);
}
}
private static Optional<String> skipUntilBegin(BufferedReader reader) throws IOException {
String line = reader.readLine();
while (line != null && !line.startsWith(BEGIN)) {
line = reader.readLine();
}
return Optional.fromNullable(line);
}
}
private static final class Pem {
private final String type;
private final byte[] content;
private Pem(String type, byte[] content) {
this.type = checkNotNull(type, "type");
this.content = checkNotNull(content, "content");
}
}
/**
* Returns the object of generic type {@code T} that is pem encoded in the supplier.
* *
* @param supplier * @param supplier
* the input stream factory * the input stream factory
@ -121,19 +191,17 @@ public class Pems {
* header that begins the PEM block * header that begins the PEM block
* @param processor * @param processor
* how to parser the object from a byte array * how to parser the object from a byte array
* @return the object of generic type {@code T} which was PEM encoded in the * @return the object of generic type {@code T} which was PEM encoded in the stream
* stream
* @throws IOException * @throws IOException
* if an I/O error occurs * if an I/O error occurs
*/ */
public static <T> T fromPem(InputSupplier<? extends InputStream> supplier, PemProcessor<T> processor) public static <T> T fromPem(InputSupplier<? extends InputStream> supplier, PemProcessor<T> processor)
throws IOException { throws IOException {
try { try {
return com.google.common.io.ByteStreams.readBytes(supplier, processor); return readBytes(supplier, processor);
} catch (RuntimeException e) { } catch (RuntimeException e) {
if (e.getCause() != null && e.getCause() instanceof IOException) { propagateIfInstanceOf(e.getCause(), IOException.class);
throw (IOException) e.getCause(); propagateIfInstanceOf(e, IOException.class);
}
throw e; throw e;
} }
} }
@ -151,14 +219,9 @@ public class Pems {
public static KeySpec privateKeySpec(InputSupplier<? extends InputStream> supplier) throws IOException { public static KeySpec privateKeySpec(InputSupplier<? extends InputStream> supplier) throws IOException {
return fromPem( return fromPem(
supplier, supplier,
new PemProcessor<KeySpec>(ImmutableMap.<String, ResultParser<KeySpec>> of(PRIVATE_PKCS1_MARKER, new PemProcessor<KeySpec>(ImmutableMap.<String, PemProcessor.ResultParser<KeySpec>> of(
new ResultParser<KeySpec>() { PRIVATE_PKCS1_MARKER, DecodeRSAPrivateCrtKeySpec.INSTANCE, PRIVATE_PKCS8_MARKER,
@Override new PemProcessor.ResultParser<KeySpec>() {
public KeySpec parseResult(byte[] bytes) throws IOException {
return new PKCS1EncodedPrivateKeySpec(bytes).getKeySpec();
}
}, PRIVATE_PKCS8_MARKER, new ResultParser<KeySpec>() {
@Override @Override
public KeySpec parseResult(byte[] bytes) throws IOException { public KeySpec parseResult(byte[] bytes) throws IOException {
return new PKCS8EncodedKeySpec(bytes); return new PKCS8EncodedKeySpec(bytes);
@ -168,8 +231,23 @@ public class Pems {
} }
/** /**
* Executes {@link Pems#privateKeySpec(InputSupplier)} on the string which * Decode PKCS#1 encoded private key into RSAPrivateCrtKeySpec.
* contains an encoded private key in PEM format. *
* @param keyBytes
* Encoded PKCS#1 rsa key.
*/
private static enum DecodeRSAPrivateCrtKeySpec implements PemProcessor.ResultParser<KeySpec> {
INSTANCE;
@Override
public KeySpec parseResult(byte[] bytes) throws IOException {
return decodeRSAPrivateKey(bytes);
}
}
/**
* Executes {@link Pems#privateKeySpec(InputSupplier)} on the string which contains an encoded private key in PEM
* format.
* *
* @param pem * @param pem
* private key in pem encoded format. * private key in pem encoded format.
@ -196,15 +274,8 @@ public class Pems {
public static KeySpec publicKeySpec(InputSupplier<? extends InputStream> supplier) throws IOException { public static KeySpec publicKeySpec(InputSupplier<? extends InputStream> supplier) throws IOException {
return fromPem( return fromPem(
supplier, supplier,
new PemProcessor<KeySpec>(ImmutableMap.<String, ResultParser<KeySpec>> of(PUBLIC_PKCS1_MARKER, new PemProcessor<KeySpec>(ImmutableMap.<String, PemProcessor.ResultParser<KeySpec>> of(PUBLIC_PKCS1_MARKER,
new ResultParser<KeySpec>() { DecodeRSAPublicKeySpec.INSTANCE, PUBLIC_X509_MARKER, new PemProcessor.ResultParser<KeySpec>() {
@Override
public KeySpec parseResult(byte[] bytes) throws IOException {
return new PKCS1EncodedPublicKeySpec(bytes).getKeySpec();
}
}, PUBLIC_X509_MARKER, new ResultParser<KeySpec>() {
@Override @Override
public X509EncodedKeySpec parseResult(byte[] bytes) throws IOException { public X509EncodedKeySpec parseResult(byte[] bytes) throws IOException {
@ -215,8 +286,28 @@ public class Pems {
} }
/** /**
* Executes {@link Pems#publicKeySpec(InputSupplier)} on the string which * Decode PKCS#1 encoded public key into RSAPublicKeySpec.
* contains an encoded public key in PEM format. * <p>
* Keys here can be in two different formats. They can have the algorithm encoded, or they can have only the modulus
* and the public exponent.
* <p>
* The latter is not a valid PEM encoded file, but it is a valid DER encoded RSA key, so this method should also
* support it.
*
* @param keyBytes
* Encoded PKCS#1 rsa key.
*/
private static enum DecodeRSAPublicKeySpec implements PemProcessor.ResultParser<KeySpec> {
INSTANCE;
@Override
public KeySpec parseResult(byte[] bytes) throws IOException {
return decodeRSAPublicKey(bytes);
}
}
/**
* Executes {@link Pems#publicKeySpec(InputSupplier)} on the string which contains an encoded public key in PEM
* format.
* *
* @param pem * @param pem
* public key in pem encoded format. * public key in pem encoded format.
@ -227,8 +318,7 @@ public class Pems {
} }
/** /**
* Returns the {@link X509EncodedKeySpec} that is pem encoded in the * Returns the {@link X509EncodedKeySpec} that is pem encoded in the supplier.
* supplier.
* *
* @param supplier * @param supplier
* the input stream factory * the input stream factory
@ -242,19 +332,17 @@ public class Pems {
*/ */
public static X509Certificate x509Certificate(InputSupplier<? extends InputStream> supplier, public static X509Certificate x509Certificate(InputSupplier<? extends InputStream> supplier,
@Nullable CertificateFactory certFactory) throws IOException, CertificateException { @Nullable CertificateFactory certFactory) throws IOException, CertificateException {
final CertificateFactory finalCertFactory = certFactory != null ? certFactory : CertificateFactory final CertificateFactory certs = certFactory != null ? certFactory : CertificateFactory.getInstance("X.509");
.getInstance("X.509");
try { try {
return fromPem( return fromPem(
supplier, supplier,
new PemProcessor<X509Certificate>(ImmutableMap.<String, ResultParser<X509Certificate>> of( new PemProcessor<X509Certificate>(ImmutableMap.<String, PemProcessor.ResultParser<X509Certificate>> of(
CERTIFICATE_X509_MARKER, new ResultParser<X509Certificate>() { CERTIFICATE_X509_MARKER, new PemProcessor.ResultParser<X509Certificate>() {
@Override @Override
public X509Certificate parseResult(byte[] bytes) throws IOException { public X509Certificate parseResult(byte[] bytes) throws IOException {
try { try {
return (X509Certificate) finalCertFactory.generateCertificate(new ByteArrayInputStream( return (X509Certificate) certs.generateCertificate(new ByteArrayInputStream(bytes));
bytes));
} catch (CertificateException e) { } catch (CertificateException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -262,16 +350,14 @@ public class Pems {
}))); })));
} catch (RuntimeException e) { } catch (RuntimeException e) {
if (e.getCause() != null && e.getCause() instanceof CertificateException) { propagateIfInstanceOf(e.getCause(), CertificateException.class);
throw (CertificateException) e.getCause();
}
throw e; throw e;
} }
} }
/** /**
* Executes {@link Pems#x509Certificate(InputSupplier, CertificateFactory)} * Executes {@link Pems#x509Certificate(InputSupplier, CertificateFactory)} on the string which contains an X.509
* on the string which contains an X.509 certificate in PEM format. * certificate in PEM format.
* *
* @param pem * @param pem
* certificate in pem encoded format. * certificate in pem encoded format.
@ -297,12 +383,6 @@ public class Pems {
/** /**
* encodes the {@link PublicKey} to PEM format. * encodes the {@link PublicKey} to PEM format.
*
* @param cert
* what to encode
* @return the PEM encoded public key
* @throws IOException
* @throws CertificateEncodingException
*/ */
public static String pem(PublicKey key) { public static String pem(PublicKey key) {
String marker = key instanceof RSAPublicKey ? PUBLIC_PKCS1_MARKER : PUBLIC_X509_MARKER; String marker = key instanceof RSAPublicKey ? PUBLIC_PKCS1_MARKER : PUBLIC_X509_MARKER;
@ -310,49 +390,23 @@ public class Pems {
} }
/** /**
* encodes the {@link PrivateKey} to PEM format. Note * encodes the {@link PrivateKey} to PEM format.
*
* @param cert
* what to encode
* @return the PEM encoded private key
* @throws IOException
* @throws CertificateEncodingException
*/ */
// TODO: understand why pem isn't passing SshKeysTest.testCanGenerate where
// keys are checked to match.
public static String pem(PrivateKey key) { public static String pem(PrivateKey key) {
String marker = key instanceof RSAPrivateCrtKey ? PRIVATE_PKCS1_MARKER : PRIVATE_PKCS8_MARKER; String marker = key instanceof RSAPrivateCrtKey ? PRIVATE_PKCS1_MARKER : PRIVATE_PKCS8_MARKER;
return pem(key instanceof RSAPrivateCrtKey ? getEncoded(RSAPrivateCrtKey.class.cast(key)) : key.getEncoded(), return pem(key instanceof RSAPrivateCrtKey ? encode(RSAPrivateCrtKey.class.cast(key)) : key.getEncoded(), marker);
marker);
}
// TODO find a way to do this without using bouncycastle
public static byte[] getEncoded(RSAPrivateCrtKey key) {
RSAPrivateKey keyStruct = new RSAPrivateKey(key.getModulus(), key.getPublicExponent(),
key.getPrivateExponent(), key.getPrimeP(), key.getPrimeQ(), key.getPrimeExponentP(),
key.getPrimeExponentQ(), key.getCrtCoefficient());
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
try {
aOut.writeObject(keyStruct);
aOut.close();
} catch (IOException e) {
throw propagate(e);
}
return bOut.toByteArray();
} }
private static String pem(byte[] key, String marker) { private static String pem(byte[] key, String marker) {
return pem(key, marker, 64); return pem(key, marker, 64);
} }
static String pem(byte[] key, String marker, int length) { private static String pem(byte[] encoded, String marker, int length) {
return new StringBuilder(marker + "\n") StringBuilder builder = new StringBuilder();
.append(Joiner.on('\n').join(Splitter.fixedLength(length).split(CryptoStreams.base64(key)))) builder.append(marker).append('\n');
.append("\n" + marker.replace("BEGIN", "END") + "\n").toString().trim(); builder.append(on('\n').join(fixedLength(64).split(base64().encode(encoded)))).append('\n');
builder.append(marker.replace("BEGIN", "END")).append('\n');
return builder.toString();
} }
} }

View File

@ -20,7 +20,6 @@ package org.jclouds.crypto;
import static com.google.common.base.Joiner.on; import static com.google.common.base.Joiner.on;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Splitter.fixedLength; import static com.google.common.base.Splitter.fixedLength;
import static com.google.common.base.Throwables.propagate; import static com.google.common.base.Throwables.propagate;
import static com.google.common.collect.Iterables.get; import static com.google.common.collect.Iterables.get;
@ -29,6 +28,7 @@ import static com.google.common.io.BaseEncoding.base64;
import static org.jclouds.crypto.CryptoStreams.base64; import static org.jclouds.crypto.CryptoStreams.base64;
import static org.jclouds.crypto.CryptoStreams.hex; import static org.jclouds.crypto.CryptoStreams.hex;
import static org.jclouds.crypto.CryptoStreams.md5; import static org.jclouds.crypto.CryptoStreams.md5;
import static org.jclouds.crypto.Pems.pem;
import static org.jclouds.crypto.Pems.privateKeySpec; import static org.jclouds.crypto.Pems.privateKeySpec;
import static org.jclouds.util.Strings2.toStringAndClose; import static org.jclouds.util.Strings2.toStringAndClose;
@ -50,9 +50,6 @@ import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPublicKeySpec; import java.security.spec.RSAPublicKeySpec;
import java.util.Map; import java.util.Map;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.jclouds.io.InputSuppliers; import org.jclouds.io.InputSuppliers;
import com.google.common.annotations.Beta; import com.google.common.annotations.Beta;
@ -153,7 +150,7 @@ public class SshKeys {
KeyPair pair = generateRsaKeyPair(generator, rand); KeyPair pair = generateRsaKeyPair(generator, rand);
Builder<String, String> builder = ImmutableMap.builder(); Builder<String, String> builder = ImmutableMap.builder();
builder.put("public", encodeAsOpenSSH(RSAPublicKey.class.cast(pair.getPublic()))); builder.put("public", encodeAsOpenSSH(RSAPublicKey.class.cast(pair.getPublic())));
builder.put("private", encodeAsPem(RSAPrivateKey.class.cast(pair.getPrivate()))); builder.put("private", pem(RSAPrivateKey.class.cast(pair.getPrivate())));
return builder.build(); return builder.build();
} }
@ -162,25 +159,6 @@ public class SshKeys {
return "ssh-rsa " + base64(keyBlob); return "ssh-rsa " + base64(keyBlob);
} }
public static String encodeAsPem(RSAPrivateKey key) {
String type = "RSA PRIVATE KEY";
byte[] encoded = asn1Encode(checkNotNull(key, type));
StringBuilder builder = new StringBuilder();
builder.append("-----BEGIN ").append(type).append("-----").append('\n');
builder.append(on('\n').join(fixedLength(64).split(base64().encode(encoded)))).append('\n');
builder.append("-----END ").append(type).append("-----").append('\n');
return builder.toString();
}
private static byte[] asn1Encode(RSAPrivateKey key) {
try {
PrivateKeyInfo info = new PrivateKeyInfo((ASN1Sequence) ASN1Primitive.fromByteArray(key.getEncoded()));
return info.parsePrivateKey().toASN1Primitive().getEncoded();
} catch (IOException e) {
throw propagate(e);
}
}
/** /**
* @param privateKeyPEM * @param privateKeyPEM
* RSA private key in PEM format * RSA private key in PEM format

View File

@ -1,78 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.crypto.pem;
import java.io.IOException;
import java.math.BigInteger;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPrivateKeySpec;
import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
/**
* PKCS#1 encoded private key spec.
*
* @author Ignasi Barrera
*/
public class PKCS1EncodedPrivateKeySpec {
private RSAPrivateCrtKeySpec keySpec;
/**
* Create a PKCS#1 keyspec from DER encoded buffer
*
* @param keyBytes
* DER encoded octet stream
* @throws IOException
*/
public PKCS1EncodedPrivateKeySpec(final byte[] keyBytes) {
decode(keyBytes);
}
/**
* Get the key spec that JCE understands.
*
* @return CRT keyspec defined by JCE
*/
public RSAPrivateKeySpec getKeySpec() {
return keySpec;
}
/**
* Decode PKCS#1 encoded private key into RSAPrivateCrtKeySpec.
*
* @param keyBytes
* Encoded PKCS#1 rsa key.
*/
private void decode(final byte[] keyBytes) {
RSAPrivateKey rsa = RSAPrivateKey.getInstance(keyBytes);
BigInteger mod = rsa.getModulus();
BigInteger pubExp = rsa.getPublicExponent();
BigInteger privExp = rsa.getPrivateExponent();
BigInteger p1 = rsa.getPrime1();
BigInteger p2 = rsa.getPrime2();
BigInteger exp1 = rsa.getExponent1();
BigInteger exp2 = rsa.getExponent2();
BigInteger crtCoef = rsa.getCoefficient();
keySpec = new RSAPrivateCrtKeySpec(mod, pubExp, privExp, p1, p2, exp1, exp2, crtCoef);
}
}

View File

@ -1,86 +0,0 @@
/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.crypto.pem;
import java.io.IOException;
import java.security.spec.RSAPublicKeySpec;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.RSAPublicKey;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
/**
* PKCS#1 encoded public key spec.
*
* @author Adrian Cole
*/
public class PKCS1EncodedPublicKeySpec {
private RSAPublicKeySpec keySpec;
/**
* Create a PKCS#1 keyspec from DER encoded buffer
*
* @param keyBytes
* DER encoded octet stream
* @throws IOException
*/
public PKCS1EncodedPublicKeySpec(final byte[] keyBytes) throws IOException {
decode(keyBytes);
}
/**
* Get the key spec that JCE understands.
*
* @return CRT keyspec defined by JCE
*/
public RSAPublicKeySpec getKeySpec() {
return keySpec;
}
/**
* Decode PKCS#1 encoded public key into RSAPublicKeySpec.
* <p>
* Keys here can be in two different formats. They can have the algorithm
* encoded, or they can have only the modulus and the public exponent.
* <p>
* The latter is not a valid PEM encoded file, but it is a valid DER encoded
* RSA key, so this method should also support it.
*
* @param keyBytes
* Encoded PKCS#1 rsa key.
*/
private void decode(final byte[] keyBytes) throws IOException {
RSAPublicKey pks = null;
ASN1Sequence seq = ASN1Sequence.getInstance(keyBytes);
try {
// Try to parse the public key normally. If the algorithm is not
// present in the encoded key, an IllegalArgumentException will be
// raised.
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(seq);
pks = RSAPublicKey.getInstance(info.parsePublicKey());
} catch (IllegalArgumentException ex) {
// If the algorithm is not found in the encoded key, try to extract
// just the modulus and the public exponent to build the public key.
pks = RSAPublicKey.getInstance(seq);
}
keySpec = new RSAPublicKeySpec(pks.getModulus(), pks.getPublicExponent());
}
}

View File

@ -56,12 +56,12 @@ public class PemsTest {
private static final String CERTIFICATE = "-----BEGIN CERTIFICATE-----\nMIIClzCCAgCgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxFjAUBgNVBAoM\nDU9wc2NvZGUsIEluYy4xHDAaBgNVBAsME0NlcnRpZmljYXRlIFNlcnZpY2UxMjAw\nBgNVBAMMKW9wc2NvZGUuY29tL2VtYWlsQWRkcmVzcz1hdXRoQG9wc2NvZGUuY29t\nMB4XDTEwMDczMDIwNDEzMFoXDTIwMDcyNzIwNDEzMFowADCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMm9mSSahptCikfvJ30CTbEnfhfbVzTFewnznFuo\n7KrPBGYIlUdPYQ9SGDo+GKjNKiTjZYMoOMUVnsHUhu0Ez49ZSaVQInWvbF8tvpM8\nmoGQNQJtDmXG6m+YaHiA4HF/ng2u/bNLtA6Jo3HzvRCobxywc/szPt0Kj0ZD1fJ2\nE237Ph41c8zlOg9QdF0d/iD2WZdgJ1rNndKoZ0rR3A1L50VUND+PNmMDfVYHHjmb\naT89AwihCeU8eUk7m/JNP87f1QDB0Gny0rkDC3drOGS7jmabTf/7gLE5sYq3qnd+\n8/vGU3QWyfCxKSfogl7kn5uWlIe4sOqMb06GNgC+d/oytlECAwEAATANBgkqhkiG\n9w0BAQUFAAOBgQBftzSZxstWw60GqRTDNN/F2GnrdtnKBoXzHww3r6jtGEylYq20\n5KfKpEx+sPX0gyZuYJiXC2CkEjImAluWKcdN9ZF6VD541sheAjbiaU7q7ZsztTxF\nWUH2tCvHeDXYKPKek3QzL7bYpUhLnCN/XxEv6ibeMDwtI7f5qpk2Aspzcw==\n-----END CERTIFICATE-----\n"; private static final String CERTIFICATE = "-----BEGIN CERTIFICATE-----\nMIIClzCCAgCgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxFjAUBgNVBAoM\nDU9wc2NvZGUsIEluYy4xHDAaBgNVBAsME0NlcnRpZmljYXRlIFNlcnZpY2UxMjAw\nBgNVBAMMKW9wc2NvZGUuY29tL2VtYWlsQWRkcmVzcz1hdXRoQG9wc2NvZGUuY29t\nMB4XDTEwMDczMDIwNDEzMFoXDTIwMDcyNzIwNDEzMFowADCCASIwDQYJKoZIhvcN\nAQEBBQADggEPADCCAQoCggEBAMm9mSSahptCikfvJ30CTbEnfhfbVzTFewnznFuo\n7KrPBGYIlUdPYQ9SGDo+GKjNKiTjZYMoOMUVnsHUhu0Ez49ZSaVQInWvbF8tvpM8\nmoGQNQJtDmXG6m+YaHiA4HF/ng2u/bNLtA6Jo3HzvRCobxywc/szPt0Kj0ZD1fJ2\nE237Ph41c8zlOg9QdF0d/iD2WZdgJ1rNndKoZ0rR3A1L50VUND+PNmMDfVYHHjmb\naT89AwihCeU8eUk7m/JNP87f1QDB0Gny0rkDC3drOGS7jmabTf/7gLE5sYq3qnd+\n8/vGU3QWyfCxKSfogl7kn5uWlIe4sOqMb06GNgC+d/oytlECAwEAATANBgkqhkiG\n9w0BAQUFAAOBgQBftzSZxstWw60GqRTDNN/F2GnrdtnKBoXzHww3r6jtGEylYq20\n5KfKpEx+sPX0gyZuYJiXC2CkEjImAluWKcdN9ZF6VD541sheAjbiaU7q7ZsztTxF\nWUH2tCvHeDXYKPKek3QzL7bYpUhLnCN/XxEv6ibeMDwtI7f5qpk2Aspzcw==\n-----END CERTIFICATE-----\n";
@Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "^Invalid PEM file: no parsers for marker -----BEGIN FOO PRIVATE KEY----- .*") @Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "^Invalid PEM: no parsers for marker -----BEGIN FOO PRIVATE KEY----- .*")
public void testPrivateKeySpecFromPemWithInvalidMarker() throws IOException { public void testPrivateKeySpecFromPemWithInvalidMarker() throws IOException {
Pems.privateKeySpec(Payloads.newStringPayload(INVALID_PRIVATE_KEY)); Pems.privateKeySpec(Payloads.newStringPayload(INVALID_PRIVATE_KEY));
} }
@Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "^Invalid PEM file: no parsers for marker -----BEGIN FOO PUBLIC KEY----- .*") @Test(expectedExceptions = IllegalStateException.class, expectedExceptionsMessageRegExp = "^Invalid PEM: no parsers for marker -----BEGIN FOO PUBLIC KEY----- .*")
public void testPublicKeySpecFromPemWithInvalidMarker() throws IOException { public void testPublicKeySpecFromPemWithInvalidMarker() throws IOException {
Pems.publicKeySpec(Payloads.newStringPayload(INVALID_PUBLIC_KEY)); Pems.publicKeySpec(Payloads.newStringPayload(INVALID_PUBLIC_KEY));
} }
@ -91,21 +91,21 @@ public class PemsTest {
RSAPrivateCrtKey key = (RSAPrivateCrtKey) KeyFactory.getInstance("RSA").generatePrivate( RSAPrivateCrtKey key = (RSAPrivateCrtKey) KeyFactory.getInstance("RSA").generatePrivate(
Pems.privateKeySpec(Payloads.newStringPayload(PRIVATE_KEY))); Pems.privateKeySpec(Payloads.newStringPayload(PRIVATE_KEY)));
String encoded = Pems.pem(key); String encoded = Pems.pem(key);
assertEquals(encoded, PRIVATE_KEY.replaceAll("\n", "\n").trim()); assertEquals(encoded, PRIVATE_KEY.replaceAll("\n", "\n"));
} }
@Test @Test
public void testRSAPublicKeySpecPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { public void testRSAPublicKeySpecPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
String encoded = Pems.pem(KeyFactory.getInstance("RSA").generatePublic( String encoded = Pems.pem(KeyFactory.getInstance("RSA").generatePublic(
Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY)))); Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY))));
assertEquals(encoded, PUBLIC_KEY.replaceAll("PUBLIC", "RSA PUBLIC").replaceAll("\n", "\n").trim()); assertEquals(encoded, PUBLIC_KEY.replaceAll("PUBLIC", "RSA PUBLIC").replaceAll("\n", "\n"));
} }
@Test @Test
public void testRSAPKCS1PublicKeySpecPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { public void testRSAPKCS1PublicKeySpecPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
String encoded = Pems.pem(KeyFactory.getInstance("RSA").generatePublic( String encoded = Pems.pem(KeyFactory.getInstance("RSA").generatePublic(
Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY_PKCS1)))); Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY_PKCS1))));
assertEquals(encoded, PUBLIC_KEY_PKCS1.replaceAll("\n", "\n").trim()); assertEquals(encoded, PUBLIC_KEY_PKCS1.replaceAll("\n", "\n"));
} }
@Test @Test
@ -132,7 +132,7 @@ public class PemsTest {
public void testX509CertificatePem() throws IOException, CertificateException { public void testX509CertificatePem() throws IOException, CertificateException {
String encoded = Pems.pem(Pems.x509Certificate(Payloads.newStringPayload(CERTIFICATE), String encoded = Pems.pem(Pems.x509Certificate(Payloads.newStringPayload(CERTIFICATE),
CertificateFactory.getInstance("X.509"))); CertificateFactory.getInstance("X.509")));
assertEquals(encoded, CERTIFICATE.replaceAll("\n", "\n").trim()); assertEquals(encoded, CERTIFICATE.replaceAll("\n", "\n"));
} }
} }

View File

@ -64,6 +64,7 @@
<dependency> <dependency>
<groupId>org.bouncycastle</groupId> <groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId> <artifactId>bcprov-jdk15on</artifactId>
<version>1.47</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -204,11 +204,6 @@
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.47</version>
</dependency>
<dependency> <dependency>
<groupId>com.jcraft</groupId> <groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId> <artifactId>jsch</artifactId>