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

Issues with RSA SHA2 client certificate authentication #85

Open
jarvijuk opened this issue Apr 26, 2024 · 1 comment
Open

Issues with RSA SHA2 client certificate authentication #85

jarvijuk opened this issue Apr 26, 2024 · 1 comment

Comments

@jarvijuk
Copy link

Hello,

We are using maverick-synergy version 3.1.1 with maverick-bc module.

We have been trying to get RSA SHA2 certificate authentication working but it's failing. ECDSA and ED25519 certificate authentication is working fine.

I have created certificates for server and client using following commands. My SSH version was OpenSSH_9.6p1, LibreSSL 3.3.6

ssh-keygen -t rsa-sha2-256 -b 4096 -f ssh_ca -C ssh_ca

ssh-keygen -f ssh_host_rsa_key -N '' -b 4096 -t rsa-sha2-256
ssh-keygen -s ssh_ca -t rsa-sha2-256 -I SERVER -h ssh_host_rsa_key.pub

ssh-keygen -f ssh_client_rsa_key -N '' -b 4096 -t rsa-sha2-256
ssh-keygen -s ssh_ca -t rsa-sha2-256 -I CLIENT -n testuser ssh_client_rsa_key.pub
ssh-keygen -t rsa-sha2-256 -o -p -f ssh_client_rsa_key

This creates following certificates with certificate type of [email protected], we'll return to this later.

ssh-keygen -L -f ssh_host_rsa_key-cert.pub                                         
ssh_host_rsa_key-cert.pub:
        Type: [email protected] host certificate
        Public key: RSA-CERT SHA256:GRoyO8CKePz0v9X35Vxhjacwfx3aBD9zd0NoFMUQ/5I
        Signing CA: RSA SHA256:GUeYV9RkU3SVZEUH3ULi2p0LocWB1dJmlTwTZrUuNVw (using rsa-sha2-256)
        Key ID: "SERVER"
        Serial: 0
        Valid: forever
        Principals: (none)
        Critical Options: (none)
        Extensions: (none)

ssh-keygen -L -f ssh_client_rsa_key-cert.pub 
ssh_client_rsa_key-cert.pub:
        Type: [email protected] user certificate
        Public key: RSA-CERT SHA256:L4fS3d6ZPP9fTLJHsteDOQ3jpS5+0IVhywdjsurKL+E
        Signing CA: RSA SHA256:KqSXZ9vqV6q0mMy5vdpqPH/84ZXie+fMbsrfkuOqkJc (using rsa-sha2-256)
        Key ID: "CLIENT"
        Serial: 0
        Valid: forever
        Principals: 
                testuser
        Critical Options: (none)
        Extensions: 
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc

First issue that we ran into was in Class com.sshtools.client.PublicKeyAuthenticator (https://github.com/sshtools/maverick-synergy/blob/master/maverick-synergy-client/src/main/java/com/sshtools/client/PublicKeyAuthenticator.java) where method private boolean setupNextKey() throws IOException, SshException {}. It seems that currentKey is both instance of SshRsaPublicKey and instance of OpenSshRsaCertificate and first condition currentKey instanceof SshRsaPublicKey matches also in certificate use case and the else if block is never visited even when certificate is used. Client then connects to the server using normal public key instead of using a certificate.

if(currentKey instanceof SshRsaPublicKey && currentKey.getBitLength() >= 1024) {
	//handling of public keys
}else if(currentKey instanceof OpenSshRsaCertificate && currentKey.getBitLength() >= 1024)
{
	//handling of certificates
}

We think that the correction could be to swap the conditions, so that OpenSshRsaCertificate is checked first. Normal public keys shouldn't match OpenSshRsaCertificate condition.

if(currentKey instanceof OpenSshRsaCertificate && currentKey.getBitLength() >= 1024) {
        //handling of certificates
}else if(currentKey instanceof SshRsaPublicKey && currentKey.getBitLength() >= 1024)
{
	//handling of public keys
}

Second issue was also in the Class com.sshtools.client.PublicKeyAuthenticator (https://github.com/sshtools/maverick-synergy/blob/master/maverick-synergy-client/src/main/java/com/sshtools/client/PublicKeyAuthenticator.java). Code checks if server supports SHA512 signatures but then currentKey is populated with new OpenSshRsaSha256Certificate and signed with SHA512. This will result in invalid signature which is rejected by SSH server.

if(policy.getSupportedSignatures().contains(SshContext.PUBLIC_KEY_RSA_SHA512)) {
	signingAlgorithm = SshContext.PUBLIC_KEY_RSA_SHA512;
	currentKey = new OpenSshRsaSha256Certificate().init(currentKey.getEncoded());
} else if(policy.getSupportedSignatures().contains(SshContext.PUBLIC_KEY_RSA_SHA256)) {
	signingAlgorithm = SshContext.PUBLIC_KEY_RSA_SHA256;
	currentKey =  new OpenSshRsaSha256Certificate().init(currentKey.getEncoded());
} else {
	Log.debug("Server does not support {} signature for key {}",
			currentKey.getSigningAlgorithm(),
			SshKeyUtils.getOpenSSHFormattedKey(currentKey));
	continue;
}

Accepted certificate ID "CLIENT" (serial 0) signed by RSA CA SHA256:KqSXZ9vqV6q0mMy5vdpqPH/84ZXie+fMbsrfkuOqkJc via /etc/ssh/ssa_ca.pub
debug3: mm_answer_keyallowed: publickey authentication: RSA-CERT key is allowed
debug3: mm_request_send: entering, type 23
debug3: mm_sshkey_verify: entering [preauth]
debug3: mm_request_send: entering, type 24 [preauth]
debug3: mm_request_receive: entering
debug3: monitor_read: checking request 24
debug3: mm_answer_keyverify: publickey RSA-CERT signature unverified: incorrect signature
debug1: auth_activate_options: setting new authentication options
debug3: mm_request_send: entering, type 25
Failed publickey for testuser from 10.20.30.40 port 54306 ssh2: RSA-CERT SHA256:L4fS3d6ZPP9fTLJHsteDOQ3jpS5+0IVhywdjsurKL+E ID CLIENT (serial 0) CA RSA SHA256:KqSXZ9vqV6q0mMy5vdpqPH/84ZXie+fMbsrfkuOqkJc

This can be fixed buy using OpenSshRsaSha512Certificate for the SHA512 signature

if(currentKey instanceof OpenSshRsaCertificate && currentKey.getBitLength() >= 1024) {
	if(policy.getSupportedSignatures().contains(SshContext.PUBLIC_KEY_RSA_SHA512)) {
		signingAlgorithm = SshContext.PUBLIC_KEY_RSA_SHA512;
		currentKey = new OpenSshRsaSha512Certificate().init(currentKey.getEncoded());
	} else if(policy.getSupportedSignatures().contains(SshContext.PUBLIC_KEY_RSA_SHA256)) {
		signingAlgorithm = SshContext.PUBLIC_KEY_RSA_SHA256;
		currentKey =  new OpenSshRsaSha256Certificate().init(currentKey.getEncoded());
	} else {
		Log.debug("Server does not support {} signature for key {}",
				currentKey.getSigningAlgorithm(),
				SshKeyUtils.getOpenSSHFormattedKey(currentKey));
		continue;
	}	 
}

Final issue was in class com.sshtools.common.publickey.OpenSshCertificate (https://github.com/sshtools/maverick-synergy/blob/master/maverick-base/src/main/java/com/sshtools/common/publickey/OpenSshCertificate.java). Method public SshPublicKey init(byte[] blob, int start, int len) throws SshException.

There is a following check which compares header to algorithm.

if (!header.equals(getAlgorithm())) {
	throw new SshException("The encoded key is not DSA",
			SshException.INTERNAL_ERROR);
}

We made a debug print from the situation which returned following

Log.info("read header = "+header);
Log.info("read algorithm = "+getAlgorithm());

INFO - read header = [email protected]
INFO - read algorithm = [email protected]

It seems to us that the code is trying to compare certificate type ([email protected]) to used algorithm ([email protected]) which can never match since only supported certificate type seems to be [email protected] (See cert creation above). We managed to get this work by bypassing this check completely.

Unlike ECDSA where certificate type contains the key length, there only seems to be one type for RSA certificates. SHA256 and SHA512 keys still use the [email protected] as certificate type.

@ludup
Copy link
Contributor

ludup commented Nov 15, 2024

Thank you for the detailed report.

I'm committing fixes for this in 3.1.3-SNAPSHOT shortly and will follow up in 3.2.0-SNAPSHOT next time I visit that branch.

I'll also expand the tests to cover these new types (hence the reason for the issues in the first place!)

Regarding encoding of RSA key algorithm name... the fix is not to ignore the check, but to check the getEncodingAlgorithm method as that contains the correct encoding name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants