/* * Freeplane - mind map editor * Copyright (C) 2008 Joerg Mueller, Daniel Polansky, Christian Foltin, Dimitry Polivaev * * This file author is Christian Foltin * It is modified by Dimitry Polivaev in 2008. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.freeplane.plugin.script; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.PrivateKey; import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.util.Collection; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.freeplane.core.resources.ResourceController; import org.freeplane.core.ui.components.EnterPasswordDialog; import org.freeplane.core.ui.components.UITools; import org.freeplane.core.util.LogUtils; import org.freeplane.features.encrypt.DesEncrypter; import org.freeplane.features.mode.Controller; /** * @author foltin */ class SignedScriptHandler { public static class ScriptContents { private static Pattern sSignWithKeyPattern = null; public String mKeyName; public String mScript; public String mSignature; public ScriptContents() { if (ScriptContents.sSignWithKeyPattern == null) { ScriptContents.sSignWithKeyPattern = Pattern.compile(SignedScriptHandler.SIGN_PREFIX_REGEXP); } } public ScriptContents(final String pScript) { this(); final int indexOfSignaturePrefix = pScript.lastIndexOf(SignedScriptHandler.SIGN_PREFIX); final int indexOfSignature = indexOfSignaturePrefix + SignedScriptHandler.SIGN_PREFIX.length(); if (indexOfSignaturePrefix > 0 && pScript.length() > indexOfSignature) { mSignature = pScript.substring(indexOfSignature); mScript = pScript.substring(0, indexOfSignaturePrefix); mKeyName = null; } else { final Matcher matcher = ScriptContents.sSignWithKeyPattern.matcher(pScript); if (matcher.find()) { mScript = pScript.substring(0, matcher.start()); mKeyName = matcher.group(1); mSignature = matcher.group(2); } else { mSignature = null; mScript = pScript; mKeyName = null; } } } @Override public String toString() { String prefix; if (mKeyName != null) { prefix = "//SIGN(" + mKeyName + "):"; } else { prefix = SignedScriptHandler.SIGN_PREFIX; } return mScript + prefix + mSignature + "\n"; } } public static final String FREEPLANE_SCRIPT_KEY_NAME = "FreeplaneScriptKey"; private static KeyStore mKeyStore = null; private static final String SIGN_PREFIX = "//SIGN:"; /** This is for / /SIGN(keyname):signature */ private static final String SIGN_PREFIX_REGEXP = "//SIGN\\((.*?)\\):(.*)"; public SignedScriptHandler() { } private void initializeKeystore(final char[] pPassword) { if (SignedScriptHandler.mKeyStore != null) { return; } java.io.FileInputStream fis = null; try { SignedScriptHandler.mKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()); fis = new java.io.FileInputStream(System.getProperty("user.home") + File.separator + ".keystore"); SignedScriptHandler.mKeyStore.load(fis, pPassword); } catch (final FileNotFoundException e) { LogUtils.warn(e); } catch (final Exception e) { LogUtils.severe(e); } finally { if (fis != null) { try { fis.close(); } catch (final IOException e) { LogUtils.severe(e); } } } } public boolean isScriptSigned(final String pScript, final OutputStream pOutStream) { final ScriptContents content = new ScriptContents(pScript); if (content.mSignature != null) { try { final Signature instanceVerify = Signature.getInstance("SHA1withDSA"); if (content.mKeyName == null) { /** * This is the Freeplane public key. keytool -v -rfc * -exportcert -alias freeplanescriptkey */ final String cer = "-----BEGIN CERTIFICATE-----\n" + "MIIDKDCCAuWgAwIBAgIESAY2ADALBgcqhkjOOAQDBQAwdzELMAkGA1UEBhMCREUxCzAJBgNVBAgT" + "AkRFMRMwEQYDVQQHEwpPcGVuU291cmNlMRgwFgYDVQQKEw9zb3VyY2Vmb3JnZS5uZXQxETAPBgNV" + "BAsTCEZyZWVNaW5kMRkwFwYDVQQDExBDaHJpc3RpYW4gRm9sdGluMB4XDTA4MDQxNjE3MjMxMloX" + "DTA4MDcxNTE3MjMxMlowdzELMAkGA1UEBhMCREUxCzAJBgNVBAgTAkRFMRMwEQYDVQQHEwpPcGVu" + "U291cmNlMRgwFgYDVQQKEw9zb3VyY2Vmb3JnZS5uZXQxETAPBgNVBAsTCEZyZWVNaW5kMRkwFwYD" + "VQQDExBDaHJpc3RpYW4gRm9sdGluMIIBtzCCASwGByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9K" + "nC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVCl" + "pJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3R" + "SAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdM" + "Cz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/" + "C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6Ae1UlZAFMO/7PSSoDgYQAAoGAZm5z5EZX" + "Vhtye5jY3X9w24DJ3yNJbNl2tfkOBIc0KfgyxONTSJKtUpmLI3btUxy3pQf/T8BShlY3PAC0fp3M" + "eDG8WRq1wM3luLd1V9SS8EG6tPJBZ3mciCUymTT7n9CZNzATIpqNIXHSD/wljRABedUi8PMg4KbV" + "Pnhu6Y6b1uAwCwYHKoZIzjgEAwUAAzAAMC0CFQCFHGwe+HHOvY0MmKYHbiq7fRxMGwIUC0voAGYU" + "u6vgVFqdLI5F96JLTqk=" + "\n-----END CERTIFICATE-----\n"; final CertificateFactory cf = CertificateFactory.getInstance("X.509"); final Collection<? extends Certificate> c = cf.generateCertificates(new ByteArrayInputStream(cer .getBytes())); if (c.isEmpty()) throw new IllegalArgumentException("Internal certificate wrong."); for (Certificate cert : c) { instanceVerify.initVerify(cert); } } else { initializeKeystore(null); instanceVerify.initVerify(SignedScriptHandler.mKeyStore.getCertificate(content.mKeyName)); } instanceVerify.update(content.mScript.getBytes()); final boolean verify = instanceVerify.verify(DesEncrypter.fromBase64(content.mSignature)); return verify; } catch (final Exception e) { LogUtils.severe(e); try { pOutStream.write(e.toString().getBytes()); pOutStream.write("\n".getBytes()); } catch (final Exception e1) { LogUtils.severe(e1); } } } return false; } public String signScript(final String pScript) { final ScriptContents content = new ScriptContents(pScript); final EnterPasswordDialog pwdDialog = new EnterPasswordDialog(Controller.getCurrentController().getViewController().getJFrame(), false); pwdDialog.setModal(true); pwdDialog.setVisible(true); if (pwdDialog.getResult() == EnterPasswordDialog.CANCEL) { return content.mScript; } final char[] password = pwdDialog.getPassword().toString().toCharArray(); initializeKeystore(password); try { final Signature instance = Signature.getInstance("SHA1withDSA"); String keyName = SignedScriptHandler.FREEPLANE_SCRIPT_KEY_NAME; final String propertyKeyName = ResourceController.getResourceController().getProperty( ScriptingPermissions.RESOURCES_SCRIPT_USER_KEY_NAME_FOR_SIGNING); if (content.mKeyName != null) { keyName = content.mKeyName; } else if (propertyKeyName != null && propertyKeyName.length() > 0) { content.mKeyName = propertyKeyName; keyName = content.mKeyName; } instance.initSign((PrivateKey) SignedScriptHandler.mKeyStore.getKey(keyName, password)); instance.update(content.mScript.getBytes()); final byte[] signature = instance.sign(); content.mSignature = DesEncrypter.toBase64(signature); return content.toString(); } catch (final Exception e) { if(! (e instanceof KeyStoreException)) LogUtils.severe(e); UITools.errorMessage(e.getLocalizedMessage()); } return content.mScript; } }