package org.fnppl.opensdx.security; /* * Copyright (C) 2010-2015 * fine people e.V. <opensdx@fnppl.org> * Henning Thieß <ht@fnppl.org> * * http://fnppl.org */ /* * Software license * * As far as this file or parts of this file is/are software, rather than documentation, this software-license applies / shall be applied. * * This file is part of openSDX * openSDX 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. * * openSDX 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 Lesser General Public License * and GNU General Public License along with openSDX. * If not, see <http://www.gnu.org/licenses/>. * */ /* * Documentation license * * As far as this file or parts of this file is/are documentation, rather than software, this documentation-license applies / shall be applied. * * This file is part of openSDX. * Permission is granted to copy, distribute and/or modify this document * under the terms of the GNU Free Documentation License, Version 1.3 * or any later version published by the Free Software Foundation; * with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. * A copy of the license is included in the section entitled "GNU * Free Documentation License" resp. in the file called "FDL.txt". * */ import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Vector; import javax.swing.JDialog; import org.fnppl.opensdx.gui.DefaultMessageHandler; import org.fnppl.opensdx.gui.Dialogs; import org.fnppl.opensdx.gui.MessageHandler; import org.fnppl.opensdx.xml.Document; import org.fnppl.opensdx.xml.Element; public class SecurityControl { private KeyApprovingStore currentKeyStore = null; private KeyVerificator keyverificator = null; private HashMap<String, KeyClient> keyclients = new HashMap<String, KeyClient>(); private MessageHandler messageHandler = new DefaultMessageHandler(); private File lastDir = getDefaultDir(); public SecurityControl() { } public Result verifyFileSignature(File signatureFile) { boolean ok = true; Element report = new Element("file_signature_verification_report"); try { report.addContent("check_datetime", SecurityHelper.getFormattedDate(System.currentTimeMillis())); Element content = Document.fromFile(signatureFile).getRootElement(); if (content.getName().equals("signature")) { Signature s = Signature.fromElement(content); File origFile = getSignaturesOrigFile(signatureFile, s.getDataName()); ok = buildSignatureVerificationReportElement(s, signatureFile, origFile, null, report); if (ok) { return Result.succeeded(report); } else { return Result.error(report); } } else if (content.getName().equals("signatures")) { boolean verifyKeys = true; Vector<Element> sList = content.getChildren("signature"); if (sList==null || sList.size()==0) { //Dialogs.showMessage("ERROR: unknown signature format in: "+signatureFile.getAbsolutePath()+"."); report.addContent("error", "ERROR: unknown signature format in: "+signatureFile.getAbsolutePath()+"."); return Result.error(report); } Signature sFirst = Signature.fromElement(sList.get(0)); File origFile = getSignaturesOrigFile(signatureFile, sFirst.getDataName()); if (origFile != null) { Vector<Signature> signatures = new Vector<Signature>(); signatures.add(sFirst); for (int i=1;i<sList.size();i++) { Signature s = Signature.fromElement(sList.get(i)); signatures.add(s); } //Element report = new Element("signatures_verification_report"); for (int i=0;i<signatures.size();i++) { Signature signature = signatures.get(i); if (i==0) { boolean sigOK = buildSignatureVerificationReportElement(signature, signatureFile, origFile, null, report); if (!sigOK) { ok = false; } } else { boolean sigOK = buildSignatureVerificationReportElement(signature, signatureFile, null, signatures.get(i-1).getSignatureBytes(), report); if (!sigOK) { ok = false; } } } } if (ok) { return Result.succeeded(report); } else { return Result.error(report); } } else { report.addContent("error", "ERROR: unknown signature format in: "+signatureFile.getAbsolutePath()+"."); return Result.error(report); } } catch (Exception e) { e.printStackTrace(); report.addContent("error", "ERROR: verifying signature for file: "+signatureFile.getAbsolutePath()+" failed"); return Result.error(report); //Dialogs.showMessage("ERROR: verifying signature for file: "+signatureFile.getAbsolutePath()+" failed"); } } private File getSignaturesOrigFile(File f, String dataname) { File origFile = null; if (f.getName().endsWith("_signature.xml")) { origFile = new File(f.getAbsolutePath().substring(0,f.getAbsolutePath().length()-14)); System.out.println("checking file: "+origFile.getAbsolutePath()); if (!origFile.exists()) origFile = null; } if (origFile == null) { origFile = new File(f.getParent(),dataname); if (!origFile.exists()) origFile = null; } if (origFile == null) { origFile = Dialogs.chooseOpenFile("Please select original file for signature verification", lastDir, ""); } return origFile; } private boolean buildSignatureVerificationReportElement(Signature s, File signatureFile, File origFile, byte[] signatureBytes, Element report) throws Exception { Result res = null; if (origFile != null) { // verify File //calc md5, sha1, sha256 FileInputStream in = new FileInputStream(origFile); BufferedInputStream bin = new BufferedInputStream(in); byte[][] kk = SecurityHelper.getMD5SHA1SHA256(in); byte[] md5 = kk[1]; byte[] sha1 = kk[2]; byte[] sha256 = kk[3]; in.close(); report.addContent("signed_filename", origFile.getName()); report.addContent("signature_filename", signatureFile.getName()); if (md5!=null) { report.addContent("md5",SecurityHelper.HexDecoder.encode(md5, ':',-1)); } if (sha1!=null) { report.addContent("sha1",SecurityHelper.HexDecoder.encode(sha1, ':',-1)); } if (sha256!=null) { report.addContent("sha256",SecurityHelper.HexDecoder.encode(sha256, ':',-1)); } res = s.tryVerification(md5, sha1, sha256); } else { //e.g. TSA Signature -> verify former signature bytes, res = s.tryVerificationMD5SHA1SHA256(signatureBytes); } Vector<Identity> ids = null; if (s.getKey().isMaster()) { ids = requestIdentitiyDetails(s.getKey().getKeyID(), null); } else { if(s.getKey().isSub()) { try { String parentkeyid = ((SubKey)s.getKey()).getParentKeyID(); if (parentkeyid==null) { OSDXKey key = s.getKey(); KeyClient client = getKeyClient(key.getAuthoritativekeyserver()); if (client!=null) { MasterKey masterkey = client.requestMasterPubKey(key.getKeyID()); if (masterkey!=null) { ids = requestIdentitiyDetails(masterkey.getKeyID(), null); } } } else { ids = requestIdentitiyDetails(parentkeyid, null); } } catch (Exception ex) { ex.printStackTrace(); } } } if (ids!=null && ids.size()>0) { res.report.addContent(ids.lastElement().toElement(true)); } else { res.report.addContent("msg","no identity details found."); } report.addContent(res.report); if (res.succeeded) { //Dialogs.showMessage("Signature verified!"); //key data matches key data on keyserver Result keymatch = keyverificator.matchKeyDataWithKeyServer(s.getKey()); res.report.addContent(keymatch.report); //verify signature key Result r = keyverificator.verifyKey(s.getKey(), s.getSignDatetime()); res.report.addContent(r.report); //keylogs Element keylogs_report = new Element("keylogs_report"); keylogs_report.addContent("keyid", s.getKey().getKeyID()); Vector<KeyLog> logs = null; if (s.getKey().isMaster()) { logs = requestKeyLogs(s.getKey().getKeyID(), null); } else { if (s.getKey().isSub()) { String pkid = ((SubKey)s.getKey()).getParentKeyID(); if (pkid!=null && pkid.length()>0) { logs = requestKeyLogs(pkid, null); } } } if (logs!=null && logs.size()>0) { for (KeyLog kl : logs) { Element ekl = new Element("keylog_entry"); ekl.addContent("keyid_from", kl.getKeyIDFrom()); ekl.addContent("action", kl.getAction()); ekl.addContent("date", kl.getActionDatetimeString()); Identity id = requestCurrentIdentitiyDetails(kl.getKeyIDFrom(), null); if (id!=null) { ekl.addContent("email", id.getEmail()); } else { ekl.addContent("email", "[not found]"); } keylogs_report.addContent(ekl); } } else { keylogs_report.addContent("msg","no keylogs found"); } res.report.addContent(keylogs_report); return r.succeeded; } else { //Dialogs.showMessage("Signature NOT verified!"); return res.succeeded; } } public Vector<Identity> requestIdentitiyDetails(String keyid, OSDXKey signingKey) { if (currentKeyStore.getKeyServer() == null) { //Dialogs.showMessage("Sorry, no keyservers found."); return null; } String authServer = keyid.substring(keyid.indexOf('@')+1); KeyClient client = getKeyClient(authServer); if (client == null) { return null; } try { client.close(); } catch (Exception ex) { ex.printStackTrace(); } Vector<Identity> ids = null; try { ids = client.requestIdentities(keyid, signingKey); } catch (Exception ex) { if (ex.getMessage()!=null && ex.getMessage().startsWith("Connection refused")) { //Dialogs.showMessage("Sorry, could not connect to server."); return null; } else { ex.printStackTrace(); } } if (ids!=null) { return ids; } //Dialogs.showMessage("No identities found for "+keyid); return null; } public Identity requestCurrentIdentitiyDetails(String keyid, OSDXKey signingKey) { if (currentKeyStore.getKeyServer() == null) { //Dialogs.showMessage("Sorry, no keyservers found."); return null; } String authServer = OSDXKey.getKeyServerFromKeyID(keyid); KeyClient client = getKeyClient(authServer); if (client == null) { return null; } try { client.close(); } catch (Exception ex) { ex.printStackTrace(); } try { Identity id = client.requestCurrentIdentity(keyid, signingKey); return id; } catch (Exception ex) { if (ex.getMessage()!=null && ex.getMessage().startsWith("Connection refused")) { //Dialogs.showMessage("Sorry, could not connect to server."); return null; } else { ex.printStackTrace(); } } //Dialogs.showMessage("No identities found for "+keyid); return null; } public Vector<KeyLog> requestKeyLogs(String keyid, OSDXKey sign) { final Vector<KeyLog> logs = new Vector<KeyLog>(); if (sign!=null) { sign.unlockPrivateKey(messageHandler); } KeyClient client = getKeyClient(OSDXKey.getKeyServerFromKeyID(keyid)); try { Vector<KeyLog> rlogs = client.requestKeyLogs(keyid,sign); if (rlogs!=null && rlogs.size()>0) { logs.addAll(rlogs); } } catch (Exception ex) { if (ex.getMessage()!=null && ex.getLocalizedMessage().startsWith("Connection refused")) { return null; } else { ex.printStackTrace(); } } return logs; } public KeyClient getKeyClient(String servername) { if (!keyclients.containsKey(servername)) { KeyServerIdentity ks = null; if (currentKeyStore!=null) { ks = currentKeyStore.getKeyServer(servername); if (ks==null) { Dialogs.showMessage("Unknown keyserver: "+servername+"\nPlease add this keyserver to your config, first."); return null; } keyverificator.addKeyServer(ks); Vector<OSDXKey> knownKeys = ks.getKnownKeys(); for (OSDXKey k : knownKeys) { keyverificator.addKeyRating(k, TrustRatingOfKey.RATING_MARGINAL); } } if (ks==null) { ks = KeyServerIdentity.make(servername, KeyClient.OSDX_KEYSERVER_DEFAULT_PORT, ""); } KeyClient client = new KeyClient(ks, keyverificator); if (ks.getKnownKeys()==null || ks.getKnownKeys().size()==0) { int ans = Dialogs.showYES_NO_Dialog("Add keys?", "No trusted keys found for keyserver "+ks.getHost()+"\nDo you want to trust all public keys from keyserver settings?"); if (ans == Dialogs.YES) { //request keyserver signing key and add to keyverificator try { client.connect(); KeyServerIdentity ksid = client.requestKeyServerIdentity(); Vector<OSDXKey> serverSigning = ksid.getKnownKeys(); for (OSDXKey k : serverSigning) { ks.addKnownKey(k); keyverificator.addKeyRating(k, TrustRatingOfKey.RATING_MARGINAL); } } catch (Exception ex) { ex.printStackTrace(); } } } keyclients.put(servername, client); return client; } return keyclients.get(servername); } public File[] encryptFileDetached(File f, SymmetricKey key, Document d) throws Exception { File fenc = new File(f.getAbsolutePath()+".osdx.enc"); FileInputStream in = new FileInputStream(f); FileOutputStream out = new FileOutputStream(fenc); key.encrypt(in, out); in.close(); out.close(); File fxml = new File(f.getAbsolutePath()+".osdx.enc.xml"); d.writeToFile(fxml); return new File[] {fenc,fxml}; } public File encryptFileInline(File f, SymmetricKey key, Document d) throws Exception { File fenc = new File(f.getAbsolutePath()+".enc.osdx"); FileInputStream in = new FileInputStream(f); FileOutputStream out = new FileOutputStream(fenc); out.write("#### openSDX symmetrical encrypted file ####\n".getBytes("UTF-8")); d.output(out); // out.write("\n".getBytes("UTF-8")); out.write("#### openSDX symmetrical encrypted file ####\n".getBytes("UTF-8")); key.encrypt(in, out); in.close(); out.close(); return fenc; } // public File asymmetricEncryptFileInline(File f, OSDXKey key, Document d, int blockSize) throws Exception { // if (blockSize>342) { // //max 342 bytes can be encrypted with asymmeric encryption -> use block sizes <= 342 // throw new RuntimeException("max blocksize is 342"); // } // File fenc = new File(f.getAbsolutePath()+".aenc.osdx"); // FileOutputStream out = new FileOutputStream(fenc); // out.write("#### openSDX asymmetrical encrypted file ####\n".getBytes("UTF-8")); // d.output(out); // // out.write("\n".getBytes("UTF-8")); // out.write("#### openSDX asymmetrical encrypted file ####\n".getBytes("UTF-8")); // // FileInputStream in = new FileInputStream(f); // byte[] buffer = new byte[blockSize]; // int read = -1; // while ((read = in.read(buffer))>0) { // byte[] crypt; // if (read==blockSize) { // crypt = key.encrypt(buffer); // } else { // crypt = key.encrypt(Arrays.copyOf(buffer, read)); // } // //System.out.println("crpyt len="+crypt.length+"\tread = "+read); // out.write(crypt); // } // in.close(); // out.close(); // return fenc; // } // // public File[] asymmetricEncryptFileDetached(File f, OSDXKey key, Document d, int blockSize) throws Exception { // if (blockSize>342) { // //max 342 bytes can be encrypted with asymmeric encryption -> use block sizes <= 342 // throw new RuntimeException("max blocksize is 342"); // } // // File fenc = new File(f.getAbsolutePath()+".osdx.aenc"); // FileInputStream in = new FileInputStream(f); // FileOutputStream out = new FileOutputStream(fenc); // // byte[] buffer = new byte[blockSize]; // int read = -1; // while ((read = in.read(buffer))>0) { // byte[] crypt; // if (read==blockSize) { // crypt = key.encrypt(buffer); // } else { // crypt = key.encrypt(Arrays.copyOf(buffer, read)); // } // out.write(crypt); // } // in.close(); // out.close(); // // File fxml = new File(f.getAbsolutePath()+".osdx.aenc.xml"); // d.writeToFile(fxml); // return new File[] {fenc,fxml}; // } public static File getDefaultDir() { File f = new File(System.getProperty("user.home")); f = new File(f, "openSDX"); if(!f.exists()) { f.mkdirs(); } return f; } public KeyApprovingStore getKeyStore() { return currentKeyStore; } public void setKeyStore(KeyApprovingStore currentKeyStore) { this.currentKeyStore = currentKeyStore; } public KeyVerificator getKeyverificator() { return keyverificator; } public void setKeyverificator(KeyVerificator keyverificator) { this.keyverificator = keyverificator; } public HashMap<String, KeyClient> getKeyclients() { return keyclients; } public void resetKeyClients() { keyclients = new HashMap<String, KeyClient>(); } public void setKeyclients(HashMap<String, KeyClient> keyclients) { this.keyclients = keyclients; } public File getLastDir() { return lastDir; } public void setLastDir(File lastDir) { this.lastDir = lastDir; } public MessageHandler getMessageHandler() { return messageHandler; } public void setMessageHandler(MessageHandler messageHandler) { this.messageHandler = messageHandler; } }