diff --git a/src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java b/src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java index 001743f5..1e9ee292 100644 --- a/src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java +++ b/src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java @@ -45,7 +45,7 @@ import java.nio.charset.Charset; import java.security.*; import java.security.spec.ECPrivateKeySpec; -import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPrivateCrtKeySpec; import java.util.Arrays; /** @@ -245,13 +245,9 @@ private KeyPair readUnencrypted(final PlainBuffer keyBuffer, final PublicKey pub kp = new KeyPair(publicKey, new EdDSAPrivateKey(new EdDSAPrivateKeySpec(privKey, EdDSANamedCurveTable.getByName("Ed25519")))); break; case RSA: - BigInteger n = keyBuffer.readMPInt(); // Modulus - keyBuffer.readMPInt(); // Public Exponent - BigInteger d = keyBuffer.readMPInt(); // Private Exponent - keyBuffer.readMPInt(); // iqmp (q^-1 mod p) - keyBuffer.readMPInt(); // p (Prime 1) - keyBuffer.readMPInt(); // q (Prime 2) - kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePrivate(new RSAPrivateKeySpec(n, d))); + final RSAPrivateCrtKeySpec rsaPrivateCrtKeySpec = readRsaPrivateKeySpec(keyBuffer); + final PrivateKey privateKey = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePrivate(rsaPrivateCrtKeySpec); + kp = new KeyPair(publicKey, privateKey); break; case ECDSA256: kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-256")); @@ -284,6 +280,35 @@ private PrivateKey createECDSAPrivateKey(KeyType kt, PlainBuffer buffer, String ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN()); ECPrivateKeySpec pks = new ECPrivateKeySpec(s, ecCurveSpec); return SecurityUtils.getKeyFactory(KeyAlgorithm.ECDSA).generatePrivate(pks); + } + + /** + * Read RSA Private CRT Key Spec according to OpenSSH sshkey_private_deserialize in sshkey.c + * + * @param buffer Buffer + * @return RSA Private CRT Key Specification + * @throws Buffer.BufferException Thrown on failure to read from buffer + */ + private RSAPrivateCrtKeySpec readRsaPrivateKeySpec(final PlainBuffer buffer) throws Buffer.BufferException { + final BigInteger modulus = buffer.readMPInt(); + final BigInteger publicExponent = buffer.readMPInt(); + final BigInteger privateExponent = buffer.readMPInt(); + final BigInteger crtCoefficient = buffer.readMPInt(); // iqmp (q^-1 mod p) + final BigInteger primeP = buffer.readMPInt(); + final BigInteger primeQ = buffer.readMPInt(); + // Calculate Prime Exponent P and Prime Exponent Q according to RFC 8017 Section 3.2 + final BigInteger primeExponentP = privateExponent.remainder(primeP.subtract(BigInteger.ONE)); + final BigInteger primeExponentQ = privateExponent.remainder(primeQ.subtract(BigInteger.ONE)); + return new RSAPrivateCrtKeySpec( + modulus, + publicExponent, + privateExponent, + primeP, + primeQ, + primeExponentP, + primeExponentQ, + crtCoefficient + ); } } diff --git a/src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java b/src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java index 0d6a4092..f62ab1db 100644 --- a/src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java +++ b/src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java @@ -43,6 +43,7 @@ import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; import java.text.DateFormat; import java.text.ParseException; @@ -103,7 +104,7 @@ public void assertWiped() { assertArrayEquals(new char[passwordChars.length], passwordChars); } } - }; + } final PasswordFinder onlyGivesWhenReady = new PasswordFinder() { @Override @@ -187,7 +188,7 @@ public void fromString() } @Test - public void shouldHaveCorrectFingerprintForECDSA256() throws IOException, GeneralSecurityException { + public void shouldHaveCorrectFingerprintForECDSA256() throws IOException { OpenSSHKeyFile keyFile = new OpenSSHKeyFile(); keyFile.init(new File("src/test/resources/keytypes/test_ecdsa_nistp256")); String expected = "256 MD5:53:ae:db:ed:8f:2d:02:d4:d5:6c:24:bc:a4:66:88:79 root@itgcpkerberosstack-cbgateway-0-20151117031915 (ECDSA)\n"; @@ -197,7 +198,7 @@ public void shouldHaveCorrectFingerprintForECDSA256() throws IOException, Genera } @Test - public void shouldHaveCorrectFingerprintForECDSA384() throws IOException, GeneralSecurityException { + public void shouldHaveCorrectFingerprintForECDSA384() throws IOException { OpenSSHKeyFile keyFile = new OpenSSHKeyFile(); keyFile.init(new File("src/test/resources/keytypes/test_ecdsa_nistp384")); String expected = "384 MD5:ee:9b:82:d1:47:01:16:1b:27:da:f5:27:fd:b2:eb:e2"; @@ -207,7 +208,7 @@ public void shouldHaveCorrectFingerprintForECDSA384() throws IOException, Genera } @Test - public void shouldHaveCorrectFingerprintForECDSA521() throws IOException, GeneralSecurityException { + public void shouldHaveCorrectFingerprintForECDSA521() throws IOException { OpenSSHKeyFile keyFile = new OpenSSHKeyFile(); keyFile.init(new File("src/test/resources/keytypes/test_ecdsa_nistp521")); String expected = "521 MD5:22:e2:f4:3c:61:ae:e9:85:a1:4d:d9:6c:13:aa:eb:00"; @@ -274,6 +275,35 @@ public void shouldLoadRSAPrivateKeyAsOpenSSHV1() throws IOException { assertThat(aPrivate.getAlgorithm(), equalTo("RSA")); } + @Test + public void shouldLoadRSAPrivateCrtKeyAsOpenSSHV1() throws IOException { + final OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile(); + keyFile.init(new File("src/test/resources/keyformats/rsa_opensshv1")); + final PrivateKey privateKey = keyFile.getPrivate(); + final PublicKey publicKey = keyFile.getPublic(); + + assertTrue(publicKey instanceof RSAPublicKey); + final RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; + + assertTrue(privateKey instanceof RSAPrivateCrtKey); + final RSAPrivateCrtKey rsaPrivateCrtKey = (RSAPrivateCrtKey) privateKey; + + assertEquals("Public Key Exponent not matched", rsaPublicKey.getPublicExponent(), rsaPrivateCrtKey.getPublicExponent()); + assertEquals("Public Key Modulus not matched", rsaPublicKey.getModulus(), rsaPrivateCrtKey.getModulus()); + + final BigInteger privateExponent = rsaPrivateCrtKey.getPrivateExponent(); + + final BigInteger expectedPrimeExponentP = privateExponent.mod(rsaPrivateCrtKey.getPrimeP().subtract(BigInteger.ONE)); + assertEquals("Prime Exponent P not matched", expectedPrimeExponentP, rsaPrivateCrtKey.getPrimeExponentP()); + + final BigInteger expectedPrimeExponentQ = privateExponent.mod(rsaPrivateCrtKey.getPrimeQ().subtract(BigInteger.ONE)); + assertEquals("Prime Exponent Q not matched", expectedPrimeExponentQ, rsaPrivateCrtKey.getPrimeExponentQ()); + + + final BigInteger expectedCoefficient = rsaPrivateCrtKey.getPrimeQ().modInverse(rsaPrivateCrtKey.getPrimeP()); + assertEquals("Prime CRT Coefficient not matched", expectedCoefficient, rsaPrivateCrtKey.getCrtCoefficient()); + } + @Test public void shouldLoadECDSAPrivateKeyAsOpenSSHV1() throws IOException { OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();