/**
* Copyright 2003-2016 SSHTOOLS Limited. All Rights Reserved.
*
* For product documentation visit https://www.sshtools.com/
*
* This file is part of J2SSH Maverick.
*
* J2SSH Maverick is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* J2SSH Maverick is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with J2SSH Maverick. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sshtools.publickey;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import com.sshtools.ssh.components.ComponentManager;
import com.sshtools.ssh.components.Digest;
import com.sshtools.ssh.components.SshCipher;
import com.sshtools.ssh.components.SshDsaPublicKey;
import com.sshtools.ssh.components.SshKeyPair;
import com.sshtools.ssh.components.jce.AES256Cbc;
import com.sshtools.ssh.components.jce.SHA1Digest;
import com.sshtools.util.Base64;
import com.sshtools.util.ByteArrayReader;
class PuTTYPrivateKeyFile implements SshPrivateKeyFile {
byte[] formattedKey;
PuTTYPrivateKeyFile(byte[] formattedKey) throws IOException {
if (!isFormatted(formattedKey)) {
throw new IOException(
"Key is not formatted in the PuTTY key format!");
}
this.formattedKey = formattedKey;
}
public boolean supportsPassphraseChange() {
return false;
}
public String getType() {
return "PuTTY";
}
public boolean isPassphraseProtected() {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(formattedKey)));
try {
String line = reader.readLine();
if (line != null
&& (line.startsWith("PuTTY-User-Key-File-2:") || line
.equals("PuTTY-User-Key-File-1:"))) {
line = reader.readLine();
if (line != null && line.startsWith("Encryption:")) {
String encryption = line.substring(line.indexOf(":") + 1)
.trim();
if (encryption.equals("aes256-cbc"))
return ComponentManager.getInstance()
.supportedSsh2CiphersCS().contains(encryption);
}
}
} catch (Exception ex) {
}
return false;
}
public static boolean isFormatted(byte[] formattedKey) {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(formattedKey)));
try {
String line = reader.readLine();
return (line != null && (line.startsWith("PuTTY-User-Key-File-2:") || line
.equals("PuTTY-User-Key-File-1:")));
} catch (IOException ex) {
return false;
}
}
public SshKeyPair toKeyPair(String passphrase) throws IOException,
InvalidPassphraseException {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(formattedKey)));
boolean wasEncrpyted = false;
try {
String line = reader.readLine();
if (line != null
&& (line.startsWith("PuTTY-User-Key-File-2:") || line
.equals("PuTTY-User-Key-File-1:"))) {
int format = line.startsWith("PuTTY-User-Key-File-2:") ? 2 : 1;
String type = line.substring(line.indexOf(":") + 1).trim();
line = reader.readLine();
if (line != null && line.startsWith("Encryption:")) {
String encryption = line.substring(line.indexOf(":") + 1)
.trim();
line = reader.readLine();
if (line != null && line.startsWith("Comment:")) {
// String comment = line.substring(line.indexOf(":") +
// 1).trim();
line = reader.readLine();
if (line != null && line.startsWith("Public-Lines:")) {
try {
int publiclines = Integer.parseInt(line
.substring(line.indexOf(":") + 1)
.trim());
String publickey = "";
for (int i = 0; i < publiclines; i++) {
line = reader.readLine();
if (line != null) {
publickey += line;
} else {
throw new IOException(
"Corrupt public key data in PuTTY private key");
}
}
ByteArrayReader pub = new ByteArrayReader(
Base64.decode(publickey));
try {
line = reader.readLine();
if (line != null
&& line.startsWith("Private-Lines:")) {
int privatelines = Integer
.parseInt(line.substring(
line.indexOf(":") + 1)
.trim());
String privatekey = "";
for (int i = 0; i < privatelines; i++) {
line = reader.readLine();
if (line != null) {
privatekey += line;
} else {
throw new IOException(
"Corrupt private key data in PuTTY private key");
}
}
byte[] blob = Base64.decode(privatekey);
if (encryption.equals("aes256-cbc")) {
SshCipher cipher = new AES256Cbc();
byte[] iv = new byte[40];
byte[] key = new byte[40];
Digest hash = new SHA1Digest();
hash.putInt(0);
hash.putBytes(passphrase.getBytes());
byte[] key1 = hash.doFinal();
hash.putInt(1);
hash.putBytes(passphrase.getBytes());
byte[] key2 = hash.doFinal();
System.arraycopy(key1, 0, key, 0,
20);
System.arraycopy(key2, 0, key, 20,
20);
cipher.init(SshCipher.DECRYPT_MODE,
iv, key);
cipher.transform(blob);
wasEncrpyted = true;
}
// Read the private key data
ByteArrayReader bar = new ByteArrayReader(
blob);
try {
// Convert the private key into the
// format requried by J2SSH
if (type.equals("ssh-dss")) {
// Read the required variables
// from the public key
pub.readString(); // Ignore sice
// we
// already
// have it
BigInteger p = pub
.readBigInteger();
BigInteger q = pub
.readBigInteger();
BigInteger g = pub
.readBigInteger();
BigInteger y = pub
.readBigInteger();
/*
* And for "ssh-dss", it will be
* composed of
*
* mpint x (the private key
* parameter) [ string hash
* 20-byte hash of mpints p || q
* || g only in old format ]
*/
// now read the private exponent
// from the private key
BigInteger x = bar
.readBigInteger();
if (format == 1) {
}
SshKeyPair pair = new SshKeyPair();
SshDsaPublicKey publ = ComponentManager
.getInstance()
.createDsaPublicKey(p,
q, g, y);
pair.setPublicKey(publ);
pair.setPrivateKey(ComponentManager
.getInstance()
.createDsaPrivateKey(p,
q, g, x,
publ.getY()));
return pair;
} else if (type.equals("ssh-rsa")) {
// Read the requried variables
// from the public key
pub.readString(); // Ignore
// since we
// already
// have it
BigInteger publicExponent = pub
.readBigInteger();
BigInteger modulus = pub
.readBigInteger();
/*
* mpint private_exponent mpint
* p (the larger of the two
* primes) mpint q (the smaller
* prime) mpint iqmp (the
* inverse of q modulo p) data
* padding (to reach a multiple
* of the cipher block size)
*/
// Read the private key
// variables from putty file
BigInteger privateExponent = bar
.readBigInteger();
SshKeyPair pair = new SshKeyPair();
pair.setPublicKey(ComponentManager
.getInstance()
.createRsaPublicKey(
modulus,
publicExponent));
pair.setPrivateKey(ComponentManager
.getInstance()
.createRsaPrivateKey(
modulus,
privateExponent));
return pair;
} else
throw new IOException(
"Unexpected key type "
+ type);
} finally {
bar.close();
}
}
} finally {
pub.close();
}
} catch (NumberFormatException ex) {
} catch (OutOfMemoryError ex) {
}
}
}
}
}
} catch (Throwable ex) {
if (!wasEncrpyted)
throw new IOException("The PuTTY key could not be read! "
+ ex.getMessage());
}
if (wasEncrpyted)
throw new InvalidPassphraseException();
throw new IOException("The PuTTY key could not be read! Invalid format");
}
public void changePassphrase(String oldpassphrase, String newpassprase)
throws IOException {
throw new IOException(
"Changing passphrase is not supported by the PuTTY key format engine");
}
public byte[] getFormattedKey() throws IOException {
return formattedKey;
}
}