/** * 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.IOException; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.math.BigInteger; import com.sshtools.ssh.SshException; import com.sshtools.ssh.SshIOException; import com.sshtools.ssh.components.ComponentManager; import com.sshtools.ssh.components.SshDsaPrivateKey; import com.sshtools.ssh.components.SshDsaPublicKey; import com.sshtools.ssh.components.SshKeyPair; import com.sshtools.ssh.components.SshRsaPrivateCrtKey; import com.sshtools.util.SimpleASNReader; import com.sshtools.util.SimpleASNWriter; class OpenSSHPrivateKeyFile implements SshPrivateKeyFile { byte[] formattedkey; OpenSSHPrivateKeyFile(byte[] formattedkey) throws IOException { if (!isFormatted(formattedkey)) { throw new IOException( "Formatted key data is not a valid OpenSSH key format"); } this.formattedkey = formattedkey; } OpenSSHPrivateKeyFile(SshKeyPair pair, String passphrase) throws IOException { formattedkey = encryptKey(pair, passphrase); } /* * (non-Javadoc) * * @see com.sshtools.publickey.SshPrivateKeyFile#isPassphraseProtected() */ public boolean isPassphraseProtected() { try { Reader r = new StringReader(new String(formattedkey, "US-ASCII")); PEMReader pem = new PEMReader(r); return pem.getHeader().containsKey("DEK-Info"); } catch (IOException e) { return true; } } public String getType() { return "OpenSSH"; } public boolean supportsPassphraseChange() { return true; } public SshKeyPair toKeyPair(String passphrase) throws IOException, InvalidPassphraseException { Reader r = new StringReader(new String(formattedkey, "US-ASCII")); PEMReader pem = new PEMReader(r); byte[] payload = pem.decryptPayload(passphrase); SimpleASNReader asn = new SimpleASNReader(payload); try { if (PEM.DSA_PRIVATE_KEY.equals(pem.getType())) { return getDSAKeyPair(asn); } else if (PEM.RSA_PRIVATE_KEY.equals(pem.getType())) { return getRSAKeyPair(asn); } else { throw new IOException("Unsupported type: " + pem.getType()); } } catch (IOException ex) { throw new InvalidPassphraseException(ex); } } SshKeyPair getRSAKeyPair(SimpleASNReader asn) throws IOException { try { asn.assertByte(0x30); // SEQUENCE asn.getLength(); asn.assertByte(0x02); // INTEGER (version) asn.getData(); asn.assertByte(0x02); // INTEGER () BigInteger modulus = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER () BigInteger publicExponent = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER () BigInteger privateExponent = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER () BigInteger primeP = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER () BigInteger primeQ = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER () BigInteger primeExponentP = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER () BigInteger primeExponentQ = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER () BigInteger crtCoefficient = new BigInteger(asn.getData()); SshKeyPair pair = new SshKeyPair(); pair.setPublicKey(ComponentManager.getInstance() .createRsaPublicKey(modulus, publicExponent)); pair.setPrivateKey(ComponentManager.getInstance() .createRsaPrivateCrtKey(modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient)); return pair; } catch (SshException e) { throw new SshIOException(e); } } SshKeyPair getDSAKeyPair(SimpleASNReader asn) throws IOException { try { asn.assertByte(0x30); // SEQUENCE asn.getLength(); asn.assertByte(0x02); // INTEGER (version) asn.getData(); asn.assertByte(0x02); // INTEGER (p) BigInteger p = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER (q) BigInteger q = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER (g) BigInteger g = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER (y) BigInteger y = new BigInteger(asn.getData()); asn.assertByte(0x02); // INTEGER (x) BigInteger x = new BigInteger(asn.getData()); SshKeyPair pair = new SshKeyPair(); SshDsaPublicKey pub = ComponentManager.getInstance() .createDsaPublicKey(p, q, g, y); pair.setPublicKey(pub); pair.setPrivateKey(ComponentManager.getInstance() .createDsaPrivateKey(p, q, g, x, pub.getY())); return pair; } catch (SshException e) { throw new SshIOException(e); } } void writeDSAKeyPair(SimpleASNWriter asn, SshDsaPrivateKey privatekey, SshDsaPublicKey publickey) { // Write to a substream temporarily. // This code needs to know the length of the substream before it can // write the data from // the substream to the main stream. SimpleASNWriter asn2 = new SimpleASNWriter(); asn2.writeByte(0x02); // INTEGER (version) byte[] version = new byte[1]; asn2.writeData(version); asn2.writeByte(0x02); // INTEGER (p) asn2.writeData(publickey.getP().toByteArray()); asn2.writeByte(0x02); // INTEGER (q) asn2.writeData(publickey.getQ().toByteArray()); asn2.writeByte(0x02); // INTEGER (g) asn2.writeData(publickey.getG().toByteArray()); asn2.writeByte(0x02); // INTEGER (y) asn2.writeData(publickey.getY().toByteArray()); asn2.writeByte(0x02); // INTEGER (x) asn2.writeData(privatekey.getX().toByteArray()); byte[] dsaKeyEncoded = asn2.toByteArray(); asn.writeByte(0x30); // SEQUENCE asn.writeData(dsaKeyEncoded); } void writeRSAKeyPair(SimpleASNWriter asn, SshRsaPrivateCrtKey privatekey) { // Write to a substream temporarily. // This code needs to know the length of the substream before it can // write the data from // the substream to the main stream. SimpleASNWriter asn2 = new SimpleASNWriter(); asn2.writeByte(0x02); // INTEGER (version) byte[] version = new byte[1]; asn2.writeData(version); asn2.writeByte(0x02); // INTEGER () asn2.writeData(privatekey.getModulus().toByteArray()); asn2.writeByte(0x02); // INTEGER () asn2.writeData(privatekey.getPublicExponent().toByteArray()); asn2.writeByte(0x02); // INTEGER () asn2.writeData(privatekey.getPrivateExponent().toByteArray()); asn2.writeByte(0x02); // INTEGER () asn2.writeData(privatekey.getPrimeP().toByteArray()); asn2.writeByte(0x02); // INTEGER () asn2.writeData(privatekey.getPrimeQ().toByteArray()); asn2.writeByte(0x02); // INTEGER () asn2.writeData(privatekey.getPrimeExponentP().toByteArray()); asn2.writeByte(0x02); // INTEGER () asn2.writeData(privatekey.getPrimeExponentQ().toByteArray()); asn2.writeByte(0x02); // INTEGER () asn2.writeData(privatekey.getCrtCoefficient().toByteArray()); byte[] rsaKeyEncoded = asn2.toByteArray(); asn.writeByte(0x30); // SEQUENCE asn.writeData(rsaKeyEncoded); } public byte[] encryptKey(SshKeyPair pair, String passphrase) throws IOException { byte[] payload; PEMWriter pem = new PEMWriter(); SimpleASNWriter asn = new SimpleASNWriter(); if (pair.getPublicKey() instanceof SshDsaPublicKey) { writeDSAKeyPair(asn, (SshDsaPrivateKey) pair.getPrivateKey(), (SshDsaPublicKey) pair.getPublicKey()); payload = asn.toByteArray(); pem.setType(PEM.DSA_PRIVATE_KEY); } else if (pair.getPrivateKey() instanceof SshRsaPrivateCrtKey) { writeRSAKeyPair(asn, (SshRsaPrivateCrtKey) pair.getPrivateKey()); payload = asn.toByteArray(); pem.setType(PEM.RSA_PRIVATE_KEY); } else { throw new IOException(pair.getPublicKey().getAlgorithm() + " is not supported"); } pem.encryptPayload(payload, passphrase); StringWriter w = new StringWriter(); pem.write(w); return w.toString().getBytes("UTF-8"); } /* * (non-Javadoc) * * @see * com.sshtools.publickey.SshPrivateKeyFile#changePassphrase(java.lang.String * , java.lang.String) */ public void changePassphrase(String oldpassphrase, String newpassphrase) throws IOException, InvalidPassphraseException { SshKeyPair pair = toKeyPair(oldpassphrase); formattedkey = encryptKey(pair, newpassphrase); } public byte[] getFormattedKey() { return formattedkey; } public static boolean isFormatted(byte[] formattedkey) { try { Reader r = new StringReader(new String(formattedkey, "UTF-8")); // PEMReader pem = new PEMReader(r); return true; } catch (IOException e) { return false; } } }