All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sshtools.common.publickey.bc.OpenSSHPrivateKeyFileBC Maven / Gradle / Ivy

/**
 * (c) 2002-2021 JADAPTIVE Limited. All Rights Reserved.
 *
 * This file is part of the Maverick Synergy Java SSH API.
 *
 * Maverick Synergy 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.
 *
 * Maverick Synergy 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Maverick Synergy.  If not, see .
 */
/* HEADER */
package com.sshtools.common.publickey.bc;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
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.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.spec.ECNamedCurveSpec;
import org.bouncycastle.openssl.EncryptionException;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;

import com.sshtools.common.publickey.InvalidPassphraseException;
import com.sshtools.common.publickey.OpenSSHPrivateKeyFile;
import com.sshtools.common.publickey.SshPrivateKeyFile;
import com.sshtools.common.ssh.components.SshKeyPair;
import com.sshtools.common.ssh.components.jce.JCEProvider;
import com.sshtools.common.ssh.components.jce.Ssh2DsaPrivateKey;
import com.sshtools.common.ssh.components.jce.Ssh2DsaPublicKey;
import com.sshtools.common.ssh.components.jce.Ssh2EcdsaSha2NistPrivateKey;
import com.sshtools.common.ssh.components.jce.Ssh2EcdsaSha2NistPublicKey;
import com.sshtools.common.ssh.components.jce.Ssh2RsaPrivateCrtKey;
import com.sshtools.common.ssh.components.jce.Ssh2RsaPublicKey;

public class OpenSSHPrivateKeyFileBC
   implements SshPrivateKeyFile {

  byte[] formattedkey;

  public OpenSSHPrivateKeyFileBC(byte[] formattedkey)
     throws IOException {
    if(!isFormatted(formattedkey)) {
      throw new IOException(
         "Formatted key data is not a valid OpenSSH key format");
    }
    this.formattedkey = formattedkey;
    /**
     * Force use of PEM reader so we don't fool ourselves 
     * into thinking this file type is supported when PEM
     * reader is not present. 
     */
   	try {
		toKeyPair(null);
	} catch (InvalidPassphraseException e) {
	}
  }

  public OpenSSHPrivateKeyFileBC(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"));
      com.sshtools.common.publickey.PEMReader pem = new com.sshtools.common.publickey.PEMReader(r);

      return pem.getHeader().containsKey("DEK-Info") || pem.getType().startsWith("ENCRYPTED");
    }
    catch(IOException e) {
      return true;
    }
  }

  public String getType() {
    return "OpenSSH";
  }

  public boolean supportsPassphraseChange() {
    return true;
  }

  public SshKeyPair toKeyPair(final String passphrase)
     throws IOException, InvalidPassphraseException {

    Reader r = new StringReader(new String(formattedkey, "US-ASCII"));
    PEMParser pem = new PEMParser(r);

    try {
	    Object obj = pem.readObject();
	    if(obj==null) {
			throw new IOException("Invalid key file");
		}
	    
	    SshKeyPair pair = new SshKeyPair();
	    
	    if(obj instanceof PKCS8EncryptedPrivateKeyInfo) {
	    	if(passphrase==null || passphrase.equals("")) {
	    		throw new InvalidPassphraseException();
	    	}
	    	PKCS8EncryptedPrivateKeyInfo encPrivKeyInfo = (PKCS8EncryptedPrivateKeyInfo) obj;
	    	InputDecryptorProvider pkcs8Prov = new JcePKCSPBEInputDecryptorProviderBuilder()
		    	    .setProvider(JCEProvider.getBCProvider().getName()).build(passphrase.toCharArray());
		    JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(JCEProvider.getBCProvider().getName());
		    obj = converter.getPrivateKey(encPrivKeyInfo.decryptPrivateKeyInfo(pkcs8Prov));
	    }
	    
	    if(obj instanceof PEMEncryptedKeyPair) {
	    	if(passphrase==null || passphrase.equals("")) {
	    		throw new InvalidPassphraseException();
	    	}
	        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(JCEProvider.getBCProvider().getName());
	        obj = converter.getKeyPair(((PEMEncryptedKeyPair)obj).decryptKeyPair(new JcePEMDecryptorProviderBuilder().setProvider(JCEProvider.getBCProvider().getName()).build(passphrase.toCharArray())));
	    }
	    
	    if(obj instanceof PEMKeyPair) {
	    	obj = new JcaPEMKeyConverter().setProvider(JCEProvider.getBCProvider().getName()).getKeyPair((PEMKeyPair)obj);
	    } else if(obj instanceof PrivateKeyInfo) {
	    	obj = new JcaPEMKeyConverter().setProvider(JCEProvider.getBCProvider().getName()).getPrivateKey((PrivateKeyInfo)obj);
	    }
	    
	    if(obj instanceof KeyPair) {
	    	
	    	KeyPair p = (KeyPair) obj;
		    if(p.getPrivate() instanceof ECPrivateKey) {
		    	ECPrivateKey prv = (ECPrivateKey) p.getPrivate();
		    	String curve = ((ECNamedCurveSpec)prv.getParams()).getName();
		    	pair.setPrivateKey(new Ssh2EcdsaSha2NistPrivateKey(prv,
		    			curve));
		    	pair.setPublicKey(new Ssh2EcdsaSha2NistPublicKey((ECPublicKey)p.getPublic(),
		    			curve));
		        return pair;
		    } else if(p.getPrivate() instanceof RSAPrivateCrtKey) {
		    	pair.setPrivateKey(new Ssh2RsaPrivateCrtKey((RSAPrivateCrtKey)p.getPrivate()));
		    	pair.setPublicKey(new Ssh2RsaPublicKey((RSAPublicKey)p.getPublic()));
		    	return pair;
		    } else if(p.getPrivate() instanceof DSAPrivateKey) {
		    	pair.setPrivateKey(new Ssh2DsaPrivateKey((DSAPrivateKey)p.getPrivate(), (DSAPublicKey)p.getPublic()));
		    	pair.setPublicKey(new Ssh2DsaPublicKey((DSAPublicKey)p.getPublic()));    
		    	return pair;
		    }
	    } else if(obj instanceof DSAPrivateKey) {
	    	DSAPrivateKey d = (DSAPrivateKey) obj;
	    	try {
	    		Ssh2DsaPrivateKey dsa = new Ssh2DsaPrivateKey(d);
				pair.setPrivateKey(dsa);
				pair.setPublicKey(dsa.getPublicKey());
			} catch (Exception e) {
				throw new IOException("Failed to generate DSA public key from private key: " + e.getMessage());
			}
	    	return pair;
	    } else if(obj instanceof RSAPrivateCrtKey) {
	    	RSAPrivateCrtKey tmp = (RSAPrivateCrtKey) obj;
	    	try {
				Ssh2RsaPrivateCrtKey rsa = new Ssh2RsaPrivateCrtKey(tmp);
				pair.setPrivateKey(rsa);
				pair.setPublicKey(new Ssh2RsaPublicKey(tmp.getModulus(), tmp.getPublicExponent()));
			} catch (Exception e) {
				throw new IOException("Failed to generate RSA public key from private key: " + e.getMessage());
			}
	    	return pair;
	    }
	    throw new IOException("Unsupported type");

    } catch(EncryptionException e) {
    	throw new InvalidPassphraseException();
    } catch(InvalidPassphraseException | IOException e) {
    	throw e;
    } catch(Throwable ex) { 
    	return new OpenSSHPrivateKeyFile(formattedkey).toKeyPair(passphrase);
    } finally {
    	pem.close();
    }
  }

 
  public byte[] encryptKey(SshKeyPair pair, String passphrase)
     throws IOException {

	  ByteArrayOutputStream bout = new ByteArrayOutputStream();
	  JcaPEMWriter pem = new JcaPEMWriter(new OutputStreamWriter(bout));
	  
	  try {
		
		  PrivateKey privateKey;
		  PublicKey publicKey;
		  
		  if(pair.getPrivateKey() instanceof Ssh2DsaPrivateKey) {
			  privateKey = ((Ssh2DsaPrivateKey)pair.getPrivateKey()).getJCEPrivateKey();
			  publicKey = ((Ssh2DsaPublicKey)pair.getPublicKey()).getJCEPublicKey();
		  } else if(pair.getPrivateKey() instanceof Ssh2RsaPrivateCrtKey) {
			  privateKey = ((Ssh2RsaPrivateCrtKey)pair.getPrivateKey()).getJCEPrivateKey();
			  publicKey = ((Ssh2RsaPublicKey)pair.getPublicKey()).getJCEPublicKey();		  
		  } else if(pair.getPrivateKey() instanceof Ssh2EcdsaSha2NistPrivateKey) {
			  privateKey = ((Ssh2EcdsaSha2NistPrivateKey)pair.getPrivateKey()).getJCEPrivateKey();
			  publicKey = ((Ssh2EcdsaSha2NistPublicKey)pair.getPublicKey()).getJCEPublicKey();	
		  } else {
			  throw new IOException(pair.getPrivateKey().getClass().getName() + " is not supported in OpenSSH private key files");
		  }
		  
		  KeyPair kp = new KeyPair(publicKey, privateKey);
		  
		  if(passphrase!=null && !"".equals(passphrase)) {
			  pem.writeObject(kp, new JcePEMEncryptorBuilder("AES-128-CBC").setProvider(JCEProvider.getBCProvider().getName()).build(passphrase.toCharArray()));
		  } else {
			  pem.writeObject(kp);
		  }
		  
		  pem.flush();
		  
		  return bout.toByteArray();
	} finally {
		pem.close();
		bout.close();
	}
  }

  /* (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"));
      @SuppressWarnings("unused")
      com.sshtools.common.publickey.PEMReader pem = 
    	  new com.sshtools.common.publickey.PEMReader(r);
      return true;
    }
    catch(IOException e) {
      return false;
    }
  }
  
	@Override
	public String getComment() {
		return "";
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy