Skip to content

Commit

Permalink
[JENKINS-62311] Add support for RFC 8332 (#47)
Browse files Browse the repository at this point in the history
This adds support for the two new key algorithms specified in RFC 8332: RSA with SHA-256 and SHA-512. These still use the same key format as the SHA-1 variant (ssh-rsa), though the signature names are updated to rsa-sha2-256 and rsa-sha2-512.

Signed-off-by: Matt Sicker <boards@gmail.com>
  • Loading branch information
jvz authored Jul 14, 2020
1 parent 4aada2d commit 480bf7a
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 12 deletions.
5 changes: 4 additions & 1 deletion src/com/trilead/ssh2/signature/KeyAlgorithmManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ private static Collection<KeyAlgorithm<PublicKey, PrivateKey>> buildSupportAlgor
// we don't use ECDSA algorithms in this case
}


// https://tools.ietf.org/html/rfc8332
algorithms.add(new RSAKeyAlgorithm("SHA256withRSA", "rsa-sha2-256"));
algorithms.add(new RSAKeyAlgorithm("SHA512withRSA", "rsa-sha2-512"));
// TODO: remove SHA-1 support soon
algorithms.add(new RSAKeyAlgorithm());
algorithms.add(new DSAKeyAlgorithm());

Expand Down
15 changes: 11 additions & 4 deletions src/com/trilead/ssh2/signature/RSAKeyAlgorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,15 @@
*/
public class RSAKeyAlgorithm extends KeyAlgorithm<RSAPublicKey, RSAPrivateKey> {

private static final String SSH_RSA = "ssh-rsa";

public RSAKeyAlgorithm() {
super("SHA1WithRSA", "ssh-rsa", RSAPrivateKey.class);
this("SHA1WithRSA", SSH_RSA);
}

// https://tools.ietf.org/html/rfc8332
public RSAKeyAlgorithm(String signatureAlgorithm, String keyFormat) {
super(signatureAlgorithm, keyFormat, RSAPrivateKey.class);
}

@Override
Expand Down Expand Up @@ -86,7 +93,7 @@ public byte[] decodeSignature(byte[] encodedSignature) throws IOException {
public byte[] encodePublicKey(RSAPublicKey publicKey) throws IOException {
final TypesWriter tw = new TypesWriter();

tw.writeString(getKeyFormat());
tw.writeString(SSH_RSA);
tw.writeMPInt(publicKey.getPublicExponent());
tw.writeMPInt(publicKey.getModulus());

Expand All @@ -98,7 +105,7 @@ public RSAPublicKey decodePublicKey(byte[] encodedPublicKey) throws IOException
final TypesReader tr = new TypesReader(encodedPublicKey);

final String key_format = tr.readString();
if (!key_format.equals(getKeyFormat())) {
if (!key_format.equals(SSH_RSA)) {
throw new IOWarningException("Unsupported key format found '" + key_format + "' while expecting " + getKeyFormat());
}

Expand All @@ -119,7 +126,7 @@ public RSAPublicKey decodePublicKey(byte[] encodedPublicKey) throws IOException

@Override
public List<CertificateDecoder> getCertificateDecoders() {
return Arrays.asList(new RSACertificateDecoder(), new OpenSshCertificateDecoder("ssh-rsa") {
return Arrays.asList(new RSACertificateDecoder(), new OpenSshCertificateDecoder(SSH_RSA) {
@Override
KeyPair generateKeyPair(TypesReader typesReader) throws GeneralSecurityException, IOException {
BigInteger n = typesReader.readMPINT();
Expand Down
6 changes: 3 additions & 3 deletions test/com/trilead/ssh2/KnownHostsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ public void testKnownHostsPreferredAlgorithmsSshDssOnly() throws IOException, No
KnownHosts testCase = new KnownHosts();
KeyPairGenerator dsaGenerator = KeyPairGenerator.getInstance("DSA");
testCase.addHostkey(new String[]{"localhost"}, "ssh-dss", new DSAKeyAlgorithm().encodePublicKey((DSAPublicKey) dsaGenerator.generateKeyPair().getPublic()));
assertArrayEquals(new String[]{"ssh-dss", "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "ssh-rsa"}, testCase.getPreferredServerHostkeyAlgorithmOrder("localhost"));
assertArrayEquals(new String[]{"ssh-dss", "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "rsa-sha2-256", "rsa-sha2-512", "ssh-rsa"}, testCase.getPreferredServerHostkeyAlgorithmOrder("localhost"));
}

@Test
public void testKnownHostsPreferredAlgorithmsSshRsaOnly() throws IOException, NoSuchAlgorithmException {
KnownHosts testCase = new KnownHosts();
KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
testCase.addHostkey(new String[]{"localhost"}, "ssh-rsa", new RSAKeyAlgorithm().encodePublicKey((RSAPublicKey) rsaGenerator.generateKeyPair().getPublic()));
assertArrayEquals(new String[]{"ssh-rsa", "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "ssh-dss"}, testCase.getPreferredServerHostkeyAlgorithmOrder("localhost"));
assertArrayEquals(new String[]{"ssh-rsa", "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", "rsa-sha2-256", "rsa-sha2-512", "ssh-dss"}, testCase.getPreferredServerHostkeyAlgorithmOrder("localhost"));
}


Expand All @@ -44,7 +44,7 @@ public void testKnownHostsPreferredAlgorithmsEcdsaOnly() throws IOException, NoS
KnownHosts testCase = new KnownHosts();
KeyPairGenerator ecGenerator = KeyPairGenerator.getInstance("EC");
testCase.addHostkey(new String[]{"localhost"}, "ecdsa-sha2-nistp256", new ECDSAKeyAlgorithm.ECDSASha2Nistp256().encodePublicKey((ECPublicKey) ecGenerator.generateKeyPair().getPublic()));
assertArrayEquals(new String[]{"ecdsa-sha2-nistp256", "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ssh-rsa", "ssh-dss"}, testCase.getPreferredServerHostkeyAlgorithmOrder("localhost"));
assertArrayEquals(new String[]{"ecdsa-sha2-nistp256", "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "rsa-sha2-256", "rsa-sha2-512", "ssh-rsa", "ssh-dss"}, testCase.getPreferredServerHostkeyAlgorithmOrder("localhost"));
}

@Test
Expand Down
24 changes: 20 additions & 4 deletions test/com/trilead/ssh2/signature/RSAKeyAlgorithmTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.trilead.ssh2.crypto.PEMDecoder;
import org.apache.commons.io.IOUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
Expand All @@ -12,6 +14,8 @@
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.Collection;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
Expand All @@ -21,11 +25,26 @@
/**
* @author Michael Clarke
*/
@RunWith(Parameterized.class)
public class RSAKeyAlgorithmTest {

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[]{"SHA1WithRSA", "ssh-rsa"},
new Object[]{"SHA256withRSA", "rsa-sha2-256"},
new Object[]{"SHA512withRSA", "rsa-sha2-512"}
);
}

private final RSAKeyAlgorithm testCase;

public RSAKeyAlgorithmTest(String signatureAlgorithm, String keyFormat) {
testCase = new RSAKeyAlgorithm(signatureAlgorithm, keyFormat);
}

@Test
public void testEncodeDecodePublicKey() throws GeneralSecurityException, IOException {
RSAKeyAlgorithm testCase = new RSAKeyAlgorithm();
KeyPairGenerator factory = KeyPairGenerator.getInstance("RSA");
RSAPublicKey publicKey = (RSAPublicKey) factory.generateKeyPair().getPublic();
byte[] encoded = testCase.encodePublicKey(publicKey);
Expand All @@ -35,7 +54,6 @@ public void testEncodeDecodePublicKey() throws GeneralSecurityException, IOExcep

@Test
public void testEncodeDecodeSignature() throws GeneralSecurityException, IOException {
RSAKeyAlgorithm testCase = new RSAKeyAlgorithm();
KeyPairGenerator factory = KeyPairGenerator.getInstance("RSA");
RSAPrivateKey privateKey = (RSAPrivateKey) factory.generateKeyPair().getPrivate();
byte[] signature = testCase.generateSignature("Sign Me".getBytes(StandardCharsets.UTF_8), privateKey, new SecureRandom());
Expand All @@ -46,7 +64,6 @@ public void testEncodeDecodeSignature() throws GeneralSecurityException, IOExcep

@Test
public void testSignAndVerify() throws GeneralSecurityException, IOException {
RSAKeyAlgorithm testCase = new RSAKeyAlgorithm();
byte[] message = "Signature Test".getBytes(StandardCharsets.UTF_8);
KeyPairGenerator factory = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = factory.generateKeyPair();
Expand All @@ -59,7 +76,6 @@ public void testSignAndVerify() throws GeneralSecurityException, IOException {

@Test
public void testSignAndVerifyFailure() throws GeneralSecurityException, IOException {
RSAKeyAlgorithm testCase = new RSAKeyAlgorithm();
byte[] message = "Signature Test 2".getBytes(StandardCharsets.UTF_8);
KeyPairGenerator factory = KeyPairGenerator.getInstance("RSA");
KeyPair keyPair = factory.generateKeyPair();
Expand Down

0 comments on commit 480bf7a

Please sign in to comment.