hierynomus/sshj

org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPublicKey

druhm opened this issue · 5 comments

druhm commented

Hi,

I have a problem with SSJ when I connect to an OpenSSH server (6.6.1) with ECDSA keys (ecdsa-sha2-nistp256). While RSA and DSA keys work without any problems, ECDSA keys fail to authenticate a user. OpenSSH is well configured; using the very same keys with other tools (e.g. putty) I can successfully connect with ECDSA as well. Therefore I think its an SSHJ issue.

The problem is a bad cast from a private key to a public key in KeyType.java:133

@Override
protected boolean isMyType(Key key) {
   return ("ECDSA".equals(key.getAlgorithm()) && ECDSAVariationsAdapter.fieldSizeFromKey((ECPublicKey) key) == 256);
}

which results in the folloing error message.

org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPublicKey

Root cause is that in KeyedAuthMethod.java:64 a call to KeyType.fromKey() uses a private key as parameter (created in KeyedAuthMethod.java:59), which basically leads to the ClassCastException later on.

[main] INFO net.schmizz.sshj.transport.random.BouncyCastleRandom - Generating random seed from SecureRandom.
[main] INFO net.schmizz.sshj.transport.TransportImpl - Client identity string: SSH-2.0-SSHJ_0.22.0
[main] INFO net.schmizz.sshj.transport.TransportImpl - Server identity string: SSH-2.0-OpenSSH_6.6.1
[reader] ERROR net.schmizz.sshj.transport.TransportImpl - Dying because - org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPublicKey
java.lang.ClassCastException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPublicKey
	at net.schmizz.sshj.common.KeyType$3.isMyType(KeyType.java:133)
	at net.schmizz.sshj.common.KeyType.fromKey(KeyType.java:297)
	at net.schmizz.sshj.userauth.method.KeyedAuthMethod.putSig(KeyedAuthMethod.java:64)
	at net.schmizz.sshj.userauth.method.AuthPublickey.sendSignedReq(AuthPublickey.java:74)
	at net.schmizz.sshj.userauth.method.AuthPublickey.handle(AuthPublickey.java:45)
	at net.schmizz.sshj.userauth.UserAuthImpl.handle(UserAuthImpl.java:142)
	at net.schmizz.sshj.transport.TransportImpl.handle(TransportImpl.java:500)
	at net.schmizz.sshj.transport.Decoder.decode(Decoder.java:102)
	at net.schmizz.sshj.transport.Decoder.received(Decoder.java:170)
	at net.schmizz.sshj.transport.Reader.run(Reader.java:59)
[reader] INFO net.schmizz.sshj.transport.TransportImpl - Disconnected - UNKNOWN
[main] ERROR net.schmizz.concurrent.Promise - <<authenticated>> woke to: net.schmizz.sshj.userauth.UserAuthException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPublicKey
Exception in thread "main" net.schmizz.sshj.userauth.UserAuthException: Exhausted available authentication methods
	at net.schmizz.sshj.SSHClient.auth(SSHClient.java:230)
	at net.schmizz.sshj.SSHClient.authPublickey(SSHClient.java:345)
	at net.schmizz.sshj.SSHClient.authPublickey(SSHClient.java:364)
	at ecdsatester.EcdsaTesterMain.main(EcdsaTesterMain.java:46)
Caused by: net.schmizz.sshj.userauth.UserAuthException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPublicKey
	at net.schmizz.sshj.userauth.UserAuthException$1.chain(UserAuthException.java:33)
	at net.schmizz.sshj.userauth.UserAuthException$1.chain(UserAuthException.java:26)
	at net.schmizz.concurrent.Promise.deliverError(Promise.java:96)
	at net.schmizz.sshj.userauth.UserAuthImpl.notifyError(UserAuthImpl.java:156)
	at net.schmizz.sshj.transport.TransportImpl.die(TransportImpl.java:601)
	at net.schmizz.sshj.transport.Reader.run(Reader.java:67)
Caused by: net.schmizz.sshj.common.SSHException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPublicKey
	at net.schmizz.sshj.common.SSHException$1.chain(SSHException.java:36)
	at net.schmizz.sshj.common.SSHException$1.chain(SSHException.java:29)
	at net.schmizz.sshj.transport.TransportImpl.die(TransportImpl.java:595)
	... 1 more
Caused by: java.lang.ClassCastException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPublicKey
	at net.schmizz.sshj.common.KeyType$3.isMyType(KeyType.java:133)
	at net.schmizz.sshj.common.KeyType.fromKey(KeyType.java:297)
	at net.schmizz.sshj.userauth.method.KeyedAuthMethod.putSig(KeyedAuthMethod.java:64)
	at net.schmizz.sshj.userauth.method.AuthPublickey.sendSignedReq(AuthPublickey.java:74)
	at net.schmizz.sshj.userauth.method.AuthPublickey.handle(AuthPublickey.java:45)
	at net.schmizz.sshj.userauth.UserAuthImpl.handle(UserAuthImpl.java:142)
	at net.schmizz.sshj.transport.TransportImpl.handle(TransportImpl.java:500)
	at net.schmizz.sshj.transport.Decoder.decode(Decoder.java:102)
	at net.schmizz.sshj.transport.Decoder.received(Decoder.java:170)
	at net.schmizz.sshj.transport.Reader.run(Reader.java:59)

There is a good change that I am using your API incorrectly here, since I have not found anything about this issue online and I somehow doubt that I would be the first to stumble across this.
If so please advise how to connect with a given private ECDSA key. But even if there is something wrong with the keys themselves (encoding etc), the code path that causes the exception is at least dubious, or am I wrong?
The keys in the sample below have been created for this issue and can be thrown away. I have locally updated java.security for BC and replaced local_policy.jar and US_export_policy.jar with their unlimited version.
I am using JRE1.8.0_144, SSHJ-0.22.0 and BC-158.

Here is a minimal sample that illustrates the problem.

package tester.sshj;
import java.net.Inet4Address;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.userauth.keyprovider.KeyPairWrapper;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile;


public class EcdsaTesterMain
{

   public static void main(final String[] args) throws Exception
   {
	  final SSHClient sshClient = new SSHClient();
	  sshClient.addHostKeyVerifier( new PromiscuousVerifier() );
	  sshClient.connect( Inet4Address.getByName( "10.2.2.20" ) );

	  final String publicKey =
			"ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAy"
			+ "NTYAAABBBA1ANSWFQ+1EUqDhncD8Y3mvhEw+iAATA/Qln5NzVUzZHbvyrJ+7l3"
			+ "C00IceDdes9SIFtm0W0m7JjPvlCl5nlso= SSH Key";

	  final String privateKey =
			"-----BEGIN EC PRIVATE KEY-----\n" +
			"MHcCAQEEIGhcvG8anyHew/xZJfozh5XIc1kmZZs6o2f0l3KFs4jgoAoGCCqGSM49\n" +
			"AwEHoUQDQgAEDUA1JYVD7URSoOGdwPxjea+ETD6IABMD9CWfk3NVTNkdu/Ksn7uX\n" +
			"cLTQhx4N16z1IgW2bRbSbsmM++UKXmeWyg==\n" +
			"-----END EC PRIVATE KEY-----\n";

	  final OpenSSHKeyFile keyFile = new OpenSSHKeyFile();
	  keyFile.init( privateKey, publicKey );

	  final KeyProvider keyProvider = new KeyPairWrapper( keyFile.getPublic(), keyFile.getPrivate());

	  sshClient.authPublickey
			( "username"
			, keyProvider
			);
	  
	  System.out.println( sshClient.isAuthenticated() );
	  sshClient.disconnect();
   }
}

Thanks for your time
Thomas

THanks for the carefully described issue! Which version of SSHJ are you using? (Just to check :))

druhm commented

That was fast :). sshj-0.22.0.jar

druhm commented

I did not build this myself, but used the jar from http://repo1.maven.org/maven2/com/hierynomus/sshj/0.22.0/

Fixed it ;)

druhm commented

Nice. Thanks a lot!