VMware {code} Community
wodge
Enthusiast
Enthusiast

Plugin Sample fails with "Host certificate thumbprint did not match expected value"

Intent

I have spent signicant time troubleshooting this error in the plugin sample code and found a way to resolve it (see below). If others should encounter it, then I hope this will provide them with a solution and accelerate their ability to use the sample plugin. It also provides a recommendation for how this issue could be fixed.

Environment

Development machine components:
Mac OS 14.2.1 (23C71)
Java Runtime: Java(TM) SE Runtime Environment 1.8.0_381-b09
vSphere client SDK: v8.0.2.00000-11654675

vCenter component:
VCSA v8.0.2 build 22617221

Encountered Behaviour

I was following the instructions in the excellent PDF manual "Developing Remote Plug-ins with the vSphere Client SDK Update 2".

In Chapter 1 "Using the vSphere Client Remote Plug-in Samples", I successfully deployed and used the Remote plugin Starter in the "Try the Remote Plugin-Sample Starter" section.

I progressed to the "Remote Plugin-Sample UI and Server". I was able to follow all instructions, and did the following:

  • Build the vSphere Client Remote Plug-in Sample
  • Find the SSL Thumbprint and GUID of vCenter Server
  • Start the Remote Plug-in Server
  • Get Thumbprint or Certificate of Remote Plug-in Server
  • Register the vSphere Client Remote Plug-in Sample
  • Viewing the vSphere Client Remote Plug-in Sample

When I attempted to view the SDK Remote plugin Global View content, I was able to view the Welcome page and setting page successfully.

However, when attempting to access the "Chassis List" page no response was received from the remote plugin server, and the chassis list page hung indefinitely with a spinning wheel.

Diagnosis

On inspecting the running Spring Boot Application Server, I found that an error was being generated when the Remote plugin was attemping to access vCenter, as it does when using this page. The error was as follows:

com.google.common.util.concurrent.UncheckedExecutionException: com.sun.xml.ws.client.ClientTransportException: HTTP transport error: javax.net.ssl.SSLException: java.lang.RuntimeException: 
Host certificate thumbprint did not match expected value.
at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2214) ~[guava-20.0.jar!/:na]
at com.google.common.cache.LocalCache.get(LocalCache.java:4053) ~[guava-20.0.jar!/:na]
at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4057) ~[guava-20.0.jar!/:na]
at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4986) ~[guava-20.0.jar!/:na]
at com.vmware.sample.remote.gateway.SessionServiceImpl.getVimSessionInfo(SessionServiceImpl.java:169) ~[classes!/:na]
at com.vmware.sample.remote.services.SecurityServiceImpl.validateGatewayCredentials(SecurityServiceImpl.java:34) [classes!/:na]
at com.vmware.sample.remote.filters.SecurityFilter.doFilter(SecurityFilter.java:43) [classes!/:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.0.7.RELEASE.jar!/:5.0.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.7.RELEASE.jar!/:5.0.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) [spring-web-5.0.7.RELEASE.jar!/:5.0.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.7.RELEASE.jar!/:5.0.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) [spring-web-5.0.7.RELEASE.jar!/:5.0.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.7.RELEASE.jar!/:5.0.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.0.7.RELEASE.jar!/:5.0.7.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.7.RELEASE.jar!/:5.0.7.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_381]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_381]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
at java.lang.Thread.run(Thread.java:750) [na:1.8.0_381]

I setup a debug session through IntellJ and was able to trace the exception to the following java source file within the remote-plugin-sample file structure:

src/main/java/com/vmware/sample/remote/util/CertificateUtil.java

The following block within CertificateUtil.java was generating the exception:

try {
md.update(certificate.getEncoded());
} catch (final CertificateEncodingException e) {
_logger.error("Unable to get the encoded form of the certificate.");
return false;
}
final String certThumb = DatatypeConverter.printHexBinary(md.digest())
.toLowerCase();

// Check match
if (expectedThumbprint.equalsIgnoreCase(certThumb)) {
return true;
}

When traced:

The expected thumbprint is the value passed using the "--vcenter.thumbprint" parameter when running the .JAR from the target directory. This did not match the thumbprint retrieved from the vCenter server (retrieved by the code into the "certThumb" variable).

I found that the SHA-256 certificate thumbprint passed into the vcenter.thumbprint parameter was, in fact, correct and matched the SHA-256 certificate thumbprint on the VCSA.

Therefore, the value being retrieved from the VCSA by the plugin code could not be the SHA-256 thumbprint. I then found that the plugin code was actually retrieving the SHA-1 thumbprint and NOT the SHA-256 thumprint as expected and documented.

N.B. In the current PDF documentation, see section "Find the SSL Thumbprint and GUID of vCenter Server":

6 Click Details to display more certificate information. The browser displays full details of the vCenter Server certificate.
7 Scroll through the certificate details to find the SHA-256 fingerprint. The SHA-256 fingerprint is a string of 64 hexadecimal digits, usually in pairs separated by spaces or other non-alphanumeric delimiters.
8 Select the fingerprint string and copy it to a text file. 9 Edit the text file to remove all spaces or other delimiters from the fingerprint string. You now have a string of 64 contiguous hexadecimal digits. This is the thumbprint of the vCenter Server instance.

What to do next
The thumbprint and GUID of the vCenter Server instance are needed to start the remote plug-in server."

Resolution / Workaround

In order to work around this issue, I then launched the plugin server from java, but using the SHA-1 thumbprint value  for the "--vcenter.thumbprint" parameter. When this was done, the plugin worked as expected and the Chassis List view was displayed correctly.

Recommendations for Fix

It is recommended that either:

  1. The plugin code is changed in future releases so that CertificateUtil.java compares using the SHA-256 thumbprint (not SHA-1) from the VCSA certificate (as documented).
    OR
  2. The documentation is changed so that developers are instructed to use the SHA-1 thumbprint for the "--vcenter.thumbprint" parameter.

I suspect the first approach is better, due to SHA-256 being preferred over SHA-1.

0 Kudos
1 Reply