/*
* Copyright (c) 2013-2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.security.keystore;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.*;
import com.emc.storageos.security.ApplicationContextUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.emc.storageos.coordinator.client.service.impl.CoordinatorClientImpl;
import com.emc.storageos.coordinator.client.service.impl.CoordinatorClientInetAddressMap;
import com.emc.storageos.coordinator.client.service.impl.DualInetAddress;
import com.emc.storageos.coordinator.common.impl.ZkConnection;
import com.emc.storageos.security.exceptions.SecurityException;
import com.emc.storageos.security.keystore.impl.KeyCertificateAlgorithmValuesHolder;
import com.emc.storageos.security.keystore.impl.KeyCertificateEntry;
import com.emc.storageos.security.keystore.impl.KeyCertificatePairGenerator;
import com.emc.storageos.svcs.errorhandling.resources.BadRequestException;
/**
* Test class for the key generator NOTE: I wanted to try with an algorithm other than
* RSA, but there is no other asymmetric algorithm, supported by default in Java 6. This
* is not the case Java 7, or if we decide to add BouncyCastle to our code.
*/
public class KeyCertificatePairGeneratorTest {
static final String LOCALHOST_IP = "LOCALHOST_IP";
private static final String CERTIFICATE_CRT_PATH =
System.getProperty("user.dir")
+ "/src/main/test/com/emc/storageos/security/keystore/RSACorporateServerCAv2.crt";
private static final String CERTIFICATE_PEM_PATH =
System.getProperty("user.dir")
+ "/src/main/test/com/emc/storageos/security/keystore/RSACorporateServerCAv2.pem";
private static final String CERTIFICATE_DER_PATH =
System.getProperty("user.dir")
+ "/src/main/test/com/emc/storageos/security/keystore/RSACorporateServerCAv2.der";
private static final String CERTIFICATE_P7B_PATH =
System.getProperty("user.dir")
+ "/src/main/test/com/emc/storageos/security/keystore/RSACorporateServerCAv2.p7b";
private static final String CERTIFICATE_P7C_PATH =
System.getProperty("user.dir")
+ "/src/main/test/com/emc/storageos/security/keystore/RSACorporateServerCAv2.p7c";
private static final String CERTIFICATE_SHA_FINGERPRINT =
"d67b3f1a57ec78fb2b9c1c3a8acdf789406dcc5a";
private static final BigInteger CERTIFICATE_SERIAL_NUMBER = new BigInteger(
"9f6c8bd34b73594063e476d369fdfdfa", 16);
private static final BigInteger KEY_MODULUS =
new BigInteger(
"141415449813198352640584459030316068987610408486312003311918794919743922191781906413424250578755834293932792374821645795933424858325487735796194380710154232701027417945794983447064558015683473053823226311432078689729475996612733400159900353688889555372959862723633150803411582349992187423815121258678374346333");
private static final BigInteger KEY_PUBLIC_EXPONENT = new BigInteger("65537");
private static final BigInteger KEY_PRIVATE_EXPONENT =
new BigInteger(
"75827094952249924624910790466215069049447142295458902732565396223121613283205028812337177313396383309017688585739467093624921539686222508256619417009254757657631145102770592566649719745911311814270927270971024131074790025383305539659913846230309109245669666976136262822378774211143821358923578648285938595297");
private static final BigInteger KEY_PRIME_P =
new BigInteger(
"12935208964662720932996951651139035181084113865514099158277613600888578618673901494975661202391906427707617503913003448716646973088555538399634321073962149");
private static final BigInteger KEY_PRIME_Q =
new BigInteger(
"10932598785185971625219171195400373011175996212037831824584500027127226918142419249584498881264583931973989625943739172400847843303631949509141140671440217");
private static final BigInteger KEY_PRIME_EXPONENT_P =
new BigInteger(
"10701543098768828737767897806197392122287877897800852287437816949817335744762484383746286073419429734505806202025772418472261453543211945802038129432690353");
private static final BigInteger KEY_PRIME_EXPONENT_Q =
new BigInteger(
"4531214135557113497044850194251496591277806202718824875279441754075665117071311382523822315818391036272784537124216658375028304128302403901869185407606857");
private static final BigInteger KEY_CRT_COEFFICIENT =
new BigInteger(
"4545576255440851624075975079297381702161407904537264067567341406895999109859305066801694272317323799472024724727092257412713562533441587060260584920416954");
private static final String RSA_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\r\n"
+ "MIICXAIBAAKBgQDJYd1wxB87o/tq+1MZdIzveINYXjPDUmgjT5ZCZlZjjV2HdG1w\r\n"
+ "h9Pp6Y6cav45c7BcvQq4fsmT5pZVzOG5U5cJTEdjPJ9nOJrh11gQJVenOXqGRI73\r\n"
+ "pZzhiG4uWNFV84skxU4ObPTsernN8kTrNQoygR8wTsZziwAaY2zhvyuuXQIDAQAB\r\n"
+ "AoGAa/s65s1yxeMO2/V5QIv7Sii/nPGeJdyZFF4HfwEqz2SswwYN7KoYWjOvEXZZ\r\n"
+ "bOr4pTGEfxsU8WZSNB2Q53PH5vMQ1B1/72QZUnLoKLOGR/EOX5RA1PhM6Ea5u18P\r\n"
+ "wzJ9TcvHsH1QIxEH0pep2qhWIl8D6JjdYaIliPynCGOONeECQQD2+fwAIUPZa5Wg\r\n"
+ "s3t/z6azFOlKdnITdkqm1B0erwl9ZgN5ZiS9P5BS30PNXxJ0EiZdXJzPcBIZFuY2\r\n"
+ "NJZAsvylAkEA0L1uAkdGroFYDp1xu3HJcO0JGkrO3Rs6W6G697damYgYB4pfTTng\r\n"
+ "pUd9FgLu4HLb6Y0muGpB8sXirb2aB1YlWQJBAMxUDZTd8JBUXbpSQ35+gV/vkQK1\r\n"
+ "87L+Tsyu+FiGX8eLOpyZURPxHqoxZJroaQ/2ZB8hm+pSweZX96Yo45YrfrECQFaE\r\n"
+ "HQNuvVn4nCG6mfgB6mcWp64xEVpNPbva5Z5kbXWzFZqSfHuKoJSAc9TatF1s3b8I\r\n"
+ "VOMcj2brI8+1BRFDYEkCQFbKUFyfFOjVZczK2NdbXSrO3iSTIZFOGETa9dxrchRP\r\n"
+ "/TXoqhkTuzu9y/E8QVQXBdCEXD72v5sn1kl1hd8Pgro=\r\n"
+ "-----END RSA PRIVATE KEY-----";
private KeyCertificateAlgorithmValuesHolder defaultValues;
private String localhostIP = "localhost";
private String localhostName = null;
private final String server = "localhost";
private final String coordinatorServer = "coordinator://" + server + ":2181";
private final String defaultOvfPropsLocation = "/etc/config.defaults";
private final String ovfPropsLocation = "/etc/ovfenv.properties";
private final CoordinatorClientImpl coordinatorClient = new CoordinatorClientImpl();
@Before
public void setup() throws IOException, URISyntaxException {
ApplicationContextUtil.initContext(System.getProperty("buildType"), ApplicationContextUtil.SECURITY_CONTEXTS);
List<URI> uri = new ArrayList<URI>();
uri.add(URI.create(coordinatorServer));
ZkConnection connection = new ZkConnection();
connection.setServer(uri);
connection.build();
coordinatorClient.setZkConnection(connection);
CoordinatorClientInetAddressMap map = new CoordinatorClientInetAddressMap();
map.setNodeId("standalone");
DualInetAddress localAddress = DualInetAddress.fromAddresses("127.0.0.1", "::1");
map.setDualInetAddress(localAddress);
Map<String, DualInetAddress> controllerNodeIPLookupMap =
new HashMap<String, DualInetAddress>();
controllerNodeIPLookupMap.put("localhost", localAddress);
map.setControllerNodeIPLookupMap(controllerNodeIPLookupMap);
coordinatorClient.setInetAddessLookupMap(map);
coordinatorClient.start();
FileInputStream is = new FileInputStream(defaultOvfPropsLocation);
Properties defaultProp = new Properties();
defaultProp.load(is);
is.close();
is = new FileInputStream(ovfPropsLocation);
Properties ovfProps = new Properties();
ovfProps.load(is);
is.close();
CoordinatorClientImpl.setDefaultProperties(defaultProp);
CoordinatorClientImpl.setOvfProperties(ovfProps);
defaultValues = new KeyCertificateAlgorithmValuesHolder(coordinatorClient);
String envVar = System.getenv(LOCALHOST_IP);
if (StringUtils.isNotBlank(envVar)) {
localhostIP = envVar;
}
InetAddress localhost = InetAddress.getByName(localhostIP);
localhostName = localhost.getCanonicalHostName();
}
@Test
public void testGenerate() throws GeneralSecurityException, IOException {
// test the defaults
KeyCertificatePairGenerator gen = new KeyCertificatePairGenerator();
gen.setKeyCertificateAlgorithmValuesHolder(defaultValues);
KeyCertificateEntry pair = gen.generateKeyCertificatePair();
assertCertInformation((X509Certificate) pair.getCertificateChain()[0],
defaultValues);
}
@Test
public void testLoadKey() throws SecurityException, NoSuchAlgorithmException {
// test the defaults
KeyCertificatePairGenerator rsaGen = new KeyCertificatePairGenerator();
rsaGen.setKeyCertificateAlgorithmValuesHolder(defaultValues);
KeyCertificateEntry pair = rsaGen.generateKeyCertificatePair();
byte[] RSAKeyBytes = pair.getKey();
PrivateKey loadedRSAKey =
KeyCertificatePairGenerator.loadPrivateKeyFromBytes(RSAKeyBytes);
byte[] loadedRSAKeyBytes = loadedRSAKey.getEncoded();
Assert.assertEquals(RSAKeyBytes.length, loadedRSAKeyBytes.length);
Assert.assertArrayEquals(RSAKeyBytes, loadedRSAKeyBytes);
}
@Test
public void testVerifyKeyCertificateEntry() {
KeyCertificatePairGenerator gen = new KeyCertificatePairGenerator();
gen.setKeyCertificateAlgorithmValuesHolder(defaultValues);
// test a generated entry
KeyCertificateEntry entry1 = gen.generateKeyCertificatePair();
try {
new KeyCertificatePairGenerator().verifyKeyCertificateEntry(entry1);
} catch (SecurityException e) {
System.err.println(e.getMessage());
System.err.println(e);
Assert.fail();
} catch (BadRequestException e) {
System.err.println(e.getMessage());
System.err.println(e);
Assert.fail();
}
// test values from 2 different generated entries
KeyCertificateEntry entry2 = gen.generateKeyCertificatePair();
KeyCertificateEntry hybridEntry =
new KeyCertificateEntry(entry1.getKey(), entry2.getCertificateChain());
boolean exceptionThrown = false;
try {
new KeyCertificatePairGenerator().verifyKeyCertificateEntry(hybridEntry);
} catch (SecurityException e) {
Assert.fail();
} catch (BadRequestException e) {
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
}
@Test
public void testGetCertificateChainFromString() throws IOException, CertificateException {
String crtCertificate = getStringFromFile(CERTIFICATE_CRT_PATH);
String derCertificate = getStringFromFile(CERTIFICATE_DER_PATH);
String p7bCertificate = getStringFromFile(CERTIFICATE_P7B_PATH);
String p7cCertificate = getStringFromFile(CERTIFICATE_P7C_PATH);
String pemCertificate = getStringFromFile(CERTIFICATE_PEM_PATH);
Certificate[] pemCertChain =
KeyCertificatePairGenerator.getCertificateChainFromString(pemCertificate);
Assert.assertEquals(3, pemCertChain.length);
X509Certificate cert = (X509Certificate) pemCertChain[0];
Assert.assertEquals(CERTIFICATE_SERIAL_NUMBER, cert.getSerialNumber());
Assert.assertEquals(CERTIFICATE_SHA_FINGERPRINT,
DigestUtils.shaHex(cert.getEncoded()));
Certificate[] certChain =
KeyCertificatePairGenerator.getCertificateChainFromString(crtCertificate);
Assert.assertArrayEquals(pemCertChain, certChain);
boolean exceptionThrown = false;
try {
certChain =
KeyCertificatePairGenerator.getCertificateChainFromString(derCertificate);
Assert.assertArrayEquals(pemCertChain, certChain);
} catch (CertificateException e) {
// we won't support DER encoded certificate chains with more than 1
// certificate
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
exceptionThrown = false;
try {
certChain =
KeyCertificatePairGenerator
.getCertificateChainFromString(p7bCertificate);
Assert.fail();
} catch (CertificateException e) {
// we won't support p7c encoded certificate chains with more than 1
// certificate
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
exceptionThrown = false;
try {
certChain =
KeyCertificatePairGenerator
.getCertificateChainFromString(p7cCertificate);
Assert.fail();
} catch (CertificateException e) {
// we won't support p7c encoded certificate chains with more than 1
// certificate
exceptionThrown = true;
}
Assert.assertTrue(exceptionThrown);
}
/**
* @throws FileNotFoundException
* @throws IOException
*/
private String getStringFromFile(String filePath) throws FileNotFoundException,
IOException {
File file;
FileInputStream fis;
byte[] data;
file = new File(filePath);
fis = new FileInputStream(file);
data = new byte[(int) file.length()];
fis.read(data);
fis.close();
return new String(data);
}
@Test
public void testGetCertificateChainAsString() throws IOException,
CertificateException {
String pemCertificate = getStringFromFile(CERTIFICATE_PEM_PATH);
Certificate[] pemCertChain =
KeyCertificatePairGenerator.getCertificateChainFromString(pemCertificate);
String generatedPemCertificate =
KeyCertificatePairGenerator.getCertificateChainAsString(pemCertChain);
Certificate[] generatedPemCertChain =
KeyCertificatePairGenerator
.getCertificateChainFromString(generatedPemCertificate);
Assert.assertArrayEquals(pemCertChain, generatedPemCertChain);
}
/*
* @Test
* public void testGetKeyBytesFromString() throws FileNotFoundException,
* SecurityException, NoSuchAlgorithmException, Exception {
* byte[] keyBytes =
* KeyCertificatePairGenerator.loadPrivateKeyFromPEMString(RSA_PRIVATE_KEY);
* RSAPrivateCrtKey rsaKey =
* (RSAPrivateCrtKey) KeyCertificatePairGenerator
* .loadPrivateKeyFromBytes(keyBytes);
* Assert.assertEquals(KEY_MODULUS, rsaKey.getModulus());
* Assert.assertEquals(KEY_PUBLIC_EXPONENT, rsaKey.getPublicExponent());
* Assert.assertEquals(KEY_PRIVATE_EXPONENT, rsaKey.getPrivateExponent());
* Assert.assertEquals(KEY_PRIME_P, rsaKey.getPrimeP());
* Assert.assertEquals(KEY_PRIME_Q, rsaKey.getPrimeQ());
* Assert.assertEquals(KEY_PRIME_EXPONENT_P, rsaKey.getPrimeExponentP());
* Assert.assertEquals(KEY_PRIME_EXPONENT_Q, rsaKey.getPrimeExponentQ());
* Assert.assertEquals(KEY_CRT_COEFFICIENT, rsaKey.getCrtCoefficient());
* }
*/
/**
* @param x509Certificate
*/
private void assertCertInformation(X509Certificate x509Certificate, KeyCertificateAlgorithmValuesHolder valuesHolder) {
try {
x509Certificate.checkValidity();
} catch (CertificateExpiredException e) {
Assert.fail("The certificate that was created is expired. Details: "
+ e.getMessage());
} catch (CertificateNotYetValidException e) {
Assert.fail("The certificate that was created is not yet valid. Details: "
+ e.getMessage());
}
// NOTBEFORE should be sometime in past say at least 13 days ago.
checkNotBefore(x509Certificate.getNotBefore());
Assert.assertEquals(x509Certificate.getNotAfter().getTime() - x509Certificate.getNotBefore().getTime(),
valuesHolder.getCertificateValidityInDays() * 86400000L);
String issuerDN = null;
if (StringUtils.isNotBlank(localhostName)) {
issuerDN =
String.format(KeyCertificatePairGenerator.CERTIFICATE_COMMON_NAME_FORMAT,
localhostName);
} else {
issuerDN =
String.format(KeyCertificatePairGenerator.CERTIFICATE_COMMON_NAME_FORMAT, localhostIP);
}
Assert.assertEquals(x509Certificate.getSubjectDN().getName(), issuerDN);
Assert.assertEquals(x509Certificate.getIssuerDN().getName(), issuerDN);
Assert.assertEquals(x509Certificate.getPublicKey().getAlgorithm(),
KeyCertificateAlgorithmValuesHolder.DEFAULT_KEY_ALGORITHM);
Assert.assertEquals(x509Certificate.getVersion(), 3);
Assert.assertEquals(x509Certificate.getSigAlgName(), valuesHolder.getSigningAlgorithm());
}
private void checkNotBefore(Date notBefore) {
long diff = new Date().getTime() - notBefore.getTime();
long daysDiff = diff / 24 / 3600 / 1000;
if (diff < 13) {
Assert.fail("The diff of NotBefore from today should be > 13 days but now it is " + daysDiff);
}
}
}