You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ne...@apache.org on 2016/12/06 21:13:59 UTC

[10/12] incubator-trafficcontrol git commit: TR - can now opt to use dns sec signing without jdnssec

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/693ced51/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/keys/ZoneSignerTest.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/keys/ZoneSignerTest.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/keys/ZoneSignerTest.java
new file mode 100644
index 0000000..e847991
--- /dev/null
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/keys/ZoneSignerTest.java
@@ -0,0 +1,188 @@
+package com.comcast.cdn.traffic_control.traffic_router.core.dns.keys;
+
+import com.comcast.cdn.traffic_control.traffic_router.core.IsEqualCollection;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.DNSKeyPairWrapper;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.DnsSecKeyPair;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.DnsSecKeyPairImpl;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.JDnsSecSigner;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.ZoneSignerImpl;
+import com.comcast.cdn.traffic_control.traffic_router.secure.Pkcs1;
+import com.verisignlabs.dnssec.security.DnsKeyPair;
+import com.verisignlabs.dnssec.security.JCEDnsSecSigner;
+import com.verisignlabs.dnssec.security.SignUtils;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.xbill.DNS.DSRecord;
+import org.xbill.DNS.Record;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+import static com.comcast.cdn.traffic_control.traffic_router.core.IsEqualCollection.equalTo;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.keySigningKeyRecord;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.ksk1;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.ksk2;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.origin;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.sep_1_2016;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.sep_1_2026;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.zoneSigningKeyRecord;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.zsk1;
+import static com.comcast.cdn.traffic_control.traffic_router.core.dns.keys.ZoneTestRecords.zsk2;
+import static java.util.Arrays.asList;
+import static java.util.stream.Collectors.toList;
+import static org.junit.Assert.assertThat;
+import static org.xbill.DNS.DSRecord.SHA256_DIGEST_ID;
+
+public class ZoneSignerTest {
+
+	private DnsKeyPair kskPair1;
+	private DnsKeyPair kskPair2;
+	private DnsKeyPair zskPair1;
+	private DnsKeyPair zskPair2;
+	private JSONObject ksk1Json;
+	private JSONObject ksk2Json;
+	private JSONObject zsk1Json;
+	private JSONObject zsk2Json;
+	private final long dsTtl = 1234000L;
+
+	@Before
+	public void before() throws Exception {
+		ZoneTestRecords.generateZoneRecords(false);
+		SigningData.recreateData();
+
+		kskPair1 = new DnsKeyPair(keySigningKeyRecord, ksk1.getPrivate());
+		kskPair2 = new DnsKeyPair(keySigningKeyRecord, ksk2.getPrivate());
+		zskPair1 = new DnsKeyPair(zoneSigningKeyRecord, zsk1.getPrivate());
+		zskPair2 = new DnsKeyPair(zoneSigningKeyRecord, zsk2.getPrivate());
+
+		// Data like we would fetch from traffic ops api for dnsseckeys.json
+		ksk1Json = new JSONObject("{" +
+			"'inceptionDate':1475280000," +
+			"'effectiveDate': 1475280000," +
+			"'expirationDate': 1790812800," +
+			"'ttl': 3600," +
+			"'name':'example.com.'," +
+			"'private': '" + SigningData.ksk1Private + "'," +
+			"'public': '" + SigningData.keyDnsKeyRecord + "'" +
+			"}");
+
+
+		ksk2Json = new JSONObject("{" +
+			"'inceptionDate':1475280000," +
+			"'effectiveDate': 1475280000," +
+			"'expirationDate': 1790812800," +
+			"'ttl': 3600," +
+			"'name':'example.com.'," +
+			"'private': '" + SigningData.ksk2Private + "'," +
+			"'public': '" + SigningData.keyDnsKeyRecord + "'" +
+			"}");
+
+		zsk1Json = new JSONObject("{" +
+			"'inceptionDate':1475280000," +
+			"'effectiveDate': 1475280000," +
+			"'expirationDate': 1790812800," +
+			"'ttl': 31556952," +
+			"'name':'example.com.'," +
+			"'private': '" + SigningData.zsk1Private + "'," +
+			"'public': '" + SigningData.zoneDnsKeyRecord + "'" +
+			"}");
+
+		zsk2Json = new JSONObject("{" +
+			"'inceptionDate':1475280000," +
+			"'effectiveDate': 1475280000," +
+			"'expirationDate': 1790812800," +
+			"'ttl': 315569520," +
+			"'name':'example.com.'," +
+			"'private': '" + SigningData.zsk2Private + "'," +
+			"'public': '" + SigningData.zoneDnsKeyRecord + "'" +
+			"}");
+	}
+
+	@Test
+	public void itCanReproduceResultsDirectlyFromJdnsSec() throws Exception {
+		List<DnsKeyPair> kskPairs = new ArrayList<>(asList(kskPair1, kskPair2));
+		List<DnsKeyPair> zskPairs = new ArrayList<>(asList(zskPair1, zskPair2));
+
+		JCEDnsSecSigner signer = new JCEDnsSecSigner(false);
+
+		final List<Record> signedRecords = signer.signZone(origin, ZoneTestRecords.records,
+			kskPairs, zskPairs, sep_1_2016, sep_1_2026, true, SHA256_DIGEST_ID);
+
+		assertThat(signedRecords, equalTo(SigningData.signedList));
+		assertThat(ZoneTestRecords.records, equalTo(SigningData.postZoneList));
+	}
+
+	@Test
+	public void itReturnsSameResults() throws Exception {
+		DNSKeyPairWrapper ksk1Wrapper = new DNSKeyPairWrapper(ksk1Json, 1234);
+		ksk1Wrapper.setPrivate(new Pkcs1(SigningData.ksk1Private).getPrivateKey());
+
+		assertThat(ksk1Wrapper.getDNSKEYRecord(), equalTo(kskPair1.getDNSKEYRecord()));
+
+		DNSKeyPairWrapper ksk2Wrapper = new DNSKeyPairWrapper(ksk2Json, 1234);
+		ksk2Wrapper.setPrivate(new Pkcs1(SigningData.ksk2Private).getPrivateKey());
+
+		assertThat(ksk2Wrapper.getDNSKEYRecord(), equalTo(kskPair2.getDNSKEYRecord()));
+
+		List<DnsSecKeyPair> kskWrapperPairs = new ArrayList<>(asList(ksk1Wrapper, ksk2Wrapper));
+
+		DNSKeyPairWrapper zsk1Wrapper = new DNSKeyPairWrapper(zsk1Json, 1234);
+		zsk1Wrapper.setPrivate(new Pkcs1(SigningData.zsk1Private).getPrivateKey());
+
+		assertThat(zsk1Wrapper.getDNSKEYRecord(), equalTo(zskPair1.getDNSKEYRecord()));
+
+		DNSKeyPairWrapper zsk2Wrapper = new DNSKeyPairWrapper(zsk2Json, 1234);
+		zsk2Wrapper.setPrivate(new Pkcs1(SigningData.zsk2Private).getPrivateKey());
+
+		assertThat(zsk2Wrapper.getDNSKEYRecord(), equalTo(zskPair2.getDNSKEYRecord()));
+
+		List<DnsSecKeyPair> zskWrapperPairs = new ArrayList<>(asList(zsk1Wrapper, zsk2Wrapper));
+
+		final List<Record> signedRecords2 = new JDnsSecSigner().signZone(origin, ZoneTestRecords.records,
+			kskWrapperPairs, zskWrapperPairs, sep_1_2016, sep_1_2026, true, SHA256_DIGEST_ID);
+
+		assertThat(signedRecords2, equalTo(SigningData.signedList));
+		assertThat(ZoneTestRecords.records, equalTo(SigningData.postZoneList));
+	}
+
+	@Test
+	public void itReturnsTheSameResultsWithoutJDnsSec() throws Exception {
+		DnsSecKeyPair kskPair1 = new DnsSecKeyPairImpl(ksk1Json, 1234);
+		DnsSecKeyPair kskPair2 = new DnsSecKeyPairImpl(ksk2Json, 1234);
+		DnsSecKeyPair zskPair1 = new DnsSecKeyPairImpl(zsk1Json, 1234);
+		DnsSecKeyPair zskPair2 = new DnsSecKeyPairImpl(zsk2Json, 1234);
+
+		List<DnsSecKeyPair> kskPairs = new ArrayList<>(asList(kskPair1, kskPair2));
+		List<DnsSecKeyPair> zskPairs = new ArrayList<>(asList(zskPair1, zskPair2));
+
+		final List<Record> signedRecords = new ZoneSignerImpl().signZone(origin, ZoneTestRecords.records,
+			kskPairs, zskPairs, sep_1_2016, sep_1_2026, true, SHA256_DIGEST_ID);
+
+		assertThat("Signed records not equal", signedRecords, equalTo(SigningData.signedList));
+		assertThat("Post Zone Records not equal", ZoneTestRecords.records, equalTo(SigningData.postZoneList));
+	}
+
+	@Test
+	public void itCanReproduceDSRecordsFromJdnsSec() throws Exception {
+		List<DnsKeyPair> kskPairs = new ArrayList<>(asList(kskPair1, kskPair2));
+		List<DSRecord> dsRecords = kskPairs.stream()
+			.map(dnsKeyPair -> SignUtils.calculateDSRecord(dnsKeyPair.getDNSKEYRecord(), SHA256_DIGEST_ID, dsTtl))
+			.collect(toList());
+
+		assertThat(dsRecords, IsEqualCollection.equalTo(SigningData.dsRecordList));
+	}
+
+	@Test
+	public void itReturnsSameDSRecords() throws Exception {
+		DnsSecKeyPair kskPair1 = new DnsSecKeyPairImpl(ksk1Json, 1234);
+		DnsSecKeyPair kskPair2 = new DnsSecKeyPairImpl(ksk2Json, 1234);
+
+		List<DSRecord> dsRecords = Stream.of(kskPair1, kskPair2)
+			.map(dnsSecKeyPair -> new ZoneSignerImpl().calculateDSRecord(kskPair1.getDNSKEYRecord(), SHA256_DIGEST_ID, 54321L))
+			.collect(toList());
+		assertThat(dsRecords, IsEqualCollection.equalTo(SigningData.dsRecordList));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/693ced51/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/keys/ZoneTestRecords.java
----------------------------------------------------------------------
diff --git a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/keys/ZoneTestRecords.java b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/keys/ZoneTestRecords.java
new file mode 100644
index 0000000..6ddf554
--- /dev/null
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/keys/ZoneTestRecords.java
@@ -0,0 +1,128 @@
+package com.comcast.cdn.traffic_control.traffic_router.core.dns.keys;
+
+import com.comcast.cdn.traffic_control.traffic_router.secure.Pkcs1;
+import org.xbill.DNS.AAAARecord;
+import org.xbill.DNS.ARecord;
+import org.xbill.DNS.CNAMERecord;
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.DNSKEYRecord;
+import org.xbill.DNS.NSRecord;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.SOARecord;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import static org.xbill.DNS.DNSKEYRecord.Flags.SEP_KEY;
+import static org.xbill.DNS.DNSKEYRecord.Flags.ZONE_KEY;
+import static org.xbill.DNS.DNSKEYRecord.Protocol.DNSSEC;
+import static org.xbill.DNS.DNSSEC.Algorithm.RSASHA1;
+
+public class ZoneTestRecords {
+	static List<Record> records;
+
+	static Date start;
+	static Date expiration;
+	static Name origin;
+	static Date sep_1_2016 = new Date(1472688000000L);
+	static Date sep_1_2026 = new Date(1788220800000L);
+	static DNSKEYRecord zoneSigningKeyRecord;
+	static DNSKEYRecord keySigningKeyRecord;
+
+	static KeyPair ksk1;
+	static KeyPair zsk1;
+	static KeyPair ksk2;
+	static KeyPair zsk2;
+
+	static List<KeyPair> generateKeyPairs() throws Exception {
+		KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+		keyPairGenerator.initialize(2048, SecureRandom.getInstance("SHA1PRNG","SUN"));
+		List<KeyPair> keyPairs = new ArrayList<>();
+		keyPairs.add(keyPairGenerator.generateKeyPair());
+		keyPairs.add(keyPairGenerator.generateKeyPair());
+		keyPairs.add(keyPairGenerator.generateKeyPair());
+		keyPairs.add(keyPairGenerator.generateKeyPair());
+		return keyPairs;
+	}
+
+	private static KeyPair recreateKeyPair(String publicKey, String privateKey) throws Exception {
+		Pkcs1 pkcs1 = new Pkcs1(privateKey, publicKey);
+
+		PrivateKey privateKeyCopy = pkcs1.getPrivateKey();
+		PublicKey publicKeyCopy = pkcs1.getPublicKey();
+
+		return new KeyPair(publicKeyCopy, privateKeyCopy);
+	}
+
+	static List<Record> generateZoneRecords(boolean makeNewKeyPairs) throws Exception {
+		start = new Date(System.currentTimeMillis() - (24 * 3600 * 1000));
+		expiration = new Date(System.currentTimeMillis() + (7 * 24 * 3600 * 1000));
+
+		origin = new Name("example.com.");
+
+		Duration tenYears = Duration.ofDays(3650);
+		Duration oneDay = Duration.ofDays(1);
+		Duration threeDays = Duration.ofDays(3);
+		Duration threeWeeks = Duration.ofDays(21);
+
+		long oneHour = 3600;
+		Name nameServer1 = new Name("ns1.example.com.");
+		Name nameServer2 = new Name("ns2.example.com.");
+
+		Name adminEmail = new Name("admin.example.com.");
+
+		Name webServer = new Name("www.example.com.");
+		Name ftpServer = new Name("ftp.example.com.");
+
+		Name webMirror = new Name("mirror.www.example.com.");
+		Name ftpMirror = new Name("mirror.ftp.example.com.");
+
+		records = new ArrayList<>(Arrays.asList(
+			new AAAARecord(webServer, DClass.IN, threeDays.getSeconds(), Inet6Address.getByName("2001:db8::5:6:7:8")),
+			new AAAARecord(ftpServer, DClass.IN, threeDays.getSeconds(), Inet6Address.getByName("2001:db8::12:34:56:78")),
+			new NSRecord(origin, DClass.IN, tenYears.getSeconds(), nameServer1),
+			new NSRecord(origin, DClass.IN, tenYears.getSeconds(), nameServer2),
+			new ARecord(webServer, DClass.IN, threeWeeks.getSeconds(), InetAddress.getByAddress(new byte[] {11, 22, 33, 44})),
+			new ARecord(webServer, DClass.IN, threeWeeks.getSeconds(), InetAddress.getByAddress(new byte[] {55, 66, 77, 88})),
+			new ARecord(ftpServer, DClass.IN, threeWeeks.getSeconds(), InetAddress.getByAddress(new byte[] {12, 34, 56, 78})),
+			new ARecord(ftpServer, DClass.IN, threeWeeks.getSeconds(), InetAddress.getByAddress(new byte[] {21, 43, 65, 87})),
+			new AAAARecord(webServer, DClass.IN, threeDays.getSeconds(), Inet6Address.getByName("2001:db8::4:3:2:1")),
+			new SOARecord(origin, DClass.IN, tenYears.getSeconds(), nameServer1,
+				adminEmail, 2016091400L, oneDay.getSeconds(), oneHour, threeWeeks.getSeconds(), threeDays.getSeconds()),
+			new AAAARecord(ftpServer, DClass.IN, threeDays.getSeconds(), Inet6Address.getByName("2001:db8::21:43:65:87")),
+			new CNAMERecord(webMirror, DClass.IN, tenYears.getSeconds(), webServer),
+			new CNAMERecord(ftpMirror, DClass.IN, tenYears.getSeconds(), ftpServer)
+		));
+
+		if (makeNewKeyPairs) {
+			List<KeyPair> keyPairs = generateKeyPairs();
+			ksk1 = keyPairs.get(0);
+			zsk1 = keyPairs.get(1);
+			ksk2 = keyPairs.get(2);
+			zsk2 = keyPairs.get(3);
+		} else {
+			ksk1 = recreateKeyPair(SigningData.ksk1Public, SigningData.ksk1Private);
+			zsk1 = recreateKeyPair(SigningData.zsk1Public, SigningData.zsk1Private);
+			ksk2 = recreateKeyPair(SigningData.ksk2Public, SigningData.ksk2Private);
+			zsk2 = recreateKeyPair(SigningData.zsk2Public, SigningData.zsk2Private);
+		}
+
+		zoneSigningKeyRecord = new DNSKEYRecord(origin, DClass.IN, 31556952L,
+			ZONE_KEY, DNSSEC, RSASHA1, zsk1.getPublic().getEncoded());
+
+		keySigningKeyRecord = new DNSKEYRecord(origin, DClass.IN, 315569520L,
+			ZONE_KEY | SEP_KEY, DNSSEC, RSASHA1, ksk1.getPublic().getEncoded());
+		return records;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/693ced51/traffic_router/shared/build/pmd/ruleset.xml
----------------------------------------------------------------------
diff --git a/traffic_router/shared/build/pmd/ruleset.xml b/traffic_router/shared/build/pmd/ruleset.xml
index dcbc791..807dc0f 100644
--- a/traffic_router/shared/build/pmd/ruleset.xml
+++ b/traffic_router/shared/build/pmd/ruleset.xml
@@ -80,7 +80,6 @@
 	<rule ref="rulesets/java/codesize.xml/NcssTypeCount" />
 	<rule ref="rulesets/java/codesize.xml/NcssConstructorCount" />
 
-    <rule ref="rulesets/java/controversial.xml/DontImportSun" />
     <rule ref="rulesets/java/controversial.xml/SuspiciousOctalEscape" />
     <rule ref="rulesets/java/controversial.xml/AvoidUsingNativeCode" />
     <rule ref="rulesets/java/controversial.xml/AvoidAccessibilityAlteration" />

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/693ced51/traffic_router/shared/pom.xml
----------------------------------------------------------------------
diff --git a/traffic_router/shared/pom.xml b/traffic_router/shared/pom.xml
index b7c7b23..2d27390 100644
--- a/traffic_router/shared/pom.xml
+++ b/traffic_router/shared/pom.xml
@@ -59,6 +59,11 @@ under the License.
 	
 	<dependencies>
 		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>${log4j.version}</version>
+		</dependency>
+		<dependency>
 			<groupId>com.fasterxml.jackson.core</groupId>
 			<artifactId>jackson-annotations</artifactId>
 			<version>2.4.0</version>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/693ced51/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs.java
----------------------------------------------------------------------
diff --git a/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs.java b/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs.java
new file mode 100644
index 0000000..e69e039
--- /dev/null
+++ b/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed 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 com.comcast.cdn.traffic_control.traffic_router.secure;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.KeySpec;
+
+@SuppressWarnings("PMD.AbstractNaming")
+public abstract class Pkcs {
+	private final String data;
+	private final PrivateKey privateKey;
+	private PublicKey publicKey;
+	private KeySpec keySpec;
+	private KeySpec publicKeySpec;
+
+	public Pkcs(final String data) throws IOException, GeneralSecurityException {
+		this.data = data;
+		keySpec = toKeySpec(data);
+		privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
+	}
+
+	public Pkcs(final String privateData, final String publicData) throws IOException, GeneralSecurityException {
+		this.data = privateData;
+		keySpec = toKeySpec(data);
+		privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);
+		publicKeySpec = toKeySpec(publicData);
+		publicKey = KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);
+	}
+
+	public String getData() {
+		return data;
+	}
+
+	public KeySpec getKeySpec() {
+		return keySpec;
+	}
+
+	public KeySpec getPublicKeySpec() {
+		return publicKeySpec;
+	}
+
+	public void setKeySpec(final KeySpec keySpec) {
+		this.keySpec = keySpec;
+	}
+
+	public PrivateKey getPrivateKey() {
+		return privateKey;
+	}
+
+	public PublicKey getPublicKey() {
+		return publicKey;
+	}
+
+	public abstract String getHeader();
+
+	public abstract String getFooter();
+
+	private String stripHeaderAndFooter(final String data) {
+		return data.replaceAll(getHeader(), "").replaceAll(getFooter(), "").replaceAll("\\s", "");
+	}
+
+	protected abstract KeySpec decodeKeySpec(final String data) throws IOException, GeneralSecurityException;
+
+	private KeySpec toKeySpec(final String data) throws IOException, GeneralSecurityException {
+		return decodeKeySpec(stripHeaderAndFooter(data));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/693ced51/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs1.java
----------------------------------------------------------------------
diff --git a/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs1.java b/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs1.java
new file mode 100644
index 0000000..ea50705
--- /dev/null
+++ b/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs1.java
@@ -0,0 +1,89 @@
+/*
+ *
+ * Licensed 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 com.comcast.cdn.traffic_control.traffic_router.secure;
+
+import sun.security.util.DerInputStream;
+import sun.security.util.DerValue;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.spec.KeySpec;
+import java.security.spec.RSAMultiPrimePrivateCrtKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Base64;
+
+public class Pkcs1 extends Pkcs {
+
+	// https://tools.ietf.org/html/rfc3447#appendix-A.1.1
+
+	static public final String HEADER = "-----BEGIN RSA PRIVATE KEY-----";
+	static public final String FOOTER = "-----END RSA PRIVATE KEY-----";
+	static final int PRIVATE_SEQUENCE_LENGTH = 9;
+	static final int PUBLIC_SEQUENCE_LENGTH = 2;
+
+	public Pkcs1(final String data) throws IOException, GeneralSecurityException {
+		super(data);
+	}
+
+	public Pkcs1(final String privateData, final String publicData) throws IOException, GeneralSecurityException {
+		super(privateData,publicData);
+	}
+
+	@Override
+	public String getHeader() {
+		return HEADER;
+	}
+
+	@Override
+	public String getFooter() {
+		return FOOTER;
+	}
+
+	@Override
+	protected KeySpec decodeKeySpec(final String data) throws IOException, GeneralSecurityException {
+		final String pemData = data.replaceAll(HEADER, "").replaceAll(FOOTER, "").replaceAll("\\s", "");
+
+		final DerInputStream derInputStream = new DerInputStream(Base64.getDecoder().decode(pemData));
+		final DerValue[] derSequence = derInputStream.getSequence(0);
+
+		if (derSequence.length != PUBLIC_SEQUENCE_LENGTH && derSequence.length != PRIVATE_SEQUENCE_LENGTH) {
+			throw new GeneralSecurityException("Invalid PKCS1 key! Missing Key Data, incorrect number of DER values for either public or private key");
+		}
+
+		if (derSequence.length == PUBLIC_SEQUENCE_LENGTH) {
+			final BigInteger n = derSequence[0].getBigInteger();
+			final BigInteger e = derSequence[1].getBigInteger();
+			return new RSAPublicKeySpec(n,e);
+		}
+
+		// man 3 rsa
+		// -- or --
+		// http://linux.die.net/man/3/rsa
+
+		// We don't need the version data at derSequence[0]
+		final BigInteger n = derSequence[1].getBigInteger();
+		final BigInteger e = derSequence[2].getBigInteger();
+		final BigInteger d = derSequence[3].getBigInteger();
+		final BigInteger p = derSequence[4].getBigInteger();
+		final BigInteger q = derSequence[5].getBigInteger();
+		final BigInteger dmp1 = derSequence[6].getBigInteger();
+		final BigInteger dmq1 = derSequence[7].getBigInteger();
+		final BigInteger iqmp = derSequence[8].getBigInteger();
+
+		return new RSAMultiPrimePrivateCrtKeySpec(n, e, d, p, q, dmp1, dmq1, iqmp, null);
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/693ced51/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs8.java
----------------------------------------------------------------------
diff --git a/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs8.java b/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs8.java
new file mode 100644
index 0000000..5a2f764
--- /dev/null
+++ b/traffic_router/shared/src/main/java/com/comcast/cdn/traffic_control/traffic_router/secure/Pkcs8.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Licensed 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 com.comcast.cdn.traffic_control.traffic_router.secure;
+
+import org.apache.log4j.Logger;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+
+public class Pkcs8 extends Pkcs {
+	private static final Logger LOGGER = Logger.getLogger(Pkcs8.class);
+	public static final String HEADER = "-----BEGIN PRIVATE KEY-----";
+	public static final String FOOTER = "-----END PRIVATE KEY-----";
+
+	public Pkcs8(final String data) throws IOException, GeneralSecurityException {
+		super(data);
+	}
+
+	@Override
+	public String getHeader() {
+		return HEADER;
+	}
+
+	@Override
+	public String getFooter() {
+		return FOOTER;
+	}
+
+	@Override
+	protected KeySpec decodeKeySpec(final String data) throws IOException, GeneralSecurityException {
+		try {
+			return new PKCS8EncodedKeySpec(Base64.getDecoder().decode((data.getBytes())));
+		} catch (Exception e) {
+			LOGGER.error("Failed to create PKCS8 Encoded Key Spec " + e.getClass().getCanonicalName() + ": " + e.getMessage(), e);
+		}
+		return null;
+	}
+}