From bf34072c3aa94911288e09f7159a04f16fb0dca0 Mon Sep 17 00:00:00 2001 From: Jeroen van Erp Date: Mon, 24 Oct 2016 09:49:45 +0200 Subject: [PATCH] Reading first part of the new openssh key format --- .../java/net/schmizz/sshj/DefaultConfig.java | 3 +- .../keyprovider/OpenSSHKeyV1KeyFile.java | 117 ++++++++++++++++++ .../sshj/keyprovider/OpenSSHKeyFileTest.java | 16 ++- 3 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/schmizz/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java diff --git a/src/main/java/net/schmizz/sshj/DefaultConfig.java b/src/main/java/net/schmizz/sshj/DefaultConfig.java index 98625521..8ed0906f 100644 --- a/src/main/java/net/schmizz/sshj/DefaultConfig.java +++ b/src/main/java/net/schmizz/sshj/DefaultConfig.java @@ -33,6 +33,7 @@ import net.schmizz.sshj.transport.random.BouncyCastleRandom; import net.schmizz.sshj.transport.random.JCERandom; import net.schmizz.sshj.transport.random.SingletonRandomFactory; import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile; +import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile; import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile; import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile; import org.slf4j.Logger; @@ -113,7 +114,7 @@ public class DefaultConfig protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) { if (bouncyCastleRegistered) { - setFileKeyProviderFactories(new PKCS8KeyFile.Factory(), new OpenSSHKeyFile.Factory(), new PuTTYKeyFile.Factory()); + setFileKeyProviderFactories(new OpenSSHKeyV1KeyFile.Factory(), new PKCS8KeyFile.Factory(), new OpenSSHKeyFile.Factory(), new PuTTYKeyFile.Factory()); } } diff --git a/src/main/java/net/schmizz/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java b/src/main/java/net/schmizz/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java new file mode 100644 index 00000000..0597b82c --- /dev/null +++ b/src/main/java/net/schmizz/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java @@ -0,0 +1,117 @@ +/* + * 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 net.schmizz.sshj.userauth.keyprovider; + +import java.io.BufferedReader; +import java.io.IOException; +import java.security.KeyPair; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.schmizz.sshj.common.Base64; +import net.schmizz.sshj.common.Buffer; +import net.schmizz.sshj.common.ByteArrayUtils; +import net.schmizz.sshj.common.IOUtils; + +public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider { + private static final Logger logger = LoggerFactory.getLogger(OpenSSHKeyV1KeyFile.class); + private static final String BEGIN = "-----BEGIN "; + private static final String END = "-----END "; + private static final byte[] AUTH_MAGIC = "openssh-key-v1\0".getBytes(); + + public static class Factory + implements net.schmizz.sshj.common.Factory.Named { + + @Override + public FileKeyProvider create() { + return new OpenSSHKeyV1KeyFile(); + } + + @Override + public String getName() { + return "openssh-key-v1"; + } + } + + @Override + protected KeyPair readKeyPair() throws IOException { + BufferedReader reader = new BufferedReader(resource.getReader()); + try { + String line = reader.readLine(); + while (line != null && !line.startsWith(BEGIN)) { + line = reader.readLine(); + } + line = line.substring(BEGIN.length()); + if (!line.startsWith("OPENSSH PRIVATE KEY-----")) { + throw new IOException("This key is not in 'openssh-key-v1' format"); + } + + StringBuffer stringBuffer = new StringBuffer(); + line = reader.readLine(); + while (!line.startsWith(END)) { + stringBuffer.append(line); + line = reader.readLine(); + } + byte[] decode = Base64.decode(stringBuffer.toString()); + System.out.println(ByteArrayUtils.printHex(decode, 0, decode.length)); + Buffer.PlainBuffer keyBuffer = new Buffer.PlainBuffer(decode); + byte[] bytes = new byte[AUTH_MAGIC.length]; + keyBuffer.readRawBytes(bytes); + if (!ByteArrayUtils.equals(bytes, 0, AUTH_MAGIC, 0, AUTH_MAGIC.length)) { + throw new IOException("This key does not contain the 'openssh-key-v1' format header"); + } + + String cipherName = keyBuffer.readString(); + String kdfName = keyBuffer.readString(); + String kdfOptions = keyBuffer.readString(); + + if ("none".equals(cipherName)) { + return readUnencrypted(keyBuffer); + } else { + logger.debug("Reading encrypted openssh-key-v1 file with cipher: " + cipherName); + + System.out.println(cipherName + " " + kdfName + " " + kdfOptions); + } + + } finally { + IOUtils.closeQuietly(reader); + } + + return null; + } + + private KeyPair readUnencrypted(final Buffer.PlainBuffer keyBuffer) throws IOException { + int i = keyBuffer.readUInt32AsInt(); + if (i != 1) { + throw new IOException("We don't support having more than 1 key in the file (yet)."); + } + logger.info("reading {} keys", i); + byte[] pubKey = keyBuffer.readBytes(); + logger.info("read key: {}", ByteArrayUtils.printHex(pubKey, 0, pubKey.length)); + int privKeyListSize = keyBuffer.readUInt32AsInt(); + if (privKeyListSize % 8 != 0) { + throw new IOException("The private key section must be a multiple of the block size (8)"); + } + int checkInt1 = keyBuffer.readUInt32AsInt(); + int checkInt2 = keyBuffer.readUInt32AsInt(); + logger.info("Read checkInts: {}, {}", checkInt1, checkInt2); + byte[] privKey = keyBuffer.readBytes(); + logger.info("read key: {}", ByteArrayUtils.printHex(privKey, 0, privKey.length)); + + return null; + } +} diff --git a/src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java b/src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java index 478a3399..402fefc8 100644 --- a/src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java +++ b/src/test/java/net/schmizz/sshj/keyprovider/OpenSSHKeyFileTest.java @@ -18,6 +18,7 @@ package net.schmizz.sshj.keyprovider; import net.schmizz.sshj.common.KeyType; import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider; import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyFile; +import net.schmizz.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile; import net.schmizz.sshj.userauth.password.PasswordFinder; import net.schmizz.sshj.userauth.password.PasswordUtils; import net.schmizz.sshj.userauth.password.Resource; @@ -30,11 +31,13 @@ import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; +import java.security.PrivateKey; import java.security.PublicKey; import java.util.Arrays; import java.util.Scanner; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -142,7 +145,7 @@ public class OpenSSHKeyFileTest { @Test public void shouldHaveCorrectFingerprintForED25519() throws IOException { - OpenSSHKeyFile keyFile = new OpenSSHKeyFile(); + OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile(); keyFile.init(new File("src/test/resources/keytypes/test_ed25519")); String expected = "256 MD5:d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32 root@sshj (ED25519)\n"; PublicKey aPublic = keyFile.getPublic(); @@ -150,6 +153,15 @@ public class OpenSSHKeyFileTest { assertThat(expected, containsString(sshjFingerprintSshjKey)); } + @Test + public void shouldLoadED25519PrivateKey() throws IOException { + OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile(); + keyFile.init(new File("src/test/resources/keytypes/test_ed25519")); + String expected = "256 MD5:d3:5e:40:72:db:08:f1:6d:0c:d7:6d:35:0d:ba:7c:32 root@sshj (ED25519)\n"; + PrivateKey aPrivate = keyFile.getPrivate(); + assertThat(aPrivate.getAlgorithm(), equalTo("ed-25519")); + } + @Before public void setup() throws UnsupportedEncodingException, GeneralSecurityException { @@ -171,4 +183,4 @@ public class OpenSSHKeyFileTest { scanner.close(); } } -} \ No newline at end of file +}