/** * A class that implements PGP interface for Java. * <P> * * It calls gpg (GnuPG) program to do all the PGP commands. $Id: GnuPG.java 4440 2008-09-25 * 12:18:51Z rgw_ch $ * * @author Yaniv Yemini, January 2004. * @author Based on a class GnuPG by John Anderson, which can be found * @author at: http://lists.gnupg.org/pipermail/gnupg-devel/2002-February/018098.html * @author modified for use in JBother by Andrey Zakirov, February 2005 * @created March 9, 2005 * @version 0.5.1 * @see GnuPG - http://www.gnupg.org/ * * Modified 2006/10 by G. Weirich for use in Elexis */ package ch.rgw.crypt; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.security.KeyPair; import java.security.PublicKey; import java.security.cert.X509Certificate; import ch.rgw.tools.ExHandler; import ch.rgw.tools.Result; import ch.rgw.tools.StringTool; import ch.rgw.tools.TimeTool; public class GnuPG implements Cryptologist { // Constants: // private final String kGnuPGCommand; private static final String kGnuPGArgs = " --batch --armor --output -"; private String homedir; private String executable; // Class vars: private int gpg_exitCode = -1; private String gpg_result; private String gpg_err; private boolean gpgOK; private char[] passphrase; private final String identity; public void setPassphrase(char[] pwd){ passphrase = pwd; } public void setExecutable(String exe){ executable = exe; } public void setHomedir(String dir){ homedir = dir; } private String createGPGCommand(){ StringBuilder sb = new StringBuilder(); sb.append(executable).append(" "); if (!StringTool.isNothing(homedir)) { sb.append("--homedir ").append(homedir); } sb.append(kGnuPGArgs); return sb.toString(); } /** * Reads an output stream from an external process. Imeplemented as a thred. * * @author synic * @created March 9, 2005 */ class ProcessStreamReader extends Thread { InputStream is; String type; OutputStream os; String fullLine = ""; /** * Constructor for the ProcessStreamReader object * * @param is * Description of the Parameter * @param type * Description of the Parameter */ ProcessStreamReader(InputStream is, String type){ this(is, type, null); } /** * Constructor for the ProcessStreamReader object * * @param is * Description of the Parameter * @param type * Description of the Parameter * @param redirect * Description of the Parameter */ ProcessStreamReader(InputStream is, String type, OutputStream redirect){ this.is = is; this.type = type; this.os = redirect; } /** * Main processing method for the ProcessStreamReader object */ public void run(){ try { InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { fullLine = fullLine + line + "\n"; } } catch (IOException ioe) { ioe.printStackTrace(); } } /** * Gets the string attribute of the ProcessStreamReader object * * @return The string value */ String getString(){ return fullLine; } } /** * Sign * * @param inStr * input string to sign * @param secID * ID of secret key to sign with * @param passPhrase * passphrase for the secret key to sign with * @return true upon success */ public boolean sign(String inStr, String secID, String passPhrase){ boolean success = false; File tmpFile = createTempFile(inStr); if (tmpFile != null) { success = runGnuPG("-u " + secID + " --passphrase-fd 0 -b " + tmpFile.getAbsolutePath(), passPhrase); tmpFile.delete(); if (success && this.gpg_exitCode != 0) { success = false; } } return success; } /** * ClearSign * * @param inStr * input string to sign * @param secID * ID of secret key to sign with * @param passPhrase * passphrase for the secret key to sign with * @return true upon success */ public boolean clearSign(String inStr, String secID, String passPhrase){ boolean success = false; File tmpFile = createTempFile(inStr); if (tmpFile != null) { success = runGnuPG( "-u " + secID + " --passphrase-fd 0 --clearsign " + tmpFile.getAbsolutePath(), passPhrase); tmpFile.delete(); if (success && this.gpg_exitCode != 0) { success = false; } } return success; } /** * Signs and encrypts a string * * @param inStr * input string to encrypt * @param secID * ID of secret key to sign with * @param keyID * ID of public key to encrypt with * @param passPhrase * passphrase for the secret key to sign with * @return true upon success */ public boolean signAndEncrypt(String inStr, String secID, String keyID, String passPhrase){ boolean success = false; File tmpFile = createTempFile(inStr); if (tmpFile != null) { success = runGnuPG( "-u " + secID + " -r " + keyID + " --passphrase-fd 0 -se " + tmpFile.getAbsolutePath(), passPhrase); tmpFile.delete(); if (success && this.gpg_exitCode != 0) { success = false; } } return success; } public boolean signAndEncrypt(File inFile, String secID, String keyID, String passphrase){ boolean success = false; success = runGnuPG( "-u " + secID + " -r " + keyID + " --passphrase-fd 0 -se " + inFile.getAbsolutePath(), passphrase); inFile.delete(); if (success && this.gpg_exitCode != 0) { success = false; } return success; } /** * Encrypt * * @param inStr * input string to encrypt * @param secID * ID of secret key to use * @param keyID * ID of public key to encrypt with * @return true upon success */ public boolean encrypt(String inStr, String keyID){ boolean success; success = runGnuPG(" -r " + keyID + " --encrypt", inStr); if (success && this.gpg_exitCode != 0) { success = false; } return success; } /** * Decrypt * * @param inStr * input string to decrypt * @param passPhrase * passphrase for the secret key to decrypt with * @return true upon success */ public boolean decrypt(String inStr, String passPhrase){ boolean success = false; File tmpFile = createTempFile(inStr); if (tmpFile != null) { success = runGnuPG("--passphrase-fd 0 --decrypt " + tmpFile.getAbsolutePath(), passPhrase); tmpFile.delete(); if (success && this.gpg_exitCode != 0) { success = false; } } return success; } public boolean signKey(String keyname, String passphrase){ boolean success = runGnuPG("--passphrase-fd 0 --yes --sign-key " + keyname, passphrase); if (success && this.gpg_exitCode != 0) { success = false; } return success; } public boolean decrypt(File inFile, String outFile, String passPhrase){ boolean success = false; if (inFile != null) { if (outFile.indexOf(' ') != -1) { outFile = "\"" + outFile + "\""; } success = runGnuPG( "-o " + outFile + " --passphrase-fd 0" + " --decrypt " + inFile.getAbsolutePath(), passPhrase); if (success && this.gpg_exitCode != 0) { success = false; } } return success; } /** * List public keys in keyring * * @param ID * ID of public key to list, blank for all * @return true upon success */ public boolean listKeys(String ID){ boolean success; success = runGnuPG("--list-keys --with-colons " + ID, null); if (success && this.gpg_exitCode != 0) { success = false; } return success; } /** * get public key * */ public boolean getKey(String id){ boolean success = runGnuPG("--armor --export " + id, null); if (success && this.gpg_exitCode != 0) { success = false; } return success; } /** * import key */ public boolean importKeyFile(String keyname){ boolean success = runGnuPG("--import " + keyname, null); if (success && this.gpg_exitCode != 0) { success = false; } return success; } public boolean importKey(String key){ File tmpFile = createTempFile(key); boolean success = false; if (tmpFile != null) { success = runGnuPG("--import " + tmpFile, null); tmpFile.delete(); if (success && this.gpg_exitCode != 0) { success = false; } } return success; } /** * List secret keys in keyring * * @param ID * ID of secret key to list, blank for all * @return true upon success */ public boolean listSecretKeys(String ID){ boolean success; success = runGnuPG("--list-secret-keys --with-colons " + ID, null); if (success && this.gpg_exitCode != 0) { success = false; } return success; } /** * Generate a key pair. This will open the gpg-console to create the key interactively * * @return */ public boolean generateKey(String name, String mail, char[] pwd, String bem){ boolean success; StringBuilder sb = new StringBuilder(); sb.append("Key-Type: DSA\n Key-Length: 2048\n Subkey-Type: ELG-E\n Subkey-Length: 2048") .append("\n Name-Real: ").append(name); if (!StringTool.isNothing(bem)) { sb.append("\n Name-Comment: ").append(bem); } sb.append("\n Name-Email: ").append(mail).append("\n Expire-Date: 0") .append("\n Passphrase: ").append(pwd).append("\n %commit\n"); success = runGnuPG("--gen-key", sb.toString()); if (success && this.gpg_exitCode != 0) { success = false; } return success; } public boolean changeKeyPassphrase(String key, String oldpwd, String newpwd){ boolean success; StringBuilder sb = new StringBuilder(); sb.append("passwd\n").append(oldpwd).append("\n").append(newpwd).append("\n") .append(newpwd).append("\n").append("quit\n"); success = runGnuPG("--edit-key " + key, sb.toString()); if (success && this.gpg_exitCode != 0) { success = false; } return success; } /** * Verify a signature * * @param inStr * signature to verify * @return true if verified. */ public boolean verify(String signedString, String dataString){ boolean success = false; File signedFile = createTempFile(signedString); File dataFile = createTempFile(dataString); if ((signedFile != null) && (dataFile != null)) { success = runGnuPG( "--verify " + signedFile.getAbsolutePath() + " " + dataFile.getAbsolutePath(), null); signedFile.delete(); dataFile.delete(); if (success && this.gpg_exitCode != 0) { success = false; } } return success; } public boolean verify(String signedString){ boolean success = false; File signedFile = createTempFile(signedString); if (signedFile != null) { success = runGnuPG("--verify " + signedFile.getAbsolutePath(), null); signedFile.delete(); if (success && this.gpg_exitCode != 0) { success = false; } } return success; } /** * Get processing result * * @return result string. */ public String getResult(){ return gpg_result; } /** * Get error output from GnuPG process * * @return error string. */ public String getErrorString(){ return gpg_err; } /** * Get GnuPG exit code * * @return exit code. */ public int getExitCode(){ return gpg_exitCode; } public void runWithCommand(String command){ try { Process p = Runtime.getRuntime().exec(executable + " " + command); // p.waitFor(); } catch (Exception e) { e.printStackTrace(); } } /** * Runs GnuPG external program * * @param commandArgs * command line arguments * @param inputStr * string to pass to GnuPG process * @return true if success. */ private boolean runGnuPG(String commandArgs, String inputStr){ Process p; String fullCommand = createGPGCommand() + " " + commandArgs; // String fullCommand = commandArgs; // log.log("GPG-Command: "+commandArgs+" "+inputStr, Log.INFOS); if (!gpgOK) { gpg_err = "GnuPG Programm nicht gefunden"; return false; } try { p = Runtime.getRuntime().exec(fullCommand); } catch (IOException io) { ExHandler.handle(io); return false; } if (inputStr != null) { BufferedWriter out = new BufferedWriter(new OutputStreamWriter(p.getOutputStream())); try { out.write(inputStr); out.close(); } catch (IOException io) { System.out.println("Exception at write! " + io.getMessage()); return false; } } ProcessStreamReader psr_stdout = new ProcessStreamReader(p.getInputStream(), "ERROR"); ProcessStreamReader psr_stderr = new ProcessStreamReader(p.getErrorStream(), "OUTPUT"); psr_stdout.start(); psr_stderr.start(); try { psr_stdout.join(); psr_stderr.join(); } catch (InterruptedException i) { System.out.println("Exception at join! " + i.getMessage()); return false; } try { p.waitFor(); } catch (InterruptedException i) { System.out.println("Exception at waitfor! " + i.getMessage()); return false; } try { gpg_exitCode = p.exitValue(); } catch (IllegalThreadStateException itse) { return false; } gpg_result = psr_stdout.getString(); gpg_err = psr_stderr.getString(); return true; } /** * A utility method for creating a unique temporary file when needed by one of the main methods. <BR> * The file handle is store in tmpFile object var. * * @param inStr * data to write into the file. * @return true if success */ private File createTempFile(String inStr){ File tmpFile = null; FileWriter fw; try { tmpFile = File.createTempFile("YGnuPG", null); } catch (Exception e) { System.out.println("Cannot create temp file " + e.getMessage()); return null; } try { fw = new FileWriter(tmpFile); fw.write(inStr); fw.flush(); fw.close(); } catch (Exception e) { // delete our file: tmpFile.delete(); System.out.println("Cannot write temp file " + e.getMessage()); return null; } return tmpFile; } /** * Default constructor */ public GnuPG(String useIdentity){ identity = useIdentity; } public boolean isAvailable(){ return gpgOK; } /** * Gets stream encoding * * @return stream encoding. */ public static String streamEncoding(){ OutputStreamWriter out = new OutputStreamWriter(new ByteArrayOutputStream()); return out.getEncoding(); } public Result<byte[]> decrypt(byte[] encrypted){ if (decrypt(StringTool.createString(encrypted), new String(passphrase))) { String dec = getResult(); return new Result<byte[]>(StringTool.getBytes(dec)); } return null; } public byte[] sign(byte[] source){ if (sign(StringTool.createString(source), identity, new String(passphrase))) { return StringTool.getBytes(getResult()); } return null; } public byte[] encrypt(byte[] source, String receiverKeyName){ if (encrypt(StringTool.createString(source), receiverKeyName)) { return StringTool.getBytes(getResult()); } return null; } public VERIFY_RESULT verify(byte[] data, byte[] signature, String signerKeyName){ if (verify(StringTool.createString(data), StringTool.createString(signature))) { return Cryptologist.VERIFY_RESULT.OK; } else { return Cryptologist.VERIFY_RESULT.BAD_SIGNATURE; } } public boolean addCertificate(X509Certificate cert){ // TODO Auto-generated method stub return false; } public X509Certificate generateCertificate(PublicKey pk, String alias, TimeTool validFrom, TimeTool validUntil){ // TODO Auto-generated method stub return null; } public KeyPair generateKeys(String alias, char[] pwd, TimeTool validFrom, TimeTool validUntil){ // TODO Auto-generated method stub return null; } public boolean hasCertificateOf(String alias){ // TODO Auto-generated method stub return false; } public boolean hasKeyOf(String alias){ // TODO Auto-generated method stub return false; } public String getUser(){ return identity; } public X509Certificate getCertificate(String alias){ // TODO Auto-generated method stub return null; } public boolean isFunctional(){ // TODO Auto-generated method stub return false; } public boolean addCertificate(byte[] certEncoded){ // TODO Auto-generated method stub return false; } public byte[] getCertificateEncoded(String alias) throws CryptologistException{ return null; } public boolean removeCertificate(String alias){ // TODO Auto-generated method stub return false; } public void decrypt(InputStream source, OutputStream dest) throws CryptologistException{ // TODO Auto-generated method stub } public void encrypt(InputStream source, OutputStream dest, String receiverKeyName) throws CryptologistException{ // TODO Auto-generated method stub } }