[SSHD-842] Split Putty key files support code to separate artifact
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/f60fcb0a Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/f60fcb0a Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/f60fcb0a Branch: refs/heads/master Commit: f60fcb0a547441f001b2a624022af0f73b80ce7f Parents: 10de190 Author: Goldstein Lyor <[email protected]> Authored: Thu Sep 6 10:43:46 2018 +0300 Committer: Lyor Goldstein <[email protected]> Committed: Thu Sep 6 19:07:54 2018 +0300 ---------------------------------------------------------------------- README.md | 44 +++- assembly/pom.xml | 5 + pom.xml | 1 + sshd-cli/pom.xml | 5 + .../loader/putty/AbstractPuttyKeyDecoder.java | 218 ------------------- .../keys/loader/putty/DSSPuttyKeyDecoder.java | 65 ------ .../keys/loader/putty/ECDSAPuttyKeyDecoder.java | 98 --------- .../keys/loader/putty/EdDSAPuttyKeyDecoder.java | 68 ------ .../putty/PuttyKeyPairResourceParser.java | 200 ----------------- .../keys/loader/putty/PuttyKeyReader.java | 75 ------- .../config/keys/loader/putty/PuttyKeyUtils.java | 67 ------ .../keys/loader/putty/RSAPuttyKeyDecoder.java | 72 ------ .../keys/loader/putty/PuttyKeyUtilsTest.java | 153 ------------- ...KeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk | 10 - ...KeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk | 11 - ...KeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk | 12 - .../putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk | 17 -- .../PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk | 9 - .../putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk | 18 -- ...-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk | 10 - ...-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk | 11 - ...-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk | 12 - ...t-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk | 17 -- ...ssphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk | 9 - ...t-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk | 18 -- sshd-putty/pom.xml | 107 +++++++++ .../loader/putty/AbstractPuttyKeyDecoder.java | 218 +++++++++++++++++++ .../keys/loader/putty/DSSPuttyKeyDecoder.java | 65 ++++++ .../keys/loader/putty/ECDSAPuttyKeyDecoder.java | 98 +++++++++ .../keys/loader/putty/EdDSAPuttyKeyDecoder.java | 68 ++++++ .../putty/PuttyKeyPairResourceParser.java | 200 +++++++++++++++++ .../keys/loader/putty/PuttyKeyReader.java | 75 +++++++ .../config/keys/loader/putty/PuttyKeyUtils.java | 67 ++++++ .../keys/loader/putty/RSAPuttyKeyDecoder.java | 72 ++++++ sshd-putty/src/main/resources/.gitignore | 0 .../keys/loader/putty/PuttyKeyUtilsTest.java | 153 +++++++++++++ sshd-putty/src/test/resources/.gitignore | 0 sshd-putty/src/test/resources/log4j.properties | 38 ++++ ...KeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk | 10 + ...KeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk | 11 + ...KeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk | 12 + .../putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk | 17 ++ .../PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk | 9 + .../putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk | 18 ++ ...-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk | 10 + ...-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk | 11 + ...-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk | 12 + ...t-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk | 17 ++ ...ssphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk | 9 + ...t-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk | 18 ++ 50 files changed, 1366 insertions(+), 1174 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md index cad3ea8..4fb0f17 100644 --- a/README.md +++ b/README.md @@ -1578,13 +1578,49 @@ The code contains [support for "wrapper" protocols](https://issues.apache.org/ji * `SshServer/ServerSession#setServerProxyAcceptor` - sets a proxy that intercept the 1st incoming packet before being processed by the server -## Useful extra components in _sshd-contrib_ +## Configuration/data files parsing support + +Most of the configuration data files parsing support resides in the _sshd-common_ artfiact: + +```xml + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-common</artifactId> + <version>...same version as the rest of the artifacts...</version> + </dependency> +``` + +The code contains support for parsing the [_authorized_keys_](http://man.openbsd.org/sshd.8#AUTHORIZED_KEYS_FILE_FORMAT), +[_known\_hosts_](http://www.manpagez.com/man/8/sshd/), [_ssh\_config_, _sshd\_config_](https://www.freebsd.org/cgi/man.cgi?query=ssh_config&sektion=5), +and [_~/config_](http://www.gsp.com/cgi-bin/man.cgi?topic=ssh_config) files. The code resides in the _sshd-common_ artifact - specifically +the `KeyUtils#getPublicKeyEntryDecoder`, `AuthorizedKeyEntry#readAuthorizedKeys`, `KnownHostEntry#readKnownHostEntries` +and `HostConfigEntry#readHostConfigEntries`. + +### PEM/OpenSSH -* PUTTY key file(s) readers - see `org.apache.sshd.common.config.keys.loader.putty` package - specifically `PuttyKeyUtils#DEFAULT_INSTANCE KeyPairResourceParser`. +The common code contains built-in support for parsing PEM and/or _OpenSSH_ formatted key files and using them for authentication purposes. +As mentioned previously, it can leverage _Bouncycastle_ if available, but can do most of the work without it as well. For _ed25519_ support, +one must provide the _eddsa_ artifact dependency. +### [PUTTY](https://www.putty.org/) + +The code contains built-in support for parsing PUTTY key files (usually _.ppk_) and using them same as SSH ones as key-pair +providers for autentication purposes. The PUTTY key file(s) readers are contained in the `org.apache.sshd.common.config.keys.loader.putty` +package (specifically `PuttyKeyUtils#DEFAULT_INSTANCE KeyPairResourceParser`) of the _sshd-putty_ artifact. **Note:** the artifact should +be included as an extra dependency: + +```xml + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-putty</artifactId> + <version>...same version as the rest of the artifacts...</version> + </dependency> +``` + +## Useful extra components in _sshd-contrib_ -* `InteractivePasswordIdentityProvider` - helps implement a `PasswordIdentityProvider` by delegating calls to `UserInteraction#getUpdatedPassword`. -The way to use it would be as follows: +* `InteractivePasswordIdentityProvider` - helps implement a `PasswordIdentityProvider` by delegating calls +to `UserInteraction#getUpdatedPassword`. The way to use it would be as follows: ```java http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/assembly/pom.xml ---------------------------------------------------------------------- diff --git a/assembly/pom.xml b/assembly/pom.xml index 93011a8..58f20a0 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -43,6 +43,11 @@ </dependency> <dependency> <groupId>org.apache.sshd</groupId> + <artifactId>sshd-putty</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sshd</groupId> <artifactId>sshd-core</artifactId> <version>${project.version}</version> </dependency> http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index a684d78..46d068e 100644 --- a/pom.xml +++ b/pom.xml @@ -1136,6 +1136,7 @@ <modules> <module>sshd-common</module> + <module>sshd-putty</module> <module>sshd-core</module> <module>sshd-mina</module> <module>sshd-netty</module> http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-cli/pom.xml ---------------------------------------------------------------------- diff --git a/sshd-cli/pom.xml b/sshd-cli/pom.xml index 3babf6b..7b2c33a 100644 --- a/sshd-cli/pom.xml +++ b/sshd-cli/pom.xml @@ -52,6 +52,11 @@ <artifactId>sshd-sftp</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-putty</artifactId> + <version>${project.version}</version> + </dependency> <!-- Test dependencies --> <dependency> http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java deleted file mode 100644 index d2428e2..0000000 --- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StreamCorruptedException; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.util.Base64; -import java.util.Base64.Decoder; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.apache.sshd.common.config.keys.FilePasswordProvider; -import org.apache.sshd.common.config.keys.impl.AbstractIdentityResourceLoader; -import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser; -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.ValidateUtils; - -/** - * @param <PUB> Generic public key type - * @param <PRV> Generic private key type - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends PrivateKey> - extends AbstractIdentityResourceLoader<PUB, PRV> - implements PuttyKeyPairResourceParser<PUB, PRV> { - public static final String ENCRYPTION_HEADER = "Encryption"; - - protected AbstractPuttyKeyDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) { - super(pubType, prvType, names); - } - - @Override - public boolean canExtractKeyPairs(String resourceKey, List<String> lines) - throws IOException, GeneralSecurityException { - if (!PuttyKeyPairResourceParser.super.canExtractKeyPairs(resourceKey, lines)) { - return false; - } - - for (String l : lines) { - l = GenericUtils.trimToEmpty(l); - if (!l.startsWith(KEY_FILE_HEADER_PREFIX)) { - continue; - } - - int pos = l.indexOf(':'); - if ((pos <= 0) || (pos >= (l.length() - 1))) { - return false; - } - - Collection<String> supported = getSupportedTypeNames(); - String typeValue = l.substring(pos + 1).trim(); - return supported.contains(typeValue); - } - - return false; - } - - @Override - public Collection<KeyPair> loadKeyPairs( - String resourceKey, FilePasswordProvider passwordProvider, List<String> lines) - throws IOException, GeneralSecurityException { - List<String> pubLines = Collections.emptyList(); - List<String> prvLines = Collections.emptyList(); - String prvEncryption = null; - for (int index = 0, numLines = lines.size(); index < numLines; index++) { - String l = lines.get(index); - l = GenericUtils.trimToEmpty(l); - int pos = l.indexOf(':'); - if ((pos <= 0) || (pos >= (l.length() - 1))) { - continue; - } - - String hdrName = l.substring(0, pos).trim(); - String hdrValue = l.substring(pos + 1).trim(); - switch (hdrName) { - case ENCRYPTION_HEADER: - if (prvEncryption != null) { - throw new StreamCorruptedException("Duplicate " + hdrName + " in" + resourceKey); - } - prvEncryption = hdrValue; - break; - case PUBLIC_LINES_HEADER: - pubLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, pubLines); - index += pubLines.size(); - break; - case PRIVATE_LINES_HEADER: - prvLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, prvLines); - index += prvLines.size(); - break; - default: // ignored - } - } - - return loadKeyPairs(resourceKey, pubLines, prvLines, prvEncryption, passwordProvider); - } - - public static List<String> extractDataLines( - String resourceKey, List<String> lines, int startIndex, String hdrName, String hdrValue, List<String> curLines) - throws IOException { - if (GenericUtils.size(curLines) > 0) { - throw new StreamCorruptedException("Duplicate " + hdrName + " in " + resourceKey); - } - - int numLines; - try { - numLines = Integer.parseInt(hdrValue); - } catch (NumberFormatException e) { - throw new StreamCorruptedException("Bad " + hdrName + " value (" + hdrValue + ") in " + resourceKey); - } - - int endIndex = startIndex + numLines; - int totalLines = lines.size(); - if (endIndex > totalLines) { - throw new StreamCorruptedException("Excessive " + hdrName + " value (" + hdrValue + ") in " + resourceKey); - } - - return lines.subList(startIndex, endIndex); - } - - public Collection<KeyPair> loadKeyPairs( - String resourceKey, List<String> pubLines, List<String> prvLines, String prvEncryption, FilePasswordProvider passwordProvider) - throws IOException, GeneralSecurityException { - return loadKeyPairs(resourceKey, - KeyPairResourceParser.joinDataLines(pubLines), KeyPairResourceParser.joinDataLines(prvLines), - prvEncryption, passwordProvider); - } - - public Collection<KeyPair> loadKeyPairs( - String resourceKey, String pubData, String prvData, String prvEncryption, FilePasswordProvider passwordProvider) - throws IOException, GeneralSecurityException { - Decoder b64Decoder = Base64.getDecoder(); - byte[] pubBytes = b64Decoder.decode(pubData); - byte[] prvBytes = b64Decoder.decode(prvData); - String password = null; - if ((GenericUtils.length(prvEncryption) > 0) - && (!NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption))) { - password = passwordProvider.getPassword(resourceKey); - } - - if (GenericUtils.isEmpty(prvEncryption) - || NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption) - || GenericUtils.isEmpty(password)) { - return loadKeyPairs(resourceKey, pubBytes, prvBytes); - } - - // format is "<cipher><bits>-<mode>" - e.g., "aes256-cbc" - int pos = prvEncryption.indexOf('-'); - if (pos <= 0) { - throw new StreamCorruptedException("Missing private key encryption mode in " + prvEncryption); - } - - String mode = prvEncryption.substring(pos + 1).toUpperCase(); - String algName = null; - int numBits = 0; - for (int index = 0; index < pos; index++) { - char ch = prvEncryption.charAt(index); - if ((ch >= '0') && (ch <= '9')) { - algName = prvEncryption.substring(0, index).toUpperCase(); - numBits = Integer.parseInt(prvEncryption.substring(index, pos)); - break; - } - } - - if (GenericUtils.isEmpty(algName) || (numBits <= 0)) { - throw new StreamCorruptedException("Missing private key encryption algorithm details in " + prvEncryption); - } - - prvBytes = PuttyKeyPairResourceParser.decodePrivateKeyBytes(prvBytes, algName, numBits, mode, password); - return loadKeyPairs(resourceKey, pubBytes, prvBytes); - } - - public Collection<KeyPair> loadKeyPairs(String resourceKey, byte[] pubData, byte[] prvData) - throws IOException, GeneralSecurityException { - ValidateUtils.checkNotNullAndNotEmpty(pubData, "No public key data in %s", resourceKey); - ValidateUtils.checkNotNullAndNotEmpty(prvData, "No private key data in %s", resourceKey); - try (InputStream pubStream = new ByteArrayInputStream(pubData); - InputStream prvStream = new ByteArrayInputStream(prvData)) { - return loadKeyPairs(resourceKey, pubStream, prvStream); - } - } - - public Collection<KeyPair> loadKeyPairs(String resourceKey, InputStream pubData, InputStream prvData) - throws IOException, GeneralSecurityException { - try (PuttyKeyReader pubReader = - new PuttyKeyReader(ValidateUtils.checkNotNull(pubData, "No public key data in %s", resourceKey)); - PuttyKeyReader prvReader = - new PuttyKeyReader(ValidateUtils.checkNotNull(prvData, "No private key data in %s", resourceKey))) { - return loadKeyPairs(resourceKey, pubReader, prvReader); - } - } - - public abstract Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader) - throws IOException, GeneralSecurityException; -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java deleted file mode 100644 index 366aead..0000000 --- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/DSSPuttyKeyDecoder.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.interfaces.DSAPrivateKey; -import java.security.interfaces.DSAPublicKey; -import java.security.spec.DSAPrivateKeySpec; -import java.security.spec.DSAPublicKeySpec; -import java.util.Collection; -import java.util.Collections; - -import org.apache.sshd.common.config.keys.KeyUtils; -import org.apache.sshd.common.keyprovider.KeyPairProvider; -import org.apache.sshd.common.util.security.SecurityUtils; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public class DSSPuttyKeyDecoder extends AbstractPuttyKeyDecoder<DSAPublicKey, DSAPrivateKey> { - public static final DSSPuttyKeyDecoder INSTANCE = new DSSPuttyKeyDecoder(); - - public DSSPuttyKeyDecoder() { - super(DSAPublicKey.class, DSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_DSS)); - } - - @Override - public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader) - throws IOException, GeneralSecurityException { - pubReader.skip(); // skip version - - BigInteger p = pubReader.readInt(); - BigInteger q = pubReader.readInt(); - BigInteger g = pubReader.readInt(); - BigInteger y = pubReader.readInt(); - BigInteger x = prvReader.readInt(); - KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM); - PublicKey pubKey = kf.generatePublic(new DSAPublicKeySpec(y, p, q, g)); - PrivateKey prvKey = kf.generatePrivate(new DSAPrivateKeySpec(x, p, q, g)); - return Collections.singletonList(new KeyPair(pubKey, prvKey)); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java deleted file mode 100644 index a257ff8..0000000 --- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/ECDSAPuttyKeyDecoder.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.InvalidKeySpecException; -import java.util.Collection; -import java.util.Collections; - -import org.apache.sshd.common.cipher.ECCurves; -import org.apache.sshd.common.config.keys.KeyUtils; -import org.apache.sshd.common.util.buffer.BufferUtils; -import org.apache.sshd.common.util.security.SecurityUtils; - -/** - * TODO Add javadoc - * - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public class ECDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<ECPublicKey, ECPrivateKey> { - public static final ECDSAPuttyKeyDecoder INSTANCE = new ECDSAPuttyKeyDecoder(); - - public ECDSAPuttyKeyDecoder() { - super(ECPublicKey.class, ECPrivateKey.class, ECCurves.KEY_TYPES); - } - - @Override - public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader) - throws IOException, GeneralSecurityException { - if (!SecurityUtils.isECCSupported()) { - throw new NoSuchAlgorithmException("ECC not supported for " + resourceKey); - } - - String keyType = pubReader.readString(); - ECCurves curve = ECCurves.fromKeyType(keyType); - if (curve == null) { - throw new InvalidKeySpecException("Not an EC curve name: " + keyType); - } - - String encCurveName = pubReader.readString(); - String keyCurveName = curve.getName(); - if (!keyCurveName.equals(encCurveName)) { - throw new InvalidKeySpecException("Mismatched key curve name (" + keyCurveName + ") vs. encoded one (" + encCurveName + ")"); - } - - byte[] octets = pubReader.read(); - ECPoint w; - try { - w = ECCurves.octetStringToEcPoint(octets); - if (w == null) { - throw new InvalidKeySpecException("No public ECPoint generated for curve=" + keyCurveName - + " from octets=" + BufferUtils.toHex(':', octets)); - } - } catch (RuntimeException e) { - throw new InvalidKeySpecException("Failed (" + e.getClass().getSimpleName() + ")" - + " to generate public ECPoint for curve=" + keyCurveName - + " from octets=" + BufferUtils.toHex(':', octets) - + ": " + e.getMessage()); - } - - KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.EC_ALGORITHM); - ECParameterSpec paramSpec = curve.getParameters(); - PublicKey pubKey = kf.generatePublic(new ECPublicKeySpec(w, paramSpec)); - - BigInteger s = prvReader.readInt(); - PrivateKey prvKey = kf.generatePrivate(new ECPrivateKeySpec(s, paramSpec)); - return Collections.singletonList(new KeyPair(pubKey, prvKey)); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java deleted file mode 100644 index f5980ab..0000000 --- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/EdDSAPuttyKeyDecoder.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.util.Collection; -import java.util.Collections; - -import org.apache.sshd.common.keyprovider.KeyPairProvider; -import org.apache.sshd.common.util.security.SecurityUtils; -import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils; - -import net.i2p.crypto.eddsa.EdDSAPrivateKey; -import net.i2p.crypto.eddsa.EdDSAPublicKey; - -/** - * TODO Add javadoc - * - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public class EdDSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<EdDSAPublicKey, EdDSAPrivateKey> { - public static final EdDSAPuttyKeyDecoder INSTANCE = new EdDSAPuttyKeyDecoder(); - - public EdDSAPuttyKeyDecoder() { - super(EdDSAPublicKey.class, EdDSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_ED25519)); - } - - @Override - public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader) - throws IOException, GeneralSecurityException { - if (!SecurityUtils.isEDDSACurveSupported()) { - throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported for " + resourceKey); - } - - String keyType = pubReader.readString(); - if (!KeyPairProvider.SSH_ED25519.equals(keyType)) { - throw new InvalidKeySpecException("Not an " + SecurityUtils.EDDSA + " key: " + keyType); - } - - byte[] seed = pubReader.read(); - PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(seed); - seed = prvReader.read(); - PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(seed); - return Collections.singletonList(new KeyPair(pubKey, prvKey)); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java deleted file mode 100644 index 06443d9..0000000 --- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyPairResourceParser.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import org.apache.sshd.common.config.keys.IdentityResourceLoader; -import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser; -import org.apache.sshd.common.digest.BuiltinDigests; -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.ValidateUtils; -import org.apache.sshd.common.util.security.SecurityUtils; - -//CHECKSTYLE:OFF -/** - * Loads a {@link KeyPair} from PuTTY's ".ppk" file. - * <P>Note(s):</P> - * <UL> - * <P><LI> - * The file appears to be a text file but it doesn't have a fixed encoding like UTF-8. - * We use UTF-8 as the default encoding - since the important part is all ASCII, - * this shouldn't really hurt the interpretation of the key. - * </LI></P> - * - * <P><LI> - * Based on code from <A HREF="https://github.com/kohsuke/trilead-putty-extension">Kohsuke's Trilead Putty Extension</A> - * </LI></P> - * - * <P><LI> - * Encrypted keys requires AES-256-CBC support, which is available only if the - * <A HREF="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html"> - * Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files</A> are installed - * </LI></P> - * </UL> - * - * <P>Sample PuTTY file format</P> - * <PRE> - * PuTTY-User-Key-File-2: ssh-rsa - * Encryption: none - * Comment: rsa-key-20080514 - * Public-Lines: 4 - * AAAAB3NzaC1yc2EAAAABJQAAAIEAiPVUpONjGeVrwgRPOqy3Ym6kF/f8bltnmjA2 - * BMdAtaOpiD8A2ooqtLS5zWYuc0xkW0ogoKvORN+RF4JI+uNUlkxWxnzJM9JLpnvA - * HrMoVFaQ0cgDMIHtE1Ob1cGAhlNInPCRnGNJpBNcJ/OJye3yt7WqHP4SPCCLb6nL - * nmBUrLM= - * Private-Lines: 8 - * AAAAgGtYgJzpktzyFjBIkSAmgeVdozVhgKmF6WsDMUID9HKwtU8cn83h6h7ug8qA - * hUWcvVxO201/vViTjWVz9ALph3uMnpJiuQaaNYIGztGJBRsBwmQW9738pUXcsUXZ - * 79KJP01oHn6Wkrgk26DIOsz04QOBI6C8RumBO4+F1WdfueM9AAAAQQDmA4hcK8Bx - * nVtEpcF310mKD3nsbJqARdw5NV9kCxPnEsmy7Sy1L4Ob/nTIrynbc3MA9HQVJkUz - * 7V0va5Pjm/T7AAAAQQCYbnG0UEekwk0LG1Hkxh1OrKMxCw2KWMN8ac3L0LVBg/Tk - * 8EnB2oT45GGeJaw7KzdoOMFZz0iXLsVLNUjNn2mpAAAAQQCN6SEfWqiNzyc/w5n/ - * lFVDHExfVUJp0wXv+kzZzylnw4fs00lC3k4PZDSsb+jYCMesnfJjhDgkUA0XPyo8 - * Emdk - * Private-MAC: 50c45751d18d74c00fca395deb7b7695e3ed6f77 - * </PRE> - * @param <PUB> Generic public key type - * @param <PRV> Generic private key type - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -//CHECKSTYLE:ON -public interface PuttyKeyPairResourceParser<PUB extends PublicKey, PRV extends PrivateKey> - extends IdentityResourceLoader<PUB, PRV>, KeyPairResourceParser { - String KEY_FILE_HEADER_PREFIX = "PuTTY-User-Key-File"; - String PUBLIC_LINES_HEADER = "Public-Lines"; - String PRIVATE_LINES_HEADER = "Private-Lines"; - String PPK_FILE_SUFFIX = ".ppk"; - - List<String> KNOWN_HEADERS = - Collections.unmodifiableList( - Arrays.asList( - KEY_FILE_HEADER_PREFIX, - PUBLIC_LINES_HEADER, - PRIVATE_LINES_HEADER)); - - /** - * Value (case insensitive) used to denote that private key is not encrypted - */ - String NO_PRIVATE_KEY_ENCRYPTION_VALUE = "none"; - - @Override - default boolean canExtractKeyPairs(String resourceKey, List<String> lines) - throws IOException, GeneralSecurityException { - if (GenericUtils.isEmpty(lines)) { - return false; - } - - for (String l : lines) { - l = GenericUtils.trimToEmpty(l); - for (String hdr : KNOWN_HEADERS) { - if (l.startsWith(hdr)) { - return true; - } - } - } - - return false; - } - - static byte[] decodePrivateKeyBytes(byte[] prvBytes, String algName, int numBits, String algMode, String password) - throws GeneralSecurityException { - Objects.requireNonNull(prvBytes, "No encrypted key bytes"); - ValidateUtils.checkNotNullAndNotEmpty(algName, "No encryption algorithm", GenericUtils.EMPTY_OBJECT_ARRAY); - ValidateUtils.checkTrue(numBits > 0, "Invalid encryption key size: %d", numBits); - ValidateUtils.checkNotNullAndNotEmpty(algMode, "No encryption mode", GenericUtils.EMPTY_OBJECT_ARRAY); - ValidateUtils.checkNotNullAndNotEmpty(password, "No encryption password", GenericUtils.EMPTY_OBJECT_ARRAY); - - if (!"AES".equalsIgnoreCase(algName)) { - throw new NoSuchAlgorithmException("decodePrivateKeyBytes(" + algName + "-" + numBits + "-" + algMode + ") N/A"); - } - - return decodePrivateKeyBytes(prvBytes, algName, algMode, numBits, new byte[16], toEncryptionKey(password)); - } - - static byte[] decodePrivateKeyBytes( - byte[] encBytes, String cipherName, String cipherMode, int numBits, byte[] initVector, byte[] keyValue) - throws GeneralSecurityException { - String xform = cipherName + "/" + cipherMode + "/NoPadding"; - int maxAllowedBits = Cipher.getMaxAllowedKeyLength(xform); - // see http://www.javamex.com/tutorials/cryptography/unrestricted_policy_files.shtml - if (numBits > maxAllowedBits) { - throw new InvalidKeySpecException("decodePrivateKeyBytes(" + xform + ")" - + " required key length (" + numBits + ") exceeds max. available: " + maxAllowedBits); - } - - SecretKeySpec skeySpec = new SecretKeySpec(keyValue, cipherName); - IvParameterSpec ivspec = new IvParameterSpec(initVector); - Cipher cipher = SecurityUtils.getCipher(xform); - cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec); - - return cipher.doFinal(encBytes); - } - - /** - * Converts a pass-phrase into a key, by following the convention that PuTTY uses. - * Used to decrypt the private key when it's encrypted. - * @param passphrase the Password to be used as seed for the key - ignored - * if {@code null}/empty - * @return The encryption key bytes - {@code null} if no pass-phrase - * @throws GeneralSecurityException If cannot retrieve SHA-1 digest - * @see <A HREF="http://security.stackexchange.com/questions/71341/how-does-putty-derive-the-encryption-key-in-its-ppk-format"> - * How does Putty derive the encryption key in its .ppk format ?</A> - */ - static byte[] toEncryptionKey(String passphrase) throws GeneralSecurityException { - if (GenericUtils.isEmpty(passphrase)) { - return null; - } - - MessageDigest hash = SecurityUtils.getMessageDigest(BuiltinDigests.sha1.getAlgorithm()); - byte[] stateValue = {0, 0, 0, 0}; - byte[] passBytes = passphrase.getBytes(StandardCharsets.UTF_8); - byte[] keyValue = new byte[32]; - for (int i = 0, remLen = keyValue.length; i < 2; i++) { - hash.reset(); // just making sure - - stateValue[3] = (byte) i; - hash.update(stateValue); - hash.update(passBytes); - - byte[] digest = hash.digest(); - System.arraycopy(digest, 0, keyValue, i * 20, Math.min(20, remLen)); - remLen -= 20; - } - - return keyValue; - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java deleted file mode 100644 index 4fb63d1..0000000 --- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyReader.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.io.Closeable; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StreamCorruptedException; -import java.math.BigInteger; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; - -/** - * Helper class for {@code Putty} key files decoders - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public class PuttyKeyReader implements Closeable { - private final DataInputStream di; - - public PuttyKeyReader(InputStream s) { - di = new DataInputStream(s); - } - - public void skip() throws IOException { - int skipSize = di.readInt(); - int effectiveSkip = di.skipBytes(skipSize); - if (skipSize != effectiveSkip) { - throw new StreamCorruptedException("Mismatched skip size: expected" + skipSize + ", actual=" + effectiveSkip); - } - } - - public String readString() throws IOException { - return readString(StandardCharsets.UTF_8); - } - - public String readString(Charset cs) throws IOException { - byte[] data = read(); - return new String(data, cs); - } - - public BigInteger readInt() throws IOException { - byte[] bytes = read(); - return new BigInteger(bytes); - } - - public byte[] read() throws IOException { - int len = di.readInt(); - byte[] r = new byte[len]; - di.readFully(r); - return r; - } - - @Override - public void close() throws IOException { - di.close(); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java deleted file mode 100644 index e750ace..0000000 --- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtils.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.NavigableMap; -import java.util.TreeMap; - -import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser; -import org.apache.sshd.common.util.security.SecurityUtils; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public final class PuttyKeyUtils { - public static final List<PuttyKeyPairResourceParser<?, ?>> DEFAULT_PARSERS; - - public static final NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> BY_KEY_TYPE; - - public static final KeyPairResourceParser DEFAULT_INSTANCE; - - static { - List<PuttyKeyPairResourceParser<?, ?>> parsers = new ArrayList<>(); - parsers.add(RSAPuttyKeyDecoder.INSTANCE); - parsers.add(DSSPuttyKeyDecoder.INSTANCE); - if (SecurityUtils.isECCSupported()) { - parsers.add(ECDSAPuttyKeyDecoder.INSTANCE); - } - if (SecurityUtils.isEDDSACurveSupported()) { - parsers.add(EdDSAPuttyKeyDecoder.INSTANCE); - } - NavigableMap<String, PuttyKeyPairResourceParser<?, ?>> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - for (PuttyKeyPairResourceParser<?, ?> p : parsers) { - Collection<String> supported = p.getSupportedTypeNames(); - for (String k : supported) { - map.put(k, p); - } - } - DEFAULT_PARSERS = Collections.unmodifiableList(parsers); - BY_KEY_TYPE = Collections.unmodifiableNavigableMap(map); - DEFAULT_INSTANCE = KeyPairResourceParser.aggregate(parsers); - } - - private PuttyKeyUtils() { - throw new UnsupportedOperationException("No instance"); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java b/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java deleted file mode 100644 index 0a55d55..0000000 --- a/sshd-contrib/src/main/java/org/apache/sshd/common/config/keys/loader/putty/RSAPuttyKeyDecoder.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.io.IOException; -import java.math.BigInteger; -import java.security.GeneralSecurityException; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; -import java.security.spec.RSAPrivateCrtKeySpec; -import java.security.spec.RSAPrivateKeySpec; -import java.security.spec.RSAPublicKeySpec; -import java.util.Collection; -import java.util.Collections; - -import org.apache.sshd.common.config.keys.KeyUtils; -import org.apache.sshd.common.keyprovider.KeyPairProvider; -import org.apache.sshd.common.util.security.SecurityUtils; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -public class RSAPuttyKeyDecoder extends AbstractPuttyKeyDecoder<RSAPublicKey, RSAPrivateKey> { - public static final RSAPuttyKeyDecoder INSTANCE = new RSAPuttyKeyDecoder(); - - public RSAPuttyKeyDecoder() { - super(RSAPublicKey.class, RSAPrivateKey.class, Collections.singletonList(KeyPairProvider.SSH_RSA)); - } - - @Override - public Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader) - throws IOException, GeneralSecurityException { - pubReader.skip(); // skip version - - KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM); - BigInteger publicExp = pubReader.readInt(); - BigInteger modulus = pubReader.readInt(); - PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, publicExp)); - - BigInteger privateExp = prvReader.readInt(); - BigInteger primeP = prvReader.readInt(); - BigInteger primeQ = prvReader.readInt(); - BigInteger crtCoef = prvReader.readInt(); - BigInteger primeExponentP = privateExp.mod(primeP.subtract(BigInteger.ONE)); - BigInteger primeExponentQ = privateExp.mod(primeQ.subtract(BigInteger.ONE)); - RSAPrivateKeySpec prvSpec = new RSAPrivateCrtKeySpec( - modulus, publicExp, privateExp, primeP, primeQ, primeExponentP, primeExponentQ, crtCoef); - PrivateKey prvKey = kf.generatePrivate(prvSpec); - return Collections.singletonList(new KeyPair(pubKey, prvKey)); - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java b/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java deleted file mode 100644 index 8b70f77..0000000 --- a/sshd-contrib/src/test/java/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.sshd.common.config.keys.loader.putty; - -import java.io.IOException; -import java.net.URL; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.util.Collection; -import java.util.List; - -import org.apache.sshd.common.cipher.BuiltinCiphers; -import org.apache.sshd.common.config.keys.KeyUtils; -import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder; -import org.apache.sshd.common.config.keys.loader.openssh.OpenSSHKeyPairResourceParser; -import org.apache.sshd.common.util.GenericUtils; -import org.apache.sshd.common.util.io.IoUtils; -import org.apache.sshd.util.test.BaseTestSupport; -import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; -import org.apache.sshd.util.test.NoIoTestCase; -import org.junit.Assume; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.runner.RunWith; -import org.junit.runners.MethodSorters; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; -import org.junit.runners.Parameterized.UseParametersRunnerFactory; - -/** - * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> - */ -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests -@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class) -@Category({ NoIoTestCase.class }) -public class PuttyKeyUtilsTest extends BaseTestSupport { - public static final String PASSWORD = "super secret passphrase"; - - private final String keyType; - private final String regularFile; - private final String encryptedFile; - private final PuttyKeyPairResourceParser<?, ?> parser; - - public PuttyKeyUtilsTest(String keyType) { - this.keyType = keyType; - this.parser = PuttyKeyUtils.BY_KEY_TYPE.get(keyType); - this.regularFile = getClass().getSimpleName() - + "-" + keyType + "-" + KeyPair.class.getSimpleName() - + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX; - this.encryptedFile = PASSWORD.replace(' ', '-') + "-AES-256-CBC" - + "-" + keyType + "-" + KeyPair.class.getSimpleName() - + PuttyKeyPairResourceParser.PPK_FILE_SUFFIX; - } - - @Parameters(name = "{0}") - public static List<Object[]> parameters() { - return parameterize(PuttyKeyUtils.BY_KEY_TYPE.keySet()); - } - - @Test - public void testCanDecodePuttyKeyFile() throws IOException, GeneralSecurityException { - for (String resource : new String[]{regularFile, encryptedFile}) { - URL url = getClass().getResource(resource); - if (GenericUtils.isSameReference(regularFile, resource)) { - assertNotNull("Missing test resource: " + resource, url); - } else { - if (url == null) { - outputDebugMessage("Skip non-existing encrypted file: %s", resource); - continue; - } - } - - List<String> lines = IoUtils.readAllLines(url); - assertTrue(resource + " - can extract key pair", parser.canExtractKeyPairs(resource, lines)); - - for (PuttyKeyPairResourceParser<?, ?> other : PuttyKeyUtils.BY_KEY_TYPE.values()) { - if (parser == other) { - continue; - } - - assertFalse(other.getClass().getSimpleName() + "/" + resource + " - unexpected extraction capability", - other.canExtractKeyPairs(resource, lines)); - } - } - } - - @Test - public void testDecodePuttyKeyFile() throws IOException, GeneralSecurityException { - URL url = getClass().getResource(regularFile); - assertNotNull("Missing test resource: " + regularFile, url); - - Collection<KeyPair> keys = parser.loadKeyPairs(url, null); - assertEquals("Mismatched loaded keys count from " + regularFile, 1, GenericUtils.size(keys)); - assertLoadedKeyPair(regularFile, keys.iterator().next()); - } - - @Test - public void testDecodeEncryptedPuttyKeyFile() throws IOException, GeneralSecurityException { - Assume.assumeTrue(BuiltinCiphers.aes256cbc.getTransformation() + " N/A", BuiltinCiphers.aes256cbc.isSupported()); - URL url = getClass().getResource(encryptedFile); - Assume.assumeTrue("Skip non-existent encrypted file: " + encryptedFile, url != null); - assertNotNull("Missing test resource: " + encryptedFile, url); - - Collection<KeyPair> keys = parser.loadKeyPairs(url, r -> PASSWORD); - assertEquals("Mismatched loaded keys count from " + encryptedFile, 1, GenericUtils.size(keys)); - - assertLoadedKeyPair(encryptedFile, keys.iterator().next()); - } - - private void assertLoadedKeyPair(String prefix, KeyPair kp) throws GeneralSecurityException { - assertNotNull(prefix + ": no key pair loaded", kp); - - PublicKey pubKey = kp.getPublic(); - assertNotNull(prefix + ": no public key loaded", pubKey); - assertEquals(prefix + ": mismatched public key type", keyType, KeyUtils.getKeyType(pubKey)); - - PrivateKey prvKey = kp.getPrivate(); - assertNotNull(prefix + ": no private key loaded", prvKey); - assertEquals(prefix + ": mismatched private key type", keyType, KeyUtils.getKeyType(prvKey)); - - @SuppressWarnings("rawtypes") - PrivateKeyEntryDecoder decoder = - OpenSSHKeyPairResourceParser.getPrivateKeyEntryDecoder(prvKey); - assertNotNull("No private key decoder", decoder); - - if (decoder.isPublicKeyRecoverySupported()) { - @SuppressWarnings("unchecked") - PublicKey recKey = decoder.recoverPublicKey(prvKey); - assertKeyEquals("Mismatched recovered public key", pubKey, recKey); - } - } -} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk deleted file mode 100644 index 509538a..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp256-KeyPair.ppk +++ /dev/null @@ -1,10 +0,0 @@ -PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 -Encryption: none -Comment: ecdsa-key-20170917 -Public-Lines: 3 -AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN -xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC -O5pCv6xFwYk= -Private-Lines: 1 -AAAAIQDdmu/Dr68r6a0PNbQUN1bX+zS6J5jFsOlEAx8sR73Tuw== -Private-MAC: 012e0d61593a431ae84beb6216dd29e4b203c1c0 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk deleted file mode 100644 index 238261c..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp384-KeyPair.ppk +++ /dev/null @@ -1,11 +0,0 @@ -PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 -Encryption: none -Comment: ecdsa-key-20170917 -Public-Lines: 3 -AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw -CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy -h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg== -Private-Lines: 2 -AAAAMHNGt3UPG3evJVl1GRoXXnqTafWLDQdWA2A1tXi8oRW0hhHMRe9/v0SCGL7S -nL3asg== -Private-MAC: 6fb6e93559ecacfa468aa5ff9776e4d4283db5ba http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk deleted file mode 100644 index f60a78e..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ecdsa-sha2-nistp521-KeyPair.ppk +++ /dev/null @@ -1,12 +0,0 @@ -PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 -Encryption: none -Comment: ecdsa-key-20170917 -Public-Lines: 4 -AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT -zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B -lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr -rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA== -Private-Lines: 2 -AAAAQgE28iZoHARx+2VzL7Y45FaY44TngX2b4StlC8wOlYF7NY/ba+Pt2RT/WcNL -ytmLdj5QeV/fFJ9x8i00mTU6KCF2AA== -Private-MAC: 7379e9986066087dff9339d2b0b968c2b31f45c7 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk deleted file mode 100644 index 59a8fac..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-dss-KeyPair.ppk +++ /dev/null @@ -1,17 +0,0 @@ -PuTTY-User-Key-File-2: ssh-dss -Encryption: none -Comment: dsa-key-20130709 -Public-Lines: 10 -AAAAB3NzaC1kc3MAAACBAMg/IxsG5BxnF5gM7IKqqR0rftxZC+n5GlbO+J4H+iIb -/KR8NBehkxG3CrBZMF96M2K1sEGYLob+3k4r71oWaPul8n5rt9kpd+JSq4iD2ygO -yg6Kd1/YDBHoxneizy6I/bGsLwhAAKWcRNrXmYVKGzhrhvZWN12AJDq2mGdj3szL -AAAAFQD7a2MltdUSF7FU3//SpW4WGjZbeQAAAIBf0nNsfKQL/TEMo7IpTrEMg5V0 -RnSigCX0+yUERS42GW/ZeCZBJw7oL2XZbuBtu63vMjDgVpnb92BdrcPgjJ7EFW6D -lcyeuywStmg1ygXmDR2AQCxv0eX2CQgrdUczmRa155SDVUTvTQlO1IyKx0vwKAh1 -H7E3yJUfkTAJstbGYQAAAIEAtv+cdRfNevYFkp55jVqazc8zRLvfb64jzgc5oSJV -c64kFs4yx+abYpGX9WxNxDlG6g2WiY8voDBB0YnUJsn0kVRjBKX9OceROxrfT4K4 -dVbQZsdt+SLaXWL4lGJFrFZL3LZqvySvq6xfhJfakQDDivW4hUOhFPXPHrE5/Ia3 -T7A= -Private-Lines: 1 -AAAAFQCE6flGnmVCAbzo9YsbdJWBnxMnBA== -Private-MAC: 6ace0a5e5bf23649c1375e91dcd71d1def6c6aa1 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk deleted file mode 100644 index 614ac69..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-ed25519-KeyPair.ppk +++ /dev/null @@ -1,9 +0,0 @@ -PuTTY-User-Key-File-2: ssh-ed25519 -Encryption: none -Comment: ed25519-key-20170917 -Public-Lines: 2 -AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/ -9fIi -Private-Lines: 1 -AAAAIADKJJPxsUp7JXLzm1zwk8UswW/lkiwPJ73CbqGvalgP -Private-MAC: 28a22234152feaf1d9a6a10ca0ae3a51b5e6dd52 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk deleted file mode 100644 index da2868e..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/PuttyKeyUtilsTest-ssh-rsa-KeyPair.ppk +++ /dev/null @@ -1,18 +0,0 @@ -PuTTY-User-Key-File-2: ssh-rsa -Encryption: none -Comment: rsa-key-20130709 -Public-Lines: 4 -AAAAB3NzaC1yc2EAAAABJQAAAIBLSj7+SllQdWjkD8EN4sA/fUv/jhc+ssGmCYx3 -uoiPMxjKH3xUPWu4zxJzhdlgFy4gchzjU51fDS4Oel6xGoWbGKGe4ZLQdE/t8N8l -jAfOm/5lGp5tFhHs9UHoSm/h3RsErWNjKPjTGIlID35IcOXVhfhp9fX0RU6y/ZBI -PhM20w== -Private-Lines: 8 -AAAAgBx89T2fl2qNSkh0qemUEWQhkmCyTfwMSUW+bIBUawXATpGrDHLmztBOWgIy -pUb0A52S9iyAgLwugCEnYhl/qCxvoARH7ZyTdYAL4KjJDySxVuqeo/ZhLscYcMAz -MOyn8g5cR4dRgEwJ1/pRuK8r4+Z96zJG4NlxlHsUjHuj7t1dAAAAQQCTrj48XKIX -M3dxWLSsSXbUCOpmAOTviuz9LD0H1ik7a6ebr0P6GTl9z7iscBgzdjBIHMFcdvar -ophUJ5iRanCvAAAAQQCCg1VU1H5FHMipRvvw8b/zRqDsx6GTZs03ffhyKrTl1Dcd -0oKy5/U3kQwdXPOSlVZeyX8nUVE2o7DOh7INsX0dAAAAQHkPjxivrN0SQuVAx+tK -uRJ8vGL7sBOZ9gdTS25T3BVEkhRt37aDcshrodzDCzd515cwhmbLSsOsgyxcTwcX -7SA= -Private-MAC: 2416438f1a7ebdd33d519f6102d843b5f2c565d4 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk deleted file mode 100644 index 4c601c7..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp256-KeyPair.ppk +++ /dev/null @@ -1,10 +0,0 @@ -PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 -Encryption: aes256-cbc -Comment: ecdsa-key-20170917 -Public-Lines: 3 -AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBM99zj2+E6AN -xMZ/2SKFP/fAvPfUJUdsgJyn4g7nf36vNpoaRyq1FyHLxyT34AgTl1n3DwcaBXXC -O5pCv6xFwYk= -Private-Lines: 1 -/8MdniIqAaST5t3/bRx4mFdFxqN8jIwI0Hh7VTy8IV143j+PktgGIoHsUwTiEujQ -Private-MAC: be4b37d4b65ad09e6890534a2ba355599da796c6 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk deleted file mode 100644 index f57f5fd..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp384-KeyPair.ppk +++ /dev/null @@ -1,11 +0,0 @@ -PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 -Encryption: aes256-cbc -Comment: ecdsa-key-20170917 -Public-Lines: 3 -AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNHjIMrdMfXw -CUqBAhkZw0vXB+qypkiTcL1CmcopmPrKvGHFieFmedeCQotjwJkoAAeb5isZNOXy -h+7TnHGNrE/pZkHuNwACilpOt659hbhR2OGHX0jdpb8y4RVkuPQssg== -Private-Lines: 2 -MOgJnSZ8ZqGHFVtQvYKpOyTGWjMVIjIOMIUwhbxBuTiQ7WEyNPn9jjTsSwXtJxrG -UI6NGeIqZ41P2e8JINhMIg== -Private-MAC: 5ec40946270150ddfca35cce61f4265d7bfe7b7f http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk deleted file mode 100644 index 97077fd..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ecdsa-sha2-nistp521-KeyPair.ppk +++ /dev/null @@ -1,12 +0,0 @@ -PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 -Encryption: aes256-cbc -Comment: ecdsa-key-20170917 -Public-Lines: 4 -AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADGM237T9rT -zE++sOFDN0VWfYfojlQ8dYP82OlgA24Yh0ZpOsezBBiHtHfMHl9tWHmch1YKmH7B -lOfqbOgcIhz9cwA2V7Nu3IUGqxZT18LOXEpcdyDSphJ6jsy1urqBLrOz4DF6Udyr -rFV4OQELovf8YdUsM91YPfe1DfnSRi1I5v20uA== -Private-Lines: 2 -+44AQO4aflPquBZbdB3UtdLuXuHV2u1YoghxYXPFGdhskMt+XjJhUlrOHNX8rmgR -E9nni474zGw9ni/3LIZwMILQI/xTfiQm3t6nKFV8nyI= -Private-MAC: 99c25e348a84de4876163758ad13b2ad1dc43629 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk deleted file mode 100644 index 9da31f1..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-dss-KeyPair.ppk +++ /dev/null @@ -1,17 +0,0 @@ -PuTTY-User-Key-File-2: ssh-dss -Encryption: aes256-cbc -Comment: dsa-key-20130909 -Public-Lines: 10 -AAAAB3NzaC1kc3MAAACBALVdprWfZ7+FiITCILnDkeMZ2ntkV2WjW5RcyiQvJvBO -jCNiVtK87xATEOfBb20YvNZ/CibBjGS1TL5TBqRV5XleucPHMJZ5rXdJ2FH5oZnL -kna3Et+L1/O/GQMmp2vfSFrO3n3+mI1Jozx3FoQO8jr1zIerJ5Mc4LKqsIQB9hvR -AAAAFQD+z1y1/4ll4ax3rri8mkYgGDhqIQAAAIBVU4VJ7V7GoEQJ5WBMbpDEcLIZ -KUgSHsJMQzWnLOi/DcsPjVMDX6FWGPLtrjd7fgInlPMCC/SPAhXdaXMvHZSkvBHV -DfNjpsDgsxBnK1FKqRGtD49rETFGDl92EOsyBhv+9ymdOPX6R0hCqS+ulZheQPXI -iHXdIvQK2Ev5Dy3xNgAAAIA8qhumHZcKss+Puuw+mY5J5Qt7Omv16MuDsYiVqrBq -1V2C9gutx3Tu+n5QYi0xPlkkP/knMtkUZS+Wt3Dr8zPcEzNBc/Tm2EdYp11jZNx4 -4PM4ing+aCU5oGcg/7RS5CrY5Fn/rvgHqK22XiC8/U55iti44bWKvI6HCejExeZX -iA== -Private-Lines: 1 -64IdcIX48CNGjhcxsVN0vSNpT7S72e3FVdQ3t4ENAvI= -Private-MAC: 8e62b44b5c080965e361936520be4feaa50285b1 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk deleted file mode 100644 index 668ef1e..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-ed25519-KeyPair.ppk +++ /dev/null @@ -1,9 +0,0 @@ -PuTTY-User-Key-File-2: ssh-ed25519 -Encryption: aes256-cbc -Comment: ed25519-key-20170917 -Public-Lines: 2 -AAAAC3NzaC1lZDI1NTE5AAAAIN7fuKSIM5TbAX/1I1Ts3tfyo5eEs7JpmKsegHs/ -9fIi -Private-Lines: 1 -0cPG5BR80jQcJmHKs6IjpHS3R4/CTnudnJB4BcjaqKlRk0l603GVMDzTxkaICCb8 -Private-MAC: 381cff136b2516331ff4511cf382533fc14f0aeb http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk ---------------------------------------------------------------------- diff --git a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk b/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk deleted file mode 100644 index 2a11d04..0000000 --- a/sshd-contrib/src/test/resources/org/apache/sshd/common/config/keys/loader/putty/super-secret-passphrase-AES-256-CBC-ssh-rsa-KeyPair.ppk +++ /dev/null @@ -1,18 +0,0 @@ -PuTTY-User-Key-File-2: ssh-rsa -Encryption: aes256-cbc -Comment: rsa-key-20130909 -Public-Lines: 4 -AAAAB3NzaC1yc2EAAAABJQAAAIEAhY5eZJrX0a3+rtEZCq0nu2zvAHp16nk93jhi -p7c9tTDlGm9QEAgqzmuilEeUQ4BssxAvhCFEo/7Qbg4M7PwcA5cFkjXE4gj0YDJM -ay7l2mb5aIoS/hACgNz54p/w/UgfQC1Vygt6QtvXXAW8Lh/YCN4Zw4ViROUhoYuy -3K5SBYs= -Private-Lines: 8 -mqcGPnrv9d1tYkJZSGaCy5REslPZ2xh8m7qAbN+bD1m7iQ77pLxlKyzs82rbRaC9 -KSnKwsbFl7o92NT+9yYKJ7ehXyWyrUXkn9KcPk7MzNVwMuWVDXwvHodGLCyVCLYq -PNipvg2USHvnCjnnvtMysBRNJiHTMOaf/gSZLyaEuznYo3FEClMPzggY9b2nrxnV -O1ttk1FJatkRflwFjn3A/R/GpowmBnkDyCkVlTvR+uBAg8iIy1Vzj5zIV9zmzfgx -DxPot+Y81y+Xe3ohVh2s1FVvLw+KQbYbCQam5j0V/dTQ+oVWjCJBlibD3aVTGK0M -Jswz8wPwXFo5N0yX/6ZTrshbvTzoO1bg0+HUu581ZSAeqttk9C1RLmWFS8YDm0Hn -GhDXrjuAvKJ3cjeVJsumgVw45NYGARuzV24TlHUtU+eze8Y/0NsPJXoCfVoYjTjb -fjlMh9rYbRdyNHXwYTzwbw== -Private-MAC: f4c50b3da0b73c34e8989411fc48c884c09e20a0 http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/pom.xml ---------------------------------------------------------------------- diff --git a/sshd-putty/pom.xml b/sshd-putty/pom.xml new file mode 100644 index 0000000..8e11385 --- /dev/null +++ b/sshd-putty/pom.xml @@ -0,0 +1,107 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + + <!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. + --> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd</artifactId> + <version>2.0.1-SNAPSHOT</version> + <relativePath>..</relativePath> + </parent> + + <artifactId>sshd-putty</artifactId> + <name>Apache Mina SSHD :: Putty key files support utilities</name> + <packaging>jar</packaging> + <inceptionYear>2018</inceptionYear> + + <properties> + <projectRoot>${project.basedir}/..</projectRoot> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-common</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcpg-jdk15on</artifactId> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.bouncycastle</groupId> + <artifactId>bcpkix-jdk15on</artifactId> + <optional>true</optional> + </dependency> + + <!-- For ed25519 support --> + <dependency> + <groupId>net.i2p.crypto</groupId> + <artifactId>eddsa</artifactId> + <optional>true</optional> + </dependency> + + <!-- test dependencies --> + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-common</artifactId> + <version>${project.version}</version> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <redirectTestOutputToFile>true</redirectTestOutputToFile> + <reportsDirectory>${project.build.directory}/surefire-reports-putty</reportsDirectory> + </configuration> + </plugin> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/f60fcb0a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java ---------------------------------------------------------------------- diff --git a/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java new file mode 100644 index 0000000..d2428e2 --- /dev/null +++ b/sshd-putty/src/main/java/org/apache/sshd/common/config/keys/loader/putty/AbstractPuttyKeyDecoder.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.sshd.common.config.keys.loader.putty; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StreamCorruptedException; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.Base64; +import java.util.Base64.Decoder; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.apache.sshd.common.config.keys.impl.AbstractIdentityResourceLoader; +import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.ValidateUtils; + +/** + * @param <PUB> Generic public key type + * @param <PRV> Generic private key type + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +public abstract class AbstractPuttyKeyDecoder<PUB extends PublicKey, PRV extends PrivateKey> + extends AbstractIdentityResourceLoader<PUB, PRV> + implements PuttyKeyPairResourceParser<PUB, PRV> { + public static final String ENCRYPTION_HEADER = "Encryption"; + + protected AbstractPuttyKeyDecoder(Class<PUB> pubType, Class<PRV> prvType, Collection<String> names) { + super(pubType, prvType, names); + } + + @Override + public boolean canExtractKeyPairs(String resourceKey, List<String> lines) + throws IOException, GeneralSecurityException { + if (!PuttyKeyPairResourceParser.super.canExtractKeyPairs(resourceKey, lines)) { + return false; + } + + for (String l : lines) { + l = GenericUtils.trimToEmpty(l); + if (!l.startsWith(KEY_FILE_HEADER_PREFIX)) { + continue; + } + + int pos = l.indexOf(':'); + if ((pos <= 0) || (pos >= (l.length() - 1))) { + return false; + } + + Collection<String> supported = getSupportedTypeNames(); + String typeValue = l.substring(pos + 1).trim(); + return supported.contains(typeValue); + } + + return false; + } + + @Override + public Collection<KeyPair> loadKeyPairs( + String resourceKey, FilePasswordProvider passwordProvider, List<String> lines) + throws IOException, GeneralSecurityException { + List<String> pubLines = Collections.emptyList(); + List<String> prvLines = Collections.emptyList(); + String prvEncryption = null; + for (int index = 0, numLines = lines.size(); index < numLines; index++) { + String l = lines.get(index); + l = GenericUtils.trimToEmpty(l); + int pos = l.indexOf(':'); + if ((pos <= 0) || (pos >= (l.length() - 1))) { + continue; + } + + String hdrName = l.substring(0, pos).trim(); + String hdrValue = l.substring(pos + 1).trim(); + switch (hdrName) { + case ENCRYPTION_HEADER: + if (prvEncryption != null) { + throw new StreamCorruptedException("Duplicate " + hdrName + " in" + resourceKey); + } + prvEncryption = hdrValue; + break; + case PUBLIC_LINES_HEADER: + pubLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, pubLines); + index += pubLines.size(); + break; + case PRIVATE_LINES_HEADER: + prvLines = extractDataLines(resourceKey, lines, index + 1, hdrName, hdrValue, prvLines); + index += prvLines.size(); + break; + default: // ignored + } + } + + return loadKeyPairs(resourceKey, pubLines, prvLines, prvEncryption, passwordProvider); + } + + public static List<String> extractDataLines( + String resourceKey, List<String> lines, int startIndex, String hdrName, String hdrValue, List<String> curLines) + throws IOException { + if (GenericUtils.size(curLines) > 0) { + throw new StreamCorruptedException("Duplicate " + hdrName + " in " + resourceKey); + } + + int numLines; + try { + numLines = Integer.parseInt(hdrValue); + } catch (NumberFormatException e) { + throw new StreamCorruptedException("Bad " + hdrName + " value (" + hdrValue + ") in " + resourceKey); + } + + int endIndex = startIndex + numLines; + int totalLines = lines.size(); + if (endIndex > totalLines) { + throw new StreamCorruptedException("Excessive " + hdrName + " value (" + hdrValue + ") in " + resourceKey); + } + + return lines.subList(startIndex, endIndex); + } + + public Collection<KeyPair> loadKeyPairs( + String resourceKey, List<String> pubLines, List<String> prvLines, String prvEncryption, FilePasswordProvider passwordProvider) + throws IOException, GeneralSecurityException { + return loadKeyPairs(resourceKey, + KeyPairResourceParser.joinDataLines(pubLines), KeyPairResourceParser.joinDataLines(prvLines), + prvEncryption, passwordProvider); + } + + public Collection<KeyPair> loadKeyPairs( + String resourceKey, String pubData, String prvData, String prvEncryption, FilePasswordProvider passwordProvider) + throws IOException, GeneralSecurityException { + Decoder b64Decoder = Base64.getDecoder(); + byte[] pubBytes = b64Decoder.decode(pubData); + byte[] prvBytes = b64Decoder.decode(prvData); + String password = null; + if ((GenericUtils.length(prvEncryption) > 0) + && (!NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption))) { + password = passwordProvider.getPassword(resourceKey); + } + + if (GenericUtils.isEmpty(prvEncryption) + || NO_PRIVATE_KEY_ENCRYPTION_VALUE.equalsIgnoreCase(prvEncryption) + || GenericUtils.isEmpty(password)) { + return loadKeyPairs(resourceKey, pubBytes, prvBytes); + } + + // format is "<cipher><bits>-<mode>" - e.g., "aes256-cbc" + int pos = prvEncryption.indexOf('-'); + if (pos <= 0) { + throw new StreamCorruptedException("Missing private key encryption mode in " + prvEncryption); + } + + String mode = prvEncryption.substring(pos + 1).toUpperCase(); + String algName = null; + int numBits = 0; + for (int index = 0; index < pos; index++) { + char ch = prvEncryption.charAt(index); + if ((ch >= '0') && (ch <= '9')) { + algName = prvEncryption.substring(0, index).toUpperCase(); + numBits = Integer.parseInt(prvEncryption.substring(index, pos)); + break; + } + } + + if (GenericUtils.isEmpty(algName) || (numBits <= 0)) { + throw new StreamCorruptedException("Missing private key encryption algorithm details in " + prvEncryption); + } + + prvBytes = PuttyKeyPairResourceParser.decodePrivateKeyBytes(prvBytes, algName, numBits, mode, password); + return loadKeyPairs(resourceKey, pubBytes, prvBytes); + } + + public Collection<KeyPair> loadKeyPairs(String resourceKey, byte[] pubData, byte[] prvData) + throws IOException, GeneralSecurityException { + ValidateUtils.checkNotNullAndNotEmpty(pubData, "No public key data in %s", resourceKey); + ValidateUtils.checkNotNullAndNotEmpty(prvData, "No private key data in %s", resourceKey); + try (InputStream pubStream = new ByteArrayInputStream(pubData); + InputStream prvStream = new ByteArrayInputStream(prvData)) { + return loadKeyPairs(resourceKey, pubStream, prvStream); + } + } + + public Collection<KeyPair> loadKeyPairs(String resourceKey, InputStream pubData, InputStream prvData) + throws IOException, GeneralSecurityException { + try (PuttyKeyReader pubReader = + new PuttyKeyReader(ValidateUtils.checkNotNull(pubData, "No public key data in %s", resourceKey)); + PuttyKeyReader prvReader = + new PuttyKeyReader(ValidateUtils.checkNotNull(prvData, "No private key data in %s", resourceKey))) { + return loadKeyPairs(resourceKey, pubReader, prvReader); + } + } + + public abstract Collection<KeyPair> loadKeyPairs(String resourceKey, PuttyKeyReader pubReader, PuttyKeyReader prvReader) + throws IOException, GeneralSecurityException; +}
