Compare commits

...

7 Commits

Author SHA1 Message Date
Jeroen van Erp
fedf8c410c Merge branch 'master' into issue-588 2020-06-02 16:32:25 +02:00
Jeroen van Erp
8b0d1ca03c Introduce KeyAlgorithm to separate KeyType from Algorithm (Fixes #588) 2020-06-02 14:12:04 +02:00
Jeroen van Erp
91105e6a07 Fix integration build 2020-06-02 14:07:31 +02:00
Fabian Henneke
4e802cec86 Make KeyType compatible with Android Keystore (#586)
* Make KeyType compatible with Android Keystore

Android Keystore private keys do not implement PrivateKey since the
raw key material is not available to applications.

With this commit, sshj's KeyType correctly detects the algorithm
associated with Android Keystore keys, which makes them usable for SSH
authentication.

* Extract RSA, DSA, ECDSA and EC into constants

* Fix license lint issue

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-05-28 16:09:43 +02:00
Fabian Henneke
dfdc464e08 Add pwdf retry logic to OpenSSHKeyV1KeyFile (#587)
* Add pwdf retry logic to OpenSSHKeyV1KeyFile

While PKCS8KeyFile uses PasswordFinder's shouldRetry to determine
whether it should call reqPassword again if decryption of they key file
fails, OpenSSHKeyV1KeyFile simply gives up and throws an exception.

With this commit, retry logic similar to that of PKCS8KeyFile is added
to OpenSSHKeyV1KeyFile. The PasswordFinder's reqPassword is called
again if the validation of the "checkint" fails, which indicates an
incorrect passphrase.

* Use new exception to signal incorrect passphrase

* Throw common exception on key decryption failure

* Add test coverage for retry logic

Co-authored-by: Jeroen van Erp <jeroen@hierynomus.com>
2020-05-28 09:25:52 +02:00
Jeroen van Erp
fa7c40cc66 Use java12 for building due to gradle --release flag 2020-05-27 16:41:48 +02:00
Vladimir Lagunov
b1be9258b4 Fix NPE in OpenSSHKnownHosts (#579)
Co-authored-by: Vladimir Lagunov <vladimir.lagunov@jetbrains.com>
2020-05-26 14:56:29 +02:00
45 changed files with 803 additions and 190 deletions

View File

@@ -11,43 +11,43 @@ on:
branches: [ master ]
jobs:
java9:
name: Build with Java 9
java12:
name: Build with Java 12
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 9
- name: Set up JDK 12
uses: actions/setup-java@v1
with:
java-version: 9
java-version: 12
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew check
java10:
name: Build with Java 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 10
uses: actions/setup-java@v1
with:
java-version: 10
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew check -xanimalsnifferMain -xanimalsnifferTest
# java10:
# name: Build with Java 10
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v2
# - name: Set up JDK 10
# uses: actions/setup-java@v1
# with:
# java-version: 10
# - name: Grant execute permission for gradlew
# run: chmod +x gradlew
# - name: Build with Gradle
# run: ./gradlew check -xanimalsnifferMain -xanimalsnifferTest
integration:
name: Integration test
needs: [java9]
needs: [java12]
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- uses: actions/setup-java@v1
with:
java-version: 9
java-version: 12
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle

View File

@@ -1,30 +0,0 @@
language: java
dist: trusty
sudo: required
services:
- docker
jdk:
- oraclejdk8
- openjdk8
- oraclejdk9
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
before_install:
- pip install --user codecov
script:
- ./gradlew check
- ./gradlew integrationTest
after_success:
- codecov

View File

@@ -9,7 +9,7 @@ plugins {
id "com.github.blindpirate.osgi" version '0.0.3'
id "maven-publish"
id 'pl.allegro.tech.build.axion-release' version '1.11.0'
id "com.bmuschko.docker-remote-api" version "3.2.1"
id "com.bmuschko.docker-remote-api" version "6.4.0"
id "com.github.hierynomus.license" version "0.12.1"
id "com.jfrog.bintray" version "1.8.5"
id 'ru.vyarus.java-lib' version '1.0.5'
@@ -260,22 +260,23 @@ jacocoTestReport {
task buildItestImage(type: DockerBuildImage) {
inputDir = file('src/itest/docker-image')
tag = 'sshj/sshd-itest'
images.add('sshj/sshd-itest:latest')
}
task createItestContainer(type: DockerCreateContainer) {
dependsOn buildItestImage
targetImageId { buildItestImage.getImageId() }
portBindings = ['2222:22']
targetImageId buildItestImage.getImageId()
hostConfig.portBindings = ['2222:22']
hostConfig.autoRemove = true
}
task startItestContainer(type: DockerStartContainer) {
dependsOn createItestContainer
targetContainerId { createItestContainer.getContainerId() }
targetContainerId createItestContainer.getContainerId()
}
task stopItestContainer(type: DockerStopContainer) {
targetContainerId { createItestContainer.getContainerId() }
targetContainerId createItestContainer.getContainerId()
}
task forkedUploadRelease(type: GradleBuild) {

View File

@@ -15,6 +15,8 @@
*/
package com.hierynomus.sshj
import com.hierynomus.sshj.key.ECDSAKeyAlgorithm
import com.hierynomus.sshj.key.EdDSAKeyAlgorithm
import com.hierynomus.sshj.signature.SignatureEdDSA
import net.schmizz.sshj.DefaultConfig
import net.schmizz.sshj.SSHClient
@@ -29,7 +31,7 @@ class IntegrationSpec extends IntegrationBaseSpec {
def "should accept correct key for #signatureName"() {
given:
def config = new DefaultConfig()
config.setSignatureFactories(signatureFactory)
config.setKeyAlgorithms(Collections.singletonList(signatureFactory))
SSHClient sshClient = new SSHClient(config)
sshClient.addHostKeyVerifier(fingerprint) // test-containers/ssh_host_ecdsa_key's fingerprint
@@ -40,7 +42,7 @@ class IntegrationSpec extends IntegrationBaseSpec {
sshClient.isConnected()
where:
signatureFactory << [new SignatureECDSA.Factory256(), new SignatureEdDSA.Factory()]
signatureFactory << [new ECDSAKeyAlgorithm.Factory256(), new EdDSAKeyAlgorithm.Factory()]
fingerprint << ["d3:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3", "dc:68:38:ce:fc:6f:2c:d6:6d:6b:34:eb:5c:f0:41:6a"]
signatureName = signatureFactory.getName()
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.signature
import com.hierynomus.sshj.IntegrationBaseSpec
import com.hierynomus.sshj.key.RSAKeyAlgorithm
import net.schmizz.sshj.DefaultConfig
import net.schmizz.sshj.signature.SignatureRSA
import spock.lang.Unroll
class SignatureSpec extends IntegrationBaseSpec {
@Unroll
def "should correctly connect with #sig Signature"() {
given:
def cfg = new DefaultConfig()
cfg.setKeyAlgorithms(Collections.singletonList(sigFactory))
def client = getConnectedClient(cfg)
when:
client.authPublickey(USERNAME, KEYFILE)
then:
client.authenticated
where:
sigFactory << [new RSAKeyAlgorithm.FactorySSHRSA(), new RSAKeyAlgorithm.FactoryRSASHA256(), new RSAKeyAlgorithm.FactoryRSASHA512()]
sig = sigFactory.name
}
}

View File

@@ -17,9 +17,6 @@ package com.hierynomus.sshj.transport.mac
import com.hierynomus.sshj.IntegrationBaseSpec
import net.schmizz.sshj.DefaultConfig
import net.schmizz.sshj.transport.mac.HMACRIPEMD160
import net.schmizz.sshj.transport.mac.HMACSHA2256
import spock.lang.AutoCleanup
import spock.lang.Unroll
class MacSpec extends IntegrationBaseSpec {

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.common;
public class KeyAlgorithm {
public static final String RSA = "RSA";
public static final String DSA = "DSA";
/** Elliptic curve signature key algorithm for use with BouncyCastle **/
public static final String ECDSA = "ECDSA";
/** General elliptic curve algorithm identifier for use with BouncyCastle **/
public static final String EC_BC = "EC";
/** General elliptic curve algorithm identifier for use with the Android Keystore **/
public static final String EC_KEYSTORE = "EC";
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.common;
import org.bouncycastle.openssl.EncryptionException;
import java.io.IOException;
/**
* Thrown when a key file could not be decrypted correctly, e.g. if its checkInts differed in the case of an OpenSSH
* key file.
*/
public class KeyDecryptionFailedException extends IOException {
public static final String MESSAGE = "Decryption of the key failed. A supplied passphrase may be incorrect.";
public KeyDecryptionFailedException() {
super(MESSAGE);
}
public KeyDecryptionFailedException(EncryptionException cause) {
super(MESSAGE, cause);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.key;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.signature.Signature;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
public abstract class AbstractKeyAlgorithm implements KeyAlgorithm {
private final String keyAlgorithm;
private final Factory.Named<Signature> signature;
private final KeyType keyFormat;
public AbstractKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
this.keyAlgorithm = keyAlgorithm;
this.signature = signature;
this.keyFormat = keyFormat;
}
public void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf) {
keyFormat.putPubKeyIntoBuffer(pk, buf);
}
@Override
public PublicKey readPubKeyFromBuffer(Buffer<?> buf) throws GeneralSecurityException {
return keyFormat.readPubKeyFromBuffer(buf);
}
@Override
public String getKeyAlgorithm() {
return keyAlgorithm;
}
@Override
public KeyType getKeyFormat() {
return keyFormat;
}
@Override
public Signature newSignature() {
return this.signature.create();
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.key;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.signature.SignatureDSA;
public class DSAKeyAlgorithm extends AbstractKeyAlgorithm {
/**
* A named factory for the SSH-DSA key algorithm.
*/
public static class FactorySSHDSA
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new DSAKeyAlgorithm(KeyType.DSA.toString(), new SignatureDSA.Factory(), KeyType.DSA);
}
@Override
public String getName() {
return KeyType.DSA.toString();
}
}
/**
* A named factory for the SSH-DSS-CERT key algorithm
*/
public static class FactorySSHDSSCert
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new DSAKeyAlgorithm(KeyType.DSA_CERT.toString(), new SignatureDSA.Factory(), KeyType.DSA_CERT);
}
@Override
public String getName() {
return KeyType.DSA_CERT.toString();
}
}
public DSAKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
super(keyAlgorithm, signature, KeyType.DSA);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.key;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.signature.SignatureECDSA;
public class ECDSAKeyAlgorithm extends AbstractKeyAlgorithm {
/** A named factory for ECDSA-256 signature */
public static class Factory256 implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new ECDSAKeyAlgorithm(KeyType.ECDSA256.toString(), new SignatureECDSA.Factory256(), KeyType.ECDSA256);
}
@Override
public String getName() {
return KeyType.ECDSA256.toString();
}
}
/** A named factory for ECDSA-384 signature */
public static class Factory384 implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new ECDSAKeyAlgorithm(KeyType.ECDSA384.toString(), new SignatureECDSA.Factory384(), KeyType.ECDSA384);
}
@Override
public String getName() {
return KeyType.ECDSA384.toString();
}
}
/** A named factory for ECDSA-521 signature */
public static class Factory521 implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new ECDSAKeyAlgorithm(KeyType.ECDSA521.toString(), new SignatureECDSA.Factory384(), KeyType.ECDSA521);
}
@Override
public String getName() {
return KeyType.ECDSA521.toString();
}
}
public ECDSAKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
super(keyAlgorithm, signature, keyFormat);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.key;
import com.hierynomus.sshj.signature.SignatureEdDSA;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.signature.Signature;
public class EdDSAKeyAlgorithm extends AbstractKeyAlgorithm {
public static class Factory implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public String getName() {
return KeyType.ED25519.toString();
}
@Override
public KeyAlgorithm create() {
return new EdDSAKeyAlgorithm(KeyType.ED25519.toString(), new SignatureEdDSA.Factory(), KeyType.ED25519);
}
}
public EdDSAKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
super(keyAlgorithm, signature, keyFormat);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.key;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.signature.Signature;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
/**
* In [RFC4252], the concept "public key algorithm" is used to establish
* a relationship between one algorithm name, and:
* <p>
* A. procedures used to generate and validate a private/public
* keypair;
* B. a format used to encode a public key; and
* C. procedures used to calculate, encode, and verify a signature.
*/
public interface KeyAlgorithm {
PublicKey readPubKeyFromBuffer(Buffer<?> buf) throws GeneralSecurityException;
void putPubKeyIntoBuffer(PublicKey pk, Buffer<?> buf);
String getKeyAlgorithm();
KeyType getKeyFormat();
Signature newSignature();
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C)2009 - SSHJ Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hierynomus.sshj.key;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.signature.SignatureRSA;
public class RSAKeyAlgorithm extends AbstractKeyAlgorithm {
/**
* A named factory for the SSH-RSA (SHA1) public key algorithm
*/
public static class FactorySSHRSA
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new RSAKeyAlgorithm("ssh-rsa", new SignatureRSA.FactorySSHRSA(), KeyType.RSA);
}
@Override
public String getName() {
return "ssh-rsa";
}
}
/**
* A named factory for the ssh-rsa-cert-v01@openssh.com (SHA1) public key algorithm
*/
public static class FactorySSHRSACert
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new RSAKeyAlgorithm("ssh-rsa-cert-v01@openssh.com", new SignatureRSA.FactoryCERT(), KeyType.RSA_CERT);
}
@Override
public String getName() {
return "ssh-rsa-cert-v01@openssh.com";
}
}
/**
* A named factory for the RSA-SHA2-256 public key algorithm
*/
public static class FactoryRSASHA256
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new RSAKeyAlgorithm("rsa-sha2-256", new SignatureRSA.FactoryRSASHA256(), KeyType.RSA);
}
@Override
public String getName() {
return "rsa-sha2-256";
}
}
/**
* A named factory for the RSA-SHA2-512 public key algorithm
*/
public static class FactoryRSASHA512
implements net.schmizz.sshj.common.Factory.Named<KeyAlgorithm> {
@Override
public KeyAlgorithm create() {
return new RSAKeyAlgorithm("rsa-sha2-512", new SignatureRSA.FactoryRSASHA512(), KeyType.RSA);
}
@Override
public String getName() {
return "rsa-sha2-512";
}
}
public RSAKeyAlgorithm(String keyAlgorithm, Factory.Named<Signature> signature, KeyType keyFormat) {
super(keyAlgorithm, signature, keyFormat);
}
}

View File

@@ -40,7 +40,7 @@ public class SignatureEdDSA extends AbstractSignature {
}
SignatureEdDSA() {
super(getEngine());
super(getEngine(), KeyType.ED25519.toString());
}
private static EdDSAEngine getEngine() {

View File

@@ -15,6 +15,8 @@
*/
package com.hierynomus.sshj.userauth.keyprovider;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
@@ -111,8 +113,16 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
return readUnencrypted(privateKeyBuffer, publicKey);
} else {
logger.info("Keypair is encrypted with: " + cipherName + ", " + kdfName + ", " + Arrays.toString(kdfOptions));
PlainBuffer decrypted = decryptBuffer(privateKeyBuffer, cipherName, kdfName, kdfOptions);
return readUnencrypted(decrypted, publicKey);
while (true) {
PlainBuffer decryptionBuffer = new PlainBuffer(privateKeyBuffer);
PlainBuffer decrypted = decryptBuffer(decryptionBuffer, cipherName, kdfName, kdfOptions);
try {
return readUnencrypted(decrypted, publicKey);
} catch (KeyDecryptionFailedException e) {
if (pwdf == null || !pwdf.shouldRetry(resource))
throw e;
}
}
// throw new IOException("Cannot read encrypted keypair with " + cipherName + " yet.");
}
}
@@ -184,7 +194,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
int checkInt1 = keyBuffer.readUInt32AsInt(); // uint32 checkint1
int checkInt2 = keyBuffer.readUInt32AsInt(); // uint32 checkint2
if (checkInt1 != checkInt2) {
throw new IOException("The checkInts differed, the key was not correctly decoded.");
throw new KeyDecryptionFailedException();
}
// The private key section contains both the public key and the private key
String keyType = keyBuffer.readString(); // string keytype
@@ -207,7 +217,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
keyBuffer.readMPInt(); // iqmp (q^-1 mod p)
keyBuffer.readMPInt(); // p (Prime 1)
keyBuffer.readMPInt(); // q (Prime 2)
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec(n, d)));
kp = new KeyPair(publicKey, SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePrivate(new RSAPrivateKeySpec(n, d)));
break;
case ECDSA256:
kp = new KeyPair(publicKey, createECDSAPrivateKey(kt, keyBuffer, "P-256"));
@@ -239,7 +249,7 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
X9ECParameters ecParams = NISTNamedCurves.getByName(name);
ECNamedCurveSpec ecCurveSpec = new ECNamedCurveSpec(name, ecParams.getCurve(), ecParams.getG(), ecParams.getN());
ECPrivateKeySpec pks = new ECPrivateKeySpec(s, ecCurveSpec);
return SecurityUtils.getKeyFactory("ECDSA").generatePrivate(pks);
return SecurityUtils.getKeyFactory(KeyAlgorithm.ECDSA).generatePrivate(pks);
}
}

View File

@@ -15,6 +15,9 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.key.DSAKeyAlgorithm;
import com.hierynomus.sshj.key.EdDSAKeyAlgorithm;
import com.hierynomus.sshj.key.RSAKeyAlgorithm;
import com.hierynomus.sshj.signature.SignatureEdDSA;
import net.schmizz.sshj.common.SecurityUtils;
@@ -23,6 +26,8 @@ import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.random.JCERandom;
import net.schmizz.sshj.transport.random.SingletonRandomFactory;
import java.util.Arrays;
/**
* Registers SpongyCastle as JCE provider.
*/
@@ -33,11 +38,14 @@ public class AndroidConfig
SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider");
}
// don't add ECDSA
protected void initSignatureFactories() {
setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(),
// but add EdDSA
new SignatureEdDSA.Factory());
@Override
protected void initKeyAlgorithms() {
setKeyAlgorithms(Arrays.asList(
new EdDSAKeyAlgorithm.Factory(),
new RSAKeyAlgorithm.FactorySSHRSA(),
new DSAKeyAlgorithm.FactorySSHDSA()
));
}
@Override

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
@@ -77,11 +78,11 @@ public interface Config {
Factory<Random> getRandomFactory();
/**
* Retrieve the list of named factories for {@link Signature}
* Retrieve the list of named factories for {@link com.hierynomus.sshj.key.KeyAlgorithm}
*
* @return a list of named {@link Signature} factories
* @return a list of named {@link com.hierynomus.sshj.key.KeyAlgorithm} factories
*/
List<Factory.Named<Signature>> getSignatureFactories();
List<Factory.Named<KeyAlgorithm>> getKeyAlgorithms();
/**
* Returns the software version information for identification during SSH connection initialization. For example,
@@ -132,11 +133,11 @@ public interface Config {
void setRandomFactory(Factory<Random> randomFactory);
/**
* Set the named factories for {@link Signature}.
* Set the named factories for {@link KeyAlgorithm}.
*
* @param signatureFactories a list of named factories
* @param keyAlgorithms a list of named factories
*/
void setSignatureFactories(List<Factory.Named<Signature>> signatureFactories);
void setKeyAlgorithms(List<Factory.Named<KeyAlgorithm>> keyAlgorithms);
/**
* Set the software version information for identification during SSH connection initialization. For example, {@code

View File

@@ -15,10 +15,10 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.Compression;
import net.schmizz.sshj.transport.kex.KeyExchange;
@@ -42,7 +42,7 @@ public class ConfigImpl
private List<Factory.Named<Cipher>> cipherFactories;
private List<Factory.Named<Compression>> compressionFactories;
private List<Factory.Named<MAC>> macFactories;
private List<Factory.Named<Signature>> signatureFactories;
private List<Factory.Named<KeyAlgorithm>> keyAlgorithms;
private List<Factory.Named<FileKeyProvider>> fileKeyProviderFactories;
private boolean waitForServerIdentBeforeSendingClientIdent = false;
@@ -78,11 +78,6 @@ public class ConfigImpl
return randomFactory;
}
@Override
public List<Factory.Named<Signature>> getSignatureFactories() {
return signatureFactories;
}
@Override
public String getVersion() {
return version;
@@ -138,15 +133,6 @@ public class ConfigImpl
this.randomFactory = randomFactory;
}
public void setSignatureFactories(Factory.Named<Signature>... signatureFactories) {
setSignatureFactories(Arrays.asList(signatureFactories));
}
@Override
public void setSignatureFactories(List<Factory.Named<Signature>> signatureFactories) {
this.signatureFactories = signatureFactories;
}
@Override
public void setVersion(String version) {
this.version = version;
@@ -172,6 +158,16 @@ public class ConfigImpl
this.waitForServerIdentBeforeSendingClientIdent = waitForServerIdentBeforeSendingClientIdent;
}
@Override
public List<Factory.Named<KeyAlgorithm>> getKeyAlgorithms() {
return keyAlgorithms;
}
@Override
public void setKeyAlgorithms(List<Factory.Named<KeyAlgorithm>> keyAlgorithms) {
this.keyAlgorithms = keyAlgorithms;
}
@Override
public LoggerFactory getLoggerFactory() {
return loggerFactory;

View File

@@ -15,7 +15,10 @@
*/
package net.schmizz.sshj;
import com.hierynomus.sshj.signature.SignatureEdDSA;
import com.hierynomus.sshj.key.DSAKeyAlgorithm;
import com.hierynomus.sshj.key.ECDSAKeyAlgorithm;
import com.hierynomus.sshj.key.EdDSAKeyAlgorithm;
import com.hierynomus.sshj.key.RSAKeyAlgorithm;
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import com.hierynomus.sshj.transport.cipher.StreamCiphers;
import com.hierynomus.sshj.transport.kex.DHGroups;
@@ -26,10 +29,7 @@ import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.LoggerFactory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.signature.SignatureDSA;
import net.schmizz.sshj.signature.SignatureECDSA;
import net.schmizz.sshj.signature.SignatureRSA;
import net.schmizz.sshj.transport.cipher.*;
import net.schmizz.sshj.transport.cipher.Cipher;
import net.schmizz.sshj.transport.compression.NoneCompression;
import net.schmizz.sshj.transport.kex.Curve25519SHA256;
import net.schmizz.sshj.transport.kex.DHGexSHA1;
@@ -56,7 +56,7 @@ import java.util.*;
* <li>{@link net.schmizz.sshj.ConfigImpl#setMACFactories MAC}: {@link net.schmizz.sshj.transport.mac.HMACSHA1}, {@link net.schmizz.sshj.transport.mac.HMACSHA196}, {@link net.schmizz.sshj.transport.mac.HMACMD5}, {@link
* net.schmizz.sshj.transport.mac.HMACMD596}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setCompressionFactories Compression}: {@link net.schmizz.sshj.transport.compression.NoneCompression}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setSignatureFactories Signature}: {@link net.schmizz.sshj.signature.SignatureRSA}, {@link net.schmizz.sshj.signature.SignatureDSA}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setKeyAlgorithms KeyAlgorithm}: {@link net.schmizz.sshj.signature.SignatureRSA}, {@link net.schmizz.sshj.signature.SignatureDSA}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setRandomFactory PRNG}: {@link net.schmizz.sshj.transport.random.BouncyCastleRandom}* or {@link net.schmizz.sshj.transport.random.JCERandom}</li>
* <li>{@link net.schmizz.sshj.ConfigImpl#setFileKeyProviderFactories Key file support}: {@link net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile}*, {@link
* net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile}*</li>
@@ -76,12 +76,12 @@ public class DefaultConfig
setVersion(readVersionFromProperties());
final boolean bouncyCastleRegistered = SecurityUtils.isBouncyCastleRegistered();
initKeyExchangeFactories(bouncyCastleRegistered);
initKeyAlgorithms();
initRandomFactory(bouncyCastleRegistered);
initFileKeyProviderFactories(bouncyCastleRegistered);
initCipherFactories();
initCompressionFactories();
initMACFactories();
initSignatureFactories();
setKeepAliveProvider(KeepAliveProvider.HEARTBEAT);
}
@@ -99,8 +99,8 @@ public class DefaultConfig
@Override
public void setLoggerFactory(LoggerFactory loggerFactory) {
super.setLoggerFactory(loggerFactory);
log = loggerFactory.getLogger(getClass());
super.setLoggerFactory(loggerFactory);
log = loggerFactory.getLogger(getClass());
}
protected void initKeyExchangeFactories(boolean bouncyCastleRegistered) {
@@ -133,6 +133,20 @@ public class DefaultConfig
}
}
protected void initKeyAlgorithms() {
setKeyAlgorithms(Arrays.asList(
new EdDSAKeyAlgorithm.Factory(),
new ECDSAKeyAlgorithm.Factory521(),
new ECDSAKeyAlgorithm.Factory384(),
new ECDSAKeyAlgorithm.Factory256(),
new RSAKeyAlgorithm.FactoryRSASHA512(),
new RSAKeyAlgorithm.FactoryRSASHA256(),
new RSAKeyAlgorithm.FactorySSHRSACert(),
new DSAKeyAlgorithm.FactorySSHDSSCert(),
new RSAKeyAlgorithm.FactorySSHRSA(),
new DSAKeyAlgorithm.FactorySSHDSA()));
}
protected void initRandomFactory(boolean bouncyCastleRegistered) {
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered
? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
@@ -207,18 +221,6 @@ public class DefaultConfig
log.debug("Available cipher factories: {}", avail);
}
protected void initSignatureFactories() {
setSignatureFactories(
new SignatureEdDSA.Factory(),
new SignatureECDSA.Factory256(),
new SignatureECDSA.Factory384(),
new SignatureECDSA.Factory521(),
new SignatureRSA.Factory(),
new SignatureRSA.FactoryCERT(),
new SignatureDSA.Factory()
);
}
protected void initMACFactories() {
setMACFactories(
Macs.HMACSHA1(),

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.common;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.secg.SecgUtils;
import org.bouncycastle.asn1.nist.NISTNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
@@ -87,7 +88,7 @@ class ECDSAVariationsAdapter {
ECPoint p = new ECPoint(bigX, bigY);
ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(p, ecCurveSpec);
KeyFactory keyFactory = KeyFactory.getInstance("ECDSA");
KeyFactory keyFactory = KeyFactory.getInstance(KeyAlgorithm.ECDSA);
return keyFactory.generatePublic(publicKeySpec);
} catch (Exception ex) {
throw new GeneralSecurityException(ex);
@@ -103,7 +104,7 @@ class ECDSAVariationsAdapter {
}
static boolean isECKeyWithFieldSize(Key key, int fieldSize) {
return "ECDSA".equals(key.getAlgorithm())
return (KeyAlgorithm.ECDSA.equals(key.getAlgorithm()) || KeyAlgorithm.EC_KEYSTORE.equals(key.getAlgorithm()))
&& fieldSizeFromKey((ECKey) key) == fieldSize;
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.common;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.signature.Ed25519PublicKey;
import com.hierynomus.sshj.userauth.certificate.Certificate;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
@@ -30,9 +31,7 @@ import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.DSAPublicKeySpec;
import java.security.spec.RSAPublicKeySpec;
@@ -53,7 +52,7 @@ public enum KeyType {
} catch (Buffer.BufferException be) {
throw new GeneralSecurityException(be);
}
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
return keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
}
@@ -66,7 +65,7 @@ public enum KeyType {
@Override
protected boolean isMyType(Key key) {
return (key instanceof RSAPublicKey || key instanceof RSAPrivateKey);
return KeyAlgorithm.RSA.equals(key.getAlgorithm());
}
},
@@ -84,7 +83,7 @@ public enum KeyType {
} catch (Buffer.BufferException be) {
throw new GeneralSecurityException(be);
}
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("DSA");
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.DSA);
return keyFactory.generatePublic(new DSAPublicKeySpec(y, p, q, g));
}
@@ -99,7 +98,7 @@ public enum KeyType {
@Override
protected boolean isMyType(Key key) {
return (key instanceof DSAPublicKey || key instanceof DSAPrivateKey);
return KeyAlgorithm.DSA.equals(key.getAlgorithm());
}
},

View File

@@ -29,18 +29,26 @@ public abstract class AbstractSignature
@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
protected final java.security.Signature signature;
private final String signatureName;
protected AbstractSignature(String algorithm) {
protected AbstractSignature(String algorithm, String signatureName) {
try {
this.signature = SecurityUtils.getSignature(algorithm);
this.signatureName = signatureName;
} catch (GeneralSecurityException e) {
throw new SSHRuntimeException(e);
}
}
protected AbstractSignature(@SuppressWarnings("PMD.UnnecessaryFullyQualifiedName")
java.security.Signature signatureEngine) {
java.security.Signature signatureEngine, String signatureName) {
this.signature = signatureEngine;
this.signatureName = signatureName;
}
@Override
public String getSignatureName() {
return signatureName;
}
@Override

View File

@@ -21,6 +21,8 @@ import java.security.PublicKey;
/** Signature interface for SSH used to sign or verify data. Usually wraps a {@code javax.crypto.Signature} object. */
public interface Signature {
String getSignatureName();
/**
* Initialize this signature with the given public key for signature verification.
*

View File

@@ -50,7 +50,7 @@ public class SignatureDSA
}
public SignatureDSA() {
super("SHA1withDSA");
super("SHA1withDSA", KeyType.DSA.toString());
}
@Override
@@ -72,8 +72,8 @@ public class SignatureDSA
// result must be 40 bytes, but length of r and s may not be 20 bytes
int r_copylen = (r.length < 20) ? r.length : 20;
int s_copylen = (s.length < 20) ? s.length : 20;
int r_copylen = Math.min(r.length, 20);
int s_copylen = Math.min(s.length, 20);
System.arraycopy(r, r.length - r_copylen, result, 20 - r_copylen, r_copylen);
System.arraycopy(s, s.length - s_copylen, result, 40 - s_copylen, s_copylen);

View File

@@ -79,7 +79,7 @@ public class SignatureECDSA extends AbstractSignature {
private String keyTypeName;
public SignatureECDSA(String algorithm, String keyTypeName) {
super(algorithm);
super(algorithm, keyTypeName);
this.keyTypeName = keyTypeName;
}

View File

@@ -22,26 +22,53 @@ import net.schmizz.sshj.common.SSHRuntimeException;
import java.security.InvalidKeyException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.util.Date;
/** RSA {@link Signature} */
public class SignatureRSA
extends AbstractSignature {
/** A named factory for RSA {@link Signature} */
public static class Factory
public static class FactorySSHRSA
implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public Signature create() {
return new SignatureRSA(KeyType.RSA.toString());
return new SignatureRSA("SHA1withRSA", KeyType.RSA, KeyType.RSA.toString());
}
@Override
public String getName() {
return KeyType.RSA.toString();
}
}
/** A named factory for RSA {@link Signature} */
public static class FactoryRSASHA256
implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public Signature create() {
return new SignatureRSA("SHA256withRSA", KeyType.RSA, "rsa-sha2-256");
}
@Override
public String getName() {
return "rsa-sha2-256";
}
}
/** A named factory for RSA {@link Signature} */
public static class FactoryRSASHA512
implements net.schmizz.sshj.common.Factory.Named<Signature> {
@Override
public Signature create() {
return new SignatureRSA("SHA512withRSA", KeyType.RSA, "rsa-sha2-512");
}
@Override
public String getName() {
return "rsa-sha2-512";
}
}
/** A named factory for RSA {@link Signature} */
@@ -50,7 +77,7 @@ public class SignatureRSA
@Override
public Signature create() {
return new SignatureRSA(KeyType.RSA_CERT.toString());
return new SignatureRSA("SHA1withRSA", KeyType.RSA_CERT, KeyType.RSA.toString());
}
@Override
@@ -60,19 +87,19 @@ public class SignatureRSA
}
private String keyTypeName;
private KeyType keyType;
public SignatureRSA(String keyTypeName) {
super("SHA1withRSA");
this.keyTypeName = keyTypeName;
public SignatureRSA(String algorithm, KeyType keyType, String name) {
super(algorithm, name);
this.keyType = keyType;
}
@Override
@SuppressWarnings("unchecked")
public void initVerify(PublicKey publicKey) {
try {
if (this.keyTypeName.equals(KeyType.RSA_CERT.toString()) && publicKey instanceof Certificate) {
if (this.keyType.equals(KeyType.RSA_CERT) && publicKey instanceof Certificate) {
signature.initVerify(((Certificate<PublicKey>) publicKey).getKey());
} else {
signature.initVerify(publicKey);
@@ -89,7 +116,7 @@ public class SignatureRSA
@Override
public boolean verify(byte[] sig) {
sig = extractSig(sig, KeyType.RSA.toString());
sig = extractSig(sig, getSignatureName());
try {
return signature.verify(sig);
} catch (SignatureException e) {

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.*;
@@ -30,9 +31,7 @@ import org.slf4j.Logger;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -232,6 +231,13 @@ final class KeyExchanger
}
kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(),
negotiatedAlgs.getKeyExchangeAlgorithm());
List<KeyAlgorithm> keyAlgorithms = new ArrayList<KeyAlgorithm>();
for (String signatureAlgorithm : negotiatedAlgs.getSignatureAlgorithms()) {
keyAlgorithms.add(Factory.Named.Util.create(transport.getConfig().getKeyAlgorithms(), signatureAlgorithm));
}
transport.setKeyAlgorithms(keyAlgorithms);
try {
kex.init(transport,
transport.getServerID(), transport.getClientID(),

View File

@@ -15,10 +15,12 @@
*/
package net.schmizz.sshj.transport;
import java.util.List;
public final class NegotiatedAlgorithms {
private final String kex;
private final String sig;
private final List<String> availableSigs;
private final String c2sCipher;
private final String s2cCipher;
private final String c2sMAC;
@@ -26,10 +28,10 @@ public final class NegotiatedAlgorithms {
private final String c2sComp;
private final String s2cComp;
NegotiatedAlgorithms(String kex, String sig, String c2sCipher, String s2cCipher, String c2sMAC, String s2cMAC,
NegotiatedAlgorithms(String kex, List<String> availableSigs, String c2sCipher, String s2cCipher, String c2sMAC, String s2cMAC,
String c2sComp, String s2cComp) {
this.kex = kex;
this.sig = sig;
this.availableSigs = availableSigs;
this.c2sCipher = c2sCipher;
this.s2cCipher = s2cCipher;
this.c2sMAC = c2sMAC;
@@ -42,8 +44,8 @@ public final class NegotiatedAlgorithms {
return kex;
}
public String getSignatureAlgorithm() {
return sig;
public List<String> getSignatureAlgorithms() {
return availableSigs;
}
public String getClient2ServerCipherAlgorithm() {
@@ -74,7 +76,7 @@ public final class NegotiatedAlgorithms {
public String toString() {
return ("[ " +
"kex=" + kex + "; " +
"sig=" + sig + "; " +
"availableSigs=" + availableSigs + "; " +
"c2sCipher=" + c2sCipher + "; " +
"s2cCipher=" + s2cCipher + "; " +
"c2sMAC=" + c2sMAC + "; " +

View File

@@ -21,6 +21,7 @@ import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHPacket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -38,7 +39,7 @@ class Proposal {
public Proposal(Config config) {
kex = Factory.Named.Util.getNames(config.getKeyExchangeFactories());
sig = Factory.Named.Util.getNames(config.getSignatureFactories());
sig = Factory.Named.Util.getNames(config.getKeyAlgorithms());
c2sCipher = s2cCipher = Factory.Named.Util.getNames(config.getCipherFactories());
c2sMAC = s2cMAC = Factory.Named.Util.getNames(config.getMACFactories());
c2sComp = s2cComp = Factory.Named.Util.getNames(config.getCompressionFactories());
@@ -126,7 +127,7 @@ class Proposal {
throws TransportException {
return new NegotiatedAlgorithms(
firstMatch(this.getKeyExchangeAlgorithms(), other.getKeyExchangeAlgorithms()),
firstMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
allMatch(this.getSignatureAlgorithms(), other.getSignatureAlgorithms()),
firstMatch(this.getClient2ServerCipherAlgorithms(), other.getClient2ServerCipherAlgorithms()),
firstMatch(this.getServer2ClientCipherAlgorithms(), other.getServer2ClientCipherAlgorithms()),
firstMatch(this.getClient2ServerMACAlgorithms(), other.getClient2ServerMACAlgorithms()),
@@ -138,19 +139,36 @@ class Proposal {
private static String firstMatch(List<String> a, List<String> b)
throws TransportException {
for (String aa : a)
for (String bb : b)
if (aa.equals(bb))
return aa;
for (String aa : a) {
if (b.contains(aa)) {
return aa;
}
}
throw new TransportException("Unable to reach a settlement: " + a + " and " + b);
}
private static List<String> allMatch(List<String> a, List<String> b) throws TransportException {
List<String> res = new ArrayList<String>();
for (String aa : a) {
if (b.contains(aa)) {
res.add(aa);
}
}
if (res.isEmpty()) {
throw new TransportException("Unable to reach a settlement: " + a + " and " + b);
}
return res;
}
private static String toCommaString(List<String> sl) {
StringBuilder sb = new StringBuilder();
int i = 0;
for (String s : sl) {
if (i++ != 0)
if (i++ != 0) {
sb.append(",");
}
sb.append(s);
}
return sb.toString();

View File

@@ -15,9 +15,11 @@
*/
package net.schmizz.sshj.transport;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.sshj.Config;
import net.schmizz.sshj.Service;
import net.schmizz.sshj.common.DisconnectReason;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.SSHPacketHandler;
import net.schmizz.sshj.transport.verification.AlgorithmsVerifier;
@@ -235,4 +237,6 @@ public interface Transport
* @param e The exception that occurred.
*/
void die(Exception e);
KeyAlgorithm getKeyAlgorithm(KeyType keyType) throws TransportException;
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport;
import com.hierynomus.sshj.key.KeyAlgorithm;
import com.hierynomus.sshj.transport.IdentificationStringParser;
import net.schmizz.concurrent.ErrorDeliveryUtil;
import net.schmizz.concurrent.Event;
@@ -30,6 +31,7 @@ import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@@ -39,6 +41,7 @@ import java.util.concurrent.locks.ReentrantLock;
public final class TransportImpl
implements Transport, DisconnectListener {
private static final class NullService
extends AbstractService {
@@ -86,6 +89,8 @@ public final class TransportImpl
private final Decoder decoder;
private List<KeyAlgorithm> keyAlgorithms;
private final Event<TransportException> serviceAccept;
private final Event<TransportException> close;
@@ -649,4 +654,18 @@ public final class TransportImpl
return connInfo;
}
@Override
public KeyAlgorithm getKeyAlgorithm(KeyType keyType) throws TransportException {
for (KeyAlgorithm ka : keyAlgorithms) {
if (ka.getKeyFormat().equals(keyType)) {
return ka;
}
}
throw new TransportException("Cannot find an available KeyAlgorithm for type " + keyType);
}
public void setKeyAlgorithms(List<KeyAlgorithm> keyAlgorithms) {
this.keyAlgorithms = keyAlgorithms;
}
}

View File

@@ -78,8 +78,8 @@ public abstract class AbstractDHG extends AbstractDH
digest.update(buf.array(), buf.rpos(), buf.available());
H = digest.digest();
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
KeyType.fromKey(hostKey).toString());
Signature signature = trans.getKeyAlgorithm(KeyType.fromKey(hostKey)).newSignature();
signature.initVerify(hostKey);
signature.update(H, 0, H.length);
if (!signature.verify(sig))

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport.kex;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.sshj.common.*;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.Transport;
@@ -30,9 +31,9 @@ import java.security.GeneralSecurityException;
public abstract class AbstractDHGex extends AbstractDH {
private final Logger log = LoggerFactory.getLogger(getClass());
private int minBits = 1024;
private int maxBits = 8192;
private int preferredBits = 2048;
private final int minBits = 1024;
private final int maxBits = 8192;
private final int preferredBits = 2048;
public AbstractDHGex(Digest digest) {
super(new DH(), digest);
@@ -84,8 +85,8 @@ public abstract class AbstractDHGex extends AbstractDH {
.putMPInt(k);
digest.update(buf.array(), buf.rpos(), buf.available());
H = digest.digest();
Signature signature = Factory.Named.Util.create(trans.getConfig().getSignatureFactories(),
KeyType.fromKey(hostKey).toString());
KeyAlgorithm keyAlgorithm = trans.getKeyAlgorithm(KeyType.fromKey(hostKey));
Signature signature = keyAlgorithm.newSignature();
signature.initVerify(hostKey);
signature.update(H, 0, H.length);
if (!signature.verify(sig))

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport.kex;
import com.hierynomus.sshj.common.KeyAlgorithm;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.transport.random.Random;
import org.bouncycastle.asn1.x9.X9ECParameters;
@@ -31,7 +32,7 @@ public class Curve25519DH extends DHBase {
private byte[] secretKey;
public Curve25519DH() {
super("ECDSA", "ECDH");
super(KeyAlgorithm.ECDSA, "ECDH");
}
@Override

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport.kex;
import com.hierynomus.sshj.common.KeyAlgorithm;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.random.Random;
@@ -54,7 +55,7 @@ public class ECDH extends DHBase {
@Override
public void computeK(byte[] f) throws GeneralSecurityException {
KeyFactory keyFactory = SecurityUtils.getKeyFactory("EC");
KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.EC_BC);
ECPublicKeySpec keySpec = new ECPublicKeySpec(getDecoded(f, ecParameterSpec.getCurve()), ecParameterSpec);
PublicKey yourPubKey = keyFactory.generatePublic(keySpec);
agreement.doPhase(yourPubKey, true);

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.transport.verification;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.transport.verification.KnownHostMatchers;
import net.schmizz.sshj.common.*;
import org.slf4j.Logger;
@@ -239,7 +240,7 @@ public class OpenSSHKnownHosts
final BigInteger e = new BigInteger(split[i++]);
final BigInteger n = new BigInteger(split[i++]);
try {
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
final KeyFactory keyFactory = SecurityUtils.getKeyFactory(KeyAlgorithm.RSA);
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
} catch (Exception ex) {
log.error("Error reading entry `{}`, could not create key", line, ex);
@@ -387,7 +388,7 @@ public class OpenSSHKnownHosts
line.append(" ").append(type.toString());
line.append(" ").append(getKeyString(key));
if (!comment.isEmpty()) line.append(" ").append(comment);
if (comment != null && !comment.isEmpty()) line.append(" ").append(comment);
return line.toString();
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import com.hierynomus.sshj.common.KeyAlgorithm;
import com.hierynomus.sshj.transport.cipher.BlockCiphers;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.ByteArrayUtils;
@@ -140,7 +141,7 @@ public class PKCS5KeyFile extends BaseFileKeyProvider {
ASN1Data asn = new ASN1Data(data = decrypt(Base64.decode(sb.toString()), cipher, iv));
switch (type) {
case RSA: {
KeyFactory factory = KeyFactory.getInstance("RSA");
KeyFactory factory = KeyFactory.getInstance(KeyAlgorithm.RSA);
asn.readNext();
BigInteger modulus = asn.readNext();
BigInteger pubExp = asn.readNext();
@@ -150,7 +151,7 @@ public class PKCS5KeyFile extends BaseFileKeyProvider {
return new KeyPair(pubKey, prvKey);
}
case DSA: {
KeyFactory factory = KeyFactory.getInstance("DSA");
KeyFactory factory = KeyFactory.getInstance(KeyAlgorithm.DSA);
asn.readNext();
BigInteger p = asn.readNext();
BigInteger q = asn.readNext();

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.userauth.password.PasswordUtils;
@@ -85,7 +86,7 @@ public class PKCS8KeyFile extends BaseFileKeyProvider {
if (pwdf != null && pwdf.shouldRetry(resource))
continue;
else
throw e;
throw new KeyDecryptionFailedException(e);
} finally {
IOUtils.closeQuietly(r);
}

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.userauth.keyprovider;
import com.hierynomus.sshj.common.KeyAlgorithm;
import net.schmizz.sshj.common.Base64;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.userauth.password.PasswordUtils;
@@ -114,7 +115,7 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
final KeyFactory factory;
try {
factory = KeyFactory.getInstance("RSA");
factory = KeyFactory.getInstance(KeyAlgorithm.RSA);
} catch (NoSuchAlgorithmException s) {
throw new IOException(s.getMessage(), s);
}
@@ -141,7 +142,7 @@ public class PuTTYKeyFile extends BaseFileKeyProvider {
final KeyFactory factory;
try {
factory = KeyFactory.getInstance("DSA");
factory = KeyFactory.getInstance(KeyAlgorithm.DSA);
} catch (NoSuchAlgorithmException s) {
throw new IOException(s.getMessage(), s);
}

View File

@@ -15,11 +15,12 @@
*/
package net.schmizz.sshj.userauth.method;
import com.hierynomus.sshj.key.KeyAlgorithm;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.Factory;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.signature.Signature;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.userauth.UserAuthException;
import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
@@ -47,9 +48,15 @@ public abstract class KeyedAuthMethod
}
// public key as 2 strings: [ key type | key blob ]
reqBuf.putString(KeyType.fromKey(key).toString())
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
return reqBuf;
KeyType keyType = KeyType.fromKey(key);
try {
KeyAlgorithm ka = params.getTransport().getKeyAlgorithm(keyType);
reqBuf.putString(ka.getKeyAlgorithm())
.putString(new Buffer.PlainBuffer().putPublicKey(key).getCompactData());
return reqBuf;
} catch (IOException ioe) {
throw new UserAuthException("No KeyAlgorithm configured for key " + keyType);
}
}
protected SSHPacket putSig(SSHPacket reqBuf)
@@ -61,17 +68,20 @@ public abstract class KeyedAuthMethod
throw new UserAuthException("Problem getting private key from " + kProv, ioe);
}
final String kt = KeyType.fromKey(key).toString();
Signature signature = Factory.Named.Util.create(params.getTransport().getConfig().getSignatureFactories(), kt);
if (signature == null)
throw new UserAuthException("Could not create signature instance for " + kt + " key");
final KeyType kt = KeyType.fromKey(key);
Signature signature;
try {
signature = params.getTransport().getKeyAlgorithm(kt).newSignature();
} catch (TransportException e) {
throw new UserAuthException("No KeyAlgorithm configured for key " + kt);
}
signature.initSign(key);
signature.update(new Buffer.PlainBuffer()
.putString(params.getTransport().getSessionID())
.putBuffer(reqBuf) // & rest of the data for sig
.getCompactData());
reqBuf.putSignature(kt, signature.encode(signature.sign()));
reqBuf.putSignature(signature.getSignatureName(), signature.encode(signature.sign()));
return reqBuf;
}

View File

@@ -30,6 +30,7 @@
*/
package net.schmizz.sshj.signature
import com.hierynomus.sshj.common.KeyAlgorithm
import spock.lang.Unroll;
import java.math.BigInteger;
@@ -47,7 +48,7 @@ import spock.lang.Specification
class SignatureDSASpec extends Specification {
def keyFactory = KeyFactory.getInstance("DSA")
def keyFactory = KeyFactory.getInstance(KeyAlgorithm.DSA)
private PublicKey createPublicKey(final byte[] y, final byte[] p, final byte[] q, final byte[] g) throws Exception {
final BigInteger publicKey = new BigInteger(y);

View File

@@ -15,6 +15,7 @@
*/
package net.schmizz.sshj.keyprovider;
import com.hierynomus.sshj.common.KeyDecryptionFailedException;
import com.hierynomus.sshj.userauth.certificate.Certificate;
import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile;
import net.schmizz.sshj.common.KeyType;
@@ -200,12 +201,34 @@ public class OpenSSHKeyFileTest {
@Test
public void shouldLoadProtectedED25519PrivateKeyAes256CTR() throws IOException {
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_protected", "sshjtest");
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_protected", "sshjtest", false);
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_protected", "sshjtest", true);
}
@Test
public void shouldLoadProtectedED25519PrivateKeyAes256CBC() throws IOException {
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_aes256cbc.pem", "foobar");
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_aes256cbc.pem", "foobar", false);
checkOpenSSHKeyV1("src/test/resources/keytypes/ed25519_aes256cbc.pem", "foobar", true);
}
@Test(expected = KeyDecryptionFailedException.class)
public void shouldFailOnIncorrectPassphraseAfterRetries() throws IOException {
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
keyFile.init(new File("src/test/resources/keytypes/ed25519_aes256cbc.pem"), new PasswordFinder() {
private int reqCounter = 0;
@Override
public char[] reqPassword(Resource<?> resource) {
reqCounter++;
return "incorrect".toCharArray();
}
@Override
public boolean shouldRetry(Resource<?> resource) {
return reqCounter <= 3;
}
});
keyFile.getPrivate();
}
@Test
@@ -224,17 +247,25 @@ public class OpenSSHKeyFileTest {
assertThat(aPrivate.getAlgorithm(), equalTo("ECDSA"));
}
private void checkOpenSSHKeyV1(String key, final String password) throws IOException {
private void checkOpenSSHKeyV1(String key, final String password, boolean withRetry) throws IOException {
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
keyFile.init(new File(key), new PasswordFinder() {
private int reqCounter = 0;
@Override
public char[] reqPassword(Resource<?> resource) {
return password.toCharArray();
if (withRetry && reqCounter < 3) {
reqCounter++;
// Return an incorrect password three times before returning the correct one.
return (password + "incorrect").toCharArray();
} else {
return password.toCharArray();
}
}
@Override
public boolean shouldRetry(Resource<?> resource) {
return false;
return withRetry && reqCounter <= 3;
}
});
PrivateKey aPrivate = keyFile.getPrivate();

View File

@@ -22,6 +22,7 @@ import java.security.spec.DSAPrivateKeySpec;
import java.security.spec.DSAPublicKeySpec;
import java.util.Arrays;
import com.hierynomus.sshj.common.KeyAlgorithm;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -35,7 +36,7 @@ public class SignatureDSATest {
@Before
public void setUp() throws NoSuchAlgorithmException {
keyFactory = KeyFactory.getInstance("DSA");
keyFactory = KeyFactory.getInstance(KeyAlgorithm.DSA);
}
@Test

View File

@@ -30,6 +30,7 @@
*/
package net.schmizz.sshj.util;
import com.hierynomus.sshj.common.KeyAlgorithm;
import net.schmizz.sshj.common.SecurityUtils;
import java.math.BigInteger;
@@ -46,7 +47,7 @@ public class KeyUtil {
/** Creates a DSA private key. */
public static PrivateKey newDSAPrivateKey(String x, String p, String q, String g)
throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("DSA").generatePrivate(new DSAPrivateKeySpec(new BigInteger(x, 16),
return SecurityUtils.getKeyFactory(KeyAlgorithm.DSA).generatePrivate(new DSAPrivateKeySpec(new BigInteger(x, 16),
new BigInteger(p, 16),
new BigInteger(q, 16),
new BigInteger(g, 16))
@@ -56,7 +57,7 @@ public class KeyUtil {
/** Creates a DSA public key. */
public static PublicKey newDSAPublicKey(String y, String p, String q, String g)
throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("DSA").generatePublic(new DSAPublicKeySpec(new BigInteger(y, 16),
return SecurityUtils.getKeyFactory(KeyAlgorithm.DSA).generatePublic(new DSAPublicKeySpec(new BigInteger(y, 16),
new BigInteger(p, 16),
new BigInteger(q, 16),
new BigInteger(g, 16))
@@ -66,7 +67,7 @@ public class KeyUtil {
/** Creates an RSA private key. */
public static PrivateKey newRSAPrivateKey(String modulus, String exponent)
throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("RSA").generatePrivate(new RSAPrivateKeySpec(new BigInteger(modulus, 16),
return SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePrivate(new RSAPrivateKeySpec(new BigInteger(modulus, 16),
new BigInteger(exponent, 16))
);
}
@@ -74,7 +75,7 @@ public class KeyUtil {
/** Creates an RSA public key. */
public static PublicKey newRSAPublicKey(String modulus, String exponent)
throws GeneralSecurityException {
return SecurityUtils.getKeyFactory("RSA").generatePublic(new RSAPublicKeySpec(new BigInteger(modulus, 16),
return SecurityUtils.getKeyFactory(KeyAlgorithm.RSA).generatePublic(new RSAPublicKeySpec(new BigInteger(modulus, 16),
new BigInteger(exponent, 16)));
}