All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zaproxy.zap.extension.dynssl.DynamicSSLPanel Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.15.0
Show newest version
/*
 * Zed Attack Proxy (ZAP) and its related class files.
 *
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 *
 * Copyright 2011 [email protected]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zaproxy.zap.extension.dynssl;

import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Locale;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
import org.bouncycastle.util.io.pem.PemWriter;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.model.OptionsParam;
import org.parosproxy.paros.security.SslCertificateService;
import org.parosproxy.paros.view.AbstractParamPanel;
import org.zaproxy.zap.utils.FontUtils;
import org.zaproxy.zap.utils.ZapTextArea;
import org.zaproxy.zap.utils.ZapXmlConfiguration;
import org.zaproxy.zap.view.widgets.WritableFileChooser;

public class DynamicSSLPanel extends AbstractParamPanel {

    private static final long serialVersionUID = 1L;
    private static final int MIN_CERT_LENGTH = 10;

    private static final String OWASP_ZAP_ROOT_CA_NAME = "owasp_zap_root_ca";
    private static final String OWASP_ZAP_ROOT_CA_FILE_EXT = ".cer";
    private static final String OWASP_ZAP_ROOT_CA_FILENAME =
            OWASP_ZAP_ROOT_CA_NAME + OWASP_ZAP_ROOT_CA_FILE_EXT;

    private static final String CONFIGURATION_FILENAME = Constant.FILE_CONFIG_NAME;

    private ZapTextArea txt_PubCert;
    private JButton bt_view;
    private JButton bt_save;

    private KeyStore rootca;
    private ExtensionDynSSL extension;

    private static final Logger logger = Logger.getLogger(DynamicSSLPanel.class);

    /** Create the panel. */
    public DynamicSSLPanel(ExtensionDynSSL extension) {
        super();
        this.extension = extension;

        setName(Constant.messages.getString("dynssl.options.name"));
        setLayout(new BorderLayout(0, 0));

        final JPanel panel = new JPanel();
        panel.setBorder(new EmptyBorder(2, 2, 2, 2));
        add(panel);

        final JLabel lbl_Cert = new JLabel(Constant.messages.getString("dynssl.label.rootca"));

        txt_PubCert = new ZapTextArea();
        txt_PubCert.setFont(FontUtils.getFont("Monospaced"));
        txt_PubCert.setEditable(false);
        txt_PubCert
                .getDocument()
                .addDocumentListener(
                        new DocumentListener() {
                            @Override
                            public void removeUpdate(DocumentEvent e) {
                                checkAndEnableButtons();
                            }

                            @Override
                            public void insertUpdate(DocumentEvent e) {
                                checkAndEnableButtons();
                            }

                            @Override
                            public void changedUpdate(DocumentEvent e) {
                                checkAndEnableButtons();
                            }

                            private void checkAndEnableButtons() {
                                checkAndEnableViewButton();
                                checkAndEnableSaveButton();
                            }
                        });

        final JScrollPane pubCertScrollPane = new JScrollPane(txt_PubCert);

        final JButton bt_generate =
                new JButton(Constant.messages.getString("dynssl.button.generate"));
        bt_generate.addActionListener(
                new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        doGenerate();
                    }
                });
        bt_generate.setIcon(
                new ImageIcon(DynamicSSLPanel.class.getResource("/resource/icon/16/041.png")));

        bt_save = new JButton(Constant.messages.getString("menu.file.save"));
        checkAndEnableSaveButton();
        bt_save.addActionListener(
                new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        doSave();
                    }
                });
        bt_save.setIcon(
                new ImageIcon(DynamicSSLPanel.class.getResource("/resource/icon/16/096.png")));

        bt_view = new JButton(Constant.messages.getString("menu.view"));
        checkAndEnableViewButton();
        bt_view.addActionListener(
                new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        doView();
                    }
                });
        bt_view.setIcon(
                new ImageIcon(DynamicSSLPanel.class.getResource("/resource/icon/16/049.png")));

        final JButton bt_import = new JButton(Constant.messages.getString("dynssl.button.import"));
        bt_import.addActionListener(
                new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        doImport();
                    }
                });
        bt_import.setIcon(
                new ImageIcon(DynamicSSLPanel.class.getResource("/resource/icon/16/047.png")));

        final GroupLayout gl_panel = new GroupLayout(panel);
        gl_panel.setHorizontalGroup(
                gl_panel.createParallelGroup(Alignment.LEADING)
                        .addGroup(
                                gl_panel.createSequentialGroup()
                                        .addContainerGap()
                                        .addGroup(
                                                gl_panel.createParallelGroup(
                                                                Alignment.LEADING, false)
                                                        .addGroup(
                                                                gl_panel.createSequentialGroup()
                                                                        .addGroup(
                                                                                gl_panel.createParallelGroup(
                                                                                                Alignment
                                                                                                        .LEADING,
                                                                                                false)
                                                                                        .addComponent(
                                                                                                lbl_Cert,
                                                                                                GroupLayout
                                                                                                        .PREFERRED_SIZE,
                                                                                                115,
                                                                                                GroupLayout
                                                                                                        .PREFERRED_SIZE)
                                                                                        .addGroup(
                                                                                                gl_panel.createSequentialGroup()
                                                                                                        .addPreferredGap(
                                                                                                                ComponentPlacement
                                                                                                                        .RELATED)
                                                                                                        .addComponent(
                                                                                                                bt_generate)))
                                                                        .addGap(6))
                                                        .addGroup(
                                                                gl_panel.createSequentialGroup()
                                                                        .addComponent(bt_import)
                                                                        .addPreferredGap(
                                                                                ComponentPlacement
                                                                                        .RELATED)))
                                        .addGroup(
                                                gl_panel.createParallelGroup(Alignment.LEADING)
                                                        .addGroup(
                                                                gl_panel.createSequentialGroup()
                                                                        .addComponent(bt_view)
                                                                        .addPreferredGap(
                                                                                ComponentPlacement
                                                                                        .RELATED)
                                                                        .addComponent(bt_save))
                                                        .addComponent(
                                                                pubCertScrollPane,
                                                                GroupLayout.DEFAULT_SIZE,
                                                                369,
                                                                Short.MAX_VALUE))
                                        .addContainerGap()));
        gl_panel.setVerticalGroup(
                gl_panel.createParallelGroup(Alignment.LEADING)
                        .addGroup(
                                gl_panel.createSequentialGroup()
                                        .addGap(10)
                                        .addGroup(
                                                gl_panel.createParallelGroup(Alignment.BASELINE)
                                                        .addGroup(
                                                                gl_panel.createSequentialGroup()
                                                                        .addComponent(lbl_Cert)
                                                                        .addGap(10)
                                                                        .addComponent(
                                                                                bt_generate,
                                                                                GroupLayout
                                                                                        .PREFERRED_SIZE,
                                                                                25,
                                                                                GroupLayout
                                                                                        .PREFERRED_SIZE)
                                                                        .addPreferredGap(
                                                                                ComponentPlacement
                                                                                        .RELATED)
                                                                        .addComponent(
                                                                                bt_import,
                                                                                GroupLayout
                                                                                        .PREFERRED_SIZE,
                                                                                25,
                                                                                GroupLayout
                                                                                        .PREFERRED_SIZE))
                                                        .addComponent(
                                                                pubCertScrollPane,
                                                                GroupLayout.PREFERRED_SIZE,
                                                                400,
                                                                GroupLayout.PREFERRED_SIZE))
                                        .addPreferredGap(ComponentPlacement.RELATED)
                                        .addGroup(
                                                gl_panel.createParallelGroup(Alignment.BASELINE)
                                                        .addComponent(
                                                                bt_save,
                                                                GroupLayout.PREFERRED_SIZE,
                                                                25,
                                                                GroupLayout.PREFERRED_SIZE)
                                                        .addComponent(
                                                                bt_view,
                                                                GroupLayout.PREFERRED_SIZE,
                                                                25,
                                                                GroupLayout.PREFERRED_SIZE))
                                        .addGap(0, 29, Short.MAX_VALUE)));
        panel.setLayout(gl_panel);
    }

    @Override
    public void initParam(Object obj) {
        final OptionsParam options = (OptionsParam) obj;
        final DynSSLParam param = options.getParamSet(DynSSLParam.class);
        setRootca(param.getRootca());
    }

    @Override
    public void saveParam(Object obj) throws Exception {
        final OptionsParam options = (OptionsParam) obj;
        final DynSSLParam param = options.getParamSet(DynSSLParam.class);
        param.setRootca(rootca);
        extension.setRootCa(rootca);
    }

    @Override
    public String getHelpIndex() {
        return "ui.dialogs.options.dynsslcert";
    }

    private void setRootca(KeyStore rootca) {
        this.rootca = rootca;
        final StringWriter sw = new StringWriter();
        if (rootca != null) {
            try {
                final Certificate cert =
                        rootca.getCertificate(SslCertificateService.ZAPROXY_JKS_ALIAS);
                try (final PemWriter pw = new PemWriter(sw)) {
                    pw.writeObject(new JcaMiscPEMGenerator(cert));
                    pw.flush();
                }
            } catch (final Exception e) {
                logger.error(
                        "Error while extracting public part from generated Root CA certificate.",
                        e);
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Certificate defined.\n" + sw.toString());
        }
        txt_PubCert.setText(sw.toString());
    }

    /**
     * Viewing is only allowed, if (a) when java.Desktop#open() works (b) there's a certificate
     * (text) in the text area
     */
    private void checkAndEnableViewButton() {
        boolean enabled = true;
        enabled &= Desktop.isDesktopSupported();
        enabled &= txt_PubCert.getDocument().getLength() > MIN_CERT_LENGTH;
        bt_view.setEnabled(enabled);
    }

    /** Saving is only allowed, if (a) there's a certificate (text) in the text area */
    private void checkAndEnableSaveButton() {
        boolean enabled = true;
        enabled &= txt_PubCert.getDocument().getLength() > MIN_CERT_LENGTH;
        bt_save.setEnabled(enabled);
    }

    /** Import Root CA certificate from other ZAP configuration files. */
    private void doImport() {
        if (checkExistingCertificate()) {
            // prevent overwriting
            return;
        }
        final JFileChooser fc = new JFileChooser(System.getProperty("user.home"));
        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        fc.setMultiSelectionEnabled(false);
        fc.setSelectedFile(new File(CONFIGURATION_FILENAME));
        fc.setFileFilter(
                new FileFilter() {
                    @Override
                    public String getDescription() {
                        // config.xml or *.pem files
                        return Constant.messages.getString("dynssl.filter.file");
                    }

                    @Override
                    public boolean accept(File f) {
                        String lcFileName = f.getName().toLowerCase(Locale.ROOT);
                        return lcFileName.endsWith(CONFIGURATION_FILENAME)
                                || lcFileName.endsWith("pem")
                                || f.isDirectory();
                    }
                });
        final int result = fc.showOpenDialog(this);
        final File f = fc.getSelectedFile();
        if (result == JFileChooser.APPROVE_OPTION && f.exists()) {
            if (logger.isInfoEnabled()) {
                logger.info("Loading Root CA certificate from " + f);
            }
            KeyStore ks = null;
            if (f.getName().toLowerCase().endsWith("pem")) {
                ks = convertPemFileToKeyStore(f.toPath());
            } else {
                try {
                    final ZapXmlConfiguration conf = new ZapXmlConfiguration(f);
                    final String rootcastr = conf.getString(DynSSLParam.PARAM_ROOT_CA);
                    if (rootcastr == null || rootcastr.isEmpty()) {
                        JOptionPane.showMessageDialog(
                                this,
                                Constant.messages.getString("dynssl.message.nocertinconf"),
                                Constant.messages.getString("dynssl.message.nocertinconf.title"),
                                JOptionPane.ERROR_MESSAGE);
                        return;
                    }

                    ks = SslCertificateUtils.string2Keystore(rootcastr);
                } catch (final Exception e) {
                    logger.error("Error importing Root CA cert from config file:", e);
                    JOptionPane.showMessageDialog(
                            this,
                            Constant.messages.getString("dynssl.message1.filecouldntloaded"),
                            Constant.messages.getString("dynssl.message1.title"),
                            JOptionPane.ERROR_MESSAGE);
                    return;
                }
            }
            if (ks != null) {
                setRootca(ks);
            }
        }
    }

    /**
     * Converts the given {@code .pem} file into a {@link KeyStore}.
     *
     * @param pemFile the {@code .pem} file that contains the certificate and the private key.
     * @return the {@code KeyStore} with the certificate, or {@code null} if the conversion failed.
     */
    private KeyStore convertPemFileToKeyStore(Path pemFile) {
        String pem;
        try {
            pem = FileUtils.readFileToString(pemFile.toFile(), StandardCharsets.US_ASCII);
        } catch (IOException e) {
            logger.warn("Failed to read .pem file:", e);
            JOptionPane.showMessageDialog(
                    this,
                    Constant.messages.getString(
                            "dynssl.importpem.failedreadfile", e.getLocalizedMessage()),
                    Constant.messages.getString("dynssl.importpem.failed.title"),
                    JOptionPane.ERROR_MESSAGE);
            return null;
        }

        byte[] cert;
        try {
            cert = SslCertificateUtils.extractCertificate(pem);
            if (cert.length == 0) {
                JOptionPane.showMessageDialog(
                        this,
                        Constant.messages.getString(
                                "dynssl.importpem.nocertsection",
                                SslCertificateUtils.BEGIN_CERTIFICATE_TOKEN,
                                SslCertificateUtils.END_CERTIFICATE_TOKEN),
                        Constant.messages.getString("dynssl.importpem.failed.title"),
                        JOptionPane.ERROR_MESSAGE);
                return null;
            }
        } catch (IllegalArgumentException e) {
            logger.warn("Failed to base64 decode the certificate from .pem file:", e);
            JOptionPane.showMessageDialog(
                    this,
                    Constant.messages.getString("dynssl.importpem.certnobase64"),
                    Constant.messages.getString("dynssl.importpem.failed.title"),
                    JOptionPane.ERROR_MESSAGE);
            return null;
        }

        byte[] key;
        try {
            key = SslCertificateUtils.extractPrivateKey(pem);
            if (key.length == 0) {
                JOptionPane.showMessageDialog(
                        this,
                        Constant.messages.getString(
                                "dynssl.importpem.noprivkeysection",
                                SslCertificateUtils.BEGIN_PRIVATE_KEY_TOKEN,
                                SslCertificateUtils.END_PRIVATE_KEY_TOKEN),
                        Constant.messages.getString("dynssl.importpem.failed.title"),
                        JOptionPane.ERROR_MESSAGE);
                return null;
            }
        } catch (IllegalArgumentException e) {
            logger.warn("Failed to base64 decode the private key from .pem file:", e);
            JOptionPane.showMessageDialog(
                    this,
                    Constant.messages.getString("dynssl.importpem.privkeynobase64"),
                    Constant.messages.getString("dynssl.importpem.failed.title"),
                    JOptionPane.ERROR_MESSAGE);
            return null;
        }

        try {
            return SslCertificateUtils.pem2KeyStore(cert, key);
        } catch (Exception e) {
            logger.error("Error creating KeyStore for Root CA cert from .pem file:", e);
            JOptionPane.showMessageDialog(
                    this,
                    Constant.messages.getString(
                            "dynssl.importpem.failedkeystore", e.getLocalizedMessage()),
                    Constant.messages.getString("dynssl.importpem.failed.title"),
                    JOptionPane.ERROR_MESSAGE);
            return null;
        }
    }

    /** Saving Root CA certificate to disk. */
    private void doSave() {
        if (txt_PubCert.getDocument().getLength() < MIN_CERT_LENGTH) {
            logger.error("Illegal state! There seems to be no certificate available.");
            bt_save.setEnabled(false);
        }
        final JFileChooser fc = new WritableFileChooser(new File(System.getProperty("user.home")));
        fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
        fc.setMultiSelectionEnabled(false);
        fc.setSelectedFile(new File(OWASP_ZAP_ROOT_CA_FILENAME));
        if (fc.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
            final File f = fc.getSelectedFile();
            if (logger.isInfoEnabled()) {
                logger.info("Saving Root CA certificate to " + f);
            }
            try {
                writePubCertificateToFile(f);
            } catch (final Exception e) {
                logger.error("Error while writing certificate data to file " + f, e);
            }
        }
    }

    private void writePubCertificateToFile(File file) throws IOException {
        try (BufferedWriter bw =
                Files.newBufferedWriter(file.toPath(), StandardCharsets.US_ASCII)) {
            bw.write(txt_PubCert.getText());
        }
    }

    /** Generates a new Root CA certificate ... */
    private void doGenerate() {
        if (checkExistingCertificate()) {
            // prevent overwriting
            return;
        }
        try {
            final KeyStore newrootca = SslCertificateUtils.createRootCA();
            setRootca(newrootca);
        } catch (final Exception e) {
            logger.error("Error while generating Root CA certificate", e);
        }
    }

    /**
     * Check if certificate already exists. It will ask the user to overwrite.
     *
     * @return True, if certificate exists OR it should be overwritten.
     */
    private boolean checkExistingCertificate() {
        boolean alreadyexists = txt_PubCert.getDocument().getLength() > MIN_CERT_LENGTH;
        if (alreadyexists) {
            final int result =
                    JOptionPane.showConfirmDialog(
                            this,
                            Constant.messages.getString("dynssl.message2.caalreadyexists")
                                    + "\n"
                                    + Constant.messages.getString("dynssl.message2.willreplace")
                                    + "\n\n"
                                    + Constant.messages.getString(
                                            "dynssl.message2.wanttooverwrite"),
                            Constant.messages.getString("dynssl.message2.title"),
                            JOptionPane.YES_NO_OPTION);
            alreadyexists = !(result == JOptionPane.YES_OPTION);
        }
        return alreadyexists;
    }

    /**
     * writes the certificate to a temporary file and tells the OS to open it using
     * java.awrt.Desktop#open()
     */
    private void doView() {
        if (txt_PubCert.getDocument().getLength() < MIN_CERT_LENGTH) {
            logger.error("Illegal state! There seems to be no certificate available.");
            bt_view.setEnabled(false);
        }
        boolean written = false;
        File tmpfile = null;
        try {
            tmpfile = File.createTempFile(OWASP_ZAP_ROOT_CA_NAME, OWASP_ZAP_ROOT_CA_FILE_EXT);
            writePubCertificateToFile(tmpfile);
            written = true;
        } catch (final Exception e) {
            logger.error("Error while writing certificate data into temporary file.", e);
        }
        if (tmpfile != null && written) {
            if (Desktop.isDesktopSupported()) {
                try {
                    Desktop.getDesktop().open(tmpfile);
                } catch (final IOException e) {
                    logger.error("Error while telling the Operating System to open " + tmpfile, e);
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy