in sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java [134:237]
public boolean next(int cmd, Buffer buffer) throws Exception {
AbstractClientSession session = getClientSession();
if (log.isDebugEnabled()) {
log.debug("next({})[{}] process command={}",
this, session, KeyExchange.getSimpleKexOpcodeName(cmd));
}
if (cmd != SshConstants.SSH_MSG_KEXDH_REPLY) {
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"Protocol error: expected packet SSH_MSG_KEXDH_REPLY, got " + KeyExchange.getSimpleKexOpcodeName(cmd));
}
byte[] k_s = buffer.getBytes();
byte[] f = updateF(buffer);
byte[] sig = buffer.getBytes();
if (kemClient == null) {
dh.setF(f);
k = normalize(dh.getK());
} else {
try {
int l = kemClient.getEncapsulationLength();
if (dh instanceof CurveSizeIndicator) {
int expectedLength = l + ((CurveSizeIndicator) dh).getByteLength();
if (f.length != expectedLength) {
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"Wrong F length (should be " + expectedLength + " bytes): " + f.length);
}
} else if (f.length <= l) {
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"Strange F length: " + f.length + " <= " + l);
}
dh.setF(Arrays.copyOfRange(f, l, f.length));
Digest keyHash = dh.getHash();
keyHash.init();
keyHash.update(kemClient.extractSecret(Arrays.copyOf(f, l)));
keyHash.update(dh.getK());
k = keyHash.digest();
} catch (IllegalArgumentException ex) {
log.error("Key encapsulation error", ex);
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"Key encapsulation error: " + ex.getMessage());
}
}
String keyAlg = session.getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
boolean wantCert = KeyUtils.isCertificateAlgorithm(keyAlg);
buffer = new ByteArrayBuffer(k_s);
PublicKey serverKey = buffer.getRawPublicKey();
PublicKey serverPublicHostKey = serverKey;
if (serverKey instanceof OpenSshCertificate) {
OpenSshCertificate openSshKey = (OpenSshCertificate) serverKey;
if (!wantCert) {
log.error("Got a server key certificate, but negotiated algorithm is {}", keyAlg);
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"OpenSshCertificate found with KEX algorithm " + keyAlg);
}
serverPublicHostKey = openSshKey.getCertPubKey();
try {
verifyCertificate(session, openSshKey);
} catch (SshException e) {
if (CoreModuleProperties.ABORT_ON_INVALID_CERTIFICATE.getRequired(session)) {
throw e;
} else {
// ignore certificate
serverKey = openSshKey.getCertPubKey();
log.info("Ignoring invalid certificate {}", openSshKey.getId(), e);
}
}
} else if (wantCert) {
log.error("Got a plain public key (not a certificate) for negotiated algorithm {}", keyAlg);
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"Server did not send a certificate with KEX algorithm " + keyAlg);
}
buffer = new ByteArrayBuffer();
buffer.putBytes(v_c);
buffer.putBytes(v_s);
buffer.putBytes(i_c);
buffer.putBytes(i_s);
buffer.putBytes(k_s);
dh.putE(buffer, getE());
dh.putF(buffer, f);
buffer.putBytes(k);
hash.update(buffer.array(), 0, buffer.available());
h = hash.digest();
Signature verif = ValidateUtils.checkNotNull(
NamedFactory.create(session.getSignatureFactories(), keyAlg),
"No verifier located for algorithm=%s", keyAlg);
verif.initVerifier(session, serverPublicHostKey);
verif.update(session, h);
if (!verif.verify(session, sig)) {
throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
"KeyExchange signature verification failed for key type=" + keyAlg);
}
session.setServerKey(serverKey);
return true;
}