From 4696ab93cbb866ddc2bfed880364a616adbd2acf Mon Sep 17 00:00:00 2001 From: Vladimir Lagunov Date: Fri, 8 Jan 2021 22:02:33 +0700 Subject: [PATCH] Support ECDSA PuTTY keys. --- .../userauth/keyprovider/PuTTYKeyFile.java | 33 +++++++++++++ .../sshj/keyprovider/PuTTYKeyFileTest.java | 49 +++++++++++++++++++ .../keytypes/test_ecdsa_nistp256_puttygen.ppk | 10 ++++ .../resources/keytypes/test_ecdsa_nistp384_2 | 10 ++++ .../keytypes/test_ecdsa_nistp384_2.pub | 1 + .../test_ecdsa_nistp384_2_puttygen.ppk | 11 +++++ .../resources/keytypes/test_ecdsa_nistp521_2 | 12 +++++ .../keytypes/test_ecdsa_nistp521_2.pub | 1 + .../test_ecdsa_nistp521_2_puttygen.ppk | 12 +++++ 9 files changed, 139 insertions(+) create mode 100644 src/test/resources/keytypes/test_ecdsa_nistp256_puttygen.ppk create mode 100644 src/test/resources/keytypes/test_ecdsa_nistp384_2 create mode 100644 src/test/resources/keytypes/test_ecdsa_nistp384_2.pub create mode 100644 src/test/resources/keytypes/test_ecdsa_nistp384_2_puttygen.ppk create mode 100644 src/test/resources/keytypes/test_ecdsa_nistp521_2 create mode 100644 src/test/resources/keytypes/test_ecdsa_nistp521_2.pub create mode 100644 src/test/resources/keytypes/test_ecdsa_nistp521_2_puttygen.ppk diff --git a/src/main/java/net/schmizz/sshj/userauth/keyprovider/PuTTYKeyFile.java b/src/main/java/net/schmizz/sshj/userauth/keyprovider/PuTTYKeyFile.java index d21af5fb..a5775a6d 100644 --- a/src/main/java/net/schmizz/sshj/userauth/keyprovider/PuTTYKeyFile.java +++ b/src/main/java/net/schmizz/sshj/userauth/keyprovider/PuTTYKeyFile.java @@ -25,7 +25,11 @@ import net.schmizz.sshj.common.Base64; import net.schmizz.sshj.common.Buffer; import net.schmizz.sshj.common.KeyType; +import net.schmizz.sshj.common.SecurityUtils; import net.schmizz.sshj.userauth.password.PasswordUtils; +import org.bouncycastle.asn1.nist.NISTNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.jce.spec.ECNamedCurveSpec; import org.bouncycastle.util.encoders.Hex; import javax.crypto.Cipher; @@ -165,6 +169,35 @@ protected KeyPair readKeyPair() throws IOException { EdDSAPrivateKeySpec privateSpec = new EdDSAPrivateKeySpec(privateKeyReader.readBytes(), ed25519); return new KeyPair(new EdDSAPublicKey(publicSpec), new EdDSAPrivateKey(privateSpec)); } + final int ecdsaCurve; + switch (this.getType()) { + case ECDSA256: + ecdsaCurve = 256; + break; + case ECDSA384: + ecdsaCurve = 384; + break; + case ECDSA521: + ecdsaCurve = 521; + break; + default: + ecdsaCurve = -1; + break; + } + if (ecdsaCurve > 0) { + BigInteger s = new BigInteger(1, privateKeyReader.readBytes()); + String name = "P-" + ecdsaCurve; + X9ECParameters ecParams = NISTNamedCurves.getByName(name); + ECNamedCurveSpec ecCurveSpec = + new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN()); + ECPrivateKeySpec pks = new ECPrivateKeySpec(s, ecCurveSpec); + try { + PrivateKey privateKey = SecurityUtils.getKeyFactory(KeyAlgorithm.ECDSA).generatePrivate(pks); + return new KeyPair(getType().readPubKeyFromBuffer(publicKeyReader), privateKey); + } catch (GeneralSecurityException e) { + throw new IOException(e.getMessage(), e); + } + } throw new IOException(String.format("Unknown key type %s", this.getType())); } diff --git a/src/test/java/net/schmizz/sshj/keyprovider/PuTTYKeyFileTest.java b/src/test/java/net/schmizz/sshj/keyprovider/PuTTYKeyFileTest.java index 4aa7869a..b22b9840 100644 --- a/src/test/java/net/schmizz/sshj/keyprovider/PuTTYKeyFileTest.java +++ b/src/test/java/net/schmizz/sshj/keyprovider/PuTTYKeyFileTest.java @@ -16,6 +16,7 @@ package net.schmizz.sshj.keyprovider; import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile; +import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile; import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile; import net.schmizz.sshj.userauth.password.PasswordFinder; import net.schmizz.sshj.userauth.password.Resource; @@ -292,6 +293,54 @@ public boolean shouldRetry(Resource resource) { assertEquals(key.getPublic(), referenceKey.getPublic()); } + @Test + public void testEcDsa256() throws Exception { + // Generated with + // puttygen src/test/resources/keytypes/test_ecdsa_nistp256 -O private \ + // -o src/test/resources/keytypes/test_ecdsa_nistp256_puttygen.ppk + PuTTYKeyFile key = new PuTTYKeyFile(); + key.init(new File("src/test/resources/keytypes/test_ecdsa_nistp256_puttygen.ppk")); + assertNotNull(key.getPrivate()); + assertNotNull(key.getPublic()); + + PKCS8KeyFile referenceKey = new PKCS8KeyFile(); + referenceKey.init(new File("src/test/resources/keytypes/test_ecdsa_nistp256")); + assertEquals(key.getPrivate(), referenceKey.getPrivate()); + assertEquals(key.getPublic(), referenceKey.getPublic()); + } + + @Test + public void testEcDsa384() throws Exception { + // Generated with + // puttygen src/test/resources/keytypes/test_ecdsa_nistp384_2 -O private \ + // -o src/test/resources/keytypes/test_ecdsa_nistp384_2_puttygen.ppk + PuTTYKeyFile key = new PuTTYKeyFile(); + key.init(new File("src/test/resources/keytypes/test_ecdsa_nistp384_2_puttygen.ppk")); + assertNotNull(key.getPrivate()); + assertNotNull(key.getPublic()); + + OpenSSHKeyV1KeyFile referenceKey = new OpenSSHKeyV1KeyFile(); + referenceKey.init(new File("src/test/resources/keytypes/test_ecdsa_nistp384_2")); + assertEquals(key.getPrivate(), referenceKey.getPrivate()); + assertEquals(key.getPublic(), referenceKey.getPublic()); + } + + @Test + public void testEcDsa521() throws Exception { + // Generated with + // puttygen src/test/resources/keytypes/test_ecdsa_nistp521_2 -O private \ + // -o src/test/resources/keytypes/test_ecdsa_nistp521_2_puttygen.ppk + PuTTYKeyFile key = new PuTTYKeyFile(); + key.init(new File("src/test/resources/keytypes/test_ecdsa_nistp521_2_puttygen.ppk")); + assertNotNull(key.getPrivate()); + assertNotNull(key.getPublic()); + + OpenSSHKeyV1KeyFile referenceKey = new OpenSSHKeyV1KeyFile(); + referenceKey.init(new File("src/test/resources/keytypes/test_ecdsa_nistp521_2")); + assertEquals(key.getPrivate(), referenceKey.getPrivate()); + assertEquals(key.getPublic(), referenceKey.getPublic()); + } + @Test public void testCorrectPassphraseRsa() throws Exception { PuTTYKeyFile key = new PuTTYKeyFile(); diff --git a/src/test/resources/keytypes/test_ecdsa_nistp256_puttygen.ppk b/src/test/resources/keytypes/test_ecdsa_nistp256_puttygen.ppk new file mode 100644 index 00000000..c093165d --- /dev/null +++ b/src/test/resources/keytypes/test_ecdsa_nistp256_puttygen.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: none +Comment: imported-openssh-key +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOEQcvowiV3i +gdRO7rKPrZrao1hCQrnC4tgsxqSJdQCbABI+vHrdbJRfWZNuSk48aAtARJzJVmkn +/r63EPJgkh8= +Private-Lines: 1 +AAAAIQCVDJbEpV6gmZgo5TeJFe4cz/qfabtH8CfK+JtapXufEg== +Private-MAC: 48f3a17cf5f65f4f225e7a21f007d8270d7c8c8f diff --git a/src/test/resources/keytypes/test_ecdsa_nistp384_2 b/src/test/resources/keytypes/test_ecdsa_nistp384_2 new file mode 100644 index 00000000..065f5a17 --- /dev/null +++ b/src/test/resources/keytypes/test_ecdsa_nistp384_2 @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTItEGNGyMGn9tCIM4oC3fpU7jVxDQP +RRkB/Qv8lfM4mmSuYLPcakV6av0ATlM6mKD/TObWQNOJAYzp3MsUn1EMgVLe/sd9TY/hP6 +8Vn+zumMqjmtdX70Ty5ftEoH9zBlgAAADYhfSye4X0snsAAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEEyLRBjRsjBp/bQiDOKAt36VO41cQ0D0UZAf0L/JXzOJ +pkrmCz3GpFemr9AE5TOpig/0zm1kDTiQGM6dzLFJ9RDIFS3v7HfU2P4T+vFZ/s7pjKo5rX +V+9E8uX7RKB/cwZYAAAAMGvH38HMnj6cELCBVQnAQYHlA/Vz1+RVZHj08cey/P3PALx7MR +pV135UZNZAtWQm+wAAAAlyb290QHNzaGoBAgMEBQYH +-----END OPENSSH PRIVATE KEY----- diff --git a/src/test/resources/keytypes/test_ecdsa_nistp384_2.pub b/src/test/resources/keytypes/test_ecdsa_nistp384_2.pub new file mode 100644 index 00000000..cff42177 --- /dev/null +++ b/src/test/resources/keytypes/test_ecdsa_nistp384_2.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMi0QY0bIwaf20IgzigLd+lTuNXENA9FGQH9C/yV8ziaZK5gs9xqRXpq/QBOUzqYoP9M5tZA04kBjOncyxSfUQyBUt7+x31Nj+E/rxWf7O6YyqOa11fvRPLl+0Sgf3MGWA== root@sshj diff --git a/src/test/resources/keytypes/test_ecdsa_nistp384_2_puttygen.ppk b/src/test/resources/keytypes/test_ecdsa_nistp384_2_puttygen.ppk new file mode 100644 index 00000000..1467ce98 --- /dev/null +++ b/src/test/resources/keytypes/test_ecdsa_nistp384_2_puttygen.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: none +Comment: root@sshj +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMi0QY0bIwaf +20IgzigLd+lTuNXENA9FGQH9C/yV8ziaZK5gs9xqRXpq/QBOUzqYoP9M5tZA04kB +jOncyxSfUQyBUt7+x31Nj+E/rxWf7O6YyqOa11fvRPLl+0Sgf3MGWA== +Private-Lines: 2 +AAAAMGvH38HMnj6cELCBVQnAQYHlA/Vz1+RVZHj08cey/P3PALx7MRpV135UZNZA +tWQm+w== +Private-MAC: aa4d48441934e15491af0a30f75a02f4e324e652 diff --git a/src/test/resources/keytypes/test_ecdsa_nistp521_2 b/src/test/resources/keytypes/test_ecdsa_nistp521_2 new file mode 100644 index 00000000..70a0e95f --- /dev/null +++ b/src/test/resources/keytypes/test_ecdsa_nistp521_2 @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS +1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQA3ilD2XkhjkSuEj8KcIXWjhjKSOfQ +QEZBFZyoPT4QV8oRiGT1NRVcN86Paymq8M8WgANFVEAZp7eDqTnsKJ6LEpoAM93DJa1ERO +RWwSeDTDy5GIxMDYgg+CKZVhAMJmS/iavsSXyKUf1ibYo9b5S8y8rpzvmiRg/dQGkfloJR +BLu7czAAAAEI8uaocPLmqHAAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ +AAAIUEAN4pQ9l5IY5ErhI/CnCF1o4Yykjn0EBGQRWcqD0+EFfKEYhk9TUVXDfOj2spqvDP +FoADRVRAGae3g6k57CieixKaADPdwyWtRETkVsEng0w8uRiMTA2IIPgimVYQDCZkv4mr7E +l8ilH9Ym2KPW+UvMvK6c75okYP3UBpH5aCUQS7u3MwAAAAQSlrwjeSrVTc6OyiA3OTfac4 ++3nKcf/PRSjIhOLsGUIs2pVCxGYP8/ZfbVfkv7nHMn5Cc0fDZEs2cSWi2QhVKBSfAAAACX +Jvb3RAc3NoagEC +-----END OPENSSH PRIVATE KEY----- diff --git a/src/test/resources/keytypes/test_ecdsa_nistp521_2.pub b/src/test/resources/keytypes/test_ecdsa_nistp521_2.pub new file mode 100644 index 00000000..fb47e276 --- /dev/null +++ b/src/test/resources/keytypes/test_ecdsa_nistp521_2.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADeKUPZeSGORK4SPwpwhdaOGMpI59BARkEVnKg9PhBXyhGIZPU1FVw3zo9rKarwzxaAA0VUQBmnt4OpOewonosSmgAz3cMlrURE5FbBJ4NMPLkYjEwNiCD4IplWEAwmZL+Jq+xJfIpR/WJtij1vlLzLyunO+aJGD91AaR+WglEEu7tzMA== root@sshj diff --git a/src/test/resources/keytypes/test_ecdsa_nistp521_2_puttygen.ppk b/src/test/resources/keytypes/test_ecdsa_nistp521_2_puttygen.ppk new file mode 100644 index 00000000..94e9bb50 --- /dev/null +++ b/src/test/resources/keytypes/test_ecdsa_nistp521_2_puttygen.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: none +Comment: root@sshj +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADeKUPZeSGO +RK4SPwpwhdaOGMpI59BARkEVnKg9PhBXyhGIZPU1FVw3zo9rKarwzxaAA0VUQBmn +t4OpOewonosSmgAz3cMlrURE5FbBJ4NMPLkYjEwNiCD4IplWEAwmZL+Jq+xJfIpR +/WJtij1vlLzLyunO+aJGD91AaR+WglEEu7tzMA== +Private-Lines: 2 +AAAAQSlrwjeSrVTc6OyiA3OTfac4+3nKcf/PRSjIhOLsGUIs2pVCxGYP8/ZfbVfk +v7nHMn5Cc0fDZEs2cSWi2QhVKBSf +Private-MAC: 052d1a2fe2c5837aec9dbe0bf10f2ccc376eda43