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.*; import java.net.URL; import java.util.*; import org.fnppl.opensdx.gui.Dialogs; import org.fnppl.opensdx.gui.MessageHandler; import org.fnppl.opensdx.xml.*; public class KeyApprovingStore { private MessageHandler messageHandler = null; private File f = null; private Vector<OSDXKey> keys = null; private Vector<KeyLog> keylogs = null; private Vector<KeyServerIdentity> keyservers; private boolean unsavedChanges = false; private OSDXKey keystoreSigningKey = null; private byte[] keystore_sha256_proof = null; private KeyApprovingStore() { } public static KeyApprovingStore createNewKeyApprovingStore(File f, MessageHandler mh) throws Exception { KeyApprovingStore kas = new KeyApprovingStore(); kas.f = f; kas.keys = new Vector<OSDXKey>(); kas.keylogs = new Vector<KeyLog>(); kas.keyservers = new Vector<KeyServerIdentity>(); kas.unsavedChanges = true; kas.messageHandler = mh; return kas; } public void addKeyserverAndPublicKeysFromConfig(URL configURL) { try { // if (!configFile.exists() || configFile.isDirectory()) { // return; // } Element root = Document.fromURL(configURL).getRootElement(); if (keys==null) keys = new Vector<OSDXKey>(); if (keyservers==null) keyservers = new Vector<KeyServerIdentity>(); if (root.getChild("defaultkeyservers")!=null) { keyservers = new Vector<KeyServerIdentity>(); Vector<Element> v = root.getChild("defaultkeyservers").getChildren("keyserver"); for (Element e : v) { unsavedChanges = true; keyservers.add(KeyServerIdentity.fromElement(e)); Element eKnownKeys = e.getChild("knownkeys"); if (eKnownKeys!=null) { Vector<Element> epks = eKnownKeys.getChildren("pubkey"); if (epks!=null) { for (Element epk : epks) { keys.add(OSDXKey.fromPubKeyElement(epk)); } } } } } if (root.getChild("knownapprovedkeys")!=null) { Vector<Element> v = root.getChild("knownapprovedkeys").getChildren("pubkey"); for (Element e : v) { unsavedChanges = true; keys.add(OSDXKey.fromPubKeyElement(e)); } } //TODO check localproofs and signatures } catch (Exception ex) { Dialogs.showMessage("ERROR: init of known keyservers failed.\n"+configURL.toString()); ex.printStackTrace(); } } public void addKeyServer(KeyServerIdentity keyserver) { if (keyservers== null) { keyservers = new Vector<KeyServerIdentity>(); } keyservers.add(keyserver); unsavedChanges = true; } public static KeyApprovingStore fromFile(File f, MessageHandler mh) throws Exception { System.out.println("loading keystore from file : "+f.getAbsolutePath()); Document d = Document.fromFile(f); Element e = d.getRootElement(); if(!e.getName().equals("keystore")) { throw new Exception("KeyStorefile must have \"keystore\" as root-element"); } //check signature boolean ignore = false; Vector<Element> ves = e.getChildren(); Element eSig = null; Element eProof = null; Signature storeProof = null; for (Element el : ves) { if (el.getName().equals("signature") && storeProof==null) { eSig = el; storeProof = Signature.fromElement(eSig); if (!storeProof.getDataName().equals("signature of complete keystore")) { storeProof = null; } } else if (el.getName().equals("keystore_sha256_proof")) { eProof = el; } } if (eSig == null || storeProof == null) { if (!ignore) ignore = mh.requestIgnoreVerificationFailure(); if (!ignore) throw new Exception("KeyStore: proof of keystore failed."); } ves.remove(eSig); ves.remove(eProof); byte[] givenProof = null; if (!ignore) { givenProof = SecurityHelper.HexDecoder.decode(eProof.getText()); byte[] bsha256 = SecurityHelper.getSHA256LocalProof(ves); if (!Arrays.equals(bsha256, givenProof)) { System.out.println("sha256localproof given : \t"+SecurityHelper.HexDecoder.encode(givenProof, ':', -1)); System.out.println("sha256localproof calculated: \t"+SecurityHelper.HexDecoder.encode(bsha256, ':', -1)); if (!ignore) ignore = mh.requestIgnoreVerificationFailure(); if (!ignore) throw new Exception("KeyStore: proof of keystore failed."); } boolean ok = false; try { Result v = storeProof.tryVerificationMD5SHA1SHA256(givenProof); ok = v.succeeded; } catch (Exception ex) { ex.printStackTrace(); } if(!ok) { if (!ignore) ignore = mh.requestIgnoreVerificationFailure(); if (!ignore) throw new Exception("KeyStore: proof of keystore failed."); } } KeyApprovingStore kas = new KeyApprovingStore(); kas.f = f; kas.unsavedChanges = false; kas.messageHandler = mh; kas.keys = new Vector<OSDXKey>(); kas.keylogs = new Vector<KeyLog>(); kas.keystore_sha256_proof = givenProof; Element keys = e.getChild("keys"); //add all keypairs as OSDXKey ves = keys.getChildren("keypair"); if (ves.size()>0) { System.out.println("keystore contains "+ves.size()+" keypairs."); for(int i=0; i<ves.size(); i++) { Element ee = ves.elementAt(i); OSDXKey osdxk = OSDXKey.fromElement(ee); kas.keys.add(osdxk); } //check sha1localproof // kas.keysSHA1localproof = SecurityHelper.HexDecoder.decode(keys.getChildText("sha1localproof")); byte[] sha256localproof = SecurityHelper.HexDecoder.decode(keys.getChildText("sha256localproof")); byte[] bsha256 = SecurityHelper.getSHA256LocalProof(ves); if (!Arrays.equals(bsha256, sha256localproof)) { System.out.println("sha256localproof given_in_xml:\t"+SecurityHelper.HexDecoder.encode(sha256localproof, ':', -1)); System.out.println("sha256localproof calculated: \t"+SecurityHelper.HexDecoder.encode(bsha256, ':', -1)); throw new Exception("KeyStore: localproof of keypairs failed."); } Signature s = null; boolean ok = false; try { s = Signature.fromElement(keys.getChild("signature")); Result v = s.tryVerificationMD5SHA1SHA256(sha256localproof); ok = v.succeeded; } catch (Exception ex) { ex.printStackTrace(); } if(!ok) { ignore = mh.requestIgnoreVerificationFailure(); if (!ignore) throw new Exception("KeyStore: signoff of localproof of keypairs failed."); kas.unsavedChanges = true; } } else { //TODO no keypairs in store } //connet subkeys to masterkeys Vector<MasterKey> masterkeys = kas.getAllMasterKeys(); for (OSDXKey k : kas.keys) { if (k instanceof SubKey) { SubKey sk = (SubKey)k; if (sk.getParentKeyID()!=null && sk.getParentKeyID().length()>0) { byte[] parentkeyid = SecurityHelper.HexDecoder.decode(OSDXKey.getFormattedKeyIDModulusOnly(sk.getParentKeyID())); for (MasterKey mk : masterkeys) { //System.out.println("comparing: "+SecurityHelper.HexDecoder.encode(parentkeyid, '\0', -1)+" - "+SecurityHelper.HexDecoder.encode(mk.getKeyModulusSHA1bytes(), '\0', -1)); if (Arrays.equals(mk.getKeyModulusSHA1bytes(), parentkeyid)) { sk.setParentKey(mk); if (sk.isRevoke()) { mk.addRevokeKey((RevokeKey)sk); } else { mk.addSubKey(sk); } sk.setUnsavedChanges(false); mk.setUnsavedChanges(false); break; } } } } } //add keylog (includes verify localproof and signoff) Vector<Element> vkl = e.getChildren("keylog"); if (vkl.size()>0) { System.out.println("keystore contains "+vkl.size()+" keylogs."); for(int i=0; i<vkl.size(); i++) { Element ee = vkl.elementAt(i); try { KeyLog kl = KeyLog.fromElement(ee); Result verified = kl.verify(); if (!verified.succeeded) { ignore = mh.requestIgnoreKeyLogVerificationFailure(); if (ignore) { kas.keylogs.add(kl); kas.unsavedChanges = true; } } else { kas.keylogs.add(kl); } } catch (Exception ex) { ex.printStackTrace(); } } } //add keyservers kas.keyservers = null; Element eK = e.getChild("keyservers"); if (eK!=null) { kas.keyservers = new Vector<KeyServerIdentity>(); Vector<Element> ekss = eK.getChildren("keyserver"); for (Element eks : ekss) { KeyServerIdentity ks = KeyServerIdentity.fromElement(eks); kas.keyservers.add(ks); } } return kas; } public String getKeyServerNameForKey(String keyid) { for (KeyServerIdentity ks : keyservers) { if (ks.hasKnownKey(keyid)) { return ks.getHost(); } } return null; } // public String getEmail(OSDXKey key) { // if (key.isMaster()) { // Identity id = ((MasterKey)key).getCurrentIdentity(); // if (id!=null) { // return id.getEmail(); // } // } else if (key.isSub()) { // MasterKey mkey = ((SubKey)key).getParentKey(); // if (mkey==null) return null; // return getEmail(mkey); // } // //get from KeyLogs // String akeyid = key.getKeyID(); // String email = null; // long date = Long.MIN_VALUE; // for (KeyLog kl : keylogs) { // String keyidto = kl.getKeyIDTo(); // if (keyidto.equals(akeyid)) { // if (kl.getAction().equals(KeyLogAction.APPROVAL)) { // Identity id = kl.getIdentity(); // if (id!=null) { // if (id.getEmail() != null && kl.getActionDatetime()>date) { // email = id.getEmail(); // date = kl.getActionDatetime(); // } // } // } // } // } // return email; // } public String getEmailAndMnemonic(String keyid) { OSDXKey key = getKey(keyid); if (key!=null) { if (key.isMaster()) { Identity id = ((MasterKey)key).getCurrentIdentity(); if (id!=null) { String email = id.getEmail(); String mnemonic = null; if (!id.isMnemonicRestricted()) { mnemonic = id.getMnemonic(); } if (email!=null) { if (mnemonic!=null) { return email + " | "+mnemonic; } else { return email; } } else { if (mnemonic!=null) { return mnemonic; } else { return null; } } } } else if (key.isSub()) { //MasterKey mkey = ((SubKey)key).getParentKey(); //if (mkey==null) return null; //return getEmailAndMnemonic(mkey.getKeyID()); String mkeyid = ((SubKey)key).getParentKeyID(); if (mkeyid==null || mkeyid.length()==0) return null; return getEmailAndMnemonic(mkeyid); } } //get from KeyLogs String email = null; String mnemonic = null; long date = Long.MIN_VALUE; for (KeyLog kl : keylogs) { String keyidto = kl.getKeyIDTo(); if (keyidto.equals(keyid)) { if (kl.getAction().equals(KeyLogAction.APPROVAL)) { Identity id = kl.getIdentity(); if (id!=null) { if (id.getEmail() != null && kl.getActionDatetime()>date) { email = id.getEmail(); date = kl.getActionDatetime(); if (id.getMnemonic()!=null && !id.isMnemonicRestricted()) { mnemonic = id.getMnemonic(); } } } } } } if (email!=null) { if (mnemonic!=null) { return email + " | "+mnemonic; } else { return email; } } else { if (mnemonic!=null) { return mnemonic; } else { return null; } } } public boolean hasUnsavedChanges() { if (unsavedChanges) return true; else { for (OSDXKey k : keys) { if (k.hasUnsavedChanges()) { System.out.println("unsaved changes in key: "+k.getKeyID()); //return true; } } } return false; } public Vector<OSDXKey> getAllKeys() { return keys; } public Vector<SubKey> getSubKeys(String keyid) { Vector<SubKey> ret = new Vector<SubKey>(); for (OSDXKey k : keys) { try { if (k instanceof SubKey && k.isSub() && ((SubKey)k).getParentKeyID().equals(keyid)) { ret.add((SubKey)k); } } catch (Exception ex) {} } return ret; } public Vector<KeyServerIdentity> getKeyServer() { return keyservers; } public KeyServerIdentity getKeyServer(String servername) { for (KeyServerIdentity ks : keyservers) { if (ks.getHost().equals(servername)) return ks; } return null; } public OSDXKey getKey(String keyid) { byte[] idbytes = SecurityHelper.HexDecoder.decode(OSDXKey.getFormattedKeyIDModulusOnly(keyid)); if (idbytes==null) return null; for (OSDXKey k : keys) { if (Arrays.equals(k.getKeyModulusSHA1bytes(), idbytes)) { return k; } } return null; } public void removeKey(OSDXKey key) { boolean ok = keys.remove(key); //System.out.println("removing key: "+ok); unsavedChanges = true; } public void removeKeyServer(KeyServerIdentity keyserver) { if (keyservers!=null) { boolean ok = keyservers.remove(keyserver); unsavedChanges = true; } } public void removeKeyLog(KeyLog keylog) { if (keylogs != null) { keylogs.remove(keylog); unsavedChanges = true; } } public Vector<RevokeKey> getRevokeKeys(String keyid) { Vector<RevokeKey> ret = new Vector<RevokeKey>(); for (OSDXKey k : keys) { if (k instanceof RevokeKey && k.isRevoke() && ((RevokeKey)k).getParentKeyID().equals(keyid)) { ret.add((RevokeKey)k); } } return ret; } public Vector<SubKey> getAllSigningSubKeys() { Vector<SubKey> skeys = new Vector<SubKey>(); for (OSDXKey k : keys) { if (k instanceof SubKey && k.isSub() && k.allowsSigning()) { skeys.add((SubKey)k); } } return skeys; } public Vector<MasterKey> getAllSigningMasterKeys() { Vector<MasterKey> skeys = new Vector<MasterKey>(); for (OSDXKey k : keys) { if (k instanceof MasterKey && k.isMaster() && k.allowsSigning()) { skeys.add((MasterKey)k); } } return skeys; } public Vector<OSDXKey> getAllPrivateSigningKeys() { Vector<OSDXKey> result = new Vector<OSDXKey>(); for (OSDXKey key : getAllSigningMasterKeys()) { if (key.hasPrivateKey()) { result.add(key); } } for (OSDXKey key : getAllSigningSubKeys()) { if (key.hasPrivateKey()) { result.add(key); } } return result; } public Vector<MasterKey> getAllMasterKeys() { Vector<MasterKey> skeys = new Vector<MasterKey>(); for (OSDXKey k : keys) { if (k instanceof MasterKey && k.isMaster()) { skeys.add((MasterKey)k); } } return skeys; } public Vector<SubKey> getAllDecyrptionSubKeys() { Vector<SubKey> skeys = new Vector<SubKey>(); for (OSDXKey k : keys) { if (k instanceof SubKey && k.isSub()) { int u = k.getUsage(); if ((u==OSDXKey.USAGE_CRYPT || u==OSDXKey.USAGE_WHATEVER) && k.hasPrivateKey()) { skeys.add((SubKey)k); } } } return skeys; } public Vector<SubKey> getAllEncyrptionSubKeys() { Vector<SubKey> skeys = new Vector<SubKey>(); for (OSDXKey k : keys) { if (k instanceof SubKey && k.isSub()) { int u = k.getUsage(); if (u==OSDXKey.USAGE_CRYPT || u==OSDXKey.USAGE_WHATEVER) { skeys.add((SubKey)k); } } } return skeys; } public boolean toFile(File file) throws Exception { this.f = file; Element root = toElement(); if (root == null) return false; Document d = Document.buildDocument(root); if (!file.exists() || messageHandler.requestOverwriteFile(file)) { d.writeToFile(file); unsavedChanges = false; return true; } return false; } public Element toElement() throws Exception { Element root = new Element("keystore"); Element ek = new Element("keys"); for (OSDXKey k : keys) { ek.addContent(k.toElement(messageHandler)); } byte[] sha256localproof = SecurityHelper.getSHA256LocalProof(ek.getChildren("keypair")); ek.addContent("sha256localproof", SecurityHelper.HexDecoder.encode(sha256localproof, ':',-1)); root.addContent(ek); if (keystoreSigningKey == null) { keystoreSigningKey = messageHandler.requestMasterSigningKey(this); } if (!keystoreSigningKey.isPrivateKeyUnlocked()) { keystoreSigningKey.unlockPrivateKey(messageHandler); } if (!keystoreSigningKey.isPrivateKeyUnlocked()) { return null; } Signature s = Signature.createSignatureFromLocalProof(sha256localproof, "signature of sha256localproof of keys", keystoreSigningKey); ek.addContent(s.toElement()); //keylog if (keylogs!=null && keylogs.size()>0) { for (KeyLog kl : keylogs) { Result vr = Result.error("unknown error"); try { vr = kl.verify(); } catch (Exception e) { //e.printStackTrace(); System.out.println("KeyLog signature NOT verified!"); } if (vr.succeeded) { root.addContent(kl.toElement(true)); } else { System.out.println("KeyLog signature NOT verified! from_keyid: "+kl.getKeyIDFrom()); System.out.println("msg: "+vr.errorMessage); //Document.buildDocument(kl.toElement(true)).output(System.out); } } } //keyserver if (keyservers!=null && keyservers.size()>0) { Element eK = new Element("keyservers"); for (KeyServerIdentity ks : keyservers) { eK.addContent(ks.toElement()); } root.addContent(eK); } //complete signoff byte[] proof = SecurityHelper.getSHA256LocalProof(root.getChildren()); root.addContent("keystore_sha256_proof",SecurityHelper.HexDecoder.encode(proof,':',-1)); Signature sign = Signature.createSignatureFromLocalProof(proof, "signature of complete keystore", keystoreSigningKey); root.addContent(sign.toElement()); return root; } public void addKey(OSDXKey key) { if (keys==null) { keys = new Vector<OSDXKey>(); } //check if already in keystore for (int i=0;i<keys.size();i++) { OSDXKey k = keys.get(i); if (k.getKeyID().equals(key.getKeyID())) { // if (!k.hasPrivateKey() && k.hasPrivateKey()) { // } return; } } unsavedChanges = true; keys.add(key); } public void addKeyLog(KeyLog kl) { if (keylogs==null) keylogs = new Vector<KeyLog>(); //check if keystore already contains keylog boolean add = true; for (int i=0;i<keylogs.size() && add;i++) { KeyLog log = keylogs.get(i); if ( kl.getActionDatetime() == log.getActionDatetime() && kl.getKeyIDFrom().equals(log.getKeyIDFrom()) && kl.getKeyIDTo().equals(log.getKeyIDTo())) { //look if log has restricted fields that are unrestricted in log if (log.hasRestrictedFields() && !kl.hasRestrictedFields()) { //replace unsavedChanges = true; keylogs.remove(i); keylogs.add(i,kl); } add = false; } } if (add) { unsavedChanges = true; keylogs.add(kl); } } public Vector<KeyLog> getKeyLogs() { return keylogs; } public Vector<KeyLog> getKeyLogs(String keyid) { if (keylogs==null) return null; String akeyid = OSDXKey.getFormattedKeyIDModulusOnly(keyid); Vector<KeyLog> ret = new Vector<KeyLog>(); for (KeyLog kl : keylogs) { String keyidto = OSDXKey.getFormattedKeyIDModulusOnly(kl.getKeyIDTo()); if (keyidto.equals(akeyid)) { ret.add(kl); } } SecurityHelper.sortKeyLogsbyDate(ret); return ret; } public KeyStatus getKeyStatus(String keyid) { return getKeyStatus(keyid, null, System.currentTimeMillis(), null); } public KeyStatus getKeyStatus(String keyid, String usage, long datetime, String keyidKeyserver) { OSDXKey key = getKey(keyid); if (key==null) { return null; } Vector<KeyLog> keylogs = getKeyLogs(keyid); if (keylogs==null) { keylogs = new Vector<KeyLog>(); } return KeyStatus.getKeyStatus(key, keylogs, usage, datetime, keyidKeyserver); } // public KeyStatus getKeyStatus(String keyid) throws Exception { // Vector<KeyLog> kls = getKeyLogs(keyid); // if (kls==null || kls.size()==0) return null; // for (KeyLog kl : kls) { // System.out.println("found keylog... "+kl.getActionDatetimeString()); // } // KeyLog kl = kls.lastElement(); // String status = kl.getAction(); // int validity = -1; // if (status.equals(KeyLogAction.APPROVAL)) { // validity = KeyStatus.STATUS_VALID; // } // else if (status.equals(KeyLogAction.DISAPPROVAL)) { // validity = KeyStatus.STATUS_UNAPPROVED; // } // else if (status.equals(KeyLogAction.APPROVAL_PENDING)) { // validity = KeyStatus.STATUS_UNAPPROVED; // } // else if (status.equals(KeyLogAction.REVOCATION)) { // validity = KeyStatus.STATUS_REVOKED; // } // // int approvalPoints = 100; // int datetimeValidFrom = 0; //TODO get from key // int datetimeValidUntil = 0; //TODO // // KeyStatus ks = new KeyStatus(validity, approvalPoints, datetimeValidFrom, datetimeValidUntil, kl); // return ks; // } public File getFile() { return f; } public void setSigningKey(OSDXKey key) { keystoreSigningKey = key; } }