diff options
Diffstat (limited to 'lib')
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; | 
