Skip to content

Commit

Permalink
Changed DGenerateKeyPairCert/DSignCsr to display SN as hex
Browse files Browse the repository at this point in the history
  • Loading branch information
kaikramer committed Oct 15, 2023
1 parent 35c2e57 commit b250fed
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 44 deletions.
14 changes: 13 additions & 1 deletion kse/src/org/kse/crypto/x509/X509CertUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@
import org.kse.KSE;
import org.kse.crypto.CryptoException;
import org.kse.crypto.signing.SignatureType;
import org.kse.gui.preferences.ApplicationSettings;
import org.kse.utilities.SerialNumbers;
import org.kse.utilities.StringUtils;
import org.kse.utilities.io.HexUtil;
import org.kse.utilities.io.IOUtils;
import org.kse.utilities.pem.PemInfo;
import org.kse.utilities.pem.PemUtil;
Expand Down Expand Up @@ -732,7 +735,7 @@ public static boolean isCertificateSelfSigned(X509Certificate cert) {
* @return Serial number as hex string
*/
public static String getSerialNumberAsHex(X509Certificate cert) {
return "0x" + new BigInteger(1, cert.getSerialNumber().toByteArray()).toString(16).toUpperCase();
return HexUtil.getHexString(cert.getSerialNumber(), "0x", 0, 0);
}

/**
Expand All @@ -745,4 +748,13 @@ public static String getSerialNumberAsDec(X509Certificate cert) {
return new BigInteger(1, cert.getSerialNumber().toByteArray()).toString(10);
}

/**
* Generate certificate serial number with the length configured in the application preferences.
*
* @return Serial number as hex string with "0x" prefix
*/
public static String generateCertSerialNumber() {
int snLength = ApplicationSettings.getInstance().getSerialNumberLengthInBytes();
return HexUtil.getHexString(SerialNumbers.generate(snLength), "0x", 0, 0);
}
}
36 changes: 7 additions & 29 deletions kse/src/org/kse/gui/dialogs/DGenerateKeyPairCert.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import org.kse.crypto.keypair.KeyPairType;
import org.kse.crypto.signing.SignatureType;
import org.kse.crypto.x509.X500NameUtils;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.crypto.x509.X509CertificateGenerator;
import org.kse.crypto.x509.X509ExtensionSet;
import org.kse.crypto.x509.X509ExtensionSetUpdater;
Expand All @@ -72,7 +73,6 @@
import org.kse.gui.dialogs.extensions.DAddExtensions;
import org.kse.gui.dialogs.sign.DListCertificatesKS;
import org.kse.gui.error.DError;
import org.kse.gui.preferences.ApplicationSettings;
import org.kse.utilities.DialogViewer;
import org.kse.utilities.SerialNumbers;

Expand Down Expand Up @@ -196,8 +196,7 @@ private void initComponents(String title) throws CryptoException {

jlSerialNumber = new JLabel(res.getString("DGenerateKeyPairCert.jlSerialNumber.text"));

final int snLength = ApplicationSettings.getInstance().getSerialNumberLengthInBytes();
jtfSerialNumber = new JTextField(SerialNumbers.generate(snLength).toString(10), 30);
jtfSerialNumber = new JTextField(X509CertUtil.generateCertSerialNumber(), 30);
jtfSerialNumber.setToolTipText(res.getString("DGenerateKeyPairCert.jtfSerialNumber.tooltip"));

jlName = new JLabel(res.getString("DGenerateKeyPairCert.jlName.text"));
Expand Down Expand Up @@ -273,8 +272,8 @@ private void initComponents(String title) throws CryptoException {
});

jrbVersion3.addChangeListener(evt -> {
jbTransferNameExt.setEnabled(jrbVersion3.isSelected());
jbAddExtensions.setEnabled(jrbVersion3.isSelected());
jbTransferNameExt.setEnabled(jrbVersion3.isSelected());
jbAddExtensions.setEnabled(jrbVersion3.isSelected());
});

jbOK.addActionListener(evt -> okPressed());
Expand Down Expand Up @@ -317,7 +316,7 @@ private void transferNameExtPressed() {
try {
if (issuerCert == null) {
String serialNumberStr = jtfSerialNumber.getText().trim();
BigInteger serialNumber = parseDecOrHex(serialNumberStr);
BigInteger serialNumber = SerialNumbers.parse(serialNumberStr);
X509ExtensionSetUpdater.update(extensions, keyPair.getPublic(), keyPair.getPublic(),
jdnName.getDistinguishedName(), serialNumber);
} else {
Expand Down Expand Up @@ -348,7 +347,7 @@ private void addExtensionsPressed() {
String serialNumberStr = jtfSerialNumber.getText().trim();
if (!serialNumberStr.isEmpty()) {
try {
caSerialNumber = parseDecOrHex(serialNumberStr);
caSerialNumber = SerialNumbers.parse(serialNumberStr);
} catch (NumberFormatException ex) {
// Don't set serial number
}
Expand All @@ -365,27 +364,6 @@ private void addExtensionsPressed() {
}
}

/**
* Parses a string initially as a decimal value, or as a hexadecimal value if that failed but the string is a
* valid hex value. To avoid ambiguity, hex parsing can be forced by prefixing the input with '0x'.
*
* @param input The String to parse
* @return a BigInteger representation of the input.
*/
private BigInteger parseDecOrHex(String input) {
try {
return new BigInteger(input);
} catch (NumberFormatException nfe) {
if (input.startsWith("0x")) {
return new BigInteger(input.substring(2), 16);
} else if (input.matches("^\\p{XDigit}+$")) {
return new BigInteger(input, 16);
} else {
throw nfe;
}
}
}

private boolean generateCertificate() {
Date validityStart = jdtValidityStart.getDateTime();
Date validityEnd = jdtValidityEnd.getDateTime();
Expand All @@ -398,7 +376,7 @@ private boolean generateCertificate() {
}
BigInteger serialNumber;
try {
serialNumber = parseDecOrHex(serialNumberStr);
serialNumber = SerialNumbers.parse(serialNumberStr);
if (serialNumber.compareTo(BigInteger.ONE) < 0) {
JOptionPane.showMessageDialog(this, res.getString("DGenerateKeyPairCert.SerialNumberNonZero.message"),
getTitle(), JOptionPane.WARNING_MESSAGE);
Expand Down
17 changes: 8 additions & 9 deletions kse/src/org/kse/gui/dialogs/sign/DSignCsr.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.kse.crypto.keypair.KeyPairUtil;
import org.kse.crypto.signing.SignatureType;
import org.kse.crypto.x509.X500NameUtils;
import org.kse.crypto.x509.X509CertUtil;
import org.kse.crypto.x509.X509CertificateVersion;
import org.kse.crypto.x509.X509ExtensionSet;
import org.kse.crypto.x509.X509ExtensionSetUpdater;
Expand All @@ -93,7 +94,6 @@
import org.kse.gui.dialogs.extensions.DAddExtensions;
import org.kse.gui.dialogs.extensions.DViewExtensions;
import org.kse.gui.error.DError;
import org.kse.gui.preferences.ApplicationSettings;
import org.kse.utilities.DialogViewer;
import org.kse.utilities.SerialNumbers;
import org.kse.utilities.asn1.Asn1Exception;
Expand Down Expand Up @@ -206,7 +206,7 @@ private void initComponents() throws CryptoException {

jlCsrFormat = new JLabel(res.getString("DSignCsr.jlCsrFormat.text"));

jtfCsrFormat = new JTextField(15);
jtfCsrFormat = new JTextField(40);
jtfCsrFormat.setEditable(false);
jtfCsrFormat.setToolTipText(res.getString("DSignCsr.jtfCsrFormat.tooltip"));

Expand All @@ -217,7 +217,7 @@ private void initComponents() throws CryptoException {

jlCsrPublicKey = new JLabel(res.getString("DSignCsr.jlCsrPublicKey.text"));

jtfCsrPublicKey = new JTextField(15);
jtfCsrPublicKey = new JTextField(40);
jtfCsrPublicKey.setEditable(false);
jtfCsrPublicKey.setToolTipText(res.getString("DSignCsr.jtfCsrPublicKey.tooltip"));

Expand All @@ -228,13 +228,13 @@ private void initComponents() throws CryptoException {

jlCsrSignatureAlgorithm = new JLabel(res.getString("DSignCsr.jlCsrSignatureAlgorithm.text"));

jtfCsrSignatureAlgorithm = new JTextField(15);
jtfCsrSignatureAlgorithm = new JTextField(40);
jtfCsrSignatureAlgorithm.setEditable(false);
jtfCsrSignatureAlgorithm.setToolTipText(res.getString("DSignCsr.jtfCsrSignatureAlgorithm.tooltip"));

jlCsrChallenge = new JLabel(res.getString("DSignCsr.jlCsrChallenge.text"));

jtfCsrChallenge = new JTextField(15);
jtfCsrChallenge = new JTextField(40);
jtfCsrChallenge.setEditable(false);
jtfCsrChallenge.setToolTipText(res.getString("DSignCsr.jtfCsrChallenge.tooltip"));

Expand Down Expand Up @@ -296,8 +296,7 @@ private void initComponents() throws CryptoException {

jlSerialNumber = new JLabel(res.getString("DSignCsr.jlSerialNumber.text"));

final int snLength = ApplicationSettings.getInstance().getSerialNumberLengthInBytes();
jtfSerialNumber = new JTextField(SerialNumbers.generate(snLength).toString(10), 40);
jtfSerialNumber = new JTextField(X509CertUtil.generateCertSerialNumber(), 40);
jtfSerialNumber.setToolTipText(res.getString("DSignCsr.jtfSerialNumber.tooltip"));

jbTransferExtensions = new JButton(res.getString("DSignCsr.jbTransferExtensions.text"));
Expand Down Expand Up @@ -325,7 +324,7 @@ private void initComponents() throws CryptoException {
pane.add(jdnCsrSubject, "wrap");
pane.add(jlCsrPublicKey, "");
pane.add(jtfCsrPublicKey, "split 2");
pane.add(jbViewCsrPublicKeyDetails, "wrap");
pane.add(jbViewCsrPublicKeyDetails, "gapx 5px, wrap"); // TODO change gap after JDistinguishedName has been migrated
pane.add(jlCsrSignatureAlgorithm, "");
pane.add(jtfCsrSignatureAlgorithm, "wrap");
pane.add(jlCsrChallenge, "");
Expand Down Expand Up @@ -706,7 +705,7 @@ private void okPressed() {
return;
}
try {
serialNumber = new BigInteger(serialNumberStr);
serialNumber = SerialNumbers.parse(serialNumberStr);
if (serialNumber.compareTo(BigInteger.ONE) < 0) {
JOptionPane.showMessageDialog(this, res.getString("DSignCsr.SerialNumberNonZero.message"), getTitle(),
JOptionPane.WARNING_MESSAGE);
Expand Down
28 changes: 25 additions & 3 deletions kse/src/org/kse/utilities/SerialNumbers.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,40 @@ private SerialNumbers() {
* @return new serial number as decimal format string
*/
public static BigInteger generate(int length) {
if (length < 8 || length > 20) {
if ((length < 8) || (length > 20)) {
throw new IllegalArgumentException("Length parameter must be between 8 and 20");
}

byte[] timeBytes = BigInteger.valueOf(System.currentTimeMillis() / 1000).toByteArray();
byte[] timeBytes = BigInteger.valueOf(System.currentTimeMillis()).toByteArray();
byte[] rndBytes = new byte[length - timeBytes.length];
rng.nextBytes(rndBytes);
byte[] snBytes = Arrays.concatenate(rndBytes, timeBytes);

// ensure most significant byte is positive
// ensure most significant byte is positive but not zero
snBytes[0] &= 0x7F;
snBytes[0] |= (1 << 6);

return new BigInteger(1, snBytes);
}

/**
* Parses a string initially as a decimal value, or as a hexadecimal value if that failed but the string is a
* valid hex value. To avoid ambiguity, hex parsing can be forced by prefixing the input with '0x'.
*
* @param input The String to parse
* @return a BigInteger representation of the input.
*/
public static BigInteger parse(String input) {
try {
return new BigInteger(input);
} catch (NumberFormatException nfe) {
if (input.startsWith("0x")) {
return new BigInteger(input.substring(2), 16);
} else if (input.matches("^\\p{XDigit}+$")) {
return new BigInteger(input, 16);
} else {
throw nfe;
}
}
}
}
24 changes: 22 additions & 2 deletions kse/test/org/kse/utilities/SerialNumbersTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.math.BigInteger;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

class SerialNumbersTest {
Expand All @@ -42,7 +43,26 @@ void generate(int length) {
@ParameterizedTest
@ValueSource(ints = { 0, 1, 2, 3, 4, 5, 6, 7, 21, 22, 100 })
void generateWrongLength(int length) {
assertThatThrownBy(() -> SerialNumbers.generate(length))
.isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> SerialNumbers.generate(length)).isInstanceOf(IllegalArgumentException.class);
}

@ParameterizedTest
@CsvSource({
"0, 0",
"0x00, 0",
"1, 1",
"01, 1",
"0x01, 1",
"a0, 160",
"ff, 255",
"0abc, 2748",
"0x0abc, 2748",
"abc, 2748",
"0xabc, 2748",
"1234567890, 1234567890",
"0x1234567890, 78187493520",
})
void parse(String sn, BigInteger expectedResult) {
assertThat(SerialNumbers.parse(sn)).isEqualTo(expectedResult);
}
}

0 comments on commit b250fed

Please sign in to comment.