mirror of
https://github.com/hierynomus/sshj.git
synced 2025-12-06 07:10:53 +03:00
Improved Curve25519 Public Key Handling (#959)
- Modified Curve25519 negotiation to determine algorithm identifier length based on PublicKey.getEncoded() length instead of hard-coded value of 44 - Runtime length determination avoids differences in X25519 implementations on Java 11 Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
This commit is contained in:
@@ -34,13 +34,14 @@ public class Curve25519DH extends DHBase {
|
|||||||
|
|
||||||
private static final String ALGORITHM = "X25519";
|
private static final String ALGORITHM = "X25519";
|
||||||
|
|
||||||
private static final int ENCODED_ALGORITHM_ID_KEY_LENGTH = 44;
|
|
||||||
|
|
||||||
private static final int ALGORITHM_ID_LENGTH = 12;
|
|
||||||
|
|
||||||
private static final int KEY_LENGTH = 32;
|
private static final int KEY_LENGTH = 32;
|
||||||
|
|
||||||
private final byte[] algorithmId = new byte[ALGORITHM_ID_LENGTH];
|
private int encodedKeyLength;
|
||||||
|
|
||||||
|
private int algorithmIdLength;
|
||||||
|
|
||||||
|
// Algorithm Identifier is set on Key Agreement Initialization
|
||||||
|
private byte[] algorithmId = new byte[KEY_LENGTH];
|
||||||
|
|
||||||
public Curve25519DH() {
|
public Curve25519DH() {
|
||||||
super(ALGORITHM, ALGORITHM);
|
super(ALGORITHM, ALGORITHM);
|
||||||
@@ -81,23 +82,24 @@ public class Curve25519DH extends DHBase {
|
|||||||
private void setPublicKey(final PublicKey publicKey) {
|
private void setPublicKey(final PublicKey publicKey) {
|
||||||
final byte[] encoded = publicKey.getEncoded();
|
final byte[] encoded = publicKey.getEncoded();
|
||||||
|
|
||||||
// Encoded public key consists of the algorithm identifier and public key
|
// Set key and algorithm identifier lengths based on initialized Public Key
|
||||||
if (encoded.length == ENCODED_ALGORITHM_ID_KEY_LENGTH) {
|
encodedKeyLength = encoded.length;
|
||||||
final byte[] publicKeyEncoded = new byte[KEY_LENGTH];
|
algorithmIdLength = encodedKeyLength - KEY_LENGTH;
|
||||||
System.arraycopy(encoded, ALGORITHM_ID_LENGTH, publicKeyEncoded, 0, KEY_LENGTH);
|
algorithmId = new byte[algorithmIdLength];
|
||||||
setE(publicKeyEncoded);
|
|
||||||
|
|
||||||
// Save Algorithm Identifier byte array
|
// Encoded public key consists of the algorithm identifier and public key
|
||||||
System.arraycopy(encoded, 0, algorithmId, 0, ALGORITHM_ID_LENGTH);
|
final byte[] publicKeyEncoded = new byte[KEY_LENGTH];
|
||||||
} else {
|
System.arraycopy(encoded, algorithmIdLength, publicKeyEncoded, 0, KEY_LENGTH);
|
||||||
throw new IllegalArgumentException(String.format("X25519 unsupported public key length [%d]", encoded.length));
|
setE(publicKeyEncoded);
|
||||||
}
|
|
||||||
|
// Save Algorithm Identifier byte array
|
||||||
|
System.arraycopy(encoded, 0, algorithmId, 0, algorithmIdLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeySpec getPeerPublicKeySpec(final byte[] peerPublicKey) {
|
private KeySpec getPeerPublicKeySpec(final byte[] peerPublicKey) {
|
||||||
final byte[] encodedKeySpec = new byte[ENCODED_ALGORITHM_ID_KEY_LENGTH];
|
final byte[] encodedKeySpec = new byte[encodedKeyLength];
|
||||||
System.arraycopy(algorithmId, 0, encodedKeySpec, 0, ALGORITHM_ID_LENGTH);
|
System.arraycopy(algorithmId, 0, encodedKeySpec, 0, algorithmIdLength);
|
||||||
System.arraycopy(peerPublicKey, 0, encodedKeySpec, ALGORITHM_ID_LENGTH, KEY_LENGTH);
|
System.arraycopy(peerPublicKey, 0, encodedKeySpec, algorithmIdLength, KEY_LENGTH);
|
||||||
return new X509EncodedKeySpec(encodedKeySpec);
|
return new X509EncodedKeySpec(encodedKeySpec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package net.schmizz.sshj.transport.kex;
|
package net.schmizz.sshj.transport.kex;
|
||||||
|
|
||||||
|
import net.schmizz.sshj.common.SecurityUtils;
|
||||||
import net.schmizz.sshj.transport.random.JCERandom;
|
import net.schmizz.sshj.transport.random.JCERandom;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyPairGenerator;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.security.Security;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
public class Curve25519DHTest {
|
public class Curve25519DHTest {
|
||||||
|
|
||||||
|
private static final String ALGORITHM_FILTER = "KeyPairGenerator.X25519";
|
||||||
|
|
||||||
private static final int KEY_LENGTH = 32;
|
private static final int KEY_LENGTH = 32;
|
||||||
|
|
||||||
private static final byte[] PEER_PUBLIC_KEY = {
|
private static final byte[] PEER_PUBLIC_KEY = {
|
||||||
@@ -35,8 +42,16 @@ public class Curve25519DHTest {
|
|||||||
1, 2, 3, 4, 5, 6, 7, 8
|
1, 2, 3, 4, 5, 6, 7, 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void clearSecurityProvider() {
|
||||||
|
SecurityUtils.setSecurityProvider(null);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInitPublicKeyLength() throws GeneralSecurityException {
|
public void testInitPublicKeyLength() throws GeneralSecurityException {
|
||||||
|
final boolean bouncyCastleRegistrationRequired = isAlgorithmUnsupported();
|
||||||
|
SecurityUtils.setRegisterBouncyCastle(bouncyCastleRegistrationRequired);
|
||||||
|
|
||||||
final Curve25519DH dh = new Curve25519DH();
|
final Curve25519DH dh = new Curve25519DH();
|
||||||
dh.init(null, new JCERandom.Factory());
|
dh.init(null, new JCERandom.Factory());
|
||||||
|
|
||||||
@@ -48,6 +63,8 @@ public class Curve25519DHTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInitComputeSharedSecretKey() throws GeneralSecurityException {
|
public void testInitComputeSharedSecretKey() throws GeneralSecurityException {
|
||||||
|
SecurityUtils.setRegisterBouncyCastle(true);
|
||||||
|
|
||||||
final Curve25519DH dh = new Curve25519DH();
|
final Curve25519DH dh = new Curve25519DH();
|
||||||
dh.init(null, new JCERandom.Factory());
|
dh.init(null, new JCERandom.Factory());
|
||||||
|
|
||||||
@@ -57,4 +74,9 @@ public class Curve25519DHTest {
|
|||||||
assertNotNull(sharedSecretKey);
|
assertNotNull(sharedSecretKey);
|
||||||
assertEquals(BigInteger.ONE.signum(), sharedSecretKey.signum());
|
assertEquals(BigInteger.ONE.signum(), sharedSecretKey.signum());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isAlgorithmUnsupported() {
|
||||||
|
final Provider[] providers = Security.getProviders(ALGORITHM_FILTER);
|
||||||
|
return providers == null || providers.length == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user