aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Root <kenny@the-b.org>2013-04-12 23:13:55 -0700
committerKenny Root <kenny@the-b.org>2013-04-13 00:37:18 -0700
commit47ba1033e11d5548f13179d9abee2b2a8a2b54d8 (patch)
tree1d046bd82624fe5300dc478cfa3b0bd43b941171
parent1adfa1979549740665d0e6d845d7575ebad5faae (diff)
downloadsshlib-47ba1033e11d5548f13179d9abee2b2a8a2b54d8.tar.gz
sshlib-47ba1033e11d5548f13179d9abee2b2a8a2b54d8.tar.bz2
sshlib-47ba1033e11d5548f13179d9abee2b2a8a2b54d8.zip
Partial support for importing EC keys
-rw-r--r--lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java81
-rw-r--r--lib/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java75
-rw-r--r--lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java17
3 files changed, 167 insertions, 6 deletions
diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java b/lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
index bb14464..430307e 100644
--- a/lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
+++ b/lib/src/main/java/com/trilead/ssh2/crypto/PEMDecoder.java
@@ -14,6 +14,10 @@ import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPrivateKeySpec;
@@ -24,6 +28,7 @@ import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CBCMode;
import com.trilead.ssh2.crypto.cipher.DES;
import com.trilead.ssh2.crypto.cipher.DESede;
+import com.trilead.ssh2.signature.ECDSASHA2Verify;
/**
* PEM Support.
@@ -35,6 +40,7 @@ public class PEMDecoder
{
public static final int PEM_RSA_PRIVATE_KEY = 1;
public static final int PEM_DSA_PRIVATE_KEY = 2;
+ public static final int PEM_EC_PRIVATE_KEY = 3;
private static final int hexToInt(char c)
{
@@ -173,6 +179,12 @@ public class PEMDecoder
ps.pemType = PEM_RSA_PRIVATE_KEY;
break;
}
+
+ if (line.startsWith("-----BEGIN EC PRIVATE KEY-----")) {
+ endLine = "-----END EC PRIVATE KEY-----";
+ ps.pemType = PEM_EC_PRIVATE_KEY;
+ break;
+ }
}
while (true)
@@ -420,9 +432,72 @@ public class PEMDecoder
PublicKey pubKey;
PrivateKey privKey;
try {
- KeyFactory kf = KeyFactory.getInstance("RSA");
- pubKey = kf.generatePublic(pubSpec);
- privKey = kf.generatePrivate(privSpec);
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ pubKey = kf.generatePublic(pubSpec);
+ privKey = kf.generatePrivate(privSpec);
+ } catch (NoSuchAlgorithmException ex) {
+ IOException ioex = new IOException();
+ ioex.initCause(ex);
+ throw ioex;
+ } catch (InvalidKeySpecException ex) {
+ IOException ioex = new IOException("invalid keyspec");
+ ioex.initCause(ex);
+ throw ioex;
+ }
+
+ return new KeyPair(pubKey, privKey);
+ }
+
+ if (ps.pemType == PEM_EC_PRIVATE_KEY) {
+ SimpleDERReader dr = new SimpleDERReader(ps.data);
+
+ byte[] seq = dr.readSequenceAsByteArray();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in EC PRIVATE KEY DER stream.");
+
+ dr.resetInput(seq);
+
+ BigInteger version = dr.readInt();
+
+ if ((version.compareTo(BigInteger.ONE) != 0))
+ throw new IOException("Wrong version (" + version + ") in EC PRIVATE KEY DER stream.");
+
+ byte[] privateBytes = dr.readOctetString();
+
+ String curveOid = null;
+ byte[] publicBytes = null;
+ while (dr.available() > 0) {
+ int type = dr.readConstructedType();
+ SimpleDERReader cr = dr.readConstructed();
+ switch (type) {
+ case 0:
+ curveOid = cr.readOid();
+ break;
+ case 1:
+ publicBytes = cr.readOctetString();
+ break;
+ }
+ }
+
+ ECParameterSpec params = ECDSASHA2Verify.getCurveForOID(curveOid);
+ if (params == null)
+ throw new IOException("invalid OID");
+
+ BigInteger s = new BigInteger(privateBytes);
+ byte[] publicBytesSlice = new byte[publicBytes.length - 1];
+ System.arraycopy(publicBytes, 1, publicBytesSlice, 0, publicBytesSlice.length);
+ ECPoint w = ECDSASHA2Verify.decodeECPoint(publicBytesSlice, params.getCurve());
+
+ ECPrivateKeySpec privSpec = new ECPrivateKeySpec(s, params);
+ ECPublicKeySpec pubSpec = new ECPublicKeySpec(w, params);
+
+ PublicKey pubKey;
+ PrivateKey privKey;
+ try {
+ KeyFactory kf = KeyFactory.getInstance("EC");
+ pubKey = kf.generatePublic(pubSpec);
+ privKey = kf.generatePrivate(privSpec);
} catch (NoSuchAlgorithmException ex) {
IOException ioex = new IOException();
ioex.initCause(ex);
diff --git a/lib/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java b/lib/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java
index 9f626ad..ff8112a 100644
--- a/lib/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java
+++ b/lib/src/main/java/com/trilead/ssh2/crypto/SimpleDERReader.java
@@ -12,6 +12,8 @@ import java.math.BigInteger;
*/
public class SimpleDERReader
{
+ private static final int CONSTRUCTED = 0x20;
+
byte[] buffer;
int pos;
int count;
@@ -123,6 +125,30 @@ public class SimpleDERReader
return bi;
}
+ public int readConstructedType() throws IOException {
+ int type = readByte() & 0xff;
+
+ if ((type & CONSTRUCTED) != CONSTRUCTED)
+ throw new IOException("Expected constructed type, but was " + type);
+
+ return type & 0x1f;
+ }
+
+ public SimpleDERReader readConstructed() throws IOException
+ {
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ SimpleDERReader cr = new SimpleDERReader(buffer, pos, len);
+
+ pos += len;
+ count -= len;
+
+ return cr;
+ }
+
public byte[] readSequenceAsByteArray() throws IOException
{
int type = readByte() & 0xff;
@@ -139,12 +165,55 @@ public class SimpleDERReader
return b;
}
-
+
+ public String readOid() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ if (type != 0x06)
+ throw new IOException("Expected DER OID, but found type " + type);
+
+ int len = readLength();
+
+ if ((len < 1) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ byte[] b = readBytes(len);
+
+ long value = 0;
+
+ StringBuilder sb = new StringBuilder(64);
+ switch(b[0] / 40) {
+ case 0:
+ sb.append('0');
+ break;
+ case 1:
+ sb.append('1');
+ b[0] -= 40;
+ break;
+ default:
+ sb.append('2');
+ b[0] -= 80;
+ break;
+ }
+
+ for (int i = 0; i < len; i++) {
+ value = (value << 7) + (b[i] & 0x7F);
+ if ((b[i] & 0x80) == 0) {
+ sb.append('.');
+ sb.append(value);
+ value = 0;
+ }
+ }
+
+ return sb.toString();
+ }
+
public byte[] readOctetString() throws IOException
{
int type = readByte() & 0xff;
-
- if (type != 0x04)
+
+ if (type != 0x04 && type != 0x03)
throw new IOException("Expected DER Octetstring, but found type " + type);
int len = readLength();
diff --git a/lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java b/lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
index 7b4f6af..49028e6 100644
--- a/lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
+++ b/lib/src/main/java/com/trilead/ssh2/signature/ECDSASHA2Verify.java
@@ -38,8 +38,11 @@ public class ECDSASHA2Verify {
public static final String ECDSA_SHA2_PREFIX = "ecdsa-sha2-";
private static final String NISTP256 = "nistp256";
+ private static final String NISTP256_OID = "1.2.840.10045.3.1.7";
private static final String NISTP384 = "nistp384";
+ private static final String NISTP384_OID = "1.3.132.0.34";
private static final String NISTP521 = "nistp521";
+ private static final String NISTP521_OID = "1.3.132.0.35";
private static final Map<String, ECParameterSpec> CURVES = new TreeMap<String, ECParameterSpec>();
static {
@@ -55,6 +58,13 @@ public class ECDSASHA2Verify {
CURVE_SIZES.put(521, NISTP521);
}
+ private static final Map<String, String> CURVE_OIDS = new TreeMap<String, String>();
+ static {
+ CURVE_OIDS.put(NISTP256_OID, NISTP256);
+ CURVE_OIDS.put(NISTP384_OID, NISTP256);
+ CURVE_OIDS.put(NISTP521_OID, NISTP256);
+ }
+
public static int[] getCurveSizes() {
int[] keys = new int[CURVE_SIZES.size()];
int i = 0;
@@ -156,6 +166,13 @@ public class ECDSASHA2Verify {
return params.getCurve().getField().getFieldSize();
}
+ public static ECParameterSpec getCurveForOID(String oid) {
+ String name = CURVE_OIDS.get(oid);
+ if (name == null)
+ return null;
+ return CURVES.get(name);
+ }
+
public static byte[] decodeSSHECDSASignature(byte[] sig) throws IOException {
byte[] rsArray = null;