From e75dc1dfd005434f54ee3bbf44fdbb1aebec1c60 Mon Sep 17 00:00:00 2001 From: alexzorin Date: Thu, 2 Feb 2023 05:48:13 +1100 Subject: [PATCH] show_account: display account thumbprint (#9540) In #9127, where @osirisinferi added the `show_account` verb, I made a call not to include the thumbprint in the output of `certbot show_account`. In hindsight, and after a community member asked for this feature, I think it's better to include it. It is useful on occasion and `show_account` is fairly specialized anyway. It's only really good for getting your account URL for rate limit increases, checking your contacts, and (now) and doing *magic* with the thumbprint for stateless/distributed HTTP-01 responders. Without this feature, a clever user might figure out their thumbprint by doing a `certonly --manual --preferred-challenges http` request, but most users would probably be lost. * show_account: display account thumbprint * use local key for display --- certbot/CHANGELOG.md | 2 +- certbot/certbot/_internal/main.py | 4 ++++ certbot/tests/main_test.py | 11 +++++++---- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/certbot/CHANGELOG.md b/certbot/CHANGELOG.md index b2762eb11..225bb82e1 100644 --- a/certbot/CHANGELOG.md +++ b/certbot/CHANGELOG.md @@ -6,7 +6,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/). ### Added -* +* `certbot show_account` now displays the [ACME Account Thumbprint](https://datatracker.ietf.org/doc/html/rfc8555#section-8.1). ### Changed diff --git a/certbot/certbot/_internal/main.py b/certbot/certbot/_internal/main.py index 316503c5d..01ee2021d 100644 --- a/certbot/certbot/_internal/main.py +++ b/certbot/certbot/_internal/main.py @@ -17,6 +17,7 @@ from typing import Union import configobj import josepy as jose +from josepy import b64 from acme import client as acme_client from acme import errors as acme_errors @@ -992,6 +993,9 @@ def show_account(config: configuration.NamespaceConfig, output = [f"Account details for server {config.server}:", f" Account URL: {regr.uri}"] + thumbprint = b64.b64encode(acc.key.thumbprint()).decode() + output.append(f" Account Thumbprint: {thumbprint}") + emails = [] for contact in regr.body.contact: diff --git a/certbot/tests/main_test.py b/certbot/tests/main_test.py index e857f6c33..58c4fdeed 100644 --- a/certbot/tests/main_test.py +++ b/certbot/tests/main_test.py @@ -2238,6 +2238,7 @@ class ShowAccountTest(test_util.ConfigTestCase): mock_storage.find_all.return_value = [mock_account] self.mocks['account'].AccountFileStorage.return_value = mock_storage mock_account.regr.body = mock_regr.body + mock_account.key.thumbprint.return_value = b'foobarbaz' self.mocks['determine_account'].return_value = (mock_account, mock.MagicMock()) def _test_show_account(self, contact): @@ -2246,7 +2247,6 @@ class ShowAccountTest(test_util.ConfigTestCase): mock_regr = mock.MagicMock() mock_regr.body.contact = contact mock_regr.uri = 'https://www.letsencrypt-demo.org/acme/reg/1' - mock_regr.body.key.thumbprint.return_value = b'foobarbaz' mock_client.acme.query_registration.return_value = mock_regr self.mocks['client'].Client.return_value = mock_client @@ -2283,7 +2283,8 @@ class ShowAccountTest(test_util.ConfigTestCase): self.mocks['notify'].assert_has_calls([ mock.call('Account details for server https://acme-v02.api.letsencr' 'ypt.org/directory:\n Account URL: https://www.letsencry' - 'pt-demo.org/acme/reg/1\n Email contact: none')]) + 'pt-demo.org/acme/reg/1\n Account Thumbprint: Zm9vYmFyYmF6\n' + ' Email contact: none')]) def test_single_email(self): contact = ('mailto:foo@example.com',) @@ -2293,7 +2294,8 @@ class ShowAccountTest(test_util.ConfigTestCase): self.mocks['notify'].assert_has_calls([ mock.call('Account details for server https://acme-v02.api.letsencr' 'ypt.org/directory:\n Account URL: https://www.letsencry' - 'pt-demo.org/acme/reg/1\n Email contact: foo@example.com')]) + 'pt-demo.org/acme/reg/1\n Account Thumbprint: Zm9vYmFyYmF6' + '\n Email contact: foo@example.com')]) def test_double_email(self): contact = ('mailto:foo@example.com', 'mailto:bar@example.com') @@ -2303,7 +2305,8 @@ class ShowAccountTest(test_util.ConfigTestCase): self.mocks['notify'].assert_has_calls([ mock.call('Account details for server https://acme-v02.api.letsencr' 'ypt.org/directory:\n Account URL: https://www.letsencry' - 'pt-demo.org/acme/reg/1\n Email contacts: foo@example.com, bar@example.com')]) + 'pt-demo.org/acme/reg/1\n Account Thumbprint: Zm9vYmFyYmF6\n' + ' Email contacts: foo@example.com, bar@example.com')]) if __name__ == '__main__':