Skip to content

Documentation could explain CN versus DNS in SSL verification #241

@tovrstra

Description

@tovrstra

The problem

The documentation mentions that the hostname in the SSL verification should be of the form appliance/p1dongle/5c2fafaabbcc. See https://api-documentation.homewizard.com/docs/v2/authorization#hostname-validation

It would be useful to document the difference between the commonName and the DNS name. Some tools use the former, other the latter.

Additional information

One can use the following Python snippet to get both names for one's device:

import ssl
import socket
from pprint import pprint
context = ssl.create_default_context()
context.load_verify_locations('homewizard-ca-cert.pem')
context.check_hostname = False
with socket.create_connection(("192.168.1.100", 443)) as sock:
    with context.wrap_socket(sock, server_hostname="192.168.1.100") as ssock:
        cert = ssock.getpeercert()
        pprint(cert)

This gives for example:

{'issuer': ((('countryName', 'NL'),),
            (('stateOrProvinceName', 'ZH'),),
            (('organizationName', 'HomeWizard'),),
            (('commonName', 'Appliance Access CA'),)),
 'notAfter': 'Dec 31 23:59:59 2049 GMT',
 'notBefore': 'Jan  1 00:00:00 2025 GMT',
 'serialNumber': '5C2FAFAABBCC',
 'subject': ((('commonName', 'appliance/p1dongle/5c2fafaabbcc'),),),
 'subjectAltName': (('DNS', '5c2fafaabbcc.p1dongle.device.homewizard.energy'),),
 'version': 3}

With this information, one can perform SSL verificatoin with cURL:

curl https://5c2fafaabbcc.p1dongle.device.homewizard.energy/api/user \
  --resolve 5c2fafaabbcc.p1dongle.device.homewizard.energy:443:192.168.1.100 \
  --cacert homewizard-ca-cert.pem

(The documentation claims cURL cannot do this, but this seems to work.)

Similarly, if one prefers to use the requests library (convenient for simple synchronous scripting), the following can be used:

import requests
import ssl
from requests.adapters import HTTPAdapter
from urllib.parse import urlparse


class SSLHostAdapter(HTTPAdapter):
    """Inject hostname for SSL verification."""
    def __init__(self, *, hostmap, cafile: str, **kwargs):
        self.hostmap = hostmap
        self.ssl_context = ssl.create_default_context(cafile=cafile)
        self.ssl_context.hostname_checks_common_name = True
        super().__init__(**kwargs)

    def init_poolmanager(self, *args, **kwargs):
        kwargs['ssl_context'] = self.ssl_context
        return super().init_poolmanager(*args, **kwargs)
   
    def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
        conn = super().get_connection_with_tls_context(request, verify, proxies, cert)
        conn.assert_hostname = self.hostmap[urlparse(request.url).hostname]
        return conn


adapter = SSLHostAdapter(
    hostmap={'192.168.1.100': '5c2fafaabbcc.p1dongle.device.homewizard.energy'}, 
    cafile='homewizard-ca-cert.pem'
)
session = requests.Session()
session.mount('https://', adapter)
response = session.get('https://192.168.1.100/api/user')
print(response.text)

I admit, the adapter is a little hacky, but works quite well.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions