From 97463e71a5341b1322a3f0bcabf64b275add01c7 Mon Sep 17 00:00:00 2001 From: gm2552 Date: Wed, 22 Jul 2020 14:16:33 -0500 Subject: [PATCH 01/13] Initial commit. Contains the TIM+ cert generator. --- .gitignore | 2 + pom.xml | 283 +++++ .../timplus/tools/certgen/CAPanel.java | 1043 +++++++++++++++++ .../tools/certgen/CertCreateFields.java | 125 ++ .../timplus/tools/certgen/CertGenerator.java | 415 +++++++ .../timplus/tools/certgen/CertLoader.java | 131 +++ .../timplus/tools/certgen/CreatePKCS12.java | 293 +++++ .../tools/certgen/LeafCertGenDialog.java | 276 +++++ .../tools/certgen/TIMPlusCertGenerator.java | 93 ++ src/main/resources/images/cert.png | Bin 0 -> 8554 bytes 10 files changed, 2661 insertions(+) create mode 100644 pom.xml create mode 100644 src/main/java/org/directtruststandards/timplus/tools/certgen/CAPanel.java create mode 100644 src/main/java/org/directtruststandards/timplus/tools/certgen/CertCreateFields.java create mode 100644 src/main/java/org/directtruststandards/timplus/tools/certgen/CertGenerator.java create mode 100644 src/main/java/org/directtruststandards/timplus/tools/certgen/CertLoader.java create mode 100644 src/main/java/org/directtruststandards/timplus/tools/certgen/CreatePKCS12.java create mode 100644 src/main/java/org/directtruststandards/timplus/tools/certgen/LeafCertGenDialog.java create mode 100644 src/main/java/org/directtruststandards/timplus/tools/certgen/TIMPlusCertGenerator.java create mode 100644 src/main/resources/images/cert.png diff --git a/.gitignore b/.gitignore index a1c2a23..7b0c321 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +/.project +/.classpath diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5508f87 --- /dev/null +++ b/pom.xml @@ -0,0 +1,283 @@ + + + org.directtruststandards + 4.0.0 + timplus-tools + TIM+ tools + 1.0.0-SNAPSHOT + TIM+ tools such as a certificate generator. + 2020 + https://github.com/DirectStandards/timplus-tools + + org.springframework.boot + spring-boot-dependencies + 2.3.1.RELEASE + + + + Greg Meyer + GM2552 + gm2552@cerner.com + + owner + + + + + DirectTrust Standards + https://directtruststandards.org + + + 3.0.0 + + + scm:git:https://github.com/DirectStandards/timplus-tools.git + scm:git:https://github.com/DirectStandards/timplus-tools.git + + + + org.springframework.boot + spring-boot-starter-logging + + + org.igniterealtime.smack + smack-core + 4.3.4 + + + org.igniterealtime.smack + smack-extensions + 4.3.4 + + + org.apache.commons + commons-lang3 + + + commons-io + commons-io + 2.7 + + + com.fasterxml.jackson.core + jackson-annotations + + + org.bouncycastle + bcprov-jdk15on + 1.60 + + + org.bouncycastle + bcpkix-jdk15on + 1.60 + + + junit + junit + test + + + + + + org.apache.maven.wagon + wagon-webdav-jackrabbit + 3.1.0 + + + org.apache.maven.wagon + wagon-ssh-external + 3.1.0 + + + org.apache.maven.wagon + wagon-ssh + 3.1.0 + + + + + src/main/resources + + + lib + ${project.basedir}/lib + + + + + src/test/resources + + + lib + ${project.basedir}/lib + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jxr-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + testCompile + + compile + + + + true + true + true + UTF-8 + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-source-plugin + + + + jar + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + true + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + -Xdoclint:none + UTF-8 + UTF-8 + true + true + true + 1.8 + public + + + + + + package + attach-javadocs + + jar + + + + + + + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 2.9 + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + -Xdoclint:none + UTF-8 + UTF-8 + true + true + true + 1.8 + public + + + + + + org.apache.maven.plugins + maven-pmd-plugin + + 1.8 + + + + org.apache.maven.plugins + maven-surefire-report-plugin + + + org.apache.maven.plugins + maven-jxr-plugin + + + org.codehaus.mojo + findbugs-maven-plugin + + Max + ${project.basedir}/src/report/findbugs-exclude.xml + + + + + + + sonatype-snapshot + Sonatype OSS Maven SNAPSHOT Repository + https://oss.sonatype.org/content/repositories/snapshots/ + false + + + sonatype-release + Sonatype OSS Maven Release Repositor + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + false + + + diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/CAPanel.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/CAPanel.java new file mode 100644 index 0000000..a0ed9d4 --- /dev/null +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/CAPanel.java @@ -0,0 +1,1043 @@ +/* +Copyright (c) 2010, NHIN Direct Project +All rights reserved. + +Authors: + Greg Meyer gm2552@cerner.com + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org). +nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.directtruststandards.timplus.tools.certgen; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; + +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.InputStreamReader; +import java.math.BigInteger; +import java.security.PrivateKey; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerNumberModel; +import javax.swing.border.BevelBorder; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.SoftBevelBorder; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.bouncycastle.asn1.x509.CRLNumber; +import org.bouncycastle.asn1.x509.CRLReason; +import org.bouncycastle.asn1.x509.X509Extensions; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.bouncycastle.x509.X509V2CRLGenerator; +import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure; + +/** + * UI component for creating or loading self signed certificates. + * @author Greg Meyer + * + */ +///CLOVER:OFF +class CAPanel extends JPanel +{ + static final long serialVersionUID = -92734291206052662L; + + protected static final int WF_CONTEXT_LOAD_CERTS = 0; + protected static final int WF_CONTEXT_CREATE_CERTS = 1; + protected static final int WF_CONTEXT_CLEAR = 2; + protected static final int WF_CONTEXT_CERT_LOADED = 3; + protected static final int WF_CONTEXT_CERT_CREATED = 4; + + protected JRadioButton createCA; + protected JRadioButton loadCA; + protected JTextField certFile; + protected JTextField certPrivKeyFile; + + protected TextEntryField cnField; + protected TextEntryField countryField; + protected TextEntryField stateField; + protected TextEntryField locField; + protected TextEntryField orgField; + protected TextEntryField emailField; + + protected SpinEntryField expField; + protected DropDownEntry keyStr; + protected PasswordField passField; + + protected FileField certFileField; + protected FileField keyFileField; + + protected JButton loadCert; + protected JButton createCert; + protected JButton clear; + protected JButton genCert; + protected JButton signCSR; + protected JButton createCRL; + protected JCheckBox addToAltSubjects; + protected JCheckBox allowedToSign; + protected JCheckBox keyEnc; + protected JCheckBox digitalSig; + + protected JPanel fieldsPanel; + + protected CertCreateFields currentCert; + + public CAPanel() + { + super(); + + initUI(); + + addActions(); + + setWorkflowContext(WF_CONTEXT_CREATE_CERTS); + } + + protected void initUI() + { + setLayout(new BorderLayout()); + //setBorder(new SoftBevelBorder(BevelBorder.LOWERED)); + setBorder(new CompoundBorder( + new SoftBevelBorder(BevelBorder.LOWERED), new EmptyBorder(5,5,5,5)) ); + + + createCA = new JRadioButton("Create New CA"); + loadCA = new JRadioButton("Load CA"); + ButtonGroup group = new ButtonGroup(); + group.add(createCA); + group.add(loadCA); + createCA.setSelected(true); + + JPanel radioPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + + radioPanel.add(createCA); + radioPanel.add(loadCA); + + fieldsPanel = new JPanel(); + fieldsPanel.setLayout(new GridLayout(3, 3, 10, 10)); + + cnField = new TextEntryField("CN:"); + fieldsPanel.add(cnField); + + countryField= new TextEntryField("Country:"); + fieldsPanel.add(countryField); + + stateField= new TextEntryField("State:"); + fieldsPanel.add(stateField); + + locField= new TextEntryField("Location:"); + fieldsPanel.add(locField); + + orgField= new TextEntryField("Org:"); + fieldsPanel.add(orgField); + + emailField= new TextEntryField("Email:"); + fieldsPanel.add(emailField); + + expField = new SpinEntryField("Experiation (Days):", 365); + fieldsPanel.add(expField); + + keyStr = new DropDownEntry("Key Strength:", new Object[] {2048, 4096}); + fieldsPanel.add(keyStr); + + passField = new PasswordField("Password:"); + fieldsPanel.add(passField); + + JPanel topPanel = new JPanel(new BorderLayout()); + topPanel.add(radioPanel, BorderLayout.NORTH); + topPanel.add(fieldsPanel, BorderLayout.CENTER); + + add(topPanel, BorderLayout.NORTH); + + new FlowLayout(FlowLayout.LEFT); + + certFileField = new FileField("Certificate Authority File:", ""); + keyFileField = new FileField("Private Key File:", ""); + + + JPanel filePanel = new JPanel(new GridLayout(1, 2)); + filePanel.add(certFileField); + filePanel.add(keyFileField); + + loadCert = new JButton("Load"); + loadCert.setVisible(false); + createCert = new JButton("Create"); + clear = new JButton("Clear"); + clear.setVisible(false); + clear = new JButton("Clear"); + genCert = new JButton("Create Leaf Cert"); + genCert.setVisible(false); + signCSR = new JButton("Sign CSR"); + signCSR.setVisible(false); + createCRL = new JButton("Create Empty CRL"); + createCRL.setVisible(false); + + addToAltSubjects = new JCheckBox("Add Email To Alt Subject Names"); + addToAltSubjects.setVisible(false); + addToAltSubjects.setSelected(true); + allowedToSign = new JCheckBox("Allowed To Sign Certificates"); + allowedToSign.setVisible(false); + keyEnc = new JCheckBox("Key Encipherment Use"); + keyEnc.setVisible(false); + keyEnc.setSelected(true); + digitalSig = new JCheckBox("Digital Signature Use"); + digitalSig.setVisible(false); + keyEnc.setSelected(true); + + + JPanel addAltPanel = new JPanel(new GridLayout(2, 2)); + addAltPanel.setPreferredSize(new Dimension(450, addAltPanel.getPreferredSize().height)); + //addAltPanel.add(addToAltSubjects); + addAltPanel.add(allowedToSign); + addAltPanel.add(keyEnc); + addAltPanel.add(digitalSig); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.add(addAltPanel); + buttonPanel.add(loadCert); + buttonPanel.add(createCert); + buttonPanel.add(clear); + buttonPanel.add(genCert); + buttonPanel.add(signCSR); + buttonPanel.add(createCRL); + + JPanel combineAltAndButtonPanel = new JPanel(new BorderLayout()); + //combineAltAndButtonPanel.add(addAltPanel, BorderLayout.WEST); + combineAltAndButtonPanel.add(buttonPanel, BorderLayout.EAST); + + JPanel bottomPannel = new JPanel(new BorderLayout()); + bottomPannel.add(filePanel, BorderLayout.NORTH); + bottomPannel.add(combineAltAndButtonPanel, BorderLayout.SOUTH); + + this.add(bottomPannel, BorderLayout.SOUTH); + } + + private void addActions() + { + loadCert.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + loadCACert(); + } + }); + + createCert.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + createCACert(); + } + }); + + createCA.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + setWorkflowContext(WF_CONTEXT_CREATE_CERTS); + } + }); + + loadCA.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + setWorkflowContext(WF_CONTEXT_LOAD_CERTS); + } + }); + + clear.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + setWorkflowContext(WF_CONTEXT_CLEAR); + } + }); + + genCert.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + createLeaf(); + } + }); + + signCSR.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + signCSR(); + } + }); + + createCRL.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + createCRL(); + } + }); + + } + + + private void setWorkflowContext(int workflowContext) + { + switch (workflowContext) + { + case WF_CONTEXT_CREATE_CERTS: + case WF_CONTEXT_CLEAR: + { + // creating a new CA + cnField.setEnabled(true); + countryField.setEnabled(true); + stateField.setEnabled(true); + locField.setEnabled(true); + orgField.setEnabled(true); + emailField.setEnabled(true); + + expField.setEnabled(true); + keyStr.setEnabled(true); + passField.setEnabled(true); + + certFileField.setEnabled(true); + keyFileField.setEnabled(true); + + createCA.setEnabled(true); + loadCA.setEnabled(true); + + loadCert.setVisible(false); + createCert.setVisible(true); + clear.setVisible(false); + genCert.setVisible(false); + signCSR.setVisible(false); + createCRL.setVisible(false); + addToAltSubjects.setVisible(false); + + if(workflowContext == WF_CONTEXT_CLEAR) + { + cnField.setText(""); + countryField.setText(""); + stateField.setText(""); + locField.setText(""); + orgField.setText(""); + emailField.setText(""); + + expField.setValue(365); + keyStr.setValue("2048"); + passField.clear(); + certFileField.setFile(null); + keyFileField.setFile(null); + + createCA.setSelected(true); + + loadCert.setVisible(false); + createCert.setVisible(true); + clear.setVisible(false); + genCert.setVisible(false); + signCSR.setVisible(false); + createCRL.setVisible(false); + addToAltSubjects.setVisible(false); + + currentCert = null; + + } + break; + } + case WF_CONTEXT_LOAD_CERTS: + { + cnField.setEnabled(false); + countryField.setEnabled(false); + stateField.setEnabled(false); + locField.setEnabled(false); + orgField.setEnabled(false); + emailField.setEnabled(false); + + expField.setEnabled(false); + keyStr.setEnabled(false); + passField.setEnabled(true); + + loadCert.setVisible(true); + createCert.setVisible(false); + clear.setVisible(false); + genCert.setVisible(false); + signCSR.setVisible(false); + createCRL.setVisible(false); + addToAltSubjects.setVisible(false); + + createCA.setEnabled(true); + loadCA.setEnabled(true); + + certFileField.setEnabled(true); + keyFileField.setEnabled(true); + + break; + } + case WF_CONTEXT_CERT_CREATED: + case WF_CONTEXT_CERT_LOADED: + { + cnField.setEnabled(false); + countryField.setEnabled(false); + stateField.setEnabled(false); + locField.setEnabled(false); + orgField.setEnabled(false); + emailField.setEnabled(false); + + expField.setEnabled(false); + keyStr.setEnabled(false); + passField.setEnabled(false); + + createCA.setEnabled(false); + loadCA.setEnabled(false); + + loadCert.setVisible(false); + createCert.setVisible(false); + clear.setVisible(true); + genCert.setVisible(true); + signCSR.setVisible(true); + createCRL.setVisible(true); + + addToAltSubjects.setVisible(false); + + break; + } + + } + } + + private void createLeaf() + { + // create leaf certificates in the sub dialog + LeafCertGenDialog generator = new LeafCertGenDialog(null, currentCert); + generator.setVisible(true); + } + + @SuppressWarnings("deprecation") + private void signCSR() + { + JFileChooser fc = new JFileChooser(); + fc.setDragEnabled(false); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setMultiSelectionEnabled(false); + fc.setDialogTitle("Open Signing Request PEM File"); + + + int result = fc.showOpenDialog(this); + + if(result == JFileChooser.APPROVE_OPTION) + { + final File fl = fc.getSelectedFile(); + + PemReader reader = null; + try + { + reader = new PemReader( new InputStreamReader(FileUtils.openInputStream(fl))); + final PemObject certReq = reader.readPemObject(); + + final X509Certificate signedCert = CertGenerator.createCertFromCSR(certReq, currentCert); + + // validate the certificate + signedCert.verify(currentCert.getSignerCert().getPublicKey()); + + // write it to a file + final String addressName = cnField.getText(); + final File outFile = new File(addressName + ".der"); + FileUtils.writeByteArrayToFile(outFile, signedCert.getEncoded()); + + JOptionPane.showMessageDialog(this,"Signing successful.\r\nCertificate saved to " + outFile.getAbsolutePath(), + "CSR Sign", JOptionPane.PLAIN_MESSAGE ); + + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this,"Error signging CSR: " + e.getMessage(), + "CSR Sign Error", JOptionPane.ERROR_MESSAGE ); + } + finally + { + IOUtils.closeQuietly(reader); + } + } + } + + + protected File createNewFileName(FileType type) + { + String fileName; + + int index; + String field = emailField.getText(); + if (!field.isEmpty()) + { + index = field.indexOf("@"); + if (index > -1) + fileName = field.substring(0, index); + else + fileName = field; + } + else + { + field = cnField.getText(); + index = field.indexOf("@"); + if (index > -1) + fileName = field.substring(0, index); + else + fileName = field; + } + + if (type == FileType.PRIVATE_KEY) + fileName += "Key"; + + if (type != FileType.CRL) + fileName += ".der"; + else + fileName += ".crl"; + + return new File(fileName); + + } + + protected void createCACert() + { + if (passField.getPassword().length == 0) + { + int selection = JOptionPane.showConfirmDialog(this, "The password field is empty. Do you wish to create a private key file without a password?", + "Empty Password", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + + if (selection == JOptionPane.NO_OPTION) + return; + } + + // make sure there is at least a CN + if (cnField.getText().isEmpty()) + { + JOptionPane.showMessageDialog(this,"Common Name (CN) must have a value.", + "Invalid Cert File", JOptionPane.ERROR_MESSAGE ); + return; + } + + // see if the file attributes are set + if (certFileField.getFile().getPath().isEmpty()) + certFileField.setFile(createNewFileName(FileType.CERT)); + + if (keyFileField.getFile().getPath().isEmpty()) + keyFileField.setFile(createNewFileName(FileType.PRIVATE_KEY)); + + // check if the files already exist + if (certFileField.getFile().exists() || keyFileField.getFile().exists()) + { + int selection = JOptionPane.showConfirmDialog(this, "The certificate or key file already exists. This operation will overwrite the file. Continue?", + "Empty Password", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + + if (selection == JOptionPane.NO_OPTION) + return; + } + + + + // get the fields + Map attributes = new HashMap(); + attributes.put("CN", cnField.getText()); + if (!countryField.getText().isEmpty()) + attributes.put("C", countryField.getText()); + if (!stateField.getText().isEmpty()) + attributes.put("ST", stateField.getText()); + if (!locField.getText().isEmpty()) + attributes.put("L", locField.getText()); + if (!orgField.getText().isEmpty()) + attributes.put("O", orgField.getText()); + if (!emailField.getText().isEmpty()) + attributes.put("EMAILADDRESS", emailField.getText()); + + int exp = Integer.parseInt(expField.getValue().toString()); + int keyStre = Integer.parseInt(keyStr.getValue().toString()); + + CertCreateFields createFields = new CertCreateFields(attributes, certFileField.getFile(), keyFileField.getFile(), + passField.getPassword(), exp, + keyStre, null, null); + + // this enough info to create the files... lets do it + CertCreateFields retCert; + try + { + retCert = CertGenerator.createCertificate(createFields); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this,"An error occured creating the certificate authority: " + e.getMessage(), + "Certificate Creation Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + if (retCert == null) + { + JOptionPane.showMessageDialog(this,"An error occured creating the certificate the authority: unknown error", + "Certificate Creation Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + JOptionPane.showMessageDialog(this,"CA certificate and private key created successfully.", + "SUCCESS", JOptionPane.PLAIN_MESSAGE); + + currentCert = retCert; + + setWorkflowContext(WF_CONTEXT_CERT_CREATED); + + } + + private void loadCACert() + { + File certFile = certFileField.getFile(); + File keyFile = keyFileField.getFile(); + + if (!certFile.exists()) + { + JOptionPane.showMessageDialog(this,"Certificate file does not exist or cannot be found.", + "Invalid Cert File", JOptionPane.ERROR_MESSAGE ); + return; + } + + if (!keyFile.exists()) + { + JOptionPane.showMessageDialog(this,"Private key file does not exist or cannot be found.", + "Invalid Key File", JOptionPane.ERROR_MESSAGE ); + + return; + } + + // load the certs from the file system + CertCreateFields retCert; + try + { + retCert = CertLoader.loadCertificate(certFile, keyFile, this.passField.getPassword()); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this,"An error occured loading the certificate authority: " + e.getMessage(), + "Certificate Load Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + if (retCert == null) + { + JOptionPane.showMessageDialog(this,"An error occured loading the certificate the authority: unknown error", + "Certificate Creation Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + // make sure this cert has privs to act as a CA and sign other CERTs + if (retCert.getSignerCert().getBasicConstraints() < 0) + { + // may be a self signed cert + if (!retCert.getSignerCert().getSubjectX500Principal().equals(retCert.getSignerCert().getIssuerX500Principal())) + { + JOptionPane.showMessageDialog(this,"This certificate's policy does not allowed it to sign other certificates.", + "Policy Validation Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + } + + // get the attributes + if (retCert.getAttributes().containsKey("EMAILADDRESS")) + this.emailField.setText(retCert.getAttributes().get("EMAILADDRESS").toString()); + else + this.emailField.setText(""); + + if (retCert.getAttributes().containsKey("CN")) + this.cnField.setText(retCert.getAttributes().get("CN").toString()); + else + this.cnField.setText(""); + + if (retCert.getAttributes().containsKey("C")) + this.countryField.setText(retCert.getAttributes().get("C").toString()); + else + this.countryField.setText(""); + + if (retCert.getAttributes().containsKey("ST")) + this.stateField.setText(retCert.getAttributes().get("ST").toString()); + else + this.stateField.setText(""); + + if (retCert.getAttributes().containsKey("L")) + this.locField.setText(retCert.getAttributes().get("L").toString()); + else + this.locField.setText(""); + + if (retCert.getAttributes().containsKey("O")) + this.orgField.setText(retCert.getAttributes().get("O").toString()); + else + this.orgField.setText(""); + + this.expField.setValue(retCert.getExpDays()); + + JOptionPane.showMessageDialog(this,"CA certificate and private key loaded successfully.", + "SUCCESS", JOptionPane.PLAIN_MESSAGE); + + currentCert = retCert; + + setWorkflowContext(WF_CONTEXT_CERT_LOADED); + } + + protected static class TextEntryField extends JPanel + { + static final long serialVersionUID = -7340775331901207365L; + + private JLabel label; + private JTextField text; + + public TextEntryField(String labelText) + { + super(); + + this.setLayout(new BorderLayout()); + + + label = new JLabel(labelText); + label.setPreferredSize(new Dimension(50, label.getPreferredSize().getSize().height)); + + text = new JTextField(); + text.setPreferredSize(new Dimension(150, label.getPreferredSize().getSize().height)); + + add(label, BorderLayout.NORTH); + + JPanel textPanel = new JPanel(new BorderLayout()); + textPanel.add(text, BorderLayout.NORTH); + add(textPanel); + } + + @Override + public void setEnabled(boolean b) + { + super.setEnabled(b); + text.setEnabled(b); + } + + public String getText() + { + return text.getText().trim(); + } + + public void setText(String value) + { + text.setText(value); + } + + public void setLabelText(String value) + { + label.setText(value); + } + } + + protected static class SpinEntryField extends JPanel + { + static final long serialVersionUID = 2260694248137330015L; + + + private JLabel label; + private JSpinner value; + + public SpinEntryField(String labelText, int intValue) + { + super(); + this.setLayout(new BorderLayout()); + + label = new JLabel(labelText); + label.setPreferredSize(new Dimension(50, label.getPreferredSize().getSize().height)); + + value = new JSpinner(new SpinnerNumberModel(intValue, + -10, //min + 100000, //max + 1)); + + + add(label, BorderLayout.NORTH); + + JPanel spinnerPanel = new JPanel(new BorderLayout()); + spinnerPanel.add(value, BorderLayout.NORTH); + add(spinnerPanel); + } + + @Override + public void setEnabled(boolean b) + { + super.setEnabled(b); + value.setEnabled(b); + } + + public Object getValue() + { + return value.getValue(); + } + + public void setValue(Object value) + { + this.value.setValue(value); + } + } + + protected static class DropDownEntry extends JPanel + { + static final long serialVersionUID = 3279442634853500454L; + + private JLabel label; + private JComboBox selections; + + public DropDownEntry(String labelText, Object[] items) + { + super(); + this.setLayout(new BorderLayout()); + + label = new JLabel(labelText); + label.setPreferredSize(new Dimension(50, label.getPreferredSize().getSize().height)); + + selections = new JComboBox(items); + + + add(label, BorderLayout.NORTH); + + JPanel dropPanel = new JPanel(new BorderLayout()); + dropPanel.add(selections, BorderLayout.NORTH); + + add(dropPanel); + } + + @Override + public void setEnabled(boolean b) + { + super.setEnabled(b); + selections.setEnabled(b); + } + + public Object getValue() + { + return selections.getSelectedItem(); + } + + public void setValue(Object value) + { + selections.setSelectedItem(value); + } + } + + protected static class PasswordField extends JPanel + { + static final long serialVersionUID = -7837326704224526655L; + + private JLabel label; + private JPasswordField pass; + + public PasswordField(String labelText) + { + super(); + this.setLayout(new BorderLayout()); + + label = new JLabel(labelText); + label.setPreferredSize(new Dimension(150, label.getPreferredSize().getSize().height)); + + pass = new JPasswordField(); + pass.setPreferredSize(new Dimension(150, label.getPreferredSize().getSize().height)); + + add(label, BorderLayout.NORTH); + + JPanel passPanel = new JPanel(new BorderLayout()); + passPanel.add(pass, BorderLayout.NORTH); + + add(passPanel); + } + + @Override + public void setEnabled(boolean b) + { + super.setEnabled(b); + pass.setEnabled(b); + } + + public char[] getPassword() + { + if (pass.getPassword() == null || pass.getPassword().length == 0) + return "".toCharArray(); + + return pass.getPassword(); + } + + public void clear() + { + pass.setText(""); + } + } + + protected static class FileField extends JPanel + { + static final long serialVersionUID = 8783281209944790372L; + + private JLabel label; + private JTextField text; + private JButton search; + + public FileField(String labelText, String file) + { + super(); + + this.setLayout(new BorderLayout()); + + + label = new JLabel(labelText); + label.setPreferredSize(new Dimension(50, label.getPreferredSize().getSize().height)); + + text = new JTextField(); + text.setPreferredSize(new Dimension(250, label.getPreferredSize().getSize().height)); + + add(label, BorderLayout.NORTH); + + JPanel filePanel = new JPanel(new BorderLayout()); + + search = new JButton("..."); + search.setPreferredSize(new Dimension(30, text.getPreferredSize().getSize().height)); + + + JPanel fileWithSearchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + fileWithSearchPanel.add(text); + fileWithSearchPanel.add(search); + + filePanel.add(fileWithSearchPanel, BorderLayout.NORTH); + add(filePanel); + + search.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) + { + selectFile(); + } + }); + } + + private void selectFile() + { + JFileChooser fc = new JFileChooser(); + fc.setDragEnabled(false); + + + // set the current directory to be the images directory + if (!text.getText().trim().isEmpty()) + { + File startFile = new File(text.getText()); + if (startFile.exists()) + { + fc.setCurrentDirectory(startFile); + } + } + + + int result = fc.showOpenDialog(this); + + // if we selected an image, load the image + if(result == JFileChooser.APPROVE_OPTION) + { + text.setText(fc.getSelectedFile().getPath()); + } + } + + public File getFile() + { + return new File(text.getText().trim()); + } + + public void setFile(File fl) + { + if (fl != null) + text.setText(fl.getAbsolutePath()); + else + text.setText(""); + } + + @Override + public void setEnabled(boolean b) + { + super.setEnabled(b); + text.setEnabled(b); + search.setEnabled(b); + } + } + + protected void createCRL() + { + try + { + X509V2CRLGenerator crlGen = new X509V2CRLGenerator(); + + Date now = new Date(); + Calendar nextUpdate = Calendar.getInstance(); + nextUpdate.add(Calendar.DAY_OF_MONTH, 30); + crlGen.setIssuerDN(currentCert.getSignerCert().getSubjectX500Principal()); + + crlGen.setThisUpdate(now); + crlGen.setNextUpdate(nextUpdate.getTime()); + crlGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); + + crlGen.addExtension(X509Extensions.AuthorityKeyIdentifier, + false, new AuthorityKeyIdentifierStructure(currentCert.getSignerCert())); + + crlGen.addExtension(X509Extensions.CRLNumber, + false, new CRLNumber(BigInteger.valueOf(1))); + + X509CRL crl = crlGen.generate((PrivateKey)currentCert.getSignerKey(), "BC"); + + final File crlFile = createNewFileName(FileType.CRL); + + FileUtils.writeByteArrayToFile(crlFile, crl.getEncoded()); + + + JOptionPane.showMessageDialog(this,"Empty CRL file created successfully:\r\n" + + crlFile.getName(), + "SUCCESS", JOptionPane.PLAIN_MESSAGE); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this,"An error occured creating the CRL: " + e.getMessage(), + "CRL Creation Error", JOptionPane.ERROR_MESSAGE); + } + + } + + protected enum FileType + { + CERT, + + PRIVATE_KEY, + + CRL + } +} +///CLOVER:ON \ No newline at end of file diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/CertCreateFields.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/CertCreateFields.java new file mode 100644 index 0000000..b978fc5 --- /dev/null +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/CertCreateFields.java @@ -0,0 +1,125 @@ +/* +Copyright (c) 2010, NHIN Direct Project +All rights reserved. + +Authors: + Greg Meyer gm2552@cerner.com + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org). +nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.directtruststandards.timplus.tools.certgen; + +import java.io.File; +import java.security.Key; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Map; + +/** + * Container for fields related to generating certificates. + * @author Greg Meyer + * + */ +///CLOVER:OFF +public class CertCreateFields +{ + private Map attributes; + private File newCertFile; + private File newKeyFile; + private char[] newPassword; + private int expDays; + private int keyStrength; + private X509Certificate signerCert; + private Key signerKey; + + public CertCreateFields(Map attributes, File newCertFile, File newKeyFile, + char[] newPassword, int expDays, int keyStrength, X509Certificate signerCert, Key signerKey) + { + this.attributes = attributes; + this.newCertFile = newCertFile; + this.newKeyFile = newKeyFile; + this.newPassword = newPassword; + this.expDays = expDays; + this.keyStrength = keyStrength; + this.signerCert = signerCert; + this.signerKey = signerKey; + } + + public Map getAttributes() { + return Collections.unmodifiableMap(attributes); + } + + public File getNewCertFile() { + return newCertFile; + } + + public File getNewKeyFile() { + return newKeyFile; + } + + public char[] getNewPassword() { + return newPassword; + } + + public int getExpDays() { + return expDays; + } + + public int getKeyStrength() { + return keyStrength; + } + + public X509Certificate getSignerCert() { + return signerCert; + } + + public Key getSignerKey() { + return signerKey; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public void setNewCertFile(File newCertFile) { + this.newCertFile = newCertFile; + } + + public void setNewKeyFile(File newKeyFile) { + this.newKeyFile = newKeyFile; + } + + public void setNewPassword(char[] newPassword) { + this.newPassword = newPassword; + } + + public void setExpDays(int expDays) { + this.expDays = expDays; + } + + public void setKeyStrength(int keyStrength) { + this.keyStrength = keyStrength; + } + + public void setSignerCert(X509Certificate signerCert) { + this.signerCert = signerCert; + } + + public void setSignerKey(Key signerKey) { + this.signerKey = signerKey; + } + +} +///CLOVER:ON \ No newline at end of file diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/CertGenerator.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/CertGenerator.java new file mode 100644 index 0000000..1d4080c --- /dev/null +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/CertGenerator.java @@ -0,0 +1,415 @@ +/* +Copyright (c) 2010, NHIN Direct Project +All rights reserved. + +Authors: + Greg Meyer gm2552@cerner.com + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org). +nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.directtruststandards.timplus.tools.certgen; + +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.Calendar; +import java.util.Enumeration; +import java.util.List; +import java.util.Random; + +import javax.crypto.Cipher; +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.apache.commons.io.FileUtils; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.cms.Attribute; +import org.bouncycastle.asn1.pkcs.CertificationRequestInfo; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.CRLDistPoint; +import org.bouncycastle.asn1.x509.DistributionPoint; +import org.bouncycastle.asn1.x509.DistributionPointName; +import org.bouncycastle.asn1.x509.ExtendedKeyUsage; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.X509Extension; +import org.bouncycastle.asn1.x509.X509Extensions; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.crypto.prng.VMPCRandomGenerator; +import org.bouncycastle.jce.PKCS10CertificationRequest; +import org.bouncycastle.jce.X509Principal; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.x509.X509V3CertificateGenerator; +import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure; + +/** + * Engine for generating self signed certificates and leaf node certificates. + * @author Greg Meyer + * + */ +///CLOVER:OFF +public class CertGenerator +{ + private static final String PBE_WITH_MD5_AND_DES_CBC_OID = "1.2.840.113549.1.5.3"; + + public static CertCreateFields createCertificate(CertCreateFields fields) throws Exception + { + // generate a key pair first using RSA and a key strength provided by the user + KeyPairGenerator kpg = (KeyPairGenerator) KeyPairGenerator.getInstance("RSA", "BC"); + + kpg.initialize(fields.getKeyStrength(), new SecureRandom()); + + KeyPair keyPair = kpg.generateKeyPair(); + + if (fields.getSignerCert() == null) + // this is request for a new CA + return createNewCA(fields, keyPair); + else + // new leaf certificate request + return createLeafCertificate(fields, keyPair); + } + + public static long generatePositiveRandom() + { + Random ranGen; + long retVal = -1; + byte[] seed = new byte[8]; + VMPCRandomGenerator seedGen = new VMPCRandomGenerator(); + seedGen.addSeedMaterial(new SecureRandom().nextLong()); + seedGen.nextBytes(seed); + ranGen = new SecureRandom(seed); + while (retVal < 1) + { + retVal = ranGen.nextLong(); + } + + return retVal; + } + + public static X509Certificate createCertFromCSR(PemObject certReq, CertCreateFields signerCert) throws Exception + { + /* + certReq.verify(); + + final CertificationRequestInfo reqInfo = certReq.getCertificationRequestInfo(); + + final X509V3CertificateGenerator v1CertGen = new X509V3CertificateGenerator(); + final Calendar start = Calendar.getInstance(); + final Calendar end = Calendar.getInstance(); + end.add(Calendar.YEAR, 3); + + v1CertGen.setSerialNumber(BigInteger.valueOf(generatePositiveRandom())); + v1CertGen.setIssuerDN(signerCert.getSignerCert().getSubjectX500Principal()); // issuer is the parent cert + v1CertGen.setNotBefore(start.getTime()); + v1CertGen.setNotAfter(end.getTime()); + v1CertGen.setSubjectDN(new X509Principal(reqInfo.getSubject().toString())); + v1CertGen.setPublicKey(certReq.getPublicKey()); + v1CertGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); + + + final ASN1Set attributesAsn1Set = reqInfo.getAttributes(); + + X509Extensions certificateRequestExtensions = null; + for (int i = 0; i < attributesAsn1Set.size(); ++i) + { + // There should be only only one attribute in the set. (that is, only + // the `Extension Request`, but loop through to find it properly) + final DEREncodable derEncodable = attributesAsn1Set.getObjectAt( i ); + + + if (derEncodable instanceof DERSequence) + { + final Attribute attribute = new Attribute( (DERSequence) attributesAsn1Set + .getObjectAt( i ) ); + + if (attribute.getAttrType().equals( PKCSObjectIdentifiers.pkcs_9_at_extensionRequest )) + { + // The `Extension Request` attribute is present. + final ASN1Set attributeValues = attribute.getAttrValues(); + + // The X509Extensions are contained as a value of the ASN.1 Set. + // Assume that it is the first value of the set. + if (attributeValues.size() >= 1) + { + certificateRequestExtensions = new X509Extensions( (ASN1Sequence) attributeValues + .getObjectAt( 0 ) ); + + // No need to search any more. + //break; + } + } + } + } + + @SuppressWarnings("unchecked") + Enumeration oids = certificateRequestExtensions.oids(); + while (oids.hasMoreElements()) + { + DERObjectIdentifier oid = oids.nextElement(); + X509Extension ex = certificateRequestExtensions.getExtension(oid); + + v1CertGen.addExtension(oid, ex.isCritical(), X509Extension.convertValueToObject(ex)); + } + + return v1CertGen.generate((PrivateKey)signerCert.getSignerKey(), CryptoExtensions.getJCEProviderName()); + */ + return null; + } + + private static CertCreateFields createNewCA(CertCreateFields fields, KeyPair keyPair) throws Exception + { + StringBuilder dnBuilder = new StringBuilder(); + + String altName = ""; + + // create the DN + if (fields.getAttributes().containsKey("EMAILADDRESS")) + { + dnBuilder.append("EMAILADDRESS=").append(fields.getAttributes().get("EMAILADDRESS")).append(", "); + altName = fields.getAttributes().get("EMAILADDRESS").toString(); + } + + if (fields.getAttributes().containsKey("CN")) + dnBuilder.append("CN=").append(fields.getAttributes().get("CN")).append(", "); + + if (fields.getAttributes().containsKey("C")) + dnBuilder.append("C=").append(fields.getAttributes().get("C")).append(", "); + + if (fields.getAttributes().containsKey("ST")) + dnBuilder.append("ST=").append(fields.getAttributes().get("ST")).append(", "); + + if (fields.getAttributes().containsKey("L")) + dnBuilder.append("L=").append(fields.getAttributes().get("L")).append(", "); + + if (fields.getAttributes().containsKey("O")) + dnBuilder.append("O=").append(fields.getAttributes().get("O")).append(", "); + + String DN = dnBuilder.toString().trim(); + if (DN.endsWith(",")) + DN = DN.substring(0, DN.length() - 1); + + X509V3CertificateGenerator v1CertGen = new X509V3CertificateGenerator(); + + Calendar start = Calendar.getInstance(); + Calendar end = Calendar.getInstance(); + end.add(Calendar.DAY_OF_MONTH, fields.getExpDays()); + + v1CertGen.setSerialNumber(BigInteger.valueOf(generatePositiveRandom())); + v1CertGen.setIssuerDN(new X509Principal(DN)); + v1CertGen.setNotBefore(start.getTime()); + v1CertGen.setNotAfter(end.getTime()); + v1CertGen.setSubjectDN(new X509Principal(DN)); // issuer and subject are the same for a CA + v1CertGen.setPublicKey(keyPair.getPublic()); + v1CertGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); + + v1CertGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true)); + + + X509Certificate newCACert = v1CertGen.generate(keyPair.getPrivate(), "BC"); + + // validate the certificate + newCACert.verify(keyPair.getPublic()); + + // write the certificate the file system + writeCertAndKey(newCACert, keyPair.getPrivate(), fields); + + return fields; + } + + private static CertCreateFields createLeafCertificate(CertCreateFields fields, KeyPair keyPair) throws Exception + { + StringBuilder dnBuilder = new StringBuilder(); + + + /* + * Create the DN + */ + if (fields.getAttributes().containsKey("CN")) + dnBuilder.append("CN=").append(fields.getAttributes().get("CN")).append(", "); + + if (fields.getAttributes().containsKey("C")) + dnBuilder.append("C=").append(fields.getAttributes().get("C")).append(", "); + + if (fields.getAttributes().containsKey("ST")) + dnBuilder.append("ST=").append(fields.getAttributes().get("ST")).append(", "); + + if (fields.getAttributes().containsKey("L")) + dnBuilder.append("L=").append(fields.getAttributes().get("L")).append(", "); + + if (fields.getAttributes().containsKey("O")) + dnBuilder.append("O=").append(fields.getAttributes().get("O")).append(", "); + + String DN = dnBuilder.toString().trim(); + if (DN.endsWith(",")) + DN = DN.substring(0, DN.length() - 1); + + + /* + * Create the valid dates + */ + Calendar start = Calendar.getInstance(); + Calendar end = Calendar.getInstance(); + end.add(Calendar.DAY_OF_MONTH, fields.getExpDays()); + + /* + * General cert fields + */ + final X509v3CertificateBuilder v1CertGen = new X509v3CertificateBuilder(new X500Name(fields.getSignerCert().getIssuerX500Principal().getName()), + BigInteger.valueOf(generatePositiveRandom()), start.getTime(), end.getTime(), new X500Name(DN), SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())); + + /* + * Auth Key ID + */ + v1CertGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, + new AuthorityKeyIdentifierStructure(fields.getSignerCert())); + + + /* + * Extended Key Usage + */ + final KeyPurposeId[] keyPurposes = {KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth}; + v1CertGen.addExtension(X509Extensions.ExtendedKeyUsage, false, new ExtendedKeyUsage(keyPurposes)); + + + /* + * Basic Constraint (critial) + */ + v1CertGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false)); + + + /* + * Key Usage (critial) + */ + int keyUsage = KeyUsage.keyEncipherment | KeyUsage.digitalSignature; + v1CertGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(keyUsage)); + + + /* + * Subject Alt Names + */ + final String domain = (String)fields.getAttributes().get("DOMAIN"); + + final GeneralName subjectAltName = new GeneralName(GeneralName.dNSName, domain); + final GeneralName ftAltName = new GeneralName(GeneralName.dNSName, "ftproxystream." + domain); + final GeneralName groupChatAltName = new GeneralName(GeneralName.dNSName, "groupchat." + domain); + + final GeneralName[] names = new GeneralName[] {subjectAltName, ftAltName, groupChatAltName};//, xmppServerAltName, xmppClientAltName}; + final DERSequence namesSeq = new DERSequence(names); + + v1CertGen.addExtension(X509Extensions.SubjectAlternativeName, false, namesSeq); + + /* + * CRL Distribution Point + */ + final String crlURL = (String)fields.getAttributes().get("CRL"); + + final GeneralNames distPointGenNames = new GeneralNames(new GeneralName(GeneralName.uniformResourceIdentifier, crlURL)); + final DistributionPointName distPointName = new DistributionPointName(distPointGenNames); + final DistributionPoint point = new DistributionPoint(distPointName, null, null); + final DistributionPoint[] points = new DistributionPoint[] {point}; + final CRLDistPoint distPoint = new CRLDistPoint(points); + v1CertGen.addExtension(X509Extensions.CRLDistributionPoints, false, distPoint); + + /* + * Sign the certificate + */ + ContentSigner signer = new JcaContentSignerBuilder( "SHA256WithRSAEncryption" ).build((PrivateKey)fields.getSignerKey()); + X509CertificateHolder certHolder = v1CertGen.build(signer); + final X509Certificate newClientCert = new JcaX509CertificateConverter().getCertificate( certHolder ); + + // validate the certificate + newClientCert.verify(fields.getSignerCert().getPublicKey()); + + // write the certificate the file system + writeCertAndKey(newClientCert, keyPair.getPrivate(), fields); + + return fields; + } + + private static void writeCertAndKey(X509Certificate cert, PrivateKey key, CertCreateFields fields) throws Exception + { + // write the cert + FileUtils.writeByteArrayToFile(fields.getNewCertFile(), cert.getEncoded()); + + if (fields.getNewPassword() == null || fields.getNewPassword().length == 0) + { + // no password... just write the file + FileUtils.writeByteArrayToFile(fields.getNewKeyFile(), key.getEncoded()); + } + else + { + // encypt it, then write it + + // prime the salts + byte[] salt = new byte[8]; + VMPCRandomGenerator ranGen = new VMPCRandomGenerator(); + ranGen.addSeedMaterial(new SecureRandom().nextLong()); + ranGen.nextBytes(salt); + + // create PBE parameters from salt and iteration count + PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, 20); + + + PBEKeySpec pbeKeySpec = new PBEKeySpec(fields.getNewPassword()); + SecretKey sKey = SecretKeyFactory.getInstance("PBEWithMD5AndDES", "BC").generateSecret(pbeKeySpec); + + // encrypt + Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES", "BC"); + cipher.init(Cipher.ENCRYPT_MODE, sKey, pbeSpec, null); + byte[] plain = (byte[])key.getEncoded(); + byte[] encrKey = cipher.doFinal(plain, 0, plain.length); + + // set the algorithm parameters + AlgorithmParameters pbeParams = AlgorithmParameters.getInstance(PBE_WITH_MD5_AND_DES_CBC_OID, Security.getProvider("SunJCE")); + + pbeParams.init(pbeSpec); + + // place in a EncryptedPrivateKeyInfo to encode to the proper file format + EncryptedPrivateKeyInfo info = new EncryptedPrivateKeyInfo(pbeParams,encrKey); + + // now write it to the file + FileUtils.writeByteArrayToFile(fields.getNewKeyFile(), info.getEncoded()); + } + + if (fields.getSignerCert() == null) + fields.setSignerCert(cert); + + if (fields.getSignerKey() == null) + fields.setSignerKey(key); + } +} +///CLOVER:ON \ No newline at end of file diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/CertLoader.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/CertLoader.java new file mode 100644 index 0000000..d84cf70 --- /dev/null +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/CertLoader.java @@ -0,0 +1,131 @@ +/* +Copyright (c) 2010, NHIN Direct Project +All rights reserved. + +Authors: + Greg Meyer gm2552@cerner.com + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org). +nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.directtruststandards.timplus.tools.certgen; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.security.auth.x500.X500Principal; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * Loads certificates and associated private key files from the file system. Passwords are optional, but must be presend + * if the private key file is encrypted. + * @author Greg Meyer + * + */ +///CLOVER:OFF +public class CertLoader +{ + @SuppressWarnings("deprecation") + public static CertCreateFields loadCertificate(File certFile, File keyFile, char[] password) throws Exception + { + byte[] certData = loadFileData(certFile); + byte[] keyData = loadFileData(keyFile); + + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + InputStream inStr = new ByteArrayInputStream(certData); + java.security.cert.Certificate holdCert = cf.generateCertificate(inStr); + X509Certificate cert = (X509Certificate)holdCert; + + IOUtils.closeQuietly(inStr); + + KeyFactory kf = KeyFactory.getInstance("RSA", "BC"); + PKCS8EncodedKeySpec keysp = null; + if (password != null && password.length > 0) + { + EncryptedPrivateKeyInfo encInfo = new EncryptedPrivateKeyInfo(keyData); + PBEKeySpec keySpec = new PBEKeySpec(password); + String alg = encInfo.getAlgName(); + + SecretKeyFactory secFactory = SecretKeyFactory.getInstance(alg, "BC"); + SecretKey secKey = secFactory.generateSecret(keySpec); + keysp = encInfo.getKeySpec(secKey, "BC"); + } + else + { + keysp = new PKCS8EncodedKeySpec ( keyData ); + } + + PrivateKey privKey = kf.generatePrivate (keysp); + + Map attributes = getAttributes(cert); + + Calendar now = Calendar.getInstance(); + Calendar exp = Calendar.getInstance(); + exp.setTime(cert.getNotAfter()); + + long diff = exp.getTimeInMillis() - now.getTimeInMillis(); + long diffDays = diff / (24 * 60 * 60 * 1000); + + // TODO: get the key strength + int keyStr = 2048; // just hard coded + + CertCreateFields retVal = new CertCreateFields(attributes, certFile, keyFile, password, (int)diffDays, keyStr, cert, privKey); + + return retVal; + } + + private static Map getAttributes(X509Certificate cert) + { + Map retVal = new HashMap(); + + // for now just do a simple parse of the DN + Map oidMap = new HashMap(); + oidMap.put("1.2.840.113549.1.9.1", "EMAILADDRESS"); // OID for email address + String prinName = cert.getSubjectX500Principal().getName(X500Principal.RFC1779, oidMap); + + String[] attributes = prinName.split(","); + if (attributes != null) + for (String attr : attributes) + { + String[] nameValue = attr.split("="); + if (nameValue != null && nameValue.length == 2) + retVal.put(nameValue[0].trim(), nameValue[1].trim()); + } + + return retVal; + } + + private static byte[] loadFileData(File file) throws Exception + { + return FileUtils.readFileToByteArray(file); + } +} +///CLOVER:ON \ No newline at end of file diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/CreatePKCS12.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/CreatePKCS12.java new file mode 100644 index 0000000..b922a99 --- /dev/null +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/CreatePKCS12.java @@ -0,0 +1,293 @@ +/* +Copyright (c) 2010, NHIN Direct Project +All rights reserved. + +Authors: + Greg Meyer gm2552@cerner.com + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org). +nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.directtruststandards.timplus.tools.certgen; + +/** + * Application class for creating PKCS12 files from X509 DER encoded files and PKCS8 DER encoded private key files. Unlike the Java keytool application, + * CreatePKCS12 creates pcks12 files without a passphrase and can accept encrypted private key files. + * + * @author Greg Meyer + */ +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.cert.CertificateFactory; +import java.security.spec.PKCS8EncodedKeySpec; + +import javax.crypto.EncryptedPrivateKeyInfo; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +///CLOVER:OFF +public class CreatePKCS12 +{ + + private static File certFile; + private static File keyFile; + private static String password; + private static String p12Pass = ""; + private static File createFile; + + /** + * Main entry point when running as an application. Use the -help option for usage. + * @param argv Application arguments. + */ + public static void main (String[] argv) + { + if (argv.length == 0) + { + printUsage(); + System.exit(-1); + } + + // Check parameters + for (int i = 0; i < argv.length; i++) + { + String arg = argv[i]; + + // Options + if (!arg.startsWith("-")) + { + System.err.println("Error: Unexpected argument [" + arg + "]\n"); + printUsage(); + System.exit(-1); + } + else if (arg.equalsIgnoreCase("-cert")) + { + if (i == argv.length - 1 || argv[i + 1].startsWith("-")) + { + System.err.println("Error: Missing X509 certificate file."); + System.exit(-1); + } + + certFile = new File(argv[++i]); + + } + else if (arg.equals("-key")) + { + if (i == argv.length - 1 || argv[i + 1].startsWith("-")) + { + System.err.println("Error: Missing PCKS8 key file."); + System.exit(-1); + } + keyFile = new File(argv[++i]); + } + else if (arg.equals("-pass")) + { + if (i == argv.length - 1 || argv[i + 1].startsWith("-")) + { + System.err.println("Error: Missing key file password."); + System.exit(-1); + } + password = argv[++i]; + } + else if (arg.equals("-p12pass")) + { + if (i == argv.length - 1 || argv[i + 1].startsWith("-")) + { + System.err.println("Error: Missing p12 file passphrase."); + System.exit(-1); + } + p12Pass = argv[++i]; + } + else if (arg.equals("-out")) + { + if (i == argv.length - 1 || argv[i + 1].startsWith("-")) + { + System.err.println("Error: Missing output file."); + System.exit(-1); + } + createFile = new File(argv[++i]); + } + else if (arg.equals("-help")) + { + printUsage(); + System.exit(-1); + } + else + { + System.err.println("Error: Unknown argument " + arg + "\n"); + printUsage(); + System.exit(-1); + } + } + + if (validateParameters()) + if (create(certFile, keyFile, password, createFile) != null) + System.out.println("Created pcks12 file " + createFile.getAbsolutePath()); + + System.exit(0); + } + + /* + * Validate the parameters when run from the command line. + */ + private static boolean validateParameters() + { + return (certFile != null && keyFile != null); + } + + /** + * Creates a PCKS12 file from the certificate and key files. + * @param certFile The X509 DER encoded certificate file. + * @param keyFile The PCKS8 DER encoded private key file. + * @param password Option password for the private key file. This is required if the private key file is encrypted. Should be null or empty + * if the private key file is not encrypted. + * @param createFile Optional file descriptor for the output file of the pkcs12 file. If this is null, the file name is based on the + * certificate file name. + * @return File descriptor of the created pcks12 file. Null if an error occurred. + */ + @SuppressWarnings("deprecation") + public static File create(File certFile, File keyFile, String password, File createFile) + { + File pkcs12File = null; + + CreatePKCS12.certFile = certFile; + CreatePKCS12.keyFile = keyFile; + + FileOutputStream outStr = null; + InputStream inStr = null; + // load cert file + try + { + KeyStore localKeyStore = KeyStore.getInstance("PKCS12", "BC"); + localKeyStore.load(null, null); + + byte[] certData = loadFileData(certFile); + byte[] keyData = loadFileData(keyFile); + + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + inStr = new ByteArrayInputStream(certData); + java.security.cert.Certificate cert = cf.generateCertificate(inStr); + + IOUtils.closeQuietly(inStr); + + KeyFactory kf = KeyFactory.getInstance("RSA", "BC"); + PKCS8EncodedKeySpec keysp = null; + if (password != null && !password.isEmpty()) + { + EncryptedPrivateKeyInfo encInfo = new EncryptedPrivateKeyInfo(keyData); + PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray()); + String alg = encInfo.getAlgName(); + + SecretKeyFactory secFactory = SecretKeyFactory.getInstance(alg, "BC"); + SecretKey secKey = secFactory.generateSecret(keySpec); + keysp = encInfo.getKeySpec(secKey, "BC"); + } + else + { + keysp = new PKCS8EncodedKeySpec ( keyData ); + } + + Key privKey = kf.generatePrivate (keysp); + + char[] array = "".toCharArray(); + + localKeyStore.setKeyEntry("privCert", privKey, array, new java.security.cert.Certificate[] {cert}); + + pkcs12File = getPKCS12OutFile(createFile); + outStr = new FileOutputStream(pkcs12File); + localKeyStore.store(outStr, p12Pass.toCharArray()); + } + catch (Exception e) + { + System.err.println("Failed to create pcks12 file: " + e.getMessage()); + e.printStackTrace(System.err); + return null; + } + finally + { + IOUtils.closeQuietly(outStr); + IOUtils.closeQuietly(inStr); + } + + + return pkcs12File; + } + + /* + * Creates the output file descriptor and creates the new file on the file system. + */ + private static File getPKCS12OutFile(File createFile) throws Exception + { + if (createFile == null) + { + + String fileName = certFile.getName(); + + int index = fileName.lastIndexOf("."); + if (index > -1) + fileName = fileName.substring(0, index); + + fileName += ".p12"; + CreatePKCS12.createFile = createFile = new File(fileName); + } + + if (createFile.exists()) + createFile.delete(); + + createFile.createNewFile(); + + return createFile; + } + + /* + * Loads the raw data from the provided file to a byte array. + */ + private static byte[] loadFileData(File file) throws Exception + { + return FileUtils.readFileToByteArray(file); + } + + /* + * Prints the command line usage. + */ + private static void printUsage() + { + StringBuffer use = new StringBuffer(); + use.append("Usage:\n"); + use.append("java CreatePKCS12 (options)...\n\n"); + use.append("options:\n"); + use.append("-cert X509 File X509 DER formatted certificate file.\n"); + use.append("\n"); + use.append("-key Key File PCKS8 DER formatted private key file.\n"); + use.append("\n"); + use.append("-pass Passwd Optional passphrase for private key file.\n"); + use.append(" Default: \"\"\n\n"); + use.append("-p12pass P12 Passwd Optional passphrase for the newly created p12 file.\n"); + use.append(" Default: \"\"\n\n"); + use.append("-out Out File Optional output file name.\n"); + use.append(" Default: .p12\n\n"); + + System.err.println(use); + } + +} +///CLOVER:ON \ No newline at end of file diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/LeafCertGenDialog.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/LeafCertGenDialog.java new file mode 100644 index 0000000..3901c1c --- /dev/null +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/LeafCertGenDialog.java @@ -0,0 +1,276 @@ +/* +Copyright (c) 2010, NHIN Direct Project +All rights reserved. + +Authors: + Greg Meyer gm2552@cerner.com + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org). +nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.directtruststandards.timplus.tools.certgen; + +import java.awt.BorderLayout; +import java.awt.Frame; +import java.awt.GraphicsEnvironment; +import java.awt.Point; +import java.io.File; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JDialog; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +/** + * UI component for creating leaf certificates. + * @author Greg Meyer + * + */ +///CLOVER:OFF +class LeafCertGenDialog extends JDialog +{ + static final long serialVersionUID = -4500679031509430866L; + + private LeafGenPanel genPanel; + + public LeafCertGenDialog(Frame parent, CertCreateFields signer) + { + super(parent, "Certificate Creation", true); + setResizable(false); + setSize(700, 280); + setResizable(false); + + Point pt = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint(); + + this.setLocation(pt.x - (150), pt.y - (130)); + + + initUI(signer); + } + + + private void initUI(CertCreateFields signer) + { + getContentPane().setLayout(new BorderLayout()); + + genPanel = new LeafGenPanel(signer); + + + getContentPane().add(genPanel); + } + + private static class LeafGenPanel extends CAPanel + { + static final long serialVersionUID = -3829240137598058532L; + + private CertCreateFields signer; + private X509Certificate signerCert; + private PrivateKey signerKey; + + private TextEntryField crlField; + + public LeafGenPanel(CertCreateFields signer) + { + super(); + + this.signer = signer; + signerCert = signer.getSignerCert(); + signerKey = (PrivateKey)signer.getSignerKey(); + + allowedToSign.setVisible(true); + keyEnc.setVisible(true); + digitalSig.setVisible(true); + + prePopulateFields(); + } + + @Override + protected void initUI() + { + super.initUI(); + + JPanel fieldsParentPanel = (JPanel)fieldsPanel.getParent(); + fieldsParentPanel.remove(fieldsParentPanel); + + JPanel newFieldsPanel = new JPanel(); + newFieldsPanel.setLayout(new BorderLayout()); + newFieldsPanel.add(fieldsPanel, BorderLayout.CENTER); + + + crlField = new TextEntryField("CRL URL:"); + newFieldsPanel.add(crlField, BorderLayout.SOUTH); + + fieldsParentPanel.add(newFieldsPanel, BorderLayout.CENTER); + + emailField.setLabelText("Domain:"); + createCA.setVisible(false); + loadCA.setVisible(false); + certFileField.setVisible(false); + keyFileField.setVisible(false); + } + + private void prePopulateFields() + { + keyEnc.setSelected(true); + digitalSig.setSelected(true); + + // get the fields from the cert and pre populate the new cert + + if (signer.getAttributes().containsKey("C")) + this.countryField.setText(signer.getAttributes().get("C").toString()); + else + this.countryField.setText(""); + + if (signer.getAttributes().containsKey("ST")) + this.stateField.setText(signer.getAttributes().get("ST").toString()); + else + this.stateField.setText(""); + + if (signer.getAttributes().containsKey("L")) + this.locField.setText(signer.getAttributes().get("L").toString()); + else + this.locField.setText(""); + + if (signer.getAttributes().containsKey("O")) + this.orgField.setText(signer.getAttributes().get("O").toString()); + else + this.orgField.setText(""); + + this.expField.setValue(signer.getExpDays()); + + } + + protected void createCACert() + { + if (passField.getPassword().length == 0) + { + int selection = JOptionPane.showConfirmDialog(this, "The password field is empty. Do you wish to create a private key file without a password?", + "Empty Password", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + + if (selection == JOptionPane.NO_OPTION) + return; + } + + // CN needs to be filled out + if (cnField.getText().isEmpty()) + { + JOptionPane.showMessageDialog(this,"Common Name (CN) must have a value.", + "Invalid Cert File", JOptionPane.ERROR_MESSAGE ); + return; + } + + // must also have an email address + if (emailField.getText().isEmpty()) + { + JOptionPane.showMessageDialog(this,"A domain name is required.", + "Invalid Domain Name", JOptionPane.ERROR_MESSAGE ); + return; + } + + // create the new files + certFileField.setFile(createNewFileName(FileType.CERT)); + keyFileField.setFile(createNewFileName(FileType.PRIVATE_KEY)); + + // check if the files already exist + if (certFileField.getFile().exists() || keyFileField.getFile().exists()) + { + int selection = JOptionPane.showConfirmDialog(this, "The certificate or key file already exists for this email address.\r\n" + + "This operation will overwrite the file. Continue?", + "Certificate Confilct", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + + if (selection == JOptionPane.NO_OPTION) + return; + } + + // make sure there is a CRL + if (crlField.getText().trim().isEmpty()) + { + JOptionPane.showMessageDialog(this,"A CRL URL is required.", + "Invalid CRL", JOptionPane.ERROR_MESSAGE ); + return; + } + + // get the fields + Map attributes = new HashMap(); + attributes.put("CN", cnField.getText()); + if (!countryField.getText().isEmpty()) + attributes.put("C", countryField.getText()); + if (!stateField.getText().isEmpty()) + attributes.put("ST", stateField.getText()); + if (!locField.getText().isEmpty()) + attributes.put("L", locField.getText()); + if (!orgField.getText().isEmpty()) + attributes.put("O", orgField.getText()); + if (!emailField.getText().isEmpty()) + attributes.put("DOMAIN", emailField.getText()); + + attributes.put("CRL", crlField.getText()); + + int exp = Integer.parseInt(expField.getValue().toString()); + int keyStre = Integer.parseInt(keyStr.getValue().toString()); + + CertCreateFields createFields = new CertCreateFields(attributes, certFileField.getFile(), keyFileField.getFile(), + passField.getPassword(), exp, + keyStre, signerCert, signerKey); + + // create the cert + CertCreateFields retCert; + try + { + retCert = CertGenerator.createCertificate(createFields); + } + catch (Exception e) + { + JOptionPane.showMessageDialog(this,"An error occured creating the certificate: " + e.getMessage(), + "Certificate Creation Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + if (retCert == null) + { + JOptionPane.showMessageDialog(this,"An error occured creating the certificate: unknown error", + "Certificate Creation Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + // SUCCESS... create the pkcs12 file + File pcks12File = CreatePKCS12.create(retCert.getNewCertFile(), retCert.getNewKeyFile(), new String(passField.getPassword()), null); + + if (pcks12File == null) + { + JOptionPane.showMessageDialog(this,"An error occured creating the pkcs12 file: unknown error", + "Certificate Creation Error", JOptionPane.ERROR_MESSAGE); + + return; + } + + JOptionPane.showMessageDialog(this,"Domain certificate and private key created successfully:\r\n" + + retCert.getNewCertFile().getName() + "\r\n" + + retCert.getNewKeyFile().getName() + "\r\n" + + pcks12File.getName(), + "SUCCESS", JOptionPane.PLAIN_MESSAGE); + + // clear the fields + cnField.setText(""); + emailField.setText(""); + passField.clear(); + } + } +} +///CLOVER:ON \ No newline at end of file diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/TIMPlusCertGenerator.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/TIMPlusCertGenerator.java new file mode 100644 index 0000000..722e9f1 --- /dev/null +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/TIMPlusCertGenerator.java @@ -0,0 +1,93 @@ +/* +Copyright (c) 2010, NHIN Direct Project +All rights reserved. + +Authors: + Greg Meyer gm2552@cerner.com + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the distribution. Neither the name of the The NHIN Direct Project (nhindirect.org). +nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.directtruststandards.timplus.tools.certgen; + +import java.awt.AWTEvent; +import java.awt.BorderLayout; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.Point; +import java.security.Security; + +import javax.swing.ImageIcon; +import javax.swing.JFrame; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +/** + * Simple Swing application for generating self signed certificates (CAs) and leaf certificates for TIM+. The certificates generated are + * streamlined to simple uses cases: it does not support the numerous options supported using tools such as openssl. + * @author Greg Meyer + * @since 1.0 + */ +///CLOVER:OFF +public class TIMPlusCertGenerator extends JFrame +{ + static + { + Security.addProvider(new BouncyCastleProvider()); + } + + /** + * + */ + private static final long serialVersionUID = -1362014092984111324L; + private CAPanel certAuth; + + public static void main(String[] _args) + { + TIMPlusCertGenerator hi = new TIMPlusCertGenerator(); + hi.setVisible(true); + + } + + public TIMPlusCertGenerator() + { + super("TIM+ Certificate Generator"); + setDefaultLookAndFeelDecorated(true); + setSize(700, 310); + setResizable(false); + + Point pt = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint(); + + this.setLocation(pt.x - (150), pt.y - (120)); + + enableEvents(AWTEvent.WINDOW_EVENT_MASK); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + initUI(); + } + + private void initUI() + { + final Image img = new ImageIcon(getClass().getResource("/images/cert.png")).getImage(); + this.setIconImage(img); + + + getContentPane().setLayout(new BorderLayout()); + + certAuth = new CAPanel(); + + getContentPane().add(certAuth); + } +} +///CLOVER:ON \ No newline at end of file diff --git a/src/main/resources/images/cert.png b/src/main/resources/images/cert.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b87d9c6fb99e3a0af9ecd2570ba28666ddd0d6 GIT binary patch literal 8554 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 literal 0 HcmV?d00001 From f7968c7eaa0b425e019333e42af2eb11abd5e169 Mon Sep 17 00:00:00 2001 From: gm2552 Date: Tue, 25 Aug 2020 06:11:45 -0500 Subject: [PATCH 02/13] Fixing issue with Email attribute in CA creation. --- .../timplus/tools/certgen/CertGenerator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/CertGenerator.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/CertGenerator.java index 1d4080c..2dd86bb 100644 --- a/src/main/java/org/directtruststandards/timplus/tools/certgen/CertGenerator.java +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/CertGenerator.java @@ -199,7 +199,7 @@ private static CertCreateFields createNewCA(CertCreateFields fields, KeyPair key // create the DN if (fields.getAttributes().containsKey("EMAILADDRESS")) { - dnBuilder.append("EMAILADDRESS=").append(fields.getAttributes().get("EMAILADDRESS")).append(", "); + dnBuilder.append("E=").append(fields.getAttributes().get("EMAILADDRESS")).append(", "); altName = fields.getAttributes().get("EMAILADDRESS").toString(); } @@ -288,14 +288,14 @@ private static CertCreateFields createLeafCertificate(CertCreateFields fields, K /* * General cert fields */ - final X509v3CertificateBuilder v1CertGen = new X509v3CertificateBuilder(new X500Name(fields.getSignerCert().getIssuerX500Principal().getName()), + final X509v3CertificateBuilder v1CertGen = new X509v3CertificateBuilder(new X500Name(fields.getSignerCert().getIssuerDN().toString()), BigInteger.valueOf(generatePositiveRandom()), start.getTime(), end.getTime(), new X500Name(DN), SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())); /* * Auth Key ID */ v1CertGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, - new AuthorityKeyIdentifierStructure(fields.getSignerCert())); + new AuthorityKeyIdentifierStructure(fields.getSignerCert().getPublicKey())); /* From 90d61e02403efc739a3589d96a375edd7b6c7e0f Mon Sep 17 00:00:00 2001 From: gm2552 Date: Tue, 25 Aug 2020 06:19:40 -0500 Subject: [PATCH 03/13] Updating to spring boot 2.3.2. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 5508f87..69eb3ee 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-dependencies - 2.3.1.RELEASE + 2.3.2.RELEASE From d5732b0f6b0e46ae900e6271666c981366280f03 Mon Sep 17 00:00:00 2001 From: gm2552 Date: Wed, 23 Sep 2020 14:49:13 -0500 Subject: [PATCH 04/13] Switching TIMPlus cert generator to a spring boot app. --- pom.xml | 231 +++--------------- .../tools/certgen/TIMPlusCertGenerator.java | 15 +- 2 files changed, 45 insertions(+), 201 deletions(-) diff --git a/pom.xml b/pom.xml index 69eb3ee..99a2005 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-dependencies - 2.3.2.RELEASE + 2.3.4.RELEASE @@ -33,22 +33,19 @@ scm:git:https://github.com/DirectStandards/timplus-tools.git scm:git:https://github.com/DirectStandards/timplus-tools.git - + + + 1.8 + org.springframework.boot spring-boot-starter-logging - - - org.igniterealtime.smack - smack-core - 4.3.4 - - - org.igniterealtime.smack - smack-extensions - 4.3.4 - + + + org.springframework.boot + spring-boot-starter + org.apache.commons commons-lang3 @@ -79,193 +76,33 @@ - - - org.apache.maven.wagon - wagon-webdav-jackrabbit - 3.1.0 - - - org.apache.maven.wagon - wagon-ssh-external - 3.1.0 - - - org.apache.maven.wagon - wagon-ssh - 3.1.0 - - - - - src/main/resources - - - lib - ${project.basedir}/lib - - - - - src/test/resources - - - lib - ${project.basedir}/lib - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - org.apache.maven.plugins - maven-jxr-plugin - - - org.apache.maven.plugins - maven-compiler-plugin - - - - testCompile - - compile - - - - true - true - true - UTF-8 - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-source-plugin - - - - jar - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - true - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - -Xdoclint:none - UTF-8 - UTF-8 - true - true - true + + + org.apache.maven.plugins + maven-compiler-plugin + + + + testCompile + + compile + + + + true + true + true + UTF-8 1.8 - public - - - - - - package - attach-javadocs - - jar - - - - - + 1.8 + + + + org.springframework.boot + spring-boot-maven-plugin + - - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.9 - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - -Xdoclint:none - UTF-8 - UTF-8 - true - true - true - 1.8 - public - - - - - - org.apache.maven.plugins - maven-pmd-plugin - - 1.8 - - - - org.apache.maven.plugins - maven-surefire-report-plugin - - - org.apache.maven.plugins - maven-jxr-plugin - - - org.codehaus.mojo - findbugs-maven-plugin - - Max - ${project.basedir}/src/report/findbugs-exclude.xml - - - - sonatype-snapshot diff --git a/src/main/java/org/directtruststandards/timplus/tools/certgen/TIMPlusCertGenerator.java b/src/main/java/org/directtruststandards/timplus/tools/certgen/TIMPlusCertGenerator.java index 722e9f1..98135ab 100644 --- a/src/main/java/org/directtruststandards/timplus/tools/certgen/TIMPlusCertGenerator.java +++ b/src/main/java/org/directtruststandards/timplus/tools/certgen/TIMPlusCertGenerator.java @@ -32,6 +32,8 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY import javax.swing.JFrame; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; /** * Simple Swing application for generating self signed certificates (CAs) and leaf certificates for TIM+. The certificates generated are @@ -40,6 +42,7 @@ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * @since 1.0 */ ///CLOVER:OFF +@SpringBootApplication public class TIMPlusCertGenerator extends JFrame { static @@ -53,12 +56,16 @@ public class TIMPlusCertGenerator extends JFrame private static final long serialVersionUID = -1362014092984111324L; private CAPanel certAuth; - public static void main(String[] _args) - { + public static void main(String[] args) + { + + new SpringApplicationBuilder(TIMPlusCertGenerator.class) + .headless(false).run(args); + TIMPlusCertGenerator hi = new TIMPlusCertGenerator(); hi.setVisible(true); - - } + } + public TIMPlusCertGenerator() { From d67edc8bae022ee006086684be55e79dcc8b5083 Mon Sep 17 00:00:00 2001 From: gm2552 Date: Wed, 23 Sep 2020 15:05:46 -0500 Subject: [PATCH 05/13] Updating pom.xml for correct spring boot build. --- pom.xml | 88 ++++++++++++++++----------------------------------------- 1 file changed, 25 insertions(+), 63 deletions(-) diff --git a/pom.xml b/pom.xml index 99a2005..f8de320 100644 --- a/pom.xml +++ b/pom.xml @@ -1,43 +1,24 @@ - - org.directtruststandards - 4.0.0 - timplus-tools + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.3.4.RELEASE + + + org.directtruststandards + timplus-tools TIM+ tools 1.0.0-SNAPSHOT TIM+ tools such as a certificate generator. 2020 - https://github.com/DirectStandards/timplus-tools - - org.springframework.boot - spring-boot-dependencies - 2.3.4.RELEASE - - - - Greg Meyer - GM2552 - gm2552@cerner.com - - owner - - - - - DirectTrust Standards - https://directtruststandards.org - - - 3.0.0 - - - scm:git:https://github.com/DirectStandards/timplus-tools.git - scm:git:https://github.com/DirectStandards/timplus-tools.git - + 1.8 - - + + org.springframework.boot spring-boot-starter-logging @@ -73,36 +54,17 @@ junit junit test - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - testCompile - - compile - - - - true - true - true - UTF-8 - 1.8 - 1.8 - - - - org.springframework.boot - spring-boot-maven-plugin - - - + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + sonatype-snapshot From 3817d0c965ed1100b8df613468762afcd7396687 Mon Sep 17 00:00:00 2001 From: gm2552 Date: Mon, 9 Nov 2020 06:11:06 -0600 Subject: [PATCH 06/13] Updating to Spring Boot 2.3.5.RELEASE --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f8de320..ce62efe 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.4.RELEASE + 2.3.5.RELEASE org.directtruststandards From fce49151672be6422a17eacd2a9bf8176ead8be1 Mon Sep 17 00:00:00 2001 From: gm2552 Date: Mon, 16 Nov 2020 18:04:03 -0600 Subject: [PATCH 07/13] Updating spring boot 2.3.6 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ce62efe..3b9b5db 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.5.RELEASE + 2.3.6.RELEASE org.directtruststandards From 637975d437dc80154ccb5f2ee4fb385a7cc10d4c Mon Sep 17 00:00:00 2001 From: gm2552 Date: Mon, 4 Jan 2021 10:14:01 -0600 Subject: [PATCH 08/13] Updating to spring boot 2.4.1. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3b9b5db..f63b372 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.3.6.RELEASE + 2.4.1 org.directtruststandards From ecb929beeb077336e2e6852ad63598f61fe2654f Mon Sep 17 00:00:00 2001 From: gm2552 Date: Tue, 19 Jan 2021 08:13:59 -0600 Subject: [PATCH 09/13] Updating to SpringBoot 2.4.2. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f63b372..e40ef0a 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.1 + 2.4.2 org.directtruststandards From 036abe26064af30acacdb4c11c0bd570b2e08480 Mon Sep 17 00:00:00 2001 From: gm2552 Date: Mon, 22 Feb 2021 06:35:30 -0600 Subject: [PATCH 10/13] Updating to spring boot 2.4.3. --- .gitignore | 1 + pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7b0c321..6686142 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ hs_err_pid* /.project /.classpath +/target/ diff --git a/pom.xml b/pom.xml index e40ef0a..cce5180 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.2 + 2.4.3 org.directtruststandards From 4174b8748eb4191e91c681758b847a46c8c5c555 Mon Sep 17 00:00:00 2001 From: gm2552 Date: Thu, 18 Mar 2021 18:57:42 -0500 Subject: [PATCH 11/13] Updating to SpringBoot 2.4.4 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cce5180..3483c9b 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.3 + 2.4.4 org.directtruststandards From 740c7772c7abec3febe863d4818dc49b97e3514c Mon Sep 17 00:00:00 2001 From: gm2552 Date: Fri, 16 Apr 2021 05:58:38 -0500 Subject: [PATCH 12/13] Updating to Spring Boot 2.4.5 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3483c9b..7847c51 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.4.4 + 2.4.5 org.directtruststandards From fed5dd20439e64279088a42381df83f49c5914ac Mon Sep 17 00:00:00 2001 From: gm2552 Date: Fri, 23 Apr 2021 10:05:28 -0500 Subject: [PATCH 13/13] Releasing 1.0.0 --- pom.xml | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 7847c51..0b71657 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.directtruststandards timplus-tools TIM+ tools - 1.0.0-SNAPSHOT + 1.0.0 TIM+ tools such as a certificate generator. 2020 @@ -62,7 +62,60 @@ org.springframework.boot spring-boot-maven-plugin - + + + org.apache.maven.plugins + maven-source-plugin + + + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + -Xdoclint:none + UTF-8 + UTF-8 + true + true + true + 1.8 + public + + + + package + attach-javadocs + + jar + + + -Xdoclint:none + + + + +