Skip to content

Commit

Permalink
Update OpenSSH Key V1 parsing using CRT information for RSA Private K…
Browse files Browse the repository at this point in the history
…eys (#726)

* Update OpenSSH Key V1 parsing using CRT information for RSA Private Keys

* Remove unndeeded BC call.

Signed-off-by: Jeroen van Erp <jeroen@hierynomus.com>

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
  • Loading branch information
exceptionfactory and hierynomus authored Sep 27, 2021
1 parent 7742d9b commit 03dd1aa
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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
);
}
}
38 changes: 34 additions & 4 deletions src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -103,7 +104,7 @@ public void assertWiped() {
assertArrayEquals(new char[passwordChars.length], passwordChars);
}
}
};
}

final PasswordFinder onlyGivesWhenReady = new PasswordFinder() {
@Override
Expand Down Expand Up @@ -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";
Expand All @@ -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";
Expand All @@ -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";
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 03dd1aa

Please sign in to comment.