Skip to content

Commit

Permalink
feat(SS): EC key support for signing/authentication
Browse files Browse the repository at this point in the history
also make SS admin UI login page look same as CS UI

Refs: XRDDEV-2694
  • Loading branch information
ovidijusnortal committed Nov 5, 2024
1 parent 5db5506 commit e007a2a
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class CertificateDetails {
private String publicKeyAlgorithm;
private BigInteger rsaPublicKeyExponent;
private String rsaPublicKeyModulus;
private String ecPublicParameters;
private String ecPublicKeyPoint;
private String serial;
private String signature;
private String signatureAlgorithm;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@

import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;

import static ee.ria.xroad.common.crypto.NamedCurves.getCurveName;
import static ee.ria.xroad.common.crypto.NamedCurves.getEncodedPoint;
import static ee.ria.xroad.common.util.CertUtils.getIssuerCommonName;
import static ee.ria.xroad.common.util.CertUtils.getSubjectAlternativeNames;
import static ee.ria.xroad.common.util.CertUtils.getSubjectCommonName;
Expand Down Expand Up @@ -108,9 +111,16 @@ private void populateCertificateDetails(final CertificateDetails certificateDeta
.setEncoded(cert);

final PublicKey publicKey = certificate.getPublicKey();
if (publicKey instanceof RSAPublicKey rsaPublicKey) {
certificateDetails.setRsaPublicKeyExponent(rsaPublicKey.getPublicExponent());
certificateDetails.setRsaPublicKeyModulus(rsaPublicKey.getModulus().toString(RADIX_FOR_HEX));
switch (publicKey) {
case RSAPublicKey rsaPublicKey -> {
certificateDetails.setRsaPublicKeyExponent(rsaPublicKey.getPublicExponent());
certificateDetails.setRsaPublicKeyModulus(rsaPublicKey.getModulus().toString(RADIX_FOR_HEX));
}
case ECPublicKey ecPublicKey -> {
certificateDetails.setEcPublicParameters(getCurveName(ecPublicKey));
certificateDetails.setEcPublicKeyPoint(getEncodedPoint(ecPublicKey));
}
default -> { /* do nothing */ }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@
:source-object="certificate"
/>

<certificate-line
child-key="ec_public_key_point"
:label="$t('cert.ecPoint')"
:source-object="certificate"
chunk
/>

<certificate-line
child-key="ec_public_parameters"
:label="$t('cert.ecParameters')"
:source-object="certificate"
/>

<certificate-line child-key="state" :source-object="certificate" />
<certificate-line
child-key="key_usages"
Expand Down
2 changes: 2 additions & 0 deletions src/central-server/admin-service/ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,8 @@
"hashInfo": "Hash (SHA-256)",
"rsaExp": "RSA Public Key Exponent",
"rsaModulus": "RSA Public Key Modulus",
"ecParameters": "EC Public Key Parameters",
"ecPoint": "EC Public Key Point",
"keyUsage": {
"CRL_SIGN": "CRL Sign",
"DATA_ENCIPHERMENT": "Data Encipherment",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3400,6 +3400,20 @@ components:
maxLength: 1000
minLength: 1
type: string
ec_public_parameters:
description: EC public key named curve (if EC key)
example: 'secp256r1(1.2.840.10045.3.1.7)'
format: text
maxLength: 255
minLength: 1
type: string
ec_public_key_point:
description: hex encoded EC public key point (if EC key)
example: c44421d601...
format: hex
maxLength: 1000
minLength: 1
type: string
serial:
description: serial number
example: '123456789'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import ee.ria.xroad.common.crypto.identifier.KeyAlgorithm;
import ee.ria.xroad.common.crypto.identifier.SignAlgorithm;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.DEROctetString;
Expand All @@ -37,13 +36,14 @@
import org.bouncycastle.jce.spec.ECPublicKeySpec;

import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;

import static ee.ria.xroad.common.crypto.identifier.Providers.BOUNCY_CASTLE;

public final class EcKeyManager extends AbstractKeyManager {

// Use no digesting algorithm, since the input data is already a digest
Expand All @@ -65,21 +65,18 @@ public SignAlgorithm getSoftwareTokenKeySignAlgorithm() {
}

@Override
public KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(cryptoAlgorithm().name(), BOUNCY_CASTLE);

var spec = org.bouncycastle.jce.ECNamedCurveTable.getParameterSpec(SystemProperties.getSignerKeyNamedCurve());
if (spec == null) {
var supported = StringUtils.join(org.bouncycastle.jce.ECNamedCurveTable.getNames().asIterator(), ", ");
throw new CryptoException(
"Named curve not found: %s, please on of supported: %s"
.formatted(SystemProperties.getSignerKeyNamedCurve(), supported)
);
}
public KeyPair generateKeyPair() {
final var namedCurve = SystemProperties.getSignerKeyNamedCurve();
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(cryptoAlgorithm().name());

keyPairGen.initialize(spec, new SecureRandom());
var spec = new ECGenParameterSpec(namedCurve);
keyPairGen.initialize(spec, new SecureRandom());

return keyPairGen.generateKeyPair();
return keyPairGen.generateKeyPair();
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
throw new CryptoException("Named curve: %s isn't supported in current environment", e);
}
}

public byte[] generateX509PublicKey(byte[] ecCurveData, byte[] ecPointData)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,28 @@
*/
package ee.ria.xroad.common.crypto;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.experimental.UtilityClass;
import org.apache.xml.security.algorithms.implementations.ECDSAUtils;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;

import java.io.IOException;
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPublicKey;
import java.security.spec.EllipticCurve;
import java.security.spec.InvalidParameterSpecException;
import java.util.concurrent.ExecutionException;

import static ee.ria.xroad.common.util.EncoderUtils.encodeHex;

@UtilityClass
public class NamedCurves {
private static final int MAXIMUM_CACHE_SIZE = 50;
private static final Cache<EllipticCurve, String> CURVE_NAMES_CACHE = CacheBuilder.newBuilder()
.maximumSize(MAXIMUM_CACHE_SIZE)
.build();

public static byte[] getOIDAsBytes(String namedCurve) {
var ident = ECNamedCurveTable.getOID(namedCurve);
Expand All @@ -43,4 +58,27 @@ public static byte[] getOIDAsBytes(String namedCurve) {
throw new CryptoException("Failed to get OID bytes for " + namedCurve, e);
}
}

public static String getCurveName(ECPublicKey ecPublicKey) {
try {
return CURVE_NAMES_CACHE.get(ecPublicKey.getParams().getCurve(), () -> internalGetCurveName(ecPublicKey));
} catch (ExecutionException e) {
throw new CryptoException("Failed to get curve name", e);
}
}

public static String getEncodedPoint(ECPublicKey ecPublicKey) {
return encodeHex(ECDSAUtils.encodePoint(ecPublicKey.getW(), ecPublicKey.getParams().getCurve()));
}


private static String internalGetCurveName(ECPublicKey ecPublicKey) {
try {
AlgorithmParameters algoParams = AlgorithmParameters.getInstance("EC");
algoParams.init(ecPublicKey.getParams());
return algoParams.toString();
} catch (NoSuchAlgorithmException | InvalidParameterSpecException e) {
throw new CryptoException("Failed to resolve curve name", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,13 @@

import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashSet;

import static ee.ria.xroad.common.crypto.NamedCurves.getCurveName;
import static ee.ria.xroad.common.crypto.NamedCurves.getEncodedPoint;

/**
* Converter for CertificateDetails related data between openapi and service domain classes
*/
Expand Down Expand Up @@ -111,11 +115,19 @@ public CertificateDetails convert(X509Certificate x509Certificate) {
certificate.setKeyUsages(new HashSet<>(keyUsageConverter.convert(x509Certificate.getKeyUsage())));

PublicKey publicKey = x509Certificate.getPublicKey();
if (publicKey instanceof RSAPublicKey rsaPublicKey) {
certificate.setRsaPublicKeyExponent(rsaPublicKey.getPublicExponent().intValue());
certificate.setRsaPublicKeyModulus(rsaPublicKey.getModulus().toString(RADIX_FOR_HEX));
switch (publicKey) {
case RSAPublicKey rsaPublicKey -> {
certificate.setRsaPublicKeyExponent(rsaPublicKey.getPublicExponent().intValue());
certificate.setRsaPublicKeyModulus(rsaPublicKey.getModulus().toString(RADIX_FOR_HEX));
}
case ECPublicKey ecPublicKey -> {
certificate.setEcPublicParameters(getCurveName(ecPublicKey));
certificate.setEcPublicKeyPoint(getEncodedPoint(ecPublicKey));
}
default -> { /* do nothing */ }
}


certificate.setSignature(EncoderUtils.encodeHex(x509Certificate.getSignature()));
certificate.setNotBefore(FormatUtils.fromDateToOffsetDateTime(x509Certificate.getNotBefore()));
certificate.setNotAfter(FormatUtils.fromDateToOffsetDateTime(x509Certificate.getNotAfter()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@
:source-object="certificate"
/>

<certificate-line
child-key="ec_public_key_point"
:label="$t('cert.ecPoint')"
:source-object="certificate"
chunk
/>

<certificate-line
child-key="ec_public_parameters"
:label="$t('cert.ecParameters')"
:source-object="certificate"
/>

<certificate-line child-key="state" :source-object="certificate" />
<certificate-line
child-key="key_usages"
Expand Down
2 changes: 2 additions & 0 deletions src/security-server/admin-service/ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@
},
"rsaExp": "RSA Public Key Exponent",
"rsaModulus": "RSA Public Key Modulus",
"ecParameters": "EC Public Key Parameters",
"ecPoint": "EC Public Key Point",
"serialNumber": "Serial Number",
"signCertificate": "Sign Certificate",
"state": "State"
Expand Down
8 changes: 4 additions & 4 deletions src/security-server/admin-service/ui/src/plugins/vuetify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import '@mdi/font/css/materialdesignicons.css';
import { createVuetify } from 'vuetify';
import * as components from 'vuetify/components';
import { aliases, mdi } from 'vuetify/iconsets/mdi';
import { Colors } from '@/global';

export default createVuetify({
components,
Expand Down Expand Up @@ -63,10 +64,9 @@ export default createVuetify({
light: {
dark: false,
colors: {
primary: '#663cdc',
secondary: '#00C9E7',
accent: '#8c9eff',
grey: '#9c9c9c',
primary: Colors.Purple100,
secondary: Colors.Purple70,
error: Colors.Error,
},
},
},
Expand Down
Loading

0 comments on commit e007a2a

Please sign in to comment.