This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 4bc6188f53 feat(#4041): Show opc ua certificate info in asset view 
(#4044)
4bc6188f53 is described below

commit 4bc6188f53678d2860c61712f9e5e7f552905331
Author: Dominik Riemer <[email protected]>
AuthorDate: Wed Dec 10 14:03:41 2025 +0100

    feat(#4041): Show opc ua certificate info in asset view (#4044)
---
 .../connectors/opcua/adapter/OpcUaAdapter.java     |   3 +-
 .../opcua/client/OpcUaClientProvider.java          |  18 +++-
 .../config/MiloOpcUaConfigurationProvider.java     |   2 +-
 .../opcua/config/OpcUaAdapterConfig.java           |   1 +
 .../connectors/opcua/config/OpcUaConfig.java       |  29 ++++++
 .../opcua/config/SpOpcUaConfigExtractor.java       |  10 ++
 .../security/CompositeCertificateValidator.java    |  24 ++++-
 .../opcua/config/security/SecurityConfig.java      |  10 +-
 .../opcua/utils/OpcUaCertificateUtils.java         | 101 +++++++++++++++++++++
 .../connectors/opcua/utils/OpcUaUtils.java         |  54 +----------
 .../streampipes/model/opcua/Certificate.java       |  27 +++++-
 .../model/opcua/CertificateBuilder.java            |  10 ++
 .../streampipes/model/opcua/CertificateUsage.java  |  22 +----
 .../streampipes/model/opcua/CertificateUtils.java  |  48 ++++++++++
 .../management/AdapterResourceManager.java         |  19 +++-
 .../management/AdapterResourceManagerTest.java     |   2 +-
 .../rest/impl/admin/CertificateResource.java       |  14 +++
 .../core/migrations/AvailableMigrations.java       |   4 +-
 .../ComputeCertificateThumbprintMigration.java     |  68 ++++++++++++++
 .../src/lib/model/gen/streampipes-model.ts         |  25 ++---
 ui/src/app/assets/assets.module.ts                 |   2 +
 .../asset-details-labels.component.ts              |   4 +-
 .../view-assset-basics.component.ts                |   8 +-
 ...asset-link-table-additional-data.component.html |  29 ++++++
 .../asset-link-table-additional-data.component.ts  |  76 ++++++++++++++++
 .../asset-link-table.component.html                |   9 +-
 .../asset-link-table/asset-link-table.component.ts |  35 ++++++-
 .../view-asset-links/view-asset-links.component.ts |   4 +-
 ui/src/app/configuration/configuration.module.ts   |   4 +-
 ...xtensions-service-details-dialog.component.html |   8 +-
 ...xtensions-service-details-dialog.component.scss |  11 +--
 .../extensions-service-details-dialog.component.ts |   6 ++
 .../certificate-configuration.component.html       |   7 +-
 .../certificate-configuration.component.scss}      |  20 +---
 .../certificate-configuration.component.ts         |   3 +-
 .../certificate-label.component.html               |  28 ++++++
 .../certificate-label.component.ts}                |  24 ++---
 .../certificate-details-dialog.component.html      |   0
 .../certificate-details-dialog.component.ts        |   0
 ui/src/app/core-ui/core-ui.module.ts               |   3 +
 ui/src/scss/sp/_spacing.scss                       |  44 ++++++++-
 41 files changed, 650 insertions(+), 166 deletions(-)

diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
index c73a8b2926..196392dd77 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
@@ -205,7 +205,8 @@ public class OpcUaAdapter implements StreamPipesAdapter, 
IPullAdapter, SupportsR
     this.opcUaAdapterConfig =
         SpOpcUaConfigExtractor.extractAdapterConfig(
             extractor.getStaticPropertyExtractor(),
-            adapterRuntimeContext.getStreamPipesClient()
+            adapterRuntimeContext.getStreamPipesClient(),
+            extractor.getAdapterDescription().getElementId()
         );
     this.collector = collector;
     this.prepareAdapter(extractor);
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/client/OpcUaClientProvider.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/client/OpcUaClientProvider.java
index b3f907bd4e..e9fe90488f 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/client/OpcUaClientProvider.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/client/OpcUaClientProvider.java
@@ -20,13 +20,16 @@ package 
org.apache.streampipes.extensions.connectors.opcua.client;
 
 import org.apache.streampipes.commons.exceptions.SpConfigurationException;
 import org.apache.streampipes.extensions.connectors.opcua.config.OpcUaConfig;
+import 
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaCertificateUtils;
 
 import org.eclipse.milo.opcua.stack.core.UaException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.net.URISyntaxException;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 
@@ -36,6 +39,7 @@ public class OpcUaClientProvider {
 
   private final Map<String, ConnectedOpcUaClient> clients = new 
ConcurrentHashMap<>();
   private final Map<String, Integer> consumers = new ConcurrentHashMap<>();
+  private final Map<String, String> serverThumbprints = new HashMap<>();
 
   public synchronized <T extends OpcUaConfig> ConnectedOpcUaClient getClient(T 
config)
       throws UaException, SpConfigurationException, URISyntaxException, 
ExecutionException, InterruptedException {
@@ -43,12 +47,24 @@ public class OpcUaClientProvider {
     if (clients.containsKey(serverId)) {
       LOG.debug("Adding new consumer to client {}", serverId);
       consumers.put(serverId, consumers.get(config.getUniqueServerId()) + 1);
-      return clients.get(serverId);
+      var client = clients.get(serverId);
+      var associatedResourceId = config.getAssociatedResourceId();
+      if (serverThumbprints.containsKey(serverId) && 
Objects.nonNull(associatedResourceId)) {
+        OpcUaCertificateUtils.sendUsageToCore(
+            serverThumbprints.get(serverId),
+            associatedResourceId,
+            config.getStreamPipesClient()
+        );
+      }
+      return client;
     } else {
       LOG.debug("Creating new client {}", serverId);
       var connectedClient = new SpOpcUaClient<>(config).connect();
       clients.put(serverId, connectedClient);
       consumers.put(serverId, 1);
+      if (config.getCertificateThumbprint() != null) {
+        serverThumbprints.put(serverId, config.getCertificateThumbprint());
+      }
       return connectedClient;
     }
   }
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/MiloOpcUaConfigurationProvider.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/MiloOpcUaConfigurationProvider.java
index 8db41a8e11..d1f3564fb9 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/MiloOpcUaConfigurationProvider.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/MiloOpcUaConfigurationProvider.java
@@ -42,7 +42,7 @@ public class MiloOpcUaConfigurationProvider {
         .setApplicationName(LocalizedText.english("Apache StreamPipes"))
         .setApplicationUri(applicationUri);
 
-    spOpcConfig.getSecurityConfig().configureSecurityPolicy(opcServerUrl, 
endpoints, builder);
+    spOpcConfig.getSecurityConfig().configureSecurityPolicy(spOpcConfig, 
endpoints, builder);
     spOpcConfig.getIdentityConfig().configureIdentity(builder);
 
     return builder.build();
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/OpcUaAdapterConfig.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/OpcUaAdapterConfig.java
index 83cf333b7d..a0bac48efd 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/OpcUaAdapterConfig.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/OpcUaAdapterConfig.java
@@ -53,4 +53,5 @@ public class OpcUaAdapterConfig extends OpcUaConfig {
   public void setNamingStrategy(OpcUaNamingStrategy namingStrategy) {
     this.namingStrategy = namingStrategy;
   }
+
 }
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/OpcUaConfig.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/OpcUaConfig.java
index 128f1bc1f9..aec8e37342 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/OpcUaConfig.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/OpcUaConfig.java
@@ -18,6 +18,7 @@
 
 package org.apache.streampipes.extensions.connectors.opcua.config;
 
+import org.apache.streampipes.client.api.IStreamPipesClient;
 import 
org.apache.streampipes.extensions.connectors.opcua.config.identity.IdentityConfig;
 import 
org.apache.streampipes.extensions.connectors.opcua.config.security.SecurityConfig;
 
@@ -29,6 +30,9 @@ public class OpcUaConfig {
   private List<String> selectedNodeNames;
   private IdentityConfig identityConfig;
   private SecurityConfig securityPolicyConfig;
+  private String associatedResourceId;
+  private String certificateThumbprint;
+  private IStreamPipesClient streamPipesClient;
 
   public OpcUaConfig() {
 
@@ -69,4 +73,29 @@ public class OpcUaConfig {
   public String getUniqueServerId() {
     return String.format("%s-%s-%s", opcServerURL, securityPolicyConfig, 
identityConfig);
   }
+
+  public String getAssociatedResourceId() {
+    return associatedResourceId;
+  }
+
+  public void setAssociatedResourceId(String associatedResourceId) {
+    this.associatedResourceId = associatedResourceId;
+  }
+
+  public String getCertificateThumbprint() {
+    return certificateThumbprint;
+  }
+
+  public void setCertificateThumbprint(String certificateThumbprint) {
+    this.certificateThumbprint = certificateThumbprint;
+  }
+
+  public IStreamPipesClient getStreamPipesClient() {
+    return streamPipesClient;
+  }
+
+  public void setStreamPipesClient(IStreamPipesClient streamPipesClient) {
+    this.streamPipesClient = streamPipesClient;
+  }
+
 }
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SpOpcUaConfigExtractor.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SpOpcUaConfigExtractor.java
index e6fea5c2d6..9363339bd8 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SpOpcUaConfigExtractor.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SpOpcUaConfigExtractor.java
@@ -49,6 +49,15 @@ import static 
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaLabe
 
 public class SpOpcUaConfigExtractor {
 
+
+  public static OpcUaAdapterConfig 
extractAdapterConfig(IStaticPropertyExtractor extractor,
+                                                        IStreamPipesClient 
streamPipesClient,
+                                                        String adapterId) {
+    var config = extractAdapterConfig(extractor, streamPipesClient);
+    config.setAssociatedResourceId(adapterId);
+    return config;
+  }
+
   /**
    * Creates {@link OpcUaAdapterConfig}  instance in accordance with the given
    * {@link org.apache.streampipes.sdk.extractor.StaticPropertyExtractor}.
@@ -116,6 +125,7 @@ public class SpOpcUaConfigExtractor {
             streamPipesClient
         )
     );
+    config.setStreamPipesClient(streamPipesClient);
 
     boolean useURL = selectedAlternativeConnection.equals(OPC_URL.name());
     if (useURL) {
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/security/CompositeCertificateValidator.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/security/CompositeCertificateValidator.java
index f5c391ffa7..b228c8be5e 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/security/CompositeCertificateValidator.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/security/CompositeCertificateValidator.java
@@ -19,9 +19,11 @@
 package org.apache.streampipes.extensions.connectors.opcua.config.security;
 
 import org.apache.streampipes.client.api.IStreamPipesClient;
-import org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaUtils;
+import org.apache.streampipes.extensions.connectors.opcua.config.OpcUaConfig;
+import 
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaCertificateUtils;
 import org.apache.streampipes.model.opcua.CertificateBuilder;
 import org.apache.streampipes.model.opcua.CertificateState;
+import org.apache.streampipes.model.opcua.CertificateUtils;
 
 import org.eclipse.milo.opcua.stack.client.security.ClientCertificateValidator;
 import org.eclipse.milo.opcua.stack.core.StatusCodes;
@@ -32,6 +34,8 @@ import 
org.eclipse.milo.opcua.stack.core.util.validation.ValidationCheck;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
 import java.security.cert.PKIXCertPathBuilderResult;
 import java.security.cert.X509CRL;
 import java.security.cert.X509Certificate;
@@ -52,15 +56,18 @@ public class CompositeCertificateValidator implements 
ClientCertificateValidator
       StatusCodes.Bad_SecurityChecksFailed
   );
 
+  private final OpcUaConfig opcUaConfig;
   private final TrustListManager trustListManager;
   private final List<X509Certificate> trustedCerts;
   private final List<ValidationCheck> validationChecks;
   private final IStreamPipesClient streamPipesClient;
 
-  public CompositeCertificateValidator(TrustListManager trustListManager,
+  public CompositeCertificateValidator(OpcUaConfig opcUaConfig,
+                                       TrustListManager trustListManager,
                                        List<X509Certificate> trustedCerts,
                                        List<ValidationCheck> validationChecks,
                                        IStreamPipesClient streamPipesClient) {
+    this.opcUaConfig = opcUaConfig;
     this.trustListManager = trustListManager;
     this.trustedCerts = trustedCerts;
     this.validationChecks = validationChecks;
@@ -96,6 +103,16 @@ public class CompositeCertificateValidator implements 
ClientCertificateValidator
         ValidationCheck.NO_OPTIONAL_CHECKS,
         false
     );
+
+    if (opcUaConfig.getAssociatedResourceId() != null) {
+      try {
+        var thumbprint = CertificateUtils.getThumbprint(peer);
+        opcUaConfig.setCertificateThumbprint(thumbprint);
+        OpcUaCertificateUtils.sendUsageToCore(thumbprint, 
opcUaConfig.getAssociatedResourceId(), streamPipesClient);
+      } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
+        LOG.warn("Error sending certificate to opcUa", e);
+      }
+    }
   }
 
   private X509Certificate getEndEntity(List<X509Certificate> chain) {
@@ -146,7 +163,8 @@ public class CompositeCertificateValidator implements 
ClientCertificateValidator
   private void sendToCore(X509Certificate cert) {
     try {
       var certificate = CertificateBuilder.fromX509(cert, 
CertificateState.REJECTED);
-      
streamPipesClient.customRequest().sendPost(OpcUaUtils.getCoreCertificatePath(), 
certificate);
+      opcUaConfig.setCertificateThumbprint(certificate.getThumbprint());
+      
streamPipesClient.customRequest().sendPost(OpcUaCertificateUtils.getCoreCertificatePath(),
 certificate);
     } catch (Exception ex) {
       LOG.error("Failed to report rejected certificate to API", ex);
     }
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/security/SecurityConfig.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/security/SecurityConfig.java
index d4279162c9..87e624e22d 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/security/SecurityConfig.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/security/SecurityConfig.java
@@ -21,7 +21,8 @@ package 
org.apache.streampipes.extensions.connectors.opcua.config.security;
 import org.apache.streampipes.client.api.IStreamPipesClient;
 import org.apache.streampipes.commons.environment.Environments;
 import org.apache.streampipes.commons.exceptions.SpConfigurationException;
-import org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaUtils;
+import org.apache.streampipes.extensions.connectors.opcua.config.OpcUaConfig;
+import 
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaCertificateUtils;
 import org.apache.streampipes.model.opcua.Certificate;
 
 import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfigBuilder;
@@ -56,11 +57,11 @@ public class SecurityConfig {
     this.streamPipesClient = streamPipesClient;
   }
 
-  public void configureSecurityPolicy(String opcServerUrl,
+  public void configureSecurityPolicy(OpcUaConfig config,
                                       List<EndpointDescription> endpoints,
                                       OpcUaClientConfigBuilder builder)
       throws SpConfigurationException, URISyntaxException {
-    String host = opcServerUrl.split("://")[1].split(":")[0];
+    String host = config.getOpcServerURL().split("://")[1].split(":")[0];
 
     EndpointDescription tmpEndpoint = endpoints
         .stream()
@@ -87,6 +88,7 @@ public class SecurityConfig {
         var loadedCerts = new AtomicReference<>(fetchTrustedCertsFromRest());
 
         var compositeValidator = new CompositeCertificateValidator(
+            config,
             trustListManager,
             loadedCerts.get(),
             List.of(),
@@ -130,7 +132,7 @@ public class SecurityConfig {
 
   private List<X509Certificate> fetchTrustedCertsFromRest() throws 
SpConfigurationException {
     try {
-      var response = 
streamPipesClient.customRequest().getList(OpcUaUtils.getCoreTrustedCertificatePath(),
 Certificate.class);
+      var response = 
streamPipesClient.customRequest().getList(OpcUaCertificateUtils.getCoreTrustedCertificatePath(),
 Certificate.class);
       return response
           .stream()
           .map(res -> {
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/utils/OpcUaCertificateUtils.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/utils/OpcUaCertificateUtils.java
new file mode 100644
index 0000000000..ef4769bb7b
--- /dev/null
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/utils/OpcUaCertificateUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.extensions.connectors.opcua.utils;
+
+import org.apache.streampipes.client.api.IStreamPipesClient;
+import 
org.apache.streampipes.extensions.connectors.opcua.config.security.CompositeCertificateValidator;
+import org.apache.streampipes.model.opcua.CertificateUsage;
+
+import org.eclipse.milo.opcua.stack.core.UaException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ExecutionException;
+
+public class OpcUaCertificateUtils {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(OpcUaCertificateUtils.class);
+
+  public static String getCoreCertificatePath() {
+    return "/api/v2/admin/certificates";
+  }
+
+  public static String getCoreCertificateUsagePath() {
+    return getCoreCertificatePath() + "/usage";
+  }
+
+  public static String getCoreTrustedCertificatePath() {
+    return getCoreCertificatePath() + "/trusted";
+  }
+
+  public static boolean isCertificateException(ExecutionException e) {
+    Throwable cause = e.getCause();
+
+    if (cause instanceof UaException uaException) {
+      return checkAndLogCertificateException(uaException);
+    }
+
+    Throwable nestedCause = cause != null ? cause.getCause() : null;
+    if (nestedCause instanceof UaException uaException) {
+      return checkAndLogCertificateException(uaException);
+    }
+
+    return false;
+  }
+
+  private static boolean checkAndLogCertificateException(UaException e) {
+    var containsRejectedStatusCode = 
CompositeCertificateValidator.REJECTED_STATUS_CODES
+        .contains(e.getStatusCode().getValue());
+
+    if (containsRejectedStatusCode) {
+      var statusCode = 
CompositeCertificateValidator.REJECTED_STATUS_CODES.stream().filter(code -> 
code.equals(e.getStatusCode().getValue())).findFirst();
+      statusCode.ifPresent(sc -> LOG.warn("Status Code: {}", sc));
+    }
+    return containsRejectedStatusCode;
+  }
+
+  public static String makeExceptionMessage(ExecutionException e) {
+    StringBuilder message = new StringBuilder(
+        "The provided certificate could not be trusted. Administrators can 
accept this certificate in the settings. "
+    );
+    Throwable cause = e.getCause();
+    if (cause != null) {
+      message.append("Reason: ").append(cause.getMessage());
+    }
+    return message.toString();
+  }
+
+  public static void sendUsageToCore(String thumbprint,
+                                     String associatedResourceId,
+                                     IStreamPipesClient streamPipesClient) {
+    try {
+      var usage = new CertificateUsage(
+          associatedResourceId,
+          thumbprint
+      );
+
+      streamPipesClient
+          .customRequest()
+          .sendPost(OpcUaCertificateUtils.getCoreCertificateUsagePath(), 
usage);
+
+    } catch (Exception ex) {
+      LOG.error("Failed to report certificate usage to API", ex);
+    }
+  }
+}
diff --git 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/utils/OpcUaUtils.java
 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/utils/OpcUaUtils.java
index 5debd21414..d446168817 100644
--- 
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/utils/OpcUaUtils.java
+++ 
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/utils/OpcUaUtils.java
@@ -26,7 +26,6 @@ import 
org.apache.streampipes.extensions.connectors.opcua.client.OpcUaClientProv
 import 
org.apache.streampipes.extensions.connectors.opcua.config.OpcUaAdapterConfig;
 import 
org.apache.streampipes.extensions.connectors.opcua.config.SharedUserConfiguration;
 import 
org.apache.streampipes.extensions.connectors.opcua.config.SpOpcUaConfigExtractor;
-import 
org.apache.streampipes.extensions.connectors.opcua.config.security.CompositeCertificateValidator;
 import 
org.apache.streampipes.extensions.management.client.StreamPipesClientResolver;
 import 
org.apache.streampipes.model.staticproperty.RuntimeResolvableTreeInputStaticProperty;
 
@@ -34,8 +33,6 @@ import org.eclipse.milo.opcua.sdk.client.api.UaClient;
 import org.eclipse.milo.opcua.stack.core.AttributeId;
 import org.eclipse.milo.opcua.stack.core.UaException;
 import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import java.net.URISyntaxException;
 import java.util.List;
@@ -47,8 +44,6 @@ import java.util.concurrent.ExecutionException;
  */
 public class OpcUaUtils {
 
-  private static final Logger LOG = LoggerFactory.getLogger(OpcUaUtils.class);
-
   private static final String OPC_TCP_PREFIX = "opc.tcp://";
 
   /***
@@ -107,9 +102,9 @@ public class OpcUaUtils {
     } catch (UaException e) {
         throw new 
SpConfigurationException(ExceptionMessageExtractor.getDescription(e), e);
     } catch (ExecutionException | InterruptedException | URISyntaxException e) 
{
-      if (e instanceof ExecutionException && 
isCertificateException((ExecutionException) e)) {
+      if (e instanceof ExecutionException && 
OpcUaCertificateUtils.isCertificateException((ExecutionException) e)) {
         throw new SpConfigurationException(
-            makeExceptionMessage((ExecutionException) e)
+            OpcUaCertificateUtils.makeExceptionMessage((ExecutionException) e)
         );
       } else {
         throw new SpConfigurationException("Could not connect to the OPC UA 
server with the provided settings", e);
@@ -132,49 +127,4 @@ public class OpcUaUtils {
       }
     }).toList();
   }
-
-  public static String getCoreCertificatePath() {
-    return "/api/v2/admin/certificates";
-  }
-
-  public static String getCoreTrustedCertificatePath() {
-    return getCoreCertificatePath() + "/trusted";
-  }
-
-  private static boolean isCertificateException(ExecutionException e) {
-    Throwable cause = e.getCause();
-
-    if (cause instanceof UaException uaException) {
-      return checkAndLogCertificateException(uaException);
-    }
-
-    Throwable nestedCause = cause != null ? cause.getCause() : null;
-    if (nestedCause instanceof UaException uaException) {
-      return checkAndLogCertificateException(uaException);
-    }
-
-    return false;
-  }
-
-  private static boolean checkAndLogCertificateException(UaException e) {
-    var containsRejectedStatusCode = 
CompositeCertificateValidator.REJECTED_STATUS_CODES
-        .contains(e.getStatusCode().getValue());
-
-    if (containsRejectedStatusCode) {
-      var statusCode = 
CompositeCertificateValidator.REJECTED_STATUS_CODES.stream().filter(code -> 
code.equals(e.getStatusCode().getValue())).findFirst();
-      statusCode.ifPresent(sc -> LOG.warn("Status Code: {}", sc));
-    }
-    return containsRejectedStatusCode;
-  }
-
-  private static String makeExceptionMessage(ExecutionException e) {
-    StringBuilder message = new StringBuilder(
-       "The provided certificate could not be trusted. Administrators can 
accept this certificate in the settings. "
-    );
-    Throwable cause = e.getCause();
-    if (cause != null) {
-      message.append("Reason: ").append(cause.getMessage());
-    }
-    return message.toString();
-  }
 }
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/Certificate.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/Certificate.java
index 85ba67e008..93c6ba1b07 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/Certificate.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/Certificate.java
@@ -24,8 +24,10 @@ import org.apache.streampipes.model.shared.api.Storable;
 import com.fasterxml.jackson.annotation.JsonAlias;
 import com.google.gson.annotations.SerializedName;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 @TsModel
 public final class Certificate implements Storable {
@@ -50,11 +52,14 @@ public final class Certificate implements Storable {
   private List<String> extendedKeyUsages;
   private List<String> subjectAlternativeNames;
   private String certificateDerBase64;
+  private String thumbprint;
+  private Set<String> associatedResourceIds;
 
   private CertificateState state;
 
 
   public Certificate() {
+    this.associatedResourceIds = new HashSet<>();
   }
 
   public Certificate(String subjectDn,
@@ -201,13 +206,33 @@ public final class Certificate implements Storable {
     this.state = state;
   }
 
+  public String getThumbprint() {
+    return thumbprint;
+  }
+
+  public void setThumbprint(String thumbprint) {
+    this.thumbprint = thumbprint;
+  }
+
+  public Set<String> getAssociatedResourceIds() {
+    return associatedResourceIds;
+  }
+
+  public void setAssociatedResourceIds(Set<String> associatedResourceIds) {
+    this.associatedResourceIds = associatedResourceIds;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (o == null || getClass() != o.getClass()) {
       return false;
     }
     Certificate that = (Certificate) o;
-    return Objects.equals(getSubjectDn(), that.getSubjectDn()) && 
Objects.equals(getIssuerDn(), that.getIssuerDn()) && 
Objects.equals(getSerialNumber(), that.getSerialNumber()) && 
Objects.equals(getNotBefore(), that.getNotBefore()) && 
Objects.equals(getNotAfter(), that.getNotAfter()) && 
Objects.equals(getSigAlgName(), that.getSigAlgName()) && 
Objects.equals(getAlgorithm(), that.getAlgorithm()) && 
Objects.equals(getCertificateDerBase64(), that.getCertificateDerBase64()) && 
getState() == t [...]
+    if (getThumbprint() != null && that.getThumbprint() != null) {
+      return getThumbprint().equals(that.getThumbprint());
+    } else {
+      return getCertificateDerBase64().equals(that.getCertificateDerBase64());
+    }
   }
 
   @Override
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateBuilder.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateBuilder.java
index 716e264157..191ff612dc 100644
--- 
a/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateBuilder.java
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateBuilder.java
@@ -18,6 +18,9 @@
 
 package org.apache.streampipes.model.opcua;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import javax.security.auth.x500.X500Principal;
 
 import java.math.BigInteger;
@@ -37,6 +40,8 @@ import java.util.Objects;
 
 public final class CertificateBuilder {
 
+  private static final Logger LOG = 
LoggerFactory.getLogger(CertificateBuilder.class);
+
   // fluent setters
   public CertificateBuilder subjectDn(String v) {
     cert.setSubjectDn(v);
@@ -128,6 +133,11 @@ public final class CertificateBuilder {
         .build();
 
     certificate.setState(state);
+    try {
+      certificate.setThumbprint(CertificateUtils.getThumbprint(cert));
+    } catch (Exception e) {
+      LOG.warn("Could not create thumbprint for certificate: {}", 
e.getMessage());
+    }
 
     return certificate;
   }
diff --git 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateUsage.java
similarity index 70%
copy from 
ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
copy to 
streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateUsage.java
index 0fed18c7fa..3b43786a58 100644
--- 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateUsage.java
@@ -1,4 +1,4 @@
-/*!
+/*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
@@ -16,22 +16,8 @@
  *
  */
 
-.service-tag {
-    border-radius: 5px;
-    margin-right: 10px;
-    margin-top: 5px;
-    margin-bottom: 5px;
-    border: 1px solid var(--color-bg-3);
-    display: inline-block;
-    padding-top: 5px;
-    padding-bottom: 5px;
-}
-
-.service-tag-prefix {
-    padding: 5px;
-}
+package org.apache.streampipes.model.opcua;
 
-.service-tag-value {
-    padding: 5px;
-    font-weight: bold;
+public record CertificateUsage(String associatedResourceId,
+                               String thumbprint) {
 }
diff --git 
a/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateUtils.java
 
b/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateUtils.java
new file mode 100644
index 0000000000..2ca092e2b8
--- /dev/null
+++ 
b/streampipes-model/src/main/java/org/apache/streampipes/model/opcua/CertificateUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.model.opcua;
+
+import java.io.ByteArrayInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.HexFormat;
+
+public class CertificateUtils {
+
+  public static String getThumbprint(X509Certificate certificate) throws 
NoSuchAlgorithmException, CertificateEncodingException {
+    MessageDigest md = MessageDigest.getInstance("SHA-256");
+    byte[] digest = md.digest(certificate.getEncoded());
+    return HexFormat.of().withUpperCase().formatHex(digest);
+  }
+
+  public static String getThumbprint(String certificateDerBase64) throws 
CertificateException, NoSuchAlgorithmException {
+      byte[] derBytes = Base64.getDecoder().decode(certificateDerBase64);
+
+      CertificateFactory cf = CertificateFactory.getInstance("X.509");
+      X509Certificate cert = (X509Certificate) cf.generateCertificate(
+          new ByteArrayInputStream(derBytes));
+
+      return getThumbprint(cert);
+  }
+}
diff --git 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java
 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java
index 8e3d4d4a85..f9f99d4a9d 100644
--- 
a/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java
+++ 
b/streampipes-resource-management/src/main/java/org/apache/streampipes/resource/management/AdapterResourceManager.java
@@ -19,20 +19,27 @@ package org.apache.streampipes.resource.management;
 
 import org.apache.streampipes.commons.exceptions.connect.AdapterException;
 import org.apache.streampipes.model.connect.adapter.AdapterDescription;
+import org.apache.streampipes.model.opcua.Certificate;
 import org.apache.streampipes.model.util.Cloner;
 import org.apache.streampipes.resource.management.secret.SecretProvider;
+import org.apache.streampipes.storage.api.CRUDStorage;
 import org.apache.streampipes.storage.api.IAdapterStorage;
 import org.apache.streampipes.storage.management.StorageDispatcher;
 
 public class AdapterResourceManager extends 
AbstractResourceManager<IAdapterStorage> {
 
-  public AdapterResourceManager(IAdapterStorage adapterStorage) {
+  private final CRUDStorage<Certificate> certificateStorage;
+
+  public AdapterResourceManager(IAdapterStorage adapterStorage,
+                                CRUDStorage<Certificate> certificateStorage) {
     super(adapterStorage);
+    this.certificateStorage = certificateStorage;
   }
 
   public AdapterResourceManager() {
     super(StorageDispatcher.INSTANCE.getNoSqlStore()
                                     .getAdapterInstanceStorage());
+    this.certificateStorage = 
StorageDispatcher.INSTANCE.getNoSqlStore().getCertificateStorage();
   }
 
   /**
@@ -69,6 +76,16 @@ public class AdapterResourceManager extends 
AbstractResourceManager<IAdapterStor
   }
 
   public void delete(String elementId) {
+    // Remove references from certificates
+    certificateStorage
+        .findAll()
+            .stream().filter(c -> 
c.getAssociatedResourceIds().contains(elementId))
+            .forEach(c -> {
+              c.getAssociatedResourceIds().remove(elementId);
+              certificateStorage.updateElement(c);
+            });
+
+    // Then delete the adapter
     db.deleteElementById(elementId);
   }
 
diff --git 
a/streampipes-resource-management/src/test/java/org/apache/streampipes/resource/management/AdapterResourceManagerTest.java
 
b/streampipes-resource-management/src/test/java/org/apache/streampipes/resource/management/AdapterResourceManagerTest.java
index d4616713da..7d9bfcda6a 100644
--- 
a/streampipes-resource-management/src/test/java/org/apache/streampipes/resource/management/AdapterResourceManagerTest.java
+++ 
b/streampipes-resource-management/src/test/java/org/apache/streampipes/resource/management/AdapterResourceManagerTest.java
@@ -43,7 +43,7 @@ public class AdapterResourceManagerTest {
   @BeforeEach
   void setUp() {
     storage = mock(IAdapterStorage.class);
-    adapterResourceManager = new AdapterResourceManager(storage);
+    adapterResourceManager = new AdapterResourceManager(storage, null);
   }
 
   @Test
diff --git 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/CertificateResource.java
 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/CertificateResource.java
index a466b52c20..d39c172afc 100644
--- 
a/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/CertificateResource.java
+++ 
b/streampipes-rest/src/main/java/org/apache/streampipes/rest/impl/admin/CertificateResource.java
@@ -20,6 +20,7 @@ package org.apache.streampipes.rest.impl.admin;
 
 import org.apache.streampipes.model.opcua.Certificate;
 import org.apache.streampipes.model.opcua.CertificateState;
+import org.apache.streampipes.model.opcua.CertificateUsage;
 import 
org.apache.streampipes.rest.core.base.impl.AbstractAuthGuardedRestResource;
 import org.apache.streampipes.rest.security.AuthConstants;
 import org.apache.streampipes.storage.api.CRUDStorage;
@@ -39,6 +40,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
+import java.util.Objects;
 
 @RestController
 @PreAuthorize(AuthConstants.IS_ADMIN_ROLE)
@@ -87,7 +89,19 @@ public class CertificateResource extends 
AbstractAuthGuardedRestResource {
     } else {
       LOG.info("Certificate with IssuerDN {} already exists, skipping 
creation", certificate.getIssuerDn());
     }
+  }
 
+  @PostMapping(value = "usage", consumes = MediaType.APPLICATION_JSON_VALUE, 
produces = MediaType.APPLICATION_JSON_VALUE)
+  public void updateUsage(@RequestBody CertificateUsage certificateUsage) {
+    var certificates = certificateStorage.findAll();
+    certificates
+        .stream()
+        .filter(c -> Objects.nonNull(c.getThumbprint()) && 
c.getThumbprint().equals(certificateUsage.thumbprint()))
+        .findFirst()
+        .ifPresent(c -> {
+          
c.getAssociatedResourceIds().add(certificateUsage.associatedResourceId());
+          certificateStorage.updateElement(c);
+        });
   }
 
   @DeleteMapping(value = "{id}", produces = MediaType.APPLICATION_JSON_VALUE)
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
index c1f72c466c..0969bf8f8e 100644
--- 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/AvailableMigrations.java
@@ -31,6 +31,7 @@ import 
org.apache.streampipes.service.core.migrations.v0980.FixImportedPermissio
 import 
org.apache.streampipes.service.core.migrations.v0980.ModifyAssetLinkTypesMigration;
 import 
org.apache.streampipes.service.core.migrations.v0980.ModifyAssetLinksMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.AddAssetManagementViewMigration;
+import 
org.apache.streampipes.service.core.migrations.v099.ComputeCertificateThumbprintMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.CreateAssetPermissionMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.MoveAssetContentMigration;
 import 
org.apache.streampipes.service.core.migrations.v099.RemoveObsoletePrivilegesMigration;
@@ -70,7 +71,8 @@ public class AvailableMigrations {
         new MoveAssetContentMigration(),
         new CreateAssetPermissionMigration(),
         new RemoveObsoletePrivilegesMigration(),
-        new UniqueDashboardIdMigration()
+        new UniqueDashboardIdMigration(),
+        new ComputeCertificateThumbprintMigration()
     );
   }
 }
diff --git 
a/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/ComputeCertificateThumbprintMigration.java
 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/ComputeCertificateThumbprintMigration.java
new file mode 100644
index 0000000000..625c843eb7
--- /dev/null
+++ 
b/streampipes-service-core/src/main/java/org/apache/streampipes/service/core/migrations/v099/ComputeCertificateThumbprintMigration.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.service.core.migrations.v099;
+
+import org.apache.streampipes.model.opcua.Certificate;
+import org.apache.streampipes.model.opcua.CertificateUtils;
+import org.apache.streampipes.service.core.migrations.Migration;
+import org.apache.streampipes.storage.api.CRUDStorage;
+import org.apache.streampipes.storage.management.StorageDispatcher;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+
+public class ComputeCertificateThumbprintMigration implements Migration {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(ComputeCertificateThumbprintMigration.class);
+
+  private CRUDStorage<Certificate> certificateStorage =
+      StorageDispatcher.INSTANCE.getNoSqlStore().getCertificateStorage();
+
+  @Override
+  public boolean shouldExecute() {
+    return true;
+  }
+
+  @Override
+  public void executeMigration() throws IOException {
+    this.certificateStorage
+        .findAll()
+        .stream()
+        .filter(certificate -> certificate.getThumbprint() == null)
+        .forEach(certificate -> {
+          try {
+            
certificate.setThumbprint(CertificateUtils.getThumbprint(certificate.getCertificateDerBase64()));
+            certificateStorage.updateElement(certificate);
+          } catch (CertificateException e) {
+            throw new RuntimeException(e);
+          } catch (NoSuchAlgorithmException e) {
+            LOG.warn("Could not compute thumbprint for existing certificate: 
{}", e.getMessage());
+          }
+        });
+  }
+
+  @Override
+  public String getDescription() {
+    return "Adding thumbprint to existing certificates";
+  }
+}
diff --git 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
index edb971862f..8d6dc8bd5e 100644
--- 
a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
+++ 
b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
@@ -20,7 +20,7 @@
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 3.2.1263 on 2025-12-04 
17:41:18.
+// Generated using typescript-generator version 3.2.1263 on 2025-12-10 
08:38:24.
 
 export class NamedStreamPipesEntity implements Storable {
     '@class':
@@ -180,23 +180,6 @@ export class AdapterEventPreview {
     }
 }
 
-export class AdapterType {
-    code: string;
-    description: string;
-    label: string;
-
-    static fromData(data: AdapterType, target?: AdapterType): AdapterType {
-        if (!data) {
-            return data;
-        }
-        const instance = target || new AdapterType();
-        instance.code = data.code;
-        instance.description = data.description;
-        instance.label = data.label;
-        return instance;
-    }
-}
-
 export class TransformationRuleDescription {
     '@class':
         | 
'org.apache.streampipes.model.connect.rules.value.ValueTransformationRuleDescription'
@@ -749,6 +732,7 @@ export class CanvasPosition {
 
 export class Certificate implements Storable {
     algorithm: string;
+    associatedResourceIds: string[];
     basicConstraints: string;
     certificateDerBase64: string;
     elementId: string;
@@ -763,6 +747,7 @@ export class Certificate implements Storable {
     state: CertificateState;
     subjectAlternativeNames: string[];
     subjectDn: string;
+    thumbprint: string;
 
     static fromData(data: Certificate, target?: Certificate): Certificate {
         if (!data) {
@@ -770,6 +755,9 @@ export class Certificate implements Storable {
         }
         const instance = target || new Certificate();
         instance.algorithm = data.algorithm;
+        instance.associatedResourceIds = 
__getCopyArrayFn(__identity<string>())(
+            data.associatedResourceIds,
+        );
         instance.basicConstraints = data.basicConstraints;
         instance.certificateDerBase64 = data.certificateDerBase64;
         instance.elementId = data.elementId;
@@ -790,6 +778,7 @@ export class Certificate implements Storable {
             __identity<string>(),
         )(data.subjectAlternativeNames);
         instance.subjectDn = data.subjectDn;
+        instance.thumbprint = data.thumbprint;
         return instance;
     }
 }
diff --git a/ui/src/app/assets/assets.module.ts 
b/ui/src/app/assets/assets.module.ts
index d0ac72f1b6..13bd455aa4 100644
--- a/ui/src/app/assets/assets.module.ts
+++ b/ui/src/app/assets/assets.module.ts
@@ -68,6 +68,7 @@ import { SpAssetSelectionMenuComponent } from 
'./components/asset-details/edit-a
 import { AssetLinkTableComponent } from 
'./components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component';
 import { AssetLinkTableTypeComponent } from 
'./components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table-link-type/asset-link-table-type.component';
 import { AssetDetailsCustomFieldsComponent } from 
'./components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-custom-fields/asset-details-custom-fields.component';
+import { AssetLinkTableAdditionalDataComponent } from 
'./components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table-additional-data/asset-link-table-additional-data.component';
 
 @NgModule({
     imports: [
@@ -151,6 +152,7 @@ import { AssetDetailsCustomFieldsComponent } from 
'./components/asset-details/ed
         AssetLinkTableComponent,
         AssetLinkTableTypeComponent,
         AssetDetailsCustomFieldsComponent,
+        AssetLinkTableAdditionalDataComponent,
     ],
     providers: [],
 })
diff --git 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component.ts
 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component.ts
index d803cb774e..2fada3c2f8 100644
--- 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details-panel/asset-details-basics/asset-details-labels/asset-details-labels.component.ts
@@ -33,9 +33,9 @@ import {
 import { MatChipInputEvent } from '@angular/material/chips';
 import { FormControl } from '@angular/forms';
 import { COMMA, ENTER } from '@angular/cdk/keycodes';
-import { Observable, of } from 'rxjs';
+import { Observable } from 'rxjs';
 import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
-import { filter, map, startWith } from 'rxjs/operators';
+import { map, startWith } from 'rxjs/operators';
 import { SpColorizationService } from '@streampipes/shared-ui';
 
 @Component({
diff --git 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-basics/view-assset-basics.component.ts
 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-basics/view-assset-basics.component.ts
index 52e7ccc2d4..8a3ae225fa 100644
--- 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-basics/view-assset-basics.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-basics/view-assset-basics.component.ts
@@ -16,13 +16,7 @@
  *
  */
 
-import {
-    Component,
-    Input,
-    OnChanges,
-    OnInit,
-    SimpleChanges,
-} from '@angular/core';
+import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
 import {
     AssetSiteDesc,
     Isa95TypeService,
diff --git 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table-additional-data/asset-link-table-additional-data.component.html
 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table-additional-data/asset-link-table-additional-data.component.html
new file mode 100644
index 0000000000..4cad5a6116
--- /dev/null
+++ 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table-additional-data/asset-link-table-additional-data.component.html
@@ -0,0 +1,29 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~    http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+@if (showCertificateInfo) {
+    <div>
+        <button
+            mat-icon-button
+            (click)="openCertificateDetailsDialog()"
+            [disabled]="matchingCertificates.length === 0"
+        >
+            <mat-icon>approval</mat-icon>
+        </button>
+    </div>
+}
diff --git 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table-additional-data/asset-link-table-additional-data.component.ts
 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table-additional-data/asset-link-table-additional-data.component.ts
new file mode 100644
index 0000000000..1a8fb20318
--- /dev/null
+++ 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table-additional-data/asset-link-table-additional-data.component.ts
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import {
+    Component,
+    inject,
+    Input,
+    OnChanges,
+    SimpleChanges,
+} from '@angular/core';
+import { AssetLink, Certificate } from '@streampipes/platform-services';
+import { CertificateDetailsDialogComponent } from 
'../../../../../../../core-ui/certificate-details/certificate-details-dialog.component';
+import { DialogService, PanelType } from '@streampipes/shared-ui';
+import { TranslateService } from '@ngx-translate/core';
+
+@Component({
+    selector: 'sp-asset-link-table-additional-data',
+    templateUrl: './asset-link-table-additional-data.component.html',
+    standalone: false,
+})
+export class AssetLinkTableAdditionalDataComponent implements OnChanges {
+    @Input()
+    assetLink: AssetLink;
+
+    @Input()
+    certificates: Certificate[] = [];
+
+    @Input()
+    isAdminUser = false;
+
+    showCertificateInfo = false;
+    matchingCertificates: Certificate[] = [];
+
+    private dialogService = inject(DialogService);
+    private translateService = inject(TranslateService);
+
+    ngOnChanges(changes: SimpleChanges) {
+        this.findAssociatedCertificates();
+    }
+
+    findAssociatedCertificates() {
+        this.matchingCertificates = this.certificates.filter(
+            c =>
+                c.associatedResourceIds.find(
+                    resourceId => resourceId === this.assetLink.resourceId,
+                ) !== undefined,
+        );
+        this.showCertificateInfo = this.matchingCertificates.length > 0;
+    }
+
+    openCertificateDetailsDialog(): void {
+        this.dialogService.open(CertificateDetailsDialogComponent, {
+            title: this.translateService.instant('Certificate details'),
+            panelType: PanelType.STANDARD_PANEL,
+            width: '60vw',
+            data: {
+                certificate: this.matchingCertificates[0],
+            },
+        });
+    }
+}
diff --git 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component.html
 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component.html
index b3f47f1197..2b71fa7ce9 100644
--- 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component.html
+++ 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component.html
@@ -66,7 +66,14 @@
         </ng-container>
         <ng-container matColumnDef="additionalInfo">
             <th mat-header-cell *matHeaderCellDef>Info</th>
-            <td mat-cell *matCellDef="let assetLink"></td>
+            <td mat-cell *matCellDef="let assetLink">
+                <sp-asset-link-table-additional-data
+                    [isAdminUser]="isAdminUser"
+                    [assetLink]="assetLink"
+                    [certificates]="certificates"
+                >
+                </sp-asset-link-table-additional-data>
+            </td>
         </ng-container>
 
         <ng-template spTableActions let-element>
diff --git 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component.ts
 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component.ts
index 07e455c6f5..5e3c06c98c 100644
--- 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/asset-link-table/asset-link-table.component.ts
@@ -22,6 +22,7 @@ import {
     inject,
     Input,
     OnChanges,
+    OnDestroy,
     OnInit,
     SimpleChanges,
     ViewChild,
@@ -29,15 +30,24 @@ import {
 import {
     AssetLink,
     AssetLinkType,
+    Certificate,
+    CertificateService,
     SpAsset,
     SpAssetModel,
 } from '@streampipes/platform-services';
 import { MatSort } from '@angular/material/sort';
 import { MatTableDataSource } from '@angular/material/table';
 import { EditAssetLinkDialogComponent } from 
'../../../../../dialog/edit-asset-link/edit-asset-link-dialog.component';
-import { DialogService, PanelType } from '@streampipes/shared-ui';
+import {
+    CurrentUserService,
+    DialogService,
+    PanelType,
+} from '@streampipes/shared-ui';
 import { TranslateService } from '@ngx-translate/core';
 import { Router } from '@angular/router';
+import { AuthService } from '../../../../../../services/auth.service';
+import { UserRole } from '../../../../../../_enums/user-role.enum';
+import { Subscription } from 'rxjs';
 
 @Component({
     selector: 'sp-asset-link-table',
@@ -45,7 +55,7 @@ import { Router } from '@angular/router';
     standalone: false,
 })
 export class AssetLinkTableComponent
-    implements OnInit, AfterViewInit, OnChanges
+    implements OnInit, AfterViewInit, OnChanges, OnDestroy
 {
     @Input()
     assetModel: SpAssetModel;
@@ -70,12 +80,29 @@ export class AssetLinkTableComponent
     ];
 
     dataSource: MatTableDataSource<AssetLink> = new MatTableDataSource();
+    isAdminUser = false;
+    certificates: Certificate[] = [];
+
+    user$: Subscription;
 
     private dialogService = inject(DialogService);
     private translateService = inject(TranslateService);
     private router = inject(Router);
+    private currentUserService = inject(CurrentUserService);
+    private authService = inject(AuthService);
+    private certificateService = inject(CertificateService);
 
     ngOnInit() {
+        this.user$ = this.currentUserService.user$.subscribe(user => {
+            this.isAdminUser = this.authService.hasRole(UserRole.ROLE_ADMIN);
+            if (this.isAdminUser) {
+                this.certificateService
+                    .getAllCertificates()
+                    .subscribe(certificates => {
+                        this.certificates = certificates;
+                    });
+            }
+        });
         this.dataSource.sortingDataAccessor = (link, column) => {
             if (column === 'type') {
                 return link.linkType;
@@ -141,4 +168,8 @@ export class AssetLinkTableComponent
         this.asset.assetLinks = [...this.asset.assetLinks];
         this.refreshData();
     }
+
+    ngOnDestroy() {
+        this.user$?.unsubscribe();
+    }
 }
diff --git 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/view-asset-links.component.ts
 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/view-asset-links.component.ts
index 97a4bf99a0..1cf6d31c1a 100644
--- 
a/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/view-asset-links.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/view-asset/view-asset-links/view-asset-links.component.ts
@@ -16,7 +16,7 @@
  *
  */
 
-import { Component, Input, OnInit } from '@angular/core';
+import { Component, inject, Input, OnInit } from '@angular/core';
 import {
     AssetConstants,
     AssetLinkType,
@@ -36,7 +36,7 @@ export class ViewAssetLinksComponent implements OnInit {
 
     assetLinkTypes: AssetLinkType[] = [];
 
-    constructor(private genericStorageService: GenericStorageService) {}
+    private genericStorageService = inject(GenericStorageService);
 
     ngOnInit() {
         this.genericStorageService
diff --git a/ui/src/app/configuration/configuration.module.ts 
b/ui/src/app/configuration/configuration.module.ts
index 71a8f65e77..05f5dd7137 100644
--- a/ui/src/app/configuration/configuration.module.ts
+++ b/ui/src/app/configuration/configuration.module.ts
@@ -98,11 +98,11 @@ import { GenericStorageItemComponent } from 
'./export/export-dialog/generic-stor
 import { GenericStorageItemsComponent } from 
'./export/export-dialog/generic-storage-items/generic-storage-items.component';
 import { TranslateModule } from '@ngx-translate/core';
 import { CertificateConfigurationComponent } from 
'./extensions-service-management/certificate-configuration/certificate-configuration.component';
-import { CertificateDetailsDialogComponent } from 
'./dialog/certificate-details/certificate-details-dialog.component';
 import { AlternateIdConfigurationComponent } from 
'./security-configuration/alternate-id-configuration/alternate-id-configuration.component';
 import { UserAcknowledgmentComponent } from 
'./general-configuration/user-acknowledgement/user-acknowledgment.component';
 import { QuillEditorComponent } from 'ngx-quill';
 import { MatStepperModule } from '@angular/material/stepper';
+import { CertificateLabelComponent } from 
'./extensions-service-management/certificate-configuration/certificate-label/certificate-label.component';
 
 @NgModule({
     imports: [
@@ -254,9 +254,9 @@ import { MatStepperModule } from 
'@angular/material/stepper';
         PipelineElementInstallationStatusFilter,
         PipelineElementTypeFilter,
         CertificateConfigurationComponent,
-        CertificateDetailsDialogComponent,
         AlternateIdConfigurationComponent,
         UserAcknowledgmentComponent,
+        CertificateLabelComponent,
     ],
     providers: [
         OrderByPipe,
diff --git 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.html
 
b/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.html
index e079412736..8122b4f195 100644
--- 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.html
+++ 
b/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.html
@@ -39,14 +39,14 @@
                 <div fxFlex="20">{{ 'Tags' | translate }}</div>
                 <div fxFlex="80">
                     <div>
-                        @for (tag of serviceReg.tags; track tag) {
+                        @for (tag of sortedTags; track tag) {
                             <div class="service-tag text-sm">
                                 <span class="service-tag-prefix">{{
                                     tag.prefix.toLowerCase()
                                 }}</span>
-                                <span class="service-tag-value">{{
-                                    tag.value
-                                }}</span>
+                                <span class="service-tag-value"
+                                    >&nbsp;{{ tag.value }}</span
+                                >
                             </div>
                         }
                     </div>
diff --git 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
 
b/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
index 0fed18c7fa..425e13e1b2 100644
--- 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
+++ 
b/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
@@ -17,21 +17,16 @@
  */
 
 .service-tag {
-    border-radius: 5px;
+    border-radius: 20px;
     margin-right: 10px;
     margin-top: 5px;
     margin-bottom: 5px;
     border: 1px solid var(--color-bg-3);
+    background: var(--color-bg-3);
     display: inline-block;
-    padding-top: 5px;
-    padding-bottom: 5px;
+    padding: 5px 10px;
 }
 
 .service-tag-prefix {
-    padding: 5px;
-}
-
-.service-tag-value {
-    padding: 5px;
     font-weight: bold;
 }
diff --git 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.ts
 
b/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.ts
index d225e3cd1f..b450b1124c 100644
--- 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.ts
+++ 
b/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.ts
@@ -37,4 +37,10 @@ export class SpExtensionsServiceDetailsDialogComponent {
     close() {
         this.dialogRef.close();
     }
+
+    get sortedTags() {
+        return [...this.serviceReg.tags].sort((a, b) =>
+            a.prefix.localeCompare(b.prefix),
+        );
+    }
 }
diff --git 
a/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.html
 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.html
index d7bd8c7823..651a1e2d6f 100644
--- 
a/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.html
+++ 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.html
@@ -40,7 +40,10 @@
                 *matCellDef="let certificate"
                 data-cy="user-accounts-table-row"
             >
-                <b>{{ certificate.issuerDn }}</b>
+                <sp-certificate-label
+                    style="display: block"
+                    [certificate]="certificate"
+                ></sp-certificate-label>
             </td>
         </ng-container>
         <ng-container matColumnDef="expires">
@@ -60,7 +63,7 @@
                 *matCellDef="let certificate"
                 data-cy="user-accounts-table-row"
             >
-                <b>{{ certificate.notAfter }}</b>
+                <span class="text-sm">{{ certificate.notAfter }}</span>
             </td>
         </ng-container>
         <ng-container matColumnDef="actions">
diff --git 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.scss
similarity index 70%
copy from 
ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
copy to 
ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.scss
index 0fed18c7fa..f2c005a77d 100644
--- 
a/ui/src/app/configuration/dialog/extensions-service-details/extensions-service-details-dialog.component.scss
+++ 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.scss
@@ -16,22 +16,6 @@
  *
  */
 
-.service-tag {
-    border-radius: 5px;
-    margin-right: 10px;
-    margin-top: 5px;
-    margin-bottom: 5px;
-    border: 1px solid var(--color-bg-3);
-    display: inline-block;
-    padding-top: 5px;
-    padding-bottom: 5px;
-}
-
-.service-tag-prefix {
-    padding: 5px;
-}
-
-.service-tag-value {
-    padding: 5px;
-    font-weight: bold;
+:host {
+    --mat-table-row-item-container-height: 100%;
 }
diff --git 
a/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.ts
 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.ts
index 66187a6a03..5458282869 100644
--- 
a/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.ts
+++ 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-configuration.component.ts
@@ -24,13 +24,14 @@ import {
 } from '@streampipes/platform-services';
 import { MatTableDataSource } from '@angular/material/table';
 import { DialogService, PanelType } from '@streampipes/shared-ui';
-import { CertificateDetailsDialogComponent } from 
'../../dialog/certificate-details/certificate-details-dialog.component';
+import { CertificateDetailsDialogComponent } from 
'../../../core-ui/certificate-details/certificate-details-dialog.component';
 import { TranslateService } from '@ngx-translate/core';
 
 @Component({
     selector: 'sp-certificate-configuration',
     standalone: false,
     templateUrl: './certificate-configuration.component.html',
+    styleUrls: ['./certificate-configuration.component.scss'],
 })
 export class CertificateConfigurationComponent implements OnInit {
     private certificateService = inject(CertificateService);
diff --git 
a/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-label/certificate-label.component.html
 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-label/certificate-label.component.html
new file mode 100644
index 0000000000..97ffc23465
--- /dev/null
+++ 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-label/certificate-label.component.html
@@ -0,0 +1,28 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~    http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<div fxLayout="column" class="pt-sm pb-sm">
+    <span class="text-sm">
+        {{ certificate.issuerDn }}
+    </span>
+    <div fxLayout="row wrap" class="mt-sm">
+        @for (dnsName of dnsNames; track $index) {
+            <sp-label size="small" [labelText]="dnsName"></sp-label>
+        }
+    </div>
+</div>
diff --git 
a/ui/src/app/configuration/dialog/certificate-details/certificate-details-dialog.component.ts
 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-label/certificate-label.component.ts
similarity index 65%
copy from 
ui/src/app/configuration/dialog/certificate-details/certificate-details-dialog.component.ts
copy to 
ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-label/certificate-label.component.ts
index 16c6b495d6..8e5afae50a 100644
--- 
a/ui/src/app/configuration/dialog/certificate-details/certificate-details-dialog.component.ts
+++ 
b/ui/src/app/configuration/extensions-service-management/certificate-configuration/certificate-label/certificate-label.component.ts
@@ -16,28 +16,24 @@
  *
  */
 
-import { Component, inject, Input } from '@angular/core';
-import { DialogRef } from '@streampipes/shared-ui';
+import { Component, Input, OnInit } from '@angular/core';
 import { Certificate } from '@streampipes/platform-services';
 
 @Component({
-    selector: 'sp-certificate-details-dialog',
-    templateUrl: './certificate-details-dialog.component.html',
+    selector: 'sp-certificate-label',
     standalone: false,
+    templateUrl: './certificate-label.component.html',
 })
-export class CertificateDetailsDialogComponent {
-    dialogRef = inject(DialogRef<CertificateDetailsDialogComponent>);
-
+export class CertificateLabelComponent implements OnInit {
     @Input()
     certificate: Certificate;
 
-    isArray(value: any): boolean {
-        return Array.isArray(value);
-    }
+    dnsNames: string[] = [];
 
-    close(): void {
-        this.dialogRef.close();
+    ngOnInit(): void {
+        this.dnsNames = this.certificate.subjectAlternativeNames.filter(san =>
+            san.startsWith('DNS'),
+        );
+        console.log(this.dnsNames);
     }
-
-    protected readonly Object = Object;
 }
diff --git 
a/ui/src/app/configuration/dialog/certificate-details/certificate-details-dialog.component.html
 
b/ui/src/app/core-ui/certificate-details/certificate-details-dialog.component.html
similarity index 100%
rename from 
ui/src/app/configuration/dialog/certificate-details/certificate-details-dialog.component.html
rename to 
ui/src/app/core-ui/certificate-details/certificate-details-dialog.component.html
diff --git 
a/ui/src/app/configuration/dialog/certificate-details/certificate-details-dialog.component.ts
 
b/ui/src/app/core-ui/certificate-details/certificate-details-dialog.component.ts
similarity index 100%
rename from 
ui/src/app/configuration/dialog/certificate-details/certificate-details-dialog.component.ts
rename to 
ui/src/app/core-ui/certificate-details/certificate-details-dialog.component.ts
diff --git a/ui/src/app/core-ui/core-ui.module.ts 
b/ui/src/app/core-ui/core-ui.module.ts
index 1bec1335d0..b55ea56a43 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -107,6 +107,7 @@ import { YamlPrettyPrintPipe } from 
'./pipes/yaml-pretty-print.pipe';
 import { TopicsComponent } from './topics/topics.component';
 import { TranslateModule } from '@ngx-translate/core';
 import { TextFieldModule } from '@angular/cdk/text-field';
+import { CertificateDetailsDialogComponent } from 
'./certificate-details/certificate-details-dialog.component';
 
 @NgModule({
     imports: [
@@ -203,6 +204,7 @@ import { TextFieldModule } from '@angular/cdk/text-field';
         PipelineOperationStatusComponent,
         JsonPrettyPrintPipe,
         YamlPrettyPrintPipe,
+        CertificateDetailsDialogComponent,
     ],
     providers: [MatDatepickerModule, DisplayRecommendedPipe],
     exports: [
@@ -237,6 +239,7 @@ import { TextFieldModule } from '@angular/cdk/text-field';
         SingleMarkerMapComponent,
         JsonPrettyPrintPipe,
         YamlPrettyPrintPipe,
+        CertificateDetailsDialogComponent,
     ],
 })
 export class CoreUiModule {}
diff --git a/ui/src/scss/sp/_spacing.scss b/ui/src/scss/sp/_spacing.scss
index 06eeac4fe9..fa56d6869e 100644
--- a/ui/src/scss/sp/_spacing.scss
+++ b/ui/src/scss/sp/_spacing.scss
@@ -190,4 +190,46 @@
     padding: var(--space-2xl);
 }
 
-/* More padding directions available on request */
+.pt-2xs {
+    padding-top: var(--space-2xs);
+}
+.pt-xs {
+    padding-top: var(--space-xs);
+}
+.pt-sm {
+    padding-top: var(--space-sm);
+}
+.pt-md {
+    padding-top: var(--space-md);
+}
+.pt-lg {
+    padding-top: var(--space-lg);
+}
+.pt-xl {
+    padding-top: var(--space-xl);
+}
+.pt-2xl {
+    padding-top: var(--space-2xl);
+}
+
+.pb-2xs {
+    padding-bottom: var(--space-2xs);
+}
+.pb-xs {
+    padding-bottom: var(--space-xs);
+}
+.pb-sm {
+    padding-bottom: var(--space-sm);
+}
+.pb-md {
+    padding-bottom: var(--space-md);
+}
+.pb-lg {
+    padding-bottom: var(--space-lg);
+}
+.pb-xl {
+    padding-bottom: var(--space-xl);
+}
+.pb-2xl {
+    padding-bottom: var(--space-2xl);
+}

Reply via email to