Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Sep 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
);
}
}
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