com.sshtools.ssh.components.jce.DiffieHellmanEcdh Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2003-2016 SSHTOOLS Limited. All Rights Reserved.
*
* For product documentation visit https://www.sshtools.com/
*
* This file is part of J2SSH Maverick.
*
* J2SSH Maverick is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* J2SSH Maverick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with J2SSH Maverick. If not, see .
*/
package com.sshtools.ssh.components.jce;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import javax.crypto.KeyAgreement;
import com.sshtools.logging.LoggerFactory;
import com.sshtools.logging.LoggerLevel;
import com.sshtools.ssh.SshException;
import com.sshtools.ssh.components.ComponentManager;
import com.sshtools.ssh.components.Digest;
import com.sshtools.ssh.components.SshKeyExchange;
import com.sshtools.ssh.components.SshKeyExchangeClient;
import com.sshtools.util.ByteArrayReader;
import com.sshtools.util.ByteArrayWriter;
public class DiffieHellmanEcdh extends SshKeyExchangeClient implements
SshKeyExchange {
public static final int SSH_MSG_KEX_ECDH_INIT = 30;
public static final int SSH_MSG_KEX_ECDH_REPLY = 31;
String name;
String curve;
byte[] Q_S;
byte[] Q_C;
String clientId;
String serverId;
byte[] clientKexInit;
byte[] serverKexInit;
protected DiffieHellmanEcdh(String name, String curve, String hashAlgorithm) {
super(hashAlgorithm);
this.name = name;
this.curve = curve;
}
@Override
public String getAlgorithm() {
return name;
}
@Override
public void performClientExchange(String clientId, String serverId,
byte[] clientKexInit, byte[] serverKexInit) throws SshException {
this.clientId = clientId;
this.serverId = serverId;
this.clientKexInit = clientKexInit;
this.serverKexInit = serverKexInit;
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec namedSpec = new ECGenParameterSpec(curve);
keyGen.initialize(namedSpec, new SecureRandom());
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
KeyPair keyPair = keyGen.generateKeyPair();
keyAgreement.init(keyPair.getPrivate());
ECPublicKey ec = (ECPublicKey) keyPair.getPublic();
ByteArrayWriter msg = new ByteArrayWriter();
Q_C = ECUtils.toByteArray(ec.getW(), ec.getParams().getCurve());
try {
msg.write(SSH_MSG_KEX_ECDH_INIT);
msg.writeBinaryString(Q_C);
if(LoggerFactory.getInstance().isLevelEnabled(LoggerLevel.DEBUG)) {
LoggerFactory.getInstance().log(LoggerLevel.DEBUG, this, "Sending SSH_MSG_KEX_ECDH_INIT");
}
transport.sendMessage(msg.toByteArray(), true);
} finally {
msg.close();
}
byte[] resp = transport.nextMessage();
if(resp[0]!=SSH_MSG_KEX_ECDH_REPLY) {
throw new SshException("Expected SSH_MSG_KEX_ECDH_REPLY but got message id " + resp[0], SshException.KEY_EXCHANGE_FAILED);
}
ByteArrayReader reply = new ByteArrayReader(resp, 1, resp.length-1);
try {
hostKey = reply.readBinaryString();
Q_S = reply.readBinaryString();
signature = reply.readBinaryString();
keyAgreement.doPhase(ECUtils.decodeKey(Q_S, curve), true);
byte[] tmp = keyAgreement.generateSecret();
if((tmp[0] & 0x80)==0x80) {
byte[] tmp2 = new byte[tmp.length+1];
System.arraycopy(tmp, 0, tmp2, 1, tmp.length);
tmp = tmp2;
}
// Calculate diffe hellman k value
secret = new BigInteger(tmp);
} finally {
reply.close();
}
calculateExchangeHash();
} catch (Exception e) {
throw new SshException("Failed to process key exchange",
SshException.INTERNAL_ERROR, e);
}
}
@Override
public boolean isKeyExchangeMessage(int messageid) {
switch(messageid) {
case SSH_MSG_KEX_ECDH_INIT:
case SSH_MSG_KEX_ECDH_REPLY:
return true;
}
return false;
}
protected void calculateExchangeHash() throws SshException {
Digest hash = (Digest) ComponentManager.getInstance()
.supportedDigests().getInstance(getHashAlgorithm());
// The local software version comments
hash.putString(clientId);
// The remote software version comments
hash.putString(serverId);
// The local kex init payload
hash.putInt(clientKexInit.length);
hash.putBytes(clientKexInit);
// The remote kex init payload
hash.putInt(serverKexInit.length);
hash.putBytes(serverKexInit);
// The host key
hash.putInt(hostKey.length);
hash.putBytes(hostKey);
hash.putInt(Q_C.length);
hash.putBytes(Q_C);
hash.putInt(Q_S.length);
hash.putBytes(Q_S);
// The diffie hellman k value
hash.putBigInteger(secret);
// Do the final output
exchangeHash = hash.doFinal();
}
}