package org.fnppl.opensdx.security; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.Vector; import org.fnppl.opensdx.xml.Document; import org.fnppl.opensdx.xml.Element; import org.fnppl.opensdx.xml.XMLHelper; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; /* * 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". * */ public class OSDXMessage { private Element content = null; private byte[] sha256localproof = null; private Vector<Signature> signatures = null; private OSDXKey contentEncryptionKey = null; private OSDXMessage() { } public static OSDXMessage buildMessage(Element content) throws Exception { if (content==null) throw new RuntimeException("ERROR: OSDXMessage::empty content"); OSDXMessage m = new OSDXMessage(); m.content = content; m.sha256localproof = SecurityHelper.getSHA256LocalProof(content); m.signatures = null; return m; } public static OSDXMessage buildMessage(Element content, OSDXKey signingkey) throws Exception { OSDXMessage m = buildMessage(content); m.signContent(signingkey); return m; } public static OSDXMessage buildEncryptedMessage(Element content, OSDXKey contentEncryptionKey) throws Exception { if (content==null) throw new RuntimeException("ERROR: OSDXMessage::empty content"); OSDXMessage m = new OSDXMessage(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); Document.buildDocument(content).outputCompact(bout); byte[] encrypted = contentEncryptionKey.encrypt(bout.toByteArray()); Element encryptedContent = new Element("encrypted_content"); encryptedContent.setText(new BASE64Encoder().encode(encrypted)); m.content = encryptedContent; m.sha256localproof = SecurityHelper.getSHA256LocalProof(m.content); m.signatures = null; return m; } public static OSDXMessage buildEncryptedMessage(Element content, OSDXKey contentEncryptionKey, OSDXKey signingkey) throws Exception { OSDXMessage m = buildEncryptedMessage(content, contentEncryptionKey); m.signContent(signingkey); return m; } public static OSDXMessage fromElement(Element osdxMessage) throws Exception { if (!osdxMessage.getName().equals("opensdx_message")) { throw new RuntimeException("ERROR: OSDXMessage::wrong format"); } if (osdxMessage.getChild("content").getChildren().size()==0) { throw new RuntimeException("ERROR: OSDXMessage::no content"); } if (osdxMessage.getChild("content").getChildren().size()>1) { throw new RuntimeException("ERROR: OSDXMessage::wrong content format"); } Element content = XMLHelper.cloneElement(osdxMessage.getChild("content").getChildren().get(0)); OSDXMessage m = buildMessage(content); Element esha256 = osdxMessage.getChild("sha256localproof"); byte[] givenLocalproof = null; if (esha256!=null) { givenLocalproof = SecurityHelper.HexDecoder.decode(esha256.getText()); } if (givenLocalproof==null || !Arrays.equals(m.sha256localproof, givenLocalproof)) { System.out.println("givenLocalproof : "+SecurityHelper.HexDecoder.encode(givenLocalproof, '\0', -1)); System.out.println("calc localproof: "+SecurityHelper.HexDecoder.encode(m.sha256localproof, '\0', -1)); System.out.println("calc2 localproof: "+SecurityHelper.HexDecoder.encode(SecurityHelper.getSHA256LocalProof(content), '\0', -1)); Document.buildDocument(m.toElement()).output(System.out); throw new RuntimeException("ERROR: OSDXMessage::wrong or missing sha256localproof"); } Element eSignatures = osdxMessage.getChild("signatures"); m.signatures = new Vector<Signature>(); if (eSignatures!=null) { for (Element eSignature : eSignatures.getChildren("signature")) { m.signatures.add(Signature.fromElement(eSignature)); } } return m; } public Element getContent() { return content; } public Element getDecryptedContent(OSDXKey decryptionKey) { try { if (!content.getName().equals("encrypted_content")) { return content; } byte[] encrypt = new BASE64Decoder().decodeBuffer(content.getText()); byte[] decrypt = decryptionKey.decrypt(encrypt); String s = new String(decrypt, "UTF-8"); //System.out.println(s); return Document.fromString(s).getRootElement(); } catch (Exception e) { e.printStackTrace(); } return null; } public void signContent(OSDXKey signingkey) throws Exception { signatures = new Vector<Signature>(); Signature signature = Signature.createSignatureFromLocalProof(sha256localproof, "signature of sha256localproof", signingkey); signatures.add(signature); } public void signLastSignature(OSDXKey signingkey, String dataname) throws Exception { if (signatures==null || signatures.size()<1) { throw new RuntimeException("ERROR: OSDXMessage::missing signature"); } Signature lastSignature = signatures.lastElement(); byte[] lastSignatureByters = lastSignature.getSignatureBytes(); Signature signature = Signature.createSignatureFromLocalProof(lastSignatureByters, dataname, signingkey); signatures.add(signature); } public Result verifySignatures(KeyVerificator keyverificator) throws Exception { return verifySignatures(true, keyverificator); } public Result verifySignaturesWithoutKeyVerification() throws Exception { return verifySignatures(false, null); } private Result verifySignatures(boolean verifyKeys, KeyVerificator keyverificator) throws Exception { // if (1==1) throw new RuntimeException("ERROR: OSDXMessage::verifySignatures not implemented"); Element report = new Element("signatures_verification_report"); boolean ok = true; for (int i=0;i<signatures.size();i++) { Signature signature = signatures.get(i); Result verified = null; //verify internal signature if (i==0) { verified = signature.tryVerificationMD5SHA1SHA256(sha256localproof); } else { verified = signature.tryVerificationMD5SHA1SHA256(signatures.get(i-1).getSignatureBytes()); } Element aReport = verified.report; if (aReport==null) throw new RuntimeException("signature verification DID NOT return a report!"); if (verified.succeeded && verifyKeys) { //verify key from signature verified = keyverificator.verifyKey(signature.getKey(), signature.getSignDatetime()); //TODO add chain of trust to report if (verified.succeeded) { aReport.addContent("key_verification", "OK"); } else { aReport.addContent("key_verification", "FAILED"); } } report.addContent(aReport); if (!verified.succeeded) { ok = false; break; } } if (ok) { return Result.succeeded(report); } else { return Result.error(report); } } public Element toElement() { if (content==null) return null; Element e = new Element("opensdx_message"); Element c = new Element("content"); c.addContent(content); Element s = new Element("signatures"); if (signatures!=null) { for (Signature signature : signatures) { s.addContent(signature.toElement()); } } e.addContent(c); e.addContent("sha256localproof",SecurityHelper.HexDecoder.encode(sha256localproof, ':',-1)); e.addContent(s); return e; } public Vector<Signature> getSignatures() { return signatures; } public byte[] getSha256LocalProof() { return sha256localproof; } }