Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added new KeystoreService.getKeyManagers() method that allows provider selection [backport release-5.6.0] #5460

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading