package org.fnppl.opensdx.security.test;
/*
* Copyright (C) 2010-2015
* fine people e.V. <opensdx@fnppl.org>
* Henning Thieß <ht@fnppl.org>
*
* http://fnppl.org
*/
/*
* Software license
*
* For those parts of this file, which are identified as 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
*
* For those parts of this file, which are identified as 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".
*
*/
/**
*
* @author Bertram Bödeker <bboedeker@gmx.de>
*
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.math.BigInteger;
import java.util.UUID;
import java.util.Vector;
import org.fnppl.opensdx.gui.DefaultMessageHandler;
import org.fnppl.opensdx.gui.MessageHandler;
import org.fnppl.opensdx.security.AsymmetricKeyPair;
import org.fnppl.opensdx.security.DataSourceStep;
import org.fnppl.opensdx.security.Identity;
import org.fnppl.opensdx.security.KeyApprovingStore;
import org.fnppl.opensdx.security.KeyClient;
import org.fnppl.opensdx.security.KeyLog;
import org.fnppl.opensdx.security.KeyLogAction;
import org.fnppl.opensdx.security.KeyServerIdentity;
import org.fnppl.opensdx.security.KeyVerificator;
import org.fnppl.opensdx.security.MasterKey;
import org.fnppl.opensdx.security.OSDXKey;
import org.fnppl.opensdx.security.PublicKey;
import org.fnppl.opensdx.security.Result;
import org.fnppl.opensdx.security.SecurityHelper;
import org.fnppl.opensdx.security.SubKey;
import org.fnppl.opensdx.security.SymmetricKey;
import org.fnppl.opensdx.security.TrustRatingOfKey;
import org.fnppl.opensdx.xml.Document;
import org.fnppl.opensdx.xml.Element;
public class Test {
public static void makeExampleKeyStore() {
try {
File testPath = new File(System.getProperty("user.home"), "openSDX_test");
testPath.mkdirs();
File fKeyStore = new File(testPath,"new_example_keystore.xml");
//build MessageHandler
MessageHandler messageHandler = new DefaultMessageHandler() {
public boolean requestOverwriteFile(File file) {//dont ask, just overwrite
return true;
}
public boolean requestIgnoreKeyLogVerificationFailure() {//dont ignore failed keylog verification
return false;
}
};
//build keystore
KeyApprovingStore keystore = KeyApprovingStore.createNewKeyApprovingStore(fKeyStore, messageHandler);
//create master and subkey
MasterKey master = MasterKey.buildNewMasterKeyfromKeyPair(AsymmetricKeyPair.generateAsymmetricKeyPair());
Identity id = Identity.newEmptyIdentity();
id.setIdentNum(1);
id.setEmail("example@fnppl.org");
id.setMnemonic("example");
id.setCountry("Example country");
id.setRegion("exmaple region");
id.setPostcode("12345");
id.setCity("Example City");
id.setCompany("fnppl e.V."); id.set_company_restricted(false);
id.setUnit("A");
id.setSubunit("111");
id.setFunction("CIO");
id.setFirstNames("Herr");
id.setMiddlename("B.");
id.setSurname("Ert");
id.setBirthday_gmt("1919-01-01");
id.setPlaceofbirth("Somewhere");
id.setPhone("+44 99 002020221");
id.setFax("+44 99 002020222");
id.setNote("example note");
BufferedImage img = new BufferedImage(90,120,BufferedImage.TYPE_INT_RGB);
Graphics g = img.getGraphics();
g.setColor(Color.BLACK);
g.fillRect(0,0,90,120);
g.setColor(Color.YELLOW);
g.fillOval(10, 25, 70, 70);
g.setColor(Color.BLACK);
g.setFont(new Font("Arial", Font.BOLD, 30));
g.drawString(";-)",25, 70);
img.flush();
id.setPhoto(img);
id.getDatapath().add(new DataSourceStep("LOCAL", System.currentTimeMillis()));
master.addIdentity(id);
SubKey sub = master.buildNewSubKeyfromKeyPair(AsymmetricKeyPair.generateAsymmetricKeyPair());
//protect with password
master.createLockedPrivateKey("password", "password");
sub.createLockedPrivateKey("password", "password");
//add keys to keystore
keystore.addKey(master);
keystore.addKey(sub);
keystore.setSigningKey(master);
//create a keylog from subkey to masterkey
Identity approveId = Identity.newEmptyIdentity();
approveId.setIdentNum(id.getIdentNum());
approveId.setEmail(id.getEmail());
approveId.setMnemonic(id.getMnemonic());
KeyLogAction kla = KeyLogAction.buildKeyLogAction(KeyLogAction.APPROVAL, sub, master.getKeyID(), approveId,null);
KeyLog kl = KeyLog.buildNewKeyLog(kla, "127.0.0.1", "127.0.0.1", sub);
//add keylog to keystore
keystore.addKeyLog(kl);
//add comments
Element eks = keystore.toElement();
//keys
Element e = eks.getChild("keys").getChild("keypair");
e.addCommentFirst("example of asymmetric key pair");
e.addCommentAfter("identities","identities should only occur on MASTER-key");
e.addCommentAfter("usage","ONLYSIGN|CRYPT|BOTH ; MASTER-keys/REVOKE-keys MUST only have SIGN-capabilities... ");
e.addCommentAfter("level","MASTER|REVOKE|SUB");
e.addCommentAfter("parentkeyid","COULD ; should be missing for MASTER-key; sha1-fingerprint in hex without leading 0x for modulus of key which this one is child of");
e.addCommentAfter("algo","RSA ; others not implemented yet");
e.addCommentAfter("modulus","as hex-string without leading 0x ; only for RSA?!");
e.getChild("pubkey").addCommentAfter("exponent","as hex-string with leading 0x");
e.addCommentAfter("privkey","privkey may be missing");
eks.getChild("keys").addCommentAfter("sha256localproof","the sha256 of all above elements (fieldnames, content and attributes)");
eks.getChild("keys").getChildren("keypair").lastElement().addComment("could have x more keypairs");
//signature
e = eks.getChild("keys").getChild("signature").getChild("data");
e.addCommentAfter("md5","the md5 of sha256localproof-bytes ; optional, but should be there ; there MUST be one data-hash-value given (md5, sha1 or sha256 - recommended to provide all three");
e.addCommentAfter("sha1","the sha1 of sha256localproof-bytes ; optional, but should be there ; there MUST be one data-hash-value given (md5, sha1 or sha256 - recommended to provide all three");
e.addCommentAfter("sha256","the sha256 of sha256localproof-bytes ; optional, but should be there ; there MUST be one data-hash-value given (md5, sha1 or sha256 - recommended to provide all three");
e.addCommentAfter("signdatetime","MUST");
e.addCommentAfter("dataname","SHOULD");
e = eks.getChild("keys").getChild("signature");
e.getChild("pubkey").addCommentFirst("given pubkey; should be verified from server/yourself");
e.addCommentAfter("signaturebytes","as hex-string");
//identity
e = eks.getChild("keys").getChild("keypair").getChild("identities").getChild("identity");
e.addCommentAfter("identnum","MUST");
e.addCommentAfter("email","MUST");
e.addCommentAfter("mnemonic","MUST ; shorthandle for this identities-purpose \"residency\" or \"work\" or whatever");
e.addCommentAfter("country","COULD");
e.addCommentAfter("region","COULD");
e.addCommentAfter("postcode","COULD");
e.addCommentAfter("city","COULD");
e.addCommentAfter("company","COULD");
e.addCommentAfter("unit","COULD");
e.addCommentAfter("subunit","COULD");
e.addCommentAfter("function","COULD ; function of that person");
e.addCommentAfter("firstname_s","COULD");
e.addCommentAfter("surname","COULD");
e.addCommentAfter("middlename","COULD");
e.addCommentAfter("birthday_gmt","COULD");
e.addCommentAfter("placeofbirth","COULD");
e.addCommentAfter("phone","COULD");
e.addCommentAfter("fax","COULD");
e.addCommentAfter("note","COULD");
e.addCommentAfter("photo","SHOULD ; base64 .png ; maxrawdatasize: 100k");
//keylogs
eks.addCommentAfter("keylog", "could have x other keylogs");
e = eks.getChild("keylog");
e.addCommentAfter("ipv4","lets assume, those actions are always made via net");
e.addCommentAfter("ipv6","lets assume, those actions are always made via net");
e.addCommentAfter("sha256localproof","localproof of keylogaction");
e.getChild("signature").addCommentFirst("signature of sha256localproof; typically from keyserver");
e = e.getChild("keylogaction");
e.addCommentFirst("keylogaction is generated and signed on client and sent to keyserver");
e.addCommentAfter("from_keyid","must be the same as signing key");
e.addCommentAfter("to_keyid","the key to which the keylog action refers to");
e.getChild("approval").addCommentFirst("action: approval/disapproval/revocation/approval pending");
e.getChild("approval").getChild("identity").addCommentFirst("list of all identity fields that are approved here");
e.addCommentAfter("sha256localproof_complete","sha256 localproof if all identitiy fields are visible; clients with a unrestricted view can verify the complete localproof");
e.addCommentAfter("sha256localproof_restricted","sha256 localproof if restricted identitiy fields are not visible; clients with a restricted view can verify the restricted localproof");
e.getChild("signature").addCommentFirst("signature of the concatenation of sha256 complete and restricted localproof; clients should verify one localproof depended an their restriction level and can take the other for granted.");
eks.addCommentAfter("keystore_sha256_proof", "complete proof of all above element; signed by signing key below");
//write to file
Document.buildDocument(eks).writeToFile(fKeyStore);
//System.out.println(kl.getSignDatetime()+" ?= "+kl.getActionSignatureKey().getValidFrom());
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* for Testing
* @param args
*/
public static void main(String[] args) {
//
// //try to read from example_keystore.xml
// try {
// KeyApprovingStore store = KeyApprovingStore.fromFile(new File("src/org/fnppl/opensdx/security/resources/example_keystore.xml"), new DefaultMessageHandler());
// } catch (Exception e) {
// e.printStackTrace();
// }
//testGenerateMasterKeyPair();
//testGeneratePublicKey();
//testHeadEmployeeSigning();
//makeExampleKeyStore();
UUID id = UUID.randomUUID();
id = UUID.nameUUIDFromBytes("hallo".getBytes());
System.out.println(id);
}
public static void testHeadEmployeeSigning() {
try {
// first we need
// a keyserver
// an employees keystore with master and subkey
// an head of department keystore with masterkey
// a contractkey (which is a subkey of head of departments masterkey
File testPath = new File(System.getProperty("user.home"), "openSDX_test");
testPath.mkdirs();
File fEmployeeKeyStore = new File(testPath,"ksEmployee.xml");
File fHeadKeyStore = new File(testPath,"ksHead.xml");
System.out.println(testPath.getAbsolutePath());
MessageHandler messageHandler = new DefaultMessageHandler() {
public boolean requestOverwriteFile(File file) {//dont ask, just overwrite
return true;
}
public boolean requestIgnoreKeyLogVerificationFailure() {//dont ignore failed keylog verification
return false;
}
};
//keyserver
KeyServerIdentity keyserver = KeyServerIdentity.make("localhost", 8889, "");
KeyVerificator keyverificator = new KeyVerificator();
keyverificator.addKeyServer(keyserver);
KeyClient client = new KeyClient(keyserver, keyverificator);
client.connect();
KeyServerIdentity ksid = client.requestKeyServerIdentity();
OSDXKey serverSigning = ksid.getKnownKeys().get(0);
keyserver.addKnownKey(serverSigning);
keyverificator.addKeyRating(serverSigning, TrustRatingOfKey.RATING_MARGINAL);
//employees keystore
KeyApprovingStore ksEmployee = null;
if (fEmployeeKeyStore.exists()) {
ksEmployee = KeyApprovingStore.fromFile(fEmployeeKeyStore, messageHandler);
} else {
//generate keys
ksEmployee = KeyApprovingStore.createNewKeyApprovingStore(fEmployeeKeyStore, messageHandler);
MasterKey master = MasterKey.buildNewMasterKeyfromKeyPair(AsymmetricKeyPair.generateAsymmetricKeyPair());
Identity id = Identity.newEmptyIdentity();
id.setEmail("employee@fnppl.org");
id.setMnemonic("employee");
master.addIdentity(id);
SubKey sub = master.buildNewSubKeyfromKeyPair(AsymmetricKeyPair.generateAsymmetricKeyPair());
//protect with password
master.createLockedPrivateKey("password", "password");
sub.createLockedPrivateKey("password", "password");
//add to keystore
ksEmployee.addKey(master);
ksEmployee.addKey(sub);
ksEmployee.setSigningKey(master);
//and upload to server
master.uploadToKeyServer(client);
sub.uploadToKeyServer(client);
ksEmployee.toFile(ksEmployee.getFile());
}
//head of departments keystore
KeyApprovingStore ksHead = null;
if (fHeadKeyStore.exists()) {
ksHead = KeyApprovingStore.fromFile(fHeadKeyStore, messageHandler);
} else {
//generate keys
ksHead = KeyApprovingStore.createNewKeyApprovingStore(fHeadKeyStore, messageHandler);
MasterKey master = MasterKey.buildNewMasterKeyfromKeyPair(AsymmetricKeyPair.generateAsymmetricKeyPair());
Identity id = Identity.newEmptyIdentity();
id.setEmail("head@fnppl.org");
id.setMnemonic("head of department");
master.addIdentity(id);
SubKey sub = master.buildNewSubKeyfromKeyPair(AsymmetricKeyPair.generateAsymmetricKeyPair());
//protect with password
master.createLockedPrivateKey("password", "password");
sub.createLockedPrivateKey("password", "password");
//add to keystore
ksHead.addKey(master);
ksHead.addKey(sub);
ksHead.setSigningKey(master);
//and upload to server
master.uploadToKeyServer(client);
sub.uploadToKeyServer(client);
ksHead.toFile(ksHead.getFile());
}
//
MasterKey masterHead = ksHead.getAllMasterKeys().get(0);
SubKey contractKey = masterHead.getSubKeys().get(0);
MasterKey masterEmployee = ksEmployee.getAllMasterKeys().get(0);
SubKey subEmployee = masterEmployee.getSubKeys().get(0);
masterHead.unlockPrivateKey("password");
contractKey.unlockPrivateKey("password");
masterEmployee.unlockPrivateKey("password");
subEmployee.unlockPrivateKey("password");
//output
System.out.println("MasterKey Head :: "+masterHead.getKeyID());
System.out.println("Sub Contract :: "+contractKey.getKeyID());
System.out.println("MasterKey Employee :: "+masterEmployee.getKeyID());
System.out.println("SubKey Employee :: "+subEmployee.getKeyID());
System.out.println("KeyServer :: "+keyserver.getKnownKeys().get(0).getKeyID());
//build approval from contractKey to subEmployee Key
//check if approval already exits
boolean approval = false;
Vector<KeyLog> logs = client.requestKeyLogs(masterEmployee.getKeyID(),null);
for (KeyLog kl : logs) {
if (kl.getAction().equals(KeyLogAction.APPROVAL) && kl.getKeyIDFrom().equals(contractKey.getKeyID())) {
approval = true;
}
}
//System.out.println("approval: "+approval);
if (!approval) {
//no approval -> build it!
KeyLogAction kl = KeyLogAction.buildKeyLogAction(KeyLogAction.APPROVAL, contractKey, masterEmployee.getKeyID(), masterEmployee.getCurrentIdentity(),null);
// Result ok = kl.verifySignature();
// if (ok.succeeded) {
// System.out.println("ok");
// } else {
// System.out.println("error");
// }
//
// Document.buildDocument(kl.toElement(true)).output(System.out);
// KeyLogAction kl2 = KeyLogAction.fromElement(kl.toElement(false));
// Document.buildDocument(kl2.toElement(true)).output(System.out);
// ok = kl2.verifySignature();
// if (ok.succeeded) {
// System.out.println("ok");
// } else {
// System.out.println("error");
// }
// if (1==1) return;
Result upload = kl.uploadToKeyServer(client, masterEmployee);
if (upload.succeeded) {
System.out.println("Generation of keylog on keyserver successful");
} else {
System.out.println("ERROR generating keylog on keyserver :: "+upload.errorMessage);
}
}
//contract partner wants to verify a message signed by the subEmployee Key
//chain of trust:: contract partner -> contractKey -> masterEmployee -> subEmployee
KeyVerificator partnerKeyverificator = new KeyVerificator();
partnerKeyverificator.addKeyServer(keyserver);
partnerKeyverificator.addKeyRating(keyserver.getKnownKeys().get(0), TrustRatingOfKey.RATING_MARGINAL);
partnerKeyverificator.addKeyRating(contractKey, TrustRatingOfKey.RATING_COMPLETE);
Result verifySubEmployeeKey = partnerKeyverificator.verifyKey(subEmployee, System.currentTimeMillis());
if (verifySubEmployeeKey.succeeded) {
System.out.println("VERIFICATION of subEmployee Key SUCCESSFUL!");
} else {
System.out.println("WARNING: subEmployee Key could not be verified!\n"+verifySubEmployeeKey.errorMessage);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void testGenerateMasterKeyPair() {
try {
String mantraname = "masterkeypassword";
String password = "password";
String initv = "00112233445566778899AABBCCDDEEFF";
SymmetricKey sk = SymmetricKey.getKeyFromPass(password.toCharArray(), SecurityHelper.HexDecoder.decode(initv));
Vector<Identity> ids = new Vector<Identity>();
Element id = new Element("identity");
id.addContent("email", "test@fnppl.org");
ids.add(Identity.fromElement(id));
Element ekp = generateMasterKeyPair(ids, mantraname, sk);
Document.buildDocument(ekp).output(System.out);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static Element generateMasterKeyPair(Vector<Identity> ids, String mantraname, SymmetricKey sk) throws Exception {
AsymmetricKeyPair kp = AsymmetricKeyPair.generateAsymmetricKeyPair();
//build structure
Element ekp = new Element("keypair");
//id part
Element eids = new Element("identities");
for (int i=0;i<ids.size();i++) {
eids.addContent(ids.get(0).toElement(true));
}
ekp.addContent(eids);
//key part
//keyid
ekp.addContent("sha1fingerprint", kp.getKeyID());
ekp.addContent("authoritativekeyserver","keys.fnppl.org");
//datapath
Element edp = new Element("datapath");
Element es1 = new Element("step1");
es1.addContent("datasource", "keys.fnppl.org");
es1.addContent("datainsertdatetime","2011-02-21 00:00:00 GMT+00:00");
Element es2 = new Element("step2");
es2.addContent("datasource", "keys.fnppl.org");
es2.addContent("datainsertdatetime","2011-02-21 00:00:00 GMT+00:00");
edp.addContent(es1);
edp.addContent(es2);
ekp.addContent(edp);
//
ekp.addContent("usage","ONLYSIGN");
ekp.addContent("level","MASTER");
ekp.addContent("parentkeyid","N.A.");
ekp.addContent("algo","RSA");
ekp.addContent("bits","3072");
ekp.addContent("modulus",kp.getPublicModulusAsHex());
//public key
Element epk = new Element("pubkey");
epk.addContent("exponent", kp.getPublicExponentAsHex());
ekp.addContent(epk);
//private key
Element esk = new Element("privkey");
Element eexp = new Element("exponent");
Element eloc = new Element("locked");
eloc.addContent("mantraname",mantraname);
eloc.addContent("algo","AES@256");
eloc.addContent("initvector",SecurityHelper.HexDecoder.encode(sk.getInitVector(),'\0',-1));
eloc.addContent("padding","CBC/PKCS#7");
eloc.addContent("bytes",SecurityHelper.HexDecoder.encode(kp.getEncrytedPrivateKey(sk),'\0',-1));
eexp.addContent(eloc);
esk.addContent(eexp);
ekp.addContent(esk);
//gpgkeyserverid
ekp.addContent("gpgkeyserverid","");
return ekp;
}
public static void testGeneratePublicKey() {
try {
String mod = "00B1C4337FE2E77E251D86334B1578C0BCA46AEB9CFE1BE7001ADA8E4C2C8BC2EE557296C46EC3D0470A6DEEC09634A424243B576F3DCA41E372F9AC2FEAA0B668F2AA000CCDCC0396BE4517B2F4B179FCCB7ACB9B3AF027DDAC3466AE80D70BEFBBC0C97E9E4AAF7D184DFE183F74BC9FFA5A5F85149B5A9808C7E12EFDEF42C4A936661F06BA15844DD0BCAF3C0CB8E04949263660A1E71DC1B4A0056519A6E662CEAB25F1B42DA537C21AD6584C2BF72092A0EC57A5C7E9A6458CC8BC06102B5902D90BD86850DA411DB004D66399F5B362EEA0DD5178AC89423FA60E63405290536067AF3EBF9F26E1DFE66E11B23209B62E062ED7F177B6F41CC97F1D6517F2542F40660ABC8D17D7B99778997013D69837FE410B137A283B461D6323F5042A49A59CFEF343B4829B751495400151514CD77ADCB9011F0054D6DF5E6B073EE2A96ECCDDE9029F18DA6C6361DA0147DD7FA59A44B7C87B1A82BDFEFD0DD8E6FCEB696E4883E27EDE204B669887B37D6C927071CFAE555BD235A3E19165C9B7";
String exp = "010001";
PublicKey k = new PublicKey(
new BigInteger(SecurityHelper.HexDecoder.decode(mod)),
new BigInteger(SecurityHelper.HexDecoder.decode(exp)));
String parentkeyid = "BEEE542006AF8301096BF0305AB4632E9982AA94@keys.fnppl.org";
Element epk = generateGeneratePublicKeyElement(k, parentkeyid);
Document.buildDocument(epk).output(System.out);
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static Element generateGeneratePublicKeyElement(PublicKey k, String parentkeyid) throws Exception {
//build structure
Element ekp = new Element("keypair");
//no id part
//key part
//keyid
ekp.addContent("sha1fingerprint", k.getKeyID());
ekp.addContent("authoritativekeyserver","keys.fnppl.org");
//datapath
Element edp = new Element("datapath");
Element es1 = new Element("step1");
es1.addContent("datasource", "keys.fnppl.org");
es1.addContent("datainsertdatetime","2011-02-21 00:00:00 GMT+00:00");
Element es2 = new Element("step2");
es2.addContent("datasource", "keys.fnppl.org");
es2.addContent("datainsertdatetime","2011-02-21 00:00:00 GMT+00:00");
edp.addContent(es1);
edp.addContent(es2);
ekp.addContent(edp);
//
ekp.addContent("usage","ONLYSIGN");
ekp.addContent("level","SUB");
ekp.addContent("parentkeyid",parentkeyid);
ekp.addContent("algo","RSA");
ekp.addContent("bits","3072");
ekp.addContent("modulus",k.getModulusAsHex());
//public key
Element epk = new Element("pubkey");
epk.addContent("exponent", k.getPublicExponentAsHex());
ekp.addContent(epk);
//no private key
//gpgkeyserverid
ekp.addContent("gpgkeyserverid","");
return ekp;
}
}