Fixed decoding of some PKCS1 encoded public keys

This commit is contained in:
Ignasi Barrera 2012-12-09 00:08:18 +01:00 committed by Adrian Cole
parent 5d62acc742
commit 72deb93570
2 changed files with 69 additions and 13 deletions

View File

@ -46,12 +46,14 @@ package org.jclouds.crypto.pem;
import java.io.IOException; import java.io.IOException;
import java.security.spec.RSAPublicKeySpec; import java.security.spec.RSAPublicKeySpec;
import net.oauth.signature.pem.PKCS1EncodedKeySpec; import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
/** /**
* PKCS#1 encoded public key spec. * PKCS#1 encoded public key spec.
* *
*
* @author Adrian Cole * @author Adrian Cole
*/ */
public class PKCS1EncodedPublicKeySpec { public class PKCS1EncodedPublicKeySpec {
@ -65,7 +67,7 @@ public class PKCS1EncodedPublicKeySpec {
* DER encoded octet stream * DER encoded octet stream
* @throws IOException * @throws IOException
*/ */
public PKCS1EncodedPublicKeySpec(byte[] keyBytes) throws IOException { public PKCS1EncodedPublicKeySpec(final byte[] keyBytes) throws IOException {
decode(keyBytes); decode(keyBytes);
} }
@ -79,12 +81,31 @@ public class PKCS1EncodedPublicKeySpec {
} }
/** /**
* get the modulus and public exponent by reusing {@link PKCS1EncodedKeySpec} * Decode PKCS#1 encoded private key into RSAPrivateCrtKeySpec.
* <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(byte[] keyBytes) throws IOException { private void decode(final byte[] keyBytes) throws IOException {
PKCS1EncodedKeySpec privateSpec = new PKCS1EncodedKeySpec(keyBytes); RSAPublicKeyStructure pks = null;
ASN1Sequence seq = (ASN1Sequence) ASN1Object.fromByteArray(keyBytes);
keySpec = new RSAPublicKeySpec(privateSpec.getKeySpec().getModulus(), privateSpec.getKeySpec() try {
.getPublicExponent()); // 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 = new RSAPublicKeyStructure((ASN1Sequence) info.getPublicKey());
} 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 = new RSAPublicKeyStructure(seq);
}
keySpec = new RSAPublicKeySpec(pks.getModulus(), pks.getPublicExponent());
} }
} }

View File

@ -19,6 +19,8 @@
package org.jclouds.crypto; package org.jclouds.crypto;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.security.KeyFactory; import java.security.KeyFactory;
@ -27,6 +29,8 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;
import org.jclouds.io.Payloads; import org.jclouds.io.Payloads;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -42,6 +46,10 @@ public class PemsTest {
public static final String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyb2ZJJqGm0KKR+8nfQJN\nsSd+F9tXNMV7CfOcW6jsqs8EZgiVR09hD1IYOj4YqM0qJONlgyg4xRWewdSG7QTP\nj1lJpVAida9sXy2+kzyagZA1Am0OZcbqb5hoeIDgcX+eDa79s0u0DomjcfO9EKhv\nHLBz+zM+3QqPRkPV8nYTbfs+HjVzzOU6D1B0XR3+IPZZl2AnWs2d0qhnStHcDUvn\nRVQ0P482YwN9VgceOZtpPz0DCKEJ5Tx5STub8k0/zt/VAMHQafLSuQMLd2s4ZLuO\nZptN//uAsTmxireqd37z+8ZTdBbJ8LEpJ+iCXuSfm5aUh7iw6oxvToY2AL53+jK2\nUQIDAQAB\n-----END PUBLIC KEY-----\n"; public static final String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyb2ZJJqGm0KKR+8nfQJN\nsSd+F9tXNMV7CfOcW6jsqs8EZgiVR09hD1IYOj4YqM0qJONlgyg4xRWewdSG7QTP\nj1lJpVAida9sXy2+kzyagZA1Am0OZcbqb5hoeIDgcX+eDa79s0u0DomjcfO9EKhv\nHLBz+zM+3QqPRkPV8nYTbfs+HjVzzOU6D1B0XR3+IPZZl2AnWs2d0qhnStHcDUvn\nRVQ0P482YwN9VgceOZtpPz0DCKEJ5Tx5STub8k0/zt/VAMHQafLSuQMLd2s4ZLuO\nZptN//uAsTmxireqd37z+8ZTdBbJ8LEpJ+iCXuSfm5aUh7iw6oxvToY2AL53+jK2\nUQIDAQAB\n-----END PUBLIC KEY-----\n";
private static final String PUBLIC_KEY_PKCS1 = "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp0ytgXbPzqJwOOixn7bT\na6VAiNvVIOn+yDPoWbyEfc0li93BHIwv01KW/mn55IXnSbMw86rdxisvwPHFfb7U\nRuKuTzME6yrphBiancmNjushZZeBWb8jqJhnFIKbaaOqew0LZSyG9ycYODB/HDK/\npWTV4Bd1OtLHBNFrnIf+r3HOjJsa4rmKWXgSQIQO7be/iRHysApV9tfVH8lo1ETn\nA08JTrQwDgo9St9YNbydb5V0CiLiQsOaIbY09buUK9lXthh/rrRVbGbSwQM6OYdX\nIEZTN2BFvQ0p5pH8AiTwFqb0ICO46a0SjfGcXNjC/QfHljAPY3T5xyIOODM8afHC\nnwIDAQAB\n-----END RSA PUBLIC KEY-----\n";
private static final String PUBLIC_KEY_PKCS1_RAW = "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAp0ytgXbPzqJwOOixn7bTa6VAiNvVIOn+yDPoWbyEfc0li93BHIwv\n01KW/mn55IXnSbMw86rdxisvwPHFfb7URuKuTzME6yrphBiancmNjushZZeBWb8j\nqJhnFIKbaaOqew0LZSyG9ycYODB/HDK/pWTV4Bd1OtLHBNFrnIf+r3HOjJsa4rmK\nWXgSQIQO7be/iRHysApV9tfVH8lo1ETnA08JTrQwDgo9St9YNbydb5V0CiLiQsOa\nIbY09buUK9lXthh/rrRVbGbSwQM6OYdXIEZTN2BFvQ0p5pH8AiTwFqb0ICO46a0S\njfGcXNjC/QfHljAPY3T5xyIOODM8afHCnwIDAQAB\n-----END RSA PUBLIC KEY-----\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"; 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 @Test
@ -67,7 +75,7 @@ public class PemsTest {
@Test @Test
public void testPrivateKeySpecPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException { public void testPrivateKeySpecPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
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").trim());
} }
@ -75,14 +83,41 @@ public class PemsTest {
@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").trim());
} }
@Test
public void testRSAPKCS1PublicKeySpecPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
String encoded = Pems.pem(KeyFactory.getInstance("RSA").generatePublic(
Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY_PKCS1))));
assertEquals(encoded, PUBLIC_KEY_PKCS1.replaceAll("\n", "\n").trim());
}
@Test
public void testRSAPKCS1RawPublicKeySpecPem() throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
KeySpec spec = Pems.publicKeySpec(Payloads.newStringPayload(PUBLIC_KEY_PKCS1_RAW));
String encoded = Pems.pem(KeyFactory.getInstance("RSA").generatePublic(spec));
KeySpec generatedSpec = Pems.publicKeySpec(encoded);
assertTrue(spec instanceof RSAPublicKeySpec);
assertTrue(generatedSpec instanceof RSAPublicKeySpec);
// The encoded is different because the generatePublic method adds the
// algorithm to the key.
assertNotEquals(encoded, PUBLIC_KEY_PKCS1_RAW.replaceAll("\n", "\n").trim());
// Verify that the modulus and public exponent of the encoded key are the
// same than the ones in the original key
assertEquals(RSAPublicKeySpec.class.cast(spec).getModulus(), RSAPublicKeySpec.class.cast(generatedSpec)
.getModulus());
assertEquals(RSAPublicKeySpec.class.cast(spec).getPublicExponent(), RSAPublicKeySpec.class.cast(generatedSpec)
.getPublicExponent());
}
@Test @Test
public void testX509CertificatePem() throws IOException, CertificateException { public void testX509CertificatePem() throws IOException, CertificateException {
String encoded = Pems.pem(Pems.x509Certificate(Payloads.newStringPayload(CERTIFICATE), CertificateFactory String encoded = Pems.pem(Pems.x509Certificate(Payloads.newStringPayload(CERTIFICATE),
.getInstance("X.509"))); CertificateFactory.getInstance("X.509")));
assertEquals(encoded, CERTIFICATE.replaceAll("\n", "\n").trim()); assertEquals(encoded, CERTIFICATE.replaceAll("\n", "\n").trim());
} }