Skip to content

Commit

Permalink
feat: Added new KeystoreService.getKeyManagers() method that allows p…
Browse files Browse the repository at this point in the history
…rovider selection [backport release-5.6.0] (#5460)

feat: Added  new KeystoreService.getKeyManagers() method that allows provider selection (#5452)

* feat: Added KeystoreService.getKeyManagers() method that allows to select provider



* Add support for selecting signature algorithm in TestCA



* Applied suggestion



---------

Signed-off-by: Nicola Timeus <[email protected]>
Co-authored-by: Matteo Maiero <[email protected]>
  • Loading branch information
nicolatimeus and MMaiero authored Oct 21, 2024
1 parent f45421e commit 0d897a3
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 10 deletions.
2 changes: 1 addition & 1 deletion kura/org.eclipse.kura.api/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Export-Package: org.eclipse.kura;version="1.7.0",
org.eclipse.kura.net.wifi;version="2.4.0",
org.eclipse.kura.position;version="1.4.0",
org.eclipse.kura.security;version="1.3.0",
org.eclipse.kura.security.keystore;version="1.1.0",
org.eclipse.kura.security.keystore;version="1.2.0",
org.eclipse.kura.security.tamper.detection;version="1.0.0",
org.eclipse.kura.ssl;version="2.1.0",
org.eclipse.kura.status;version="1.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021 Eurotech and/or its affiliates and others
* Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -104,6 +104,24 @@ public interface KeystoreService {
*/
public List<KeyManager> getKeyManagers(String algorithm) throws KuraException;

/**
* Returns one key manager for each type of key material using the Java Security API Provider matching the given name.
*
* @param algorithm
* @param provider
* the name of the Provider to be used
* @return a list of key manager
* @throws KuraException
* if the provided algorithm/provider is not supported or does not exist or if the associated keystore
* cannot be
* accessed
* @throws IllegalArgumentException
* if algorithm or provider is null
*
* @since 2.8
*/
public List<KeyManager> getKeyManagers(String algorithm, String provider) throws KuraException;

/**
* Creates and persists a new keypair in the managed keystore using the specified alias.
*
Expand Down
2 changes: 1 addition & 1 deletion kura/org.eclipse.kura.core.keystore/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Import-Package: com.eclipsesource.json;version="0.9.5",
org.eclipse.kura.message;version="[1.4,2.0)",
org.eclipse.kura.request.handler.jaxrs;version="[1.0,2.0)",
org.eclipse.kura.rest.utils;version="[1.0,2.0)",
org.eclipse.kura.security.keystore;version="[1.1,1.2)",
org.eclipse.kura.security.keystore;version="[1.2,1.3)",
org.eclipse.kura.system;version="[1.5,2.0)",
org.eclipse.kura.util.configuration;version="[1.0,2.0)",
org.eclipse.kura.util.service;version="[1.0,2.0)",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 Eurotech and/or its affiliates and others
* Copyright (c) 2022, 2024 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -257,6 +257,26 @@ public List<KeyManager> getKeyManagers(String algorithm) throws KuraException {
}
}

@Override
public List<KeyManager> getKeyManagers(String algorithm, String provider) throws KuraException {
if (isNull(algorithm)) {
throw new IllegalArgumentException("Algorithm cannot be null!");
}
if (isNull(provider)) {
throw new IllegalArgumentException("Provider cannot be null!");
}
KeystoreInstance ks = loadKeystore();
try {
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm, provider);
kmf.init(ks.getKeystore(), ks.getPassword());

return Arrays.asList(kmf.getKeyManagers());
} catch (GeneralSecurityException e) {
throw new KuraException(KuraErrorCode.BAD_REQUEST, e,
"Failed to get the key managers for algorithm " + algorithm + " and provider " + provider);
}
}

@Override
public void createKeyPair(String alias, String algorithm, int keySize, String signatureAlgorithm, String attributes)
throws KuraException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others
* Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -562,6 +562,89 @@ public void testGetKeyManagersEmptyAlg() throws GeneralSecurityException, IOExce
assertNotNull(keyManagers);
}

@Test
public void testGetKeyManagersPKIXSunJSSE() throws GeneralSecurityException, IOException, KuraException {
Map<String, Object> properties = new HashMap<>();
properties.put(KEY_KEYSTORE_PATH, STORE_PATH);
properties.put(KEY_KEYSTORE_PASSWORD, STORE_PASS);

CryptoService cryptoService = mock(CryptoService.class);
when(cryptoService.decryptAes(STORE_PASS.toCharArray())).thenReturn(STORE_PASS.toCharArray());
when(cryptoService.getKeyStorePassword(STORE_PATH)).thenReturn(STORE_PASS.toCharArray());

ComponentContext componentContext = mock(ComponentContext.class);

FilesystemKeystoreServiceImpl keystoreService = new FilesystemKeystoreServiceImpl();
keystoreService.setEventAdmin(mock(EventAdmin.class));
keystoreService.setCryptoService(cryptoService);
keystoreService.activate(componentContext, properties);

List<KeyManager> keyManagers = keystoreService.getKeyManagers("PKIX", "SunJSSE");
assertNotNull(keyManagers);
}

@Test(expected = KuraException.class)
public void testGetKeyManagersNonExistingProvider() throws GeneralSecurityException, IOException, KuraException {
Map<String, Object> properties = new HashMap<>();
properties.put(KEY_KEYSTORE_PATH, STORE_PATH);
properties.put(KEY_KEYSTORE_PASSWORD, STORE_PASS);

CryptoService cryptoService = mock(CryptoService.class);
when(cryptoService.decryptAes(STORE_PASS.toCharArray())).thenReturn(STORE_PASS.toCharArray());
when(cryptoService.getKeyStorePassword(STORE_PATH)).thenReturn(STORE_PASS.toCharArray());

ComponentContext componentContext = mock(ComponentContext.class);

FilesystemKeystoreServiceImpl keystoreService = new FilesystemKeystoreServiceImpl();
keystoreService.setEventAdmin(mock(EventAdmin.class));
keystoreService.setCryptoService(cryptoService);
keystoreService.activate(componentContext, properties);

keystoreService.getKeyManagers("PKIX", "nonexisting");
}

@Test(expected = IllegalArgumentException.class)
public void testGetKeyManagersWithProvideNullAlgorithm()
throws GeneralSecurityException, IOException, KuraException {
Map<String, Object> properties = new HashMap<>();
properties.put(KEY_KEYSTORE_PATH, STORE_PATH);
properties.put(KEY_KEYSTORE_PASSWORD, STORE_PASS);

CryptoService cryptoService = mock(CryptoService.class);
when(cryptoService.decryptAes(STORE_PASS.toCharArray())).thenReturn(STORE_PASS.toCharArray());
when(cryptoService.getKeyStorePassword(STORE_PATH)).thenReturn(STORE_PASS.toCharArray());

ComponentContext componentContext = mock(ComponentContext.class);

FilesystemKeystoreServiceImpl keystoreService = new FilesystemKeystoreServiceImpl();
keystoreService.setEventAdmin(mock(EventAdmin.class));
keystoreService.setCryptoService(cryptoService);
keystoreService.activate(componentContext, properties);

keystoreService.getKeyManagers(null, "SunJSSE");
}

@Test(expected = IllegalArgumentException.class)
public void testGetKeyManagersWithProvideNullProvider()
throws GeneralSecurityException, IOException, KuraException {
Map<String, Object> properties = new HashMap<>();
properties.put(KEY_KEYSTORE_PATH, STORE_PATH);
properties.put(KEY_KEYSTORE_PASSWORD, STORE_PASS);

CryptoService cryptoService = mock(CryptoService.class);
when(cryptoService.decryptAes(STORE_PASS.toCharArray())).thenReturn(STORE_PASS.toCharArray());
when(cryptoService.getKeyStorePassword(STORE_PATH)).thenReturn(STORE_PASS.toCharArray());

ComponentContext componentContext = mock(ComponentContext.class);

FilesystemKeystoreServiceImpl keystoreService = new FilesystemKeystoreServiceImpl();
keystoreService.setEventAdmin(mock(EventAdmin.class));
keystoreService.setCryptoService(cryptoService);
keystoreService.activate(componentContext, properties);

keystoreService.getKeyManagers("PKIX", null);
}

@Test(expected = IllegalArgumentException.class)
public void testCreateKeyPairNullAlg() throws KuraException {
Map<String, Object> properties = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ Export-Package: org.eclipse.kura.core.testutil;version="1.0.0",
org.eclipse.kura.core.testutil.event;version="1.0.0",
org.eclipse.kura.core.testutil.http;version="1.0.0",
org.eclipse.kura.core.testutil.json;version="1.0.0",
org.eclipse.kura.core.testutil.pki;version="1.1.0",
org.eclipse.kura.core.testutil.pki;version="1.2.0",
org.eclipse.kura.core.testutil.requesthandler;version="1.2.0",
org.eclipse.kura.core.testutil.service;version="1.0.0"
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2023 Eurotech and/or its affiliates and others
* Copyright (c) 2021, 2024 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -66,7 +66,6 @@ public class TestCA {

public static final String TEST_KEYSTORE_PASSWORD = "changeit";

private static final String SIGNATURE_ALGORITHM = "SHA256WithRSA";
private static final Instant DEFAULT_START_INSTANT = Instant.now();
private static final Instant DEFAULT_END_INSTANT = DEFAULT_START_INSTANT.plus(365, ChronoUnit.DAYS);

Expand Down Expand Up @@ -131,7 +130,7 @@ public X509Certificate getCertificate() {
private static X509Certificate buildCertificate(final CertificateCreationOptions options, final BigInteger serial,
final KeyPair certPair, final X500Name issuerName, final KeyPair issuerPair) throws TestCAException {
try {
final ContentSigner contentSigner = new JcaContentSignerBuilder(SIGNATURE_ALGORITHM)
final ContentSigner contentSigner = new JcaContentSignerBuilder(options.getSignatureAlgorithm())
.build(issuerPair.getPrivate());

final JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(issuerName, serial,
Expand Down Expand Up @@ -211,7 +210,7 @@ public X509CRL generateCRL(final CRLCreationOptions options) throws TestCAExcept
SubjectPublicKeyInfo.getInstance(this.caKeyPair.getPublic().getEncoded())));
crlBuilder.addExtension(Extension.cRLNumber, false, new CRLNumber(this.nextCrlNumber));

final ContentSigner contentSigner = new JcaContentSignerBuilder(SIGNATURE_ALGORITHM)
final ContentSigner contentSigner = new JcaContentSignerBuilder(options.getSignatureAlgorithm())
.build(this.caKeyPair.getPrivate());

return new JcaX509CRLConverter().getCRL(crlBuilder.build(contentSigner));
Expand Down Expand Up @@ -291,10 +290,12 @@ public static class CRLCreationOptions {

private final Optional<Date> startDate;
private final Optional<Date> endDate;
private final String signatureAlgorithm;

private CRLCreationOptions(final Builder builder) {
this.startDate = builder.startDate;
this.endDate = builder.endDate;
this.signatureAlgorithm = builder.signatureAlgorithm;
}

public Optional<Date> getStartDate() {
Expand All @@ -305,6 +306,10 @@ public Optional<Date> getEndDate() {
return endDate;
}

public String getSignatureAlgorithm() {
return signatureAlgorithm;
}

public static Builder builder() {
return new Builder();
}
Expand All @@ -313,6 +318,7 @@ public static class Builder {

private Optional<Date> startDate = Optional.empty();
private Optional<Date> endDate = Optional.empty();
private String signatureAlgorithm = "SHA256WithRSA";

public Builder withStartDate(final Date startDate) {
this.startDate = Optional.of(startDate);
Expand All @@ -324,6 +330,11 @@ public Builder withEndDate(final Date endDate) {
return this;
}

public Builder withSignatureAlgorithm(final String signatureAlgorithm) {
this.signatureAlgorithm = signatureAlgorithm;
return this;
}

public CRLCreationOptions build() {
return new CRLCreationOptions(this);
}
Expand All @@ -336,12 +347,14 @@ public static class CertificateCreationOptions {
private final Optional<Date> startDate;
private final Optional<Date> endDate;
private final Optional<URI> crlDownloadURL;
private final String signatureAlgorithm;

private CertificateCreationOptions(final Builder builder) {
this.dn = builder.dn;
this.startDate = builder.startDate;
this.endDate = builder.endDate;
this.crlDownloadURL = builder.crlDownloadURL;
this.signatureAlgorithm = builder.signatureAlgorithm;
}

public static Builder builder(final X500Name dn) {
Expand All @@ -364,12 +377,17 @@ public Optional<URI> getGetDownloadURL() {
return crlDownloadURL;
}

public String getSignatureAlgorithm() {
return signatureAlgorithm;
}

public static class Builder {

private final X500Name dn;
private Optional<Date> startDate = Optional.empty();
private Optional<Date> endDate = Optional.empty();
private Optional<URI> crlDownloadURL = Optional.empty();
private String signatureAlgorithm = "SHA256WithRSA";

public Builder(final X500Name dn) {
this.dn = dn;
Expand All @@ -390,6 +408,11 @@ public Builder withCRLDownloadURI(final URI uri) {
return this;
}

public Builder withSignatureAlgorithm(final String signatureAlgorithm) {
this.signatureAlgorithm = signatureAlgorithm;
return this;
}

public CertificateCreationOptions build() {
return new CertificateCreationOptions(this);
}
Expand Down

0 comments on commit 0d897a3

Please sign in to comment.