Skip to content

Commit

Permalink
support more digest algorithms and nonce extension with OCSP URL (issue
Browse files Browse the repository at this point in the history
  • Loading branch information
emattheis authored Dec 8, 2024
1 parent 4ba0377 commit 4bb8b68
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 8 deletions.
76 changes: 76 additions & 0 deletions kse/src/main/java/org/kse/crypto/ocsp/OcspDigestAlgorithm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2004 - 2013 Wayne Grant
* 2013 - 2024 Kai Kramer
*
* This file is part of KeyStore Explorer.
*
* KeyStore Explorer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KeyStore Explorer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KeyStore Explorer. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kse.crypto.ocsp;

import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cert.ocsp.CertificateID;

/**
* Enumeration of Digest Types supported by the DigestUtil class.
*/
public enum OcspDigestAlgorithm {

// @formatter:off

SHA1(CertificateID.HASH_SHA1, "SHA-1"),

SHA256(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256), "SHA-256"),
SHA384(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha384), "SHA-384"),
SHA512(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512), "SHA-512");

// @formatter:on

private AlgorithmIdentifier algorithmIdentifier;
private String friendly;

OcspDigestAlgorithm(AlgorithmIdentifier algorithmIdentifier, String friendly) {
this.algorithmIdentifier = algorithmIdentifier;
this.friendly = friendly;
}

/**
* Get algorithm identifier.
*
* @return algorithm identifier
*/
public AlgorithmIdentifier algorithmIdentifier() {
return algorithmIdentifier;
}

/**
* Get digest algorithm friendly name.
*
* @return Friendly name
*/
public String friendly() {
return friendly;
}

/**
* Returns friendly name.
*
* @return Friendly name
*/
@Override
public String toString() {
return friendly();
}
}
25 changes: 19 additions & 6 deletions kse/src/main/java/org/kse/gui/actions/VerifyCertificateAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.CertPath;
Expand Down Expand Up @@ -64,6 +65,10 @@

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.ExtensionsGenerator;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.CertificateStatus;
Expand All @@ -79,6 +84,7 @@
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.kse.KSE;
import org.kse.crypto.CryptoException;
import org.kse.crypto.ocsp.OcspDigestAlgorithm;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.gui.KseFrame;
import org.kse.gui.dialogs.DVerifyCertificate;
Expand Down Expand Up @@ -144,7 +150,7 @@ protected void doAction() {
} else if (verifyOptions == VerifyOptions.OCSP_AIA) {
verifyStatusOCSP(keyStoreHistory, alias);
} else if (verifyOptions == VerifyOptions.OCSP_URL) {
verifyStatusOcspUrl(keyStoreHistory, alias, dVerifyCertificate.getOcspUrl());
verifyStatusOcspUrl(keyStoreHistory, alias, dVerifyCertificate.getOcspUrl(), dVerifyCertificate.getOcspDigestAlgorithm(), dVerifyCertificate.isOcspIncludeNonceSelected());
} else {
verifyChain(keyStoreHistory, alias);
}
Expand Down Expand Up @@ -181,7 +187,7 @@ private void verifyStatusOCSP(KeyStoreHistory keyStoreHistory, String alias)
}
}

private void verifyStatusOcspUrl(KeyStoreHistory keyStoreHistory, String alias, String ocspUrl)
private void verifyStatusOcspUrl(KeyStoreHistory keyStoreHistory, String alias, String ocspUrl, OcspDigestAlgorithm ocspDigestAlgorithm, boolean ocspIncludeNonce)
throws OperatorCreationException, OCSPException, IOException, HeadlessException, CertPathValidatorException,
KeyStoreException, NoSuchAlgorithmException, CertificateException,
InvalidAlgorithmParameterException, IllegalStateException, CryptoException {
Expand Down Expand Up @@ -210,7 +216,7 @@ private void verifyStatusOcspUrl(KeyStoreHistory keyStoreHistory, String alias,
if (issuer == null) {
throw new CertPathValidatorException(res.getString("VerifyCertificateAction.trustStoreEmpty.message"));
}
OCSPReq request = makeOcspRequest(issuer, certificateEval);
OCSPReq request = makeOcspRequest(issuer, certificateEval, ocspDigestAlgorithm.algorithmIdentifier(), ocspIncludeNonce);
OCSPResp response = requestOCSPResponse(ocspUrl, request);
if (isGoodCertificate(response)) {
JOptionPane.showMessageDialog(frame, res.getString("VerifyCertificateAction.OcspSuccessful.message"),
Expand All @@ -221,16 +227,23 @@ private void verifyStatusOcspUrl(KeyStoreHistory keyStoreHistory, String alias,
}
}

private static OCSPReq makeOcspRequest(X509Certificate caCert, X509Certificate certToCheck)
throws OCSPException, OperatorCreationException, CertificateEncodingException {
private static OCSPReq makeOcspRequest(X509Certificate caCert, X509Certificate certToCheck, AlgorithmIdentifier digestAlgorithm, boolean includeNonce)
throws IOException, OCSPException, OperatorCreationException, CertificateEncodingException {
DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(KSE.BC)
.build();

CertificateID certId = new JcaCertificateID(digCalcProv.get(CertificateID.HASH_SHA1), caCert,
CertificateID certId = new JcaCertificateID(digCalcProv.get(digestAlgorithm), caCert,
certToCheck.getSerialNumber());

OCSPReqBuilder gen = new OCSPReqBuilder();
gen.addRequest(certId);
if (includeNonce) {
byte[] nonce = new byte[16];
new SecureRandom().nextBytes(nonce);
ExtensionsGenerator extGen = new ExtensionsGenerator();
extGen.addExtension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, false, new DEROctetString(nonce));
gen.setRequestExtensions(extGen.generate());
}
return gen.build();
}

Expand Down
34 changes: 32 additions & 2 deletions kse/src/main/java/org/kse/gui/dialogs/DVerifyCertificate.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import javax.swing.JTextField;
import javax.swing.KeyStroke;

import org.kse.crypto.ocsp.OcspDigestAlgorithm;
import org.kse.gui.CurrentDirectory;
import org.kse.gui.CursorUtil;
import org.kse.gui.FileChooserFactory;
Expand Down Expand Up @@ -99,6 +100,9 @@ public enum VerifyOptions {
private JComboBox<KeyStoreHistory> jcbKeyStore;
private JButton jbLoadKeystore;

private JComboBox<OcspDigestAlgorithm> jcbOcspDigestAlgorithm;
private JCheckBox jcbOcspIncludeNonce;

private boolean verifySelected = false;
private VerifyOptions verifyOption = VerifyOptions.CRL_DIST;
private String fileCrl;
Expand Down Expand Up @@ -147,6 +151,14 @@ private void initComponents() {
jtfOcspUrl.setEditable(false);
jtfOcspUrl.setToolTipText(res.getString("DVerifyCertificate.jtfOcspUrl.tooltip"));

jcbOcspDigestAlgorithm = new JComboBox<>(new DefaultComboBoxModel<>(OcspDigestAlgorithm.values()));
jcbOcspDigestAlgorithm.setToolTipText(res.getString("DVerifyCertificate.jcbOcspDigestAlgorithm.tooltip"));
jcbOcspDigestAlgorithm.setEnabled(false);

jcbOcspIncludeNonce = new JCheckBox(res.getString("DVerifyCertificate.jcbOcspIncludeNonce.text"));
jcbOcspIncludeNonce.setToolTipText(res.getString("DVerifyCertificate.jcbOcspIncludeNonce.tooltip"));
jcbOcspIncludeNonce.setEnabled(false);

jrbChainCheck = new JRadioButton(res.getString("DVerifyCertificate.jrbChainCheck.text"));
jrbChainCheck.setToolTipText(res.getString("DVerifyCertificate.jrbChainCheck.tooltip"));

Expand Down Expand Up @@ -181,8 +193,11 @@ private void initComponents() {
pane.add(jtfCrlFile, "growx");
pane.add(jbLoadCrl, "wrap");
pane.add(jrbOcspAiaCheck, "gapleft indent, wrap");
pane.add(jrbOcspUrlCheck, "gapleft indent, split 2");
pane.add(jtfOcspUrl, "growx, wrap, left");
pane.add(jrbOcspUrlCheck, "gapleft indent, split 3");
pane.add(jtfOcspUrl, "growx, left, wrap");
pane.add(new JLabel(res.getString("DVerifyCertificate.jlOcspDigestAlgorithm.text")), "gapleft indent, split 3, right");
pane.add(jcbOcspDigestAlgorithm, "right");
pane.add(jcbOcspIncludeNonce, "right, wrap");
pane.add(jrbChainCheck, "gapleft indent, spanx, wrap");
pane.add(new JSeparator(), "spanx, growx, wrap");
pane.add(jcbSelectKeyStore, "left, spanx, wrap");
Expand Down Expand Up @@ -269,6 +284,13 @@ private void updateVerifyControls() {
jcbKeyStore.setEnabled(false);
jbLoadKeystore.setEnabled(false);
}
if (jrbOcspUrlCheck.isSelected()) {
jcbOcspDigestAlgorithm.setEnabled(true);
jcbOcspIncludeNonce.setEnabled(true);
} else {
jcbOcspDigestAlgorithm.setEnabled(false);
jcbOcspIncludeNonce.setEnabled(false);
}
}

public boolean isVerifySelected() {
Expand Down Expand Up @@ -367,6 +389,14 @@ private ComboBoxModel<KeyStoreHistory> getKeystoreNames() {
return new DefaultComboBoxModel<>(keyStoreHistories);
}

public OcspDigestAlgorithm getOcspDigestAlgorithm() {
return (OcspDigestAlgorithm) jcbOcspDigestAlgorithm.getSelectedItem();
}

public boolean isOcspIncludeNonceSelected() {
return jcbOcspIncludeNonce.isSelected();
}

public static void main(String[] args) throws Exception {
DialogViewer.run(new DVerifyCertificate(new javax.swing.JFrame(), "Test", null));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,14 @@ DVerifyCertificate.jbLoadCrl.tooltip = Open a CRL from disk
DVerifyCertificate.jbLoadKeystore.tooltip = Open an KeyStore from disk
DVerifyCertificate.jbOk.text = OK
DVerifyCertificate.jcbKeyStore.tooltip = Select the KeyStore
DVerifyCertificate.jcbOcspDigestAlgorithm.tooltip = Select the digest algorithm to use in the OCSP request
DVerifyCertificate.jcbOcspIncludeNonce.text = Include Nonce
DVerifyCertificate.jcbOcspIncludeNonce.tooltip = Include RFC 8954 nonce extension in the OCSP request
DVerifyCertificate.jcbSelectKeyStore.text = Use an alternate CA keystore for validating the certificate:
DVerifyCertificate.jlCacertFile.text = Keystore:
DVerifyCertificate.jlCheckStatus.text = Validate certificate chain and check revocation status using
DVerifyCertificate.jlKeyStore.text = KeyStore:
DVerifyCertificate.jlOcspDigestAlgorithm.text = Digest Algorithm:
DVerifyCertificate.jrbChainCheck.text = Do not check revocation status, only verify certificate chain
DVerifyCertificate.jrbChainCheck.tooltip = Validate chain
DVerifyCertificate.jrbCrlCheckDistPoint.text = CRL Distribution Point extension
Expand Down

0 comments on commit 4bb8b68

Please sign in to comment.