JWS verification error on account deactivation preceded by key rollover

Recently I stumbled upon an JWS verification error (urn:ietf:params:acme:error:malformed) error when performing the following steps:

  1. register account with key A
  2. account key rollover to key B
  3. deactivate account

In step 3. the request should be of course signed with the new key B, but if steps 2. and 3. are performed in close succession the account deactivation sometimes fails with the error mentioned above. I assume it is some kind of race condition because when performing step 3. with some time delay the error does not appear. To test this therory I just signed the request to deactivate the account with the old key A. Surprisingly it sometimes works, here is the log:

Array
(
    [A] => -----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDCHsFbPE/8nM+eiPg7yMe+d4v2QpB/nQyxuR5gIs9WGqfSuJ60jk4Md
QkxOs+FOjTqgBwYFK4EEACKhZANiAAQpDvXfSd9Ai7DnJUkJxte4wOn0njha7yV+
uQixKxxMMvxcqa4e8bunxpzSqBREphV/i13+u45+jBBAqzursLeRITBf7+tC2kcv
fnshf5maxBLICRrtJvW5AcsIGDK/J1k=
-----END EC PRIVATE KEY-----

    [B] => -----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDA7/fUghoYAmf2SYM3u4M655puD6ga6fivsVWu+w4zrzaJ9sbDhd7rI
E8x+r0Av9BigBwYFK4EEACKhZANiAASi467i7avYRQVziShx5ZYp+hlif0WlgsR2
lxLo3FLBDpprCid1xcq6g3/fMwahSUgIS1qwNob/dSYBjP8Y8EmMs2MaDEbUsXSp
quLt8SLZ+9jKzWh0ZebiwPq0VCdjRps=
-----END EC PRIVATE KEY-----

)
Registering account
Initializing ACME v2 environment: https://acme-staging-v02.api.letsencrypt.org/directory
Using cURL
  https://acme-staging-v02.api.letsencrypt.org/directory [200] (0.43s)
Receive Array
(
    [dD9Cfj71phw] => https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417
    [keyChange] => https://acme-staging-v02.api.letsencrypt.org/acme/key-change
    [meta] => Array
        (
            [caaIdentities] => Array
                (
                    [0] => letsencrypt.org
                )

            [termsOfService] => https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
            [website] => https://letsencrypt.org/docs/staging-environment/
        )

    [newAccount] => https://acme-staging-v02.api.letsencrypt.org/acme/new-acct
    [newNonce] => https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce
    [newOrder] => https://acme-staging-v02.api.letsencrypt.org/acme/new-order
    [renewalInfo] => https://acme-staging-v02.api.letsencrypt.org/get/draft-aaron-ari/renewalInfo/
    [revokeCert] => https://acme-staging-v02.api.letsencrypt.org/acme/revoke-cert
)
Initialized
  https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce [200] (0.14s)
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [jwk] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => KQ7130nfQIuw5yVJCcbXuMDp9J44Wu8lfrkIsSscTDL8XKmuHvG7p8ac0qgURKYV
                    [y] => f4td_ruOfowQQKs7q7C3kSEwX-_rQtpHL357IX-ZmsQSyAka7Sb1uQHLCBgyvydZ
                )

            [nonce] => 0002B4B-fNfX0Jti4Ww-BGJ68rEXTGIx_ZUFk1EUyLnqvqE
            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/new-acct
        )

    [payload] => Array
        (
            [termsOfServiceAgreed] => 1
            [contact] => Array
                (
                )

        )

    [signature] => bW6bG3mp8WJsckdavK0uXRgApfBIk9UWDx8blLE3_RlyFsbujyCxvCE5FYquI-qX6rVVP4CmL9j6QdZQXcM1Khlt987w4V2RUdubW37D4hqvnSIKGvabF87U9qSAmWmP
)
Sign Key: Array
(
    [x] => KQ7130nfQIuw5yVJCcbXuMDp9J44Wu8lfrkIsSscTDL8XKmuHvG7p8ac0qgURKYV
    [y] => f4td_ruOfowQQKs7q7C3kSEwX-_rQtpHL357IX-ZmsQSyAka7Sb1uQHLCBgyvydZ
)
  https://acme-staging-v02.api.letsencrypt.org/acme/new-acct [201] (1.93s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => KQ7130nfQIuw5yVJCcbXuMDp9J44Wu8lfrkIsSscTDL8XKmuHvG7p8ac0qgURKYV
            [y] => f4td_ruOfowQQKs7q7C3kSEwX-_rQtpHL357IX-ZmsQSyAka7Sb1uQHLCBgyvydZ
        )

    [createdAt] => 2022-04-09T19:05:14.744517854Z
    [status] => valid
)
AccountID: https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318168
Account registered
Account Key Roll-Over
Inner JWS: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [jwk] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => ouOu4u2r2EUFc4koceWWKfoZYn9FpYLEdpcS6NxSwQ6aawondcXKuoN_3zMGoUlI
                    [y] => CEtasDaG_3UmAYz_GPBJjLNjGgxG1LF0qari7fEi2fvYys1odGXm4sD6tFQnY0ab
                )

            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/key-change
        )

    [payload] => Array
        (
            [account] => https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318168
            [oldKey] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => KQ7130nfQIuw5yVJCcbXuMDp9J44Wu8lfrkIsSscTDL8XKmuHvG7p8ac0qgURKYV
                    [y] => f4td_ruOfowQQKs7q7C3kSEwX-_rQtpHL357IX-ZmsQSyAka7Sb1uQHLCBgyvydZ
                )

        )

    [signature] => _1goVwZzjWuOSzwwxxmPOC7I56i63qDoJfVw1DUN72MNaqsha2nFlEadvEgYH9unRdS_s1KmY8Q6IrchExL6H3hvVg0vT2CP9XrwLxSPJOAg6QA9AI2Kgbh-z8H2HoPV
)
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [kid] => https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318168
            [nonce] => 00020a0G6VUvzpNzJEKoUkIAoqpopyFb28lj1iU7rBroIpw
            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/key-change
        )

    [payload] => Array
        (
            [protected] => eyJhbGciOiJFUzM4NCIsImp3ayI6eyJjcnYiOiJQLTM4NCIsImt0eSI6IkVDIiwieCI6Im91T3U0dTJyMkVVRmM0a29jZVdXS2ZvWlluOUZwWUxFZHBjUzZOeFN3UTZhYXdvbmRjWEt1b05fM3pNR29VbEkiLCJ5IjoiQ0V0YXNEYUdfM1VtQVl6X0dQQkpqTE5qR2d4RzFMRjBxYXJpN2ZFaTJmdll5czFvZEdYbTRzRDZ0RlFuWTBhYiJ9LCJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2tleS1jaGFuZ2UifQ
            [payload] => eyJhY2NvdW50IjoiaHR0cHM6Ly9hY21lLXN0YWdpbmctdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9hY2N0LzUwMzE4MTY4Iiwib2xkS2V5Ijp7ImNydiI6IlAtMzg0Iiwia3R5IjoiRUMiLCJ4IjoiS1E3MTMwbmZRSXV3NXlWSkNjYlh1TURwOUo0NFd1OGxmcmtJc1NzY1RETDhYS211SHZHN3A4YWMwcWdVUktZViIsInkiOiJmNHRkX3J1T2Zvd1FRS3M3cTdDM2tTRXdYLV9yUXRwSEwzNTdJWC1abXNRU3lBa2E3U2IxdVFITENCZ3l2eWRaIn19
            [signature] => _1goVwZzjWuOSzwwxxmPOC7I56i63qDoJfVw1DUN72MNaqsha2nFlEadvEgYH9unRdS_s1KmY8Q6IrchExL6H3hvVg0vT2CP9XrwLxSPJOAg6QA9AI2Kgbh-z8H2HoPV
        )

    [signature] => 1r5UqQYRc5i_03rO-Hq7DNiFyKK9zrssuSA-6Kjv5RA6XqRAk5GchH01m89p1p4DjXR3N97aliUsBc6ZzyHuzjhfZebwTg4p1zkZPhre7K1FUAh2l0PCaU3AvEfti_We
)
Sign Key: Array
(
    [x] => KQ7130nfQIuw5yVJCcbXuMDp9J44Wu8lfrkIsSscTDL8XKmuHvG7p8ac0qgURKYV
    [y] => f4td_ruOfowQQKs7q7C3kSEwX-_rQtpHL357IX-ZmsQSyAka7Sb1uQHLCBgyvydZ
)
  https://acme-staging-v02.api.letsencrypt.org/acme/key-change [200] (2.4s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => ouOu4u2r2EUFc4koceWWKfoZYn9FpYLEdpcS6NxSwQ6aawondcXKuoN_3zMGoUlI
            [y] => CEtasDaG_3UmAYz_GPBJjLNjGgxG1LF0qari7fEi2fvYys1odGXm4sD6tFQnY0ab
        )

    [createdAt] => 2022-04-09T19:05:14Z
    [status] => valid
)
Account Key Roll-Over successful
Deactivating account
Deactivating resource: https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318168
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [kid] => https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318168
            [nonce] => 0001iNKXrWvUTgRx1iRwU-I72hxsAmHtiv6UcbevOcSlYYU
            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318168
        )

    [payload] => Array
        (
            [status] => deactivated
        )

    [signature] => aZ6Pglk8lnZy5hMy1Pgjk8h4XAS8b1foZyuB-NLf3KzM1_zlr2_f33MK-srZjJzxxIvsPNqvKbjMvjdz0k2gfUUCDOTBsBgipSbT_i5KPQjUPjKTXvsHTT6kyOeZduQ8
)
Sign Key: Array (this request is signed using the old key A instead of B)
(
    [x] => KQ7130nfQIuw5yVJCcbXuMDp9J44Wu8lfrkIsSscTDL8XKmuHvG7p8ac0qgURKYV
    [y] => f4td_ruOfowQQKs7q7C3kSEwX-_rQtpHL357IX-ZmsQSyAka7Sb1uQHLCBgyvydZ
)
  https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318168 [200] (1.91s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => KQ7130nfQIuw5yVJCcbXuMDp9J44Wu8lfrkIsSscTDL8XKmuHvG7p8ac0qgURKYV
            [y] => f4td_ruOfowQQKs7q7C3kSEwX-_rQtpHL357IX-ZmsQSyAka7Sb1uQHLCBgyvydZ
        )

    [createdAt] => 2022-04-09T19:05:14Z
    [status] => deactivated
)
Resource deactivated
Account deactivated

For comparison here is the log of the expected behaviour (when lucky or using a delay between step 2. and 3.):

Array
(
    [A] => -----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAbT5NM8Idt1ujr5juUs1+IS8wzzo1CakL06ANTUW2We/YQ2GnmalwO
x240jzN5aYegBwYFK4EEACKhZANiAAQ7EgUq0CSUWG1g+Qh2/3l8txTAtyjWNsg3
EUdU+5zPe982iVFHnd9LFzKKU+izoEbgRLqBx8CLQVCWymgEuU8RijGvZs7WvHPB
VnpKSl9euZqxWogteCsoarAnElecr9E=
-----END EC PRIVATE KEY-----

    [B] => -----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDADRTDe+kD/dT5zU5URFDPv6PyAMI9tMSNYextprpWdD5+MvVFTQL9a
LoHoVrcFYaKgBwYFK4EEACKhZANiAATNoVt3nr9MlK25Zr/zmlUwannQ/U0VKzpW
0pwPRX7OJSThlLJK3IuoA3lW4kmxhOXi3VMdaM3X8EvHUJvpwMUZMxnaUJfpr1g/
JFJ/tWFv5M7OIgG49c566mD3QEyNLiA=
-----END EC PRIVATE KEY-----

)
Registering account
Initializing ACME v2 environment: https://acme-staging-v02.api.letsencrypt.org/directory
Using cURL
  https://acme-staging-v02.api.letsencrypt.org/directory [200] (0.43s)
Receive Array
(
    [hVKX2h_MKvk] => https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417
    [keyChange] => https://acme-staging-v02.api.letsencrypt.org/acme/key-change
    [meta] => Array
        (
            [caaIdentities] => Array
                (
                    [0] => letsencrypt.org
                )

            [termsOfService] => https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
            [website] => https://letsencrypt.org/docs/staging-environment/
        )

    [newAccount] => https://acme-staging-v02.api.letsencrypt.org/acme/new-acct
    [newNonce] => https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce
    [newOrder] => https://acme-staging-v02.api.letsencrypt.org/acme/new-order
    [renewalInfo] => https://acme-staging-v02.api.letsencrypt.org/get/draft-aaron-ari/renewalInfo/
    [revokeCert] => https://acme-staging-v02.api.letsencrypt.org/acme/revoke-cert
)
Initialized
  https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce [200] (0.14s)
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [jwk] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => OxIFKtAklFhtYPkIdv95fLcUwLco1jbINxFHVPucz3vfNolRR53fSxcyilPos6BG
                    [y] => 4ES6gcfAi0FQlspoBLlPEYoxr2bO1rxzwVZ6SkpfXrmasVqILXgrKGqwJxJXnK_R
                )

            [nonce] => 0002kODTYDL_WXutaSO2eMRpEk9rDNLjJW6947_q5P9waPc
            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/new-acct
        )

    [payload] => Array
        (
            [termsOfServiceAgreed] => 1
            [contact] => Array
                (
                )

        )

    [signature] => PePmU7o8AlKnwL496-M_ng2Vs4hPrdfgo6b2cPv4HgsUsU8FPCzn88xOW0zFHtGLnqmj4GjhObkJE2K1D77SQxm75AQ89T-RHvOgpPZkfm7Jc3mVg4axW_ImbssD_AT4
)
Sign Key: Array
(
    [x] => OxIFKtAklFhtYPkIdv95fLcUwLco1jbINxFHVPucz3vfNolRR53fSxcyilPos6BG
    [y] => 4ES6gcfAi0FQlspoBLlPEYoxr2bO1rxzwVZ6SkpfXrmasVqILXgrKGqwJxJXnK_R
)
  https://acme-staging-v02.api.letsencrypt.org/acme/new-acct [201] (0.18s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => OxIFKtAklFhtYPkIdv95fLcUwLco1jbINxFHVPucz3vfNolRR53fSxcyilPos6BG
            [y] => 4ES6gcfAi0FQlspoBLlPEYoxr2bO1rxzwVZ6SkpfXrmasVqILXgrKGqwJxJXnK_R
        )

    [createdAt] => 2022-04-09T19:04:48.060909451Z
    [status] => valid
)
AccountID: https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318118
Account registered
Account Key Roll-Over
Inner JWS: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [jwk] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => zaFbd56_TJStuWa_85pVMGp50P1NFSs6VtKcD0V-ziUk4ZSyStyLqAN5VuJJsYTl
                    [y] => 4t1THWjN1_BLx1Cb6cDFGTMZ2lCX6a9YPyRSf7Vhb-TOziIBuPXOeupg90BMjS4g
                )

            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/key-change
        )

    [payload] => Array
        (
            [account] => https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318118
            [oldKey] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => OxIFKtAklFhtYPkIdv95fLcUwLco1jbINxFHVPucz3vfNolRR53fSxcyilPos6BG
                    [y] => 4ES6gcfAi0FQlspoBLlPEYoxr2bO1rxzwVZ6SkpfXrmasVqILXgrKGqwJxJXnK_R
                )

        )

    [signature] => 0cce8HilrpFY4jLbnGmMI9PK-Kv__JXePr4Gi5sIGOg3QJOv_HIj1Q-T-gpWbExsrwZJC8BR-Ea58uIOewIr3iVpmZl5a3_eUyUxKFuMQbB4EIWdrtEgYw6U0XPlezi-
)
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [kid] => https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318118
            [nonce] => 0001QjmJhE-nbqK3ZOmZgLKAsLWQqEmCxONFf-5R03QIeuk
            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/key-change
        )

    [payload] => Array
        (
            [protected] => eyJhbGciOiJFUzM4NCIsImp3ayI6eyJjcnYiOiJQLTM4NCIsImt0eSI6IkVDIiwieCI6InphRmJkNTZfVEpTdHVXYV84NXBWTUdwNTBQMU5GU3M2VnRLY0QwVi16aVVrNFpTeVN0eUxxQU41VnVKSnNZVGwiLCJ5IjoiNHQxVEhXak4xX0JMeDFDYjZjREZHVE1aMmxDWDZhOVlQeVJTZjdWaGItVE96aUlCdVBYT2V1cGc5MEJNalM0ZyJ9LCJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2tleS1jaGFuZ2UifQ
            [payload] => eyJhY2NvdW50IjoiaHR0cHM6Ly9hY21lLXN0YWdpbmctdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9hY2N0LzUwMzE4MTE4Iiwib2xkS2V5Ijp7ImNydiI6IlAtMzg0Iiwia3R5IjoiRUMiLCJ4IjoiT3hJRkt0QWtsRmh0WVBrSWR2OTVmTGNVd0xjbzFqYklOeEZIVlB1Y3ozdmZOb2xSUjUzZlN4Y3lpbFBvczZCRyIsInkiOiI0RVM2Z2NmQWkwRlFsc3BvQkxsUEVZb3hyMmJPMXJ4endWWjZTa3BmWHJtYXNWcUlMWGdyS0dxd0p4SlhuS19SIn19
            [signature] => 0cce8HilrpFY4jLbnGmMI9PK-Kv__JXePr4Gi5sIGOg3QJOv_HIj1Q-T-gpWbExsrwZJC8BR-Ea58uIOewIr3iVpmZl5a3_eUyUxKFuMQbB4EIWdrtEgYw6U0XPlezi-
        )

    [signature] => proehRG5dYnwFXvHBipyriQ87KUFVdvVNy2kgARi1eVDFCdDlsCuH2iukps9u_KO77YOeyrES-jv4cp_964MV27yX3mzci_ENhXm-AXmARahG7LgLoavvodHY2xrT1l8
)
Sign Key: Array
(
    [x] => OxIFKtAklFhtYPkIdv95fLcUwLco1jbINxFHVPucz3vfNolRR53fSxcyilPos6BG
    [y] => 4ES6gcfAi0FQlspoBLlPEYoxr2bO1rxzwVZ6SkpfXrmasVqILXgrKGqwJxJXnK_R
)
  https://acme-staging-v02.api.letsencrypt.org/acme/key-change [200] (0.19s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => zaFbd56_TJStuWa_85pVMGp50P1NFSs6VtKcD0V-ziUk4ZSyStyLqAN5VuJJsYTl
            [y] => 4t1THWjN1_BLx1Cb6cDFGTMZ2lCX6a9YPyRSf7Vhb-TOziIBuPXOeupg90BMjS4g
        )

    [createdAt] => 2022-04-09T19:04:48Z
    [status] => valid
)
Account Key Roll-Over successful
Deactivating account
Getting account info
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [jwk] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => zaFbd56_TJStuWa_85pVMGp50P1NFSs6VtKcD0V-ziUk4ZSyStyLqAN5VuJJsYTl
                    [y] => 4t1THWjN1_BLx1Cb6cDFGTMZ2lCX6a9YPyRSf7Vhb-TOziIBuPXOeupg90BMjS4g
                )

            [nonce] => 00012FK3EemhOQ2TqaGR0Hf6KsaYfG6NfKDGQQvcwb9WmwE
            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/new-acct
        )

    [payload] => Array
        (
            [onlyReturnExisting] => 1
        )

    [signature] => ZecB20-htAFmNIoflvttOiDuAqjD42tj1m7pnap8bqgceyvQ-1Wcowt5KJ5opq9t8Yufo69__rdhSLr-jN_pTZ3ZSEK1XVdDissdMEO49tBQCWt2dIy5_iEX34khu1tr
)
Sign Key: Array
(
    [x] => zaFbd56_TJStuWa_85pVMGp50P1NFSs6VtKcD0V-ziUk4ZSyStyLqAN5VuJJsYTl
    [y] => 4t1THWjN1_BLx1Cb6cDFGTMZ2lCX6a9YPyRSf7Vhb-TOziIBuPXOeupg90BMjS4g
)
  https://acme-staging-v02.api.letsencrypt.org/acme/new-acct [200] (0.17s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => zaFbd56_TJStuWa_85pVMGp50P1NFSs6VtKcD0V-ziUk4ZSyStyLqAN5VuJJsYTl
            [y] => 4t1THWjN1_BLx1Cb6cDFGTMZ2lCX6a9YPyRSf7Vhb-TOziIBuPXOeupg90BMjS4g
        )

    [createdAt] => 2022-04-09T19:04:48Z
    [status] => valid
)
AccountID: https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318118
Account info retrieved
Deactivating resource: https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318118
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [kid] => https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318118
            [nonce] => 0001LdP4Ue5T5wOXQdn0JCXAXNcwTdOo_d2AO-T3NIb_N4s
            [url] => https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318118
        )

    [payload] => Array
        (
            [status] => deactivated
        )

    [signature] => Dtfa0PNdtJlUMToWGmgK9Xg2bh6QJQiJ3nnDCCjHUR5sOAI9GUUupHfKvI_B_LWrovRe-l3C6cMyXBlOh75vRfy8GhFx3MbvtD9tlaYc6j5yBjBTetBxTO9_nBcfaztO
)
Sign Key: Array (this request is signed using the new key B)
(
    [x] => zaFbd56_TJStuWa_85pVMGp50P1NFSs6VtKcD0V-ziUk4ZSyStyLqAN5VuJJsYTl
    [y] => 4t1THWjN1_BLx1Cb6cDFGTMZ2lCX6a9YPyRSf7Vhb-TOziIBuPXOeupg90BMjS4g
)
  https://acme-staging-v02.api.letsencrypt.org/acme/acct/50318118 [200] (0.16s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => zaFbd56_TJStuWa_85pVMGp50P1NFSs6VtKcD0V-ziUk4ZSyStyLqAN5VuJJsYTl
            [y] => 4t1THWjN1_BLx1Cb6cDFGTMZ2lCX6a9YPyRSf7Vhb-TOziIBuPXOeupg90BMjS4g
        )

    [createdAt] => 2022-04-09T19:04:48Z
    [status] => deactivated
)
Resource deactivated
Account deactivated
1 Like

I'm sorry, but I don't understand anything of what you're saying..

What is it exactly you're trying to do here? Develop your own ACME client? Could you please elaborate about the background of your thread, because right now, it's rather difficult to understand..

2 Likes

I just stumbled upon this problem when running some tests for the next release of my ACME client ACMECert.
It is not a big problem since it is pretty much an edge case. Just wanted to let you know that there is a potential bug in boulder.
Maybe this is the wrong place to post this and it is better if I open a github issue on boulder?

They key point is that boulder accepts (a short period of time) requests signed with the old key after account key rollover to a new key.

3 Likes

Hmm. Boulder has an in-memory cache for account registrations, sitting in front of the database.

I wonder if there is potentially a missing invalidation in the key rollover path. I can't see one in the code, but I didn't look super closely. Going to tag @lestaff just in case.

6 Likes

Are you seeing this on a self-hosted version of Boulder? Do you see this on Pebble? If you can reproduce this on self-hosted versions of either, I would post reports there and cross-reference.

If you can't recreate there, but can on LetsEncrypt's production and/or staging, it may be a dev/ops issue with caching or something in that environment and posting here would be better.

4 Likes

So far I only tested it on Let's Encrypt live and staging. Here is the log of the live system (I modified the script to sleep 1 second after receiving the JWS verification error and trying the same request again, which then succeeds):

Array
(
    [A] => -----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAWWEVRtbbA9X4HovYR13301yUeX4hoFaKXLjcJAYzjKoyMRKKSaETx
cxKtTJF/LE6gBwYFK4EEACKhZANiAATMlMMKP68QZw5g/1hh3lnYLnNkoESguZxz
hiRUUjUSONT27CNLKzFZjEx36baSOK3ttcNiptVL7Ndb4aez68Fz0yIfRYmnQA54
BvVgqEgLEjYU6p5ch8OHq4xyxeOroc4=
-----END EC PRIVATE KEY-----

    [B] => -----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDD36rHNsFsnQCuK3PTwj3nTAWG4dzAxqpDXHWYC2SbjLbRIx8RdHPq5
hV4whexNJTGgBwYFK4EEACKhZANiAAQzN3X0zkMk0NW0BtnmWkMrgXPDU57tmp35
8XBhkjAHZXvWiQMOAciYnrIrJoAylE7j0WDD8Nyrnr57imV7UlzituY/zyuGvCPB
Ju/9u7uNQMvp/DPMF2oGdlQiCyhgjxw=
-----END EC PRIVATE KEY-----

)
Registering account
Initializing ACME v2 environment: https://acme-v02.api.letsencrypt.org/directory
Using cURL
  https://acme-v02.api.letsencrypt.org/directory [200] (0.4s)
Receive Array
(
    [TSzbcsOvwBE] => https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417
    [keyChange] => https://acme-v02.api.letsencrypt.org/acme/key-change
    [meta] => Array
        (
            [caaIdentities] => Array
                (
                    [0] => letsencrypt.org
                )

            [termsOfService] => https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf
            [website] => https://letsencrypt.org
        )

    [newAccount] => https://acme-v02.api.letsencrypt.org/acme/new-acct
    [newNonce] => https://acme-v02.api.letsencrypt.org/acme/new-nonce
    [newOrder] => https://acme-v02.api.letsencrypt.org/acme/new-order
    [revokeCert] => https://acme-v02.api.letsencrypt.org/acme/revoke-cert
)
Initialized
  https://acme-v02.api.letsencrypt.org/acme/new-nonce [200] (0.13s)
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [jwk] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => zJTDCj-vEGcOYP9YYd5Z2C5zZKBEoLmcc4YkVFI1EjjU9uwjSysxWYxMd-m2kjit
                    [y] => 7bXDYqbVS-zXW-Gns-vBc9MiH0WJp0AOeAb1YKhICxI2FOqeXIfDh6uMcsXjq6HO
                )

            [nonce] => 0102PVpvnElX7FNl_13ok-NeopkslF9b5VsU7gI3JR-dYOA
            [url] => https://acme-v02.api.letsencrypt.org/acme/new-acct
        )

    [payload] => Array
        (
            [termsOfServiceAgreed] => 1
            [contact] => Array
                (
                )

        )

    [signature] => IwcRI3_aOtqSXi7bqBoMHwJ0zAqUUUSo23oLogQo9mjRCZ84oo8p8yQ6jwpV_cjr-gJKN6xyZlZ_KJUh7S3ILeKyLzMUYhHfhQTRz8aRrINkUIl1bfjqBHa5mHsLOlx2
)
Sign Key: Array
(
    [x] => zJTDCj-vEGcOYP9YYd5Z2C5zZKBEoLmcc4YkVFI1EjjU9uwjSysxWYxMd-m2kjit
    [y] => 7bXDYqbVS-zXW-Gns-vBc9MiH0WJp0AOeAb1YKhICxI2FOqeXIfDh6uMcsXjq6HO
)
  https://acme-v02.api.letsencrypt.org/acme/new-acct [201] (0.27s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => zJTDCj-vEGcOYP9YYd5Z2C5zZKBEoLmcc4YkVFI1EjjU9uwjSysxWYxMd-m2kjit
            [y] => 7bXDYqbVS-zXW-Gns-vBc9MiH0WJp0AOeAb1YKhICxI2FOqeXIfDh6uMcsXjq6HO
        )

    [createdAt] => 2022-04-12T16:05:09.393256701Z
    [status] => valid
)
AccountID: https://acme-v02.api.letsencrypt.org/acme/acct/493968940
Account registered
Account Key Roll-Over
Inner JWS: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [jwk] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => Mzd19M5DJNDVtAbZ5lpDK4Fzw1Oe7Zqd-fFwYZIwB2V71okDDgHImJ6yKyaAMpRO
                    [y] => 49Fgw_Dcq56-e4ple1Jc4rbmP88rhrwjwSbv_bu7jUDL6fwzzBdqBnZUIgsoYI8c
                )

            [url] => https://acme-v02.api.letsencrypt.org/acme/key-change
        )

    [payload] => Array
        (
            [account] => https://acme-v02.api.letsencrypt.org/acme/acct/493968940
            [oldKey] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => zJTDCj-vEGcOYP9YYd5Z2C5zZKBEoLmcc4YkVFI1EjjU9uwjSysxWYxMd-m2kjit
                    [y] => 7bXDYqbVS-zXW-Gns-vBc9MiH0WJp0AOeAb1YKhICxI2FOqeXIfDh6uMcsXjq6HO
                )

        )

    [signature] => 0QPKQvd8dvhpe90kxA7bV6MYxVglKGk832wxutTLzPWXPHYfF03M-RwEU-GAL6w6ihUWXN8TZUcvIjAvCp-lEMfnNX8qRRbbe1PD3PtizjWYHvBTiHG6R7dQNgpCeArM
)
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [kid] => https://acme-v02.api.letsencrypt.org/acme/acct/493968940
            [nonce] => 0101fH_buj6puDZV76uf_C_3RNH99mVZDe8fHhatnEtV4uw
            [url] => https://acme-v02.api.letsencrypt.org/acme/key-change
        )

    [payload] => Array
        (
            [protected] => eyJhbGciOiJFUzM4NCIsImp3ayI6eyJjcnYiOiJQLTM4NCIsImt0eSI6IkVDIiwieCI6Ik16ZDE5TTVESk5EVnRBYlo1bHBESzRGencxT2U3WnFkLWZGd1laSXdCMlY3MW9rRERnSEltSjZ5S3lhQU1wUk8iLCJ5IjoiNDlGZ3dfRGNxNTYtZTRwbGUxSmM0cmJtUDg4cmhyd2p3U2J2X2J1N2pVREw2Znd6ekJkcUJuWlVJZ3NvWUk4YyJ9LCJ1cmwiOiJodHRwczovL2FjbWUtdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9rZXktY2hhbmdlIn0
            [payload] => eyJhY2NvdW50IjoiaHR0cHM6Ly9hY21lLXYwMi5hcGkubGV0c2VuY3J5cHQub3JnL2FjbWUvYWNjdC80OTM5Njg5NDAiLCJvbGRLZXkiOnsiY3J2IjoiUC0zODQiLCJrdHkiOiJFQyIsIngiOiJ6SlREQ2otdkVHY09ZUDlZWWQ1WjJDNXpaS0JFb0xtY2M0WWtWRkkxRWpqVTl1d2pTeXN4V1l4TWQtbTJraml0IiwieSI6IjdiWERZcWJWUy16WFctR25zLXZCYzlNaUgwV0pwMEFPZUFiMVlLaElDeEkyRk9xZVhJZkRoNnVNY3NYanE2SE8ifX0
            [signature] => 0QPKQvd8dvhpe90kxA7bV6MYxVglKGk832wxutTLzPWXPHYfF03M-RwEU-GAL6w6ihUWXN8TZUcvIjAvCp-lEMfnNX8qRRbbe1PD3PtizjWYHvBTiHG6R7dQNgpCeArM
        )

    [signature] => hnLdUhlh4CXMs3DKUzXsDcZLCSzo9gZSEPQIv2D_kpD4J5dAsp0Rl5-dJWg54sAThncINHXHOiB6K7ZOwZsq3Kc4fFCyZdrolSV7G8NkbklrigoLJWwnHRZGXJGjkATw
)
Sign Key: Array
(
    [x] => zJTDCj-vEGcOYP9YYd5Z2C5zZKBEoLmcc4YkVFI1EjjU9uwjSysxWYxMd-m2kjit
    [y] => 7bXDYqbVS-zXW-Gns-vBc9MiH0WJp0AOeAb1YKhICxI2FOqeXIfDh6uMcsXjq6HO
)
  https://acme-v02.api.letsencrypt.org/acme/key-change [200] (0.27s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => Mzd19M5DJNDVtAbZ5lpDK4Fzw1Oe7Zqd-fFwYZIwB2V71okDDgHImJ6yKyaAMpRO
            [y] => 49Fgw_Dcq56-e4ple1Jc4rbmP88rhrwjwSbv_bu7jUDL6fwzzBdqBnZUIgsoYI8c
        )

    [createdAt] => 2022-04-12T16:05:09Z
    [status] => valid
)
Account Key Roll-Over successful
Deactivating account
Getting account info
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [jwk] => Array
                (
                    [crv] => P-384
                    [kty] => EC
                    [x] => Mzd19M5DJNDVtAbZ5lpDK4Fzw1Oe7Zqd-fFwYZIwB2V71okDDgHImJ6yKyaAMpRO
                    [y] => 49Fgw_Dcq56-e4ple1Jc4rbmP88rhrwjwSbv_bu7jUDL6fwzzBdqBnZUIgsoYI8c
                )

            [nonce] => 0102ivV3IswWwwRFsDKa4OYponLwV9tdD6MYwFPQjZPeLCY
            [url] => https://acme-v02.api.letsencrypt.org/acme/new-acct
        )

    [payload] => Array
        (
            [onlyReturnExisting] => 1
        )

    [signature] => hREF7CoBny-Co82wx-tNbXaGJNxhwHk6C5SbJKjsDSI0jzad7ShJho3gOh8qOUIS2_HIWXZZ2QQIiZ_29DhxHImpZdtn6Vu6v68trz9T2Mx5qoFCRSNLm9CIW6__16xC
)
Sign Key: Array
(
    [x] => Mzd19M5DJNDVtAbZ5lpDK4Fzw1Oe7Zqd-fFwYZIwB2V71okDDgHImJ6yKyaAMpRO
    [y] => 49Fgw_Dcq56-e4ple1Jc4rbmP88rhrwjwSbv_bu7jUDL6fwzzBdqBnZUIgsoYI8c
)
  https://acme-v02.api.letsencrypt.org/acme/new-acct [200] (0.22s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => Mzd19M5DJNDVtAbZ5lpDK4Fzw1Oe7Zqd-fFwYZIwB2V71okDDgHImJ6yKyaAMpRO
            [y] => 49Fgw_Dcq56-e4ple1Jc4rbmP88rhrwjwSbv_bu7jUDL6fwzzBdqBnZUIgsoYI8c
        )

    [createdAt] => 2022-04-12T16:05:09Z
    [status] => valid
)
AccountID: https://acme-v02.api.letsencrypt.org/acme/acct/493968940
Account info retrieved
Deactivating resource: https://acme-v02.api.letsencrypt.org/acme/acct/493968940
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [kid] => https://acme-v02.api.letsencrypt.org/acme/acct/493968940
            [nonce] => 0101dELnY4aNHhicyNUW2dPnL62asMbs1HTMO9zqmP08Lg4
            [url] => https://acme-v02.api.letsencrypt.org/acme/acct/493968940
        )

    [payload] => Array
        (
            [status] => deactivated
        )

    [signature] => GL5Y4Gc8JZdFaQ9nQkwf5ul-1FwYpPWgdvnXoT0Etgywql4Sbf7XBseQt9JPXtSSGy6rb4h12WbCz8yuYPBlAGkdjvLf-NbqSk1M3umTBCoUClCk8UasM03dzpIf5ckL
)
Sign Key: Array
(
    [x] => Mzd19M5DJNDVtAbZ5lpDK4Fzw1Oe7Zqd-fFwYZIwB2V71okDDgHImJ6yKyaAMpRO
    [y] => 49Fgw_Dcq56-e4ple1Jc4rbmP88rhrwjwSbv_bu7jUDL6fwzzBdqBnZUIgsoYI8c
)
  https://acme-v02.api.letsencrypt.org/acme/acct/493968940 [400] (0.14s)
Receive Array
(
    [type] => urn:ietf:params:acme:error:malformed
    [detail] => JWS verification error
    [status] => 400
)
ACME_Exception: JWS verification error (urn:ietf:params:acme:error:malformed)
Sleep 1 second.. retrying previous request..
Send: Array
(
    [protected] => Array
        (
            [alg] => ES384
            [kid] => https://acme-v02.api.letsencrypt.org/acme/acct/493968940
            [nonce] => 0101yUL36VWrbmmIG9zP8borDxvwwraSCsKqHNI7SVhpzBA
            [url] => https://acme-v02.api.letsencrypt.org/acme/acct/493968940
        )

    [payload] => Array
        (
            [status] => deactivated
        )

    [signature] => eizeJERIDiMsVsRYSWCme-isncIuVM1Ddc2F3BamA9XbBrdjAQUxp-3ESEsmIphGBveDAjms_I-GNl8lAMsgWvlxVnvWzWYEOChqUNAhQDH3pzLNRJPnaRQ6UcuUOHrm
)
Sign Key: Array
(
    [x] => Mzd19M5DJNDVtAbZ5lpDK4Fzw1Oe7Zqd-fFwYZIwB2V71okDDgHImJ6yKyaAMpRO
    [y] => 49Fgw_Dcq56-e4ple1Jc4rbmP88rhrwjwSbv_bu7jUDL6fwzzBdqBnZUIgsoYI8c
)
  https://acme-v02.api.letsencrypt.org/acme/acct/493968940 [200] (0.19s)
Receive Array
(
    [key] => Array
        (
            [kty] => EC
            [crv] => P-384
            [x] => Mzd19M5DJNDVtAbZ5lpDK4Fzw1Oe7Zqd-fFwYZIwB2V71okDDgHImJ6yKyaAMpRO
            [y] => 49Fgw_Dcq56-e4ple1Jc4rbmP88rhrwjwSbv_bu7jUDL6fwzzBdqBnZUIgsoYI8c
        )

    [createdAt] => 2022-04-12T16:05:09Z
    [status] => deactivated
)
Resource deactivated
Account deactivated

I'm going to try to test it using a self-hosted boulder/pebble next.

1 Like

Just tested it with pebble, there I can't reproduce the problem.

So it seems to be a caching/missing invalidation problem..

1 Like

@jsha sorry for the second tag, I'm not sure whether you came across this thread. I think @stewe is right about something being wrong here.

My own reproduction using config-next:

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"log"

	"github.com/eggsampler/acme/v3"
)

// docker-compose -f docker-compose.yml -f docker-compose.next.yml up
// go run main.go

func main() {
	cl, err := acme.NewClient("http://localhost:4001/directory")
	if err != nil {
		log.Fatalf("Couldn't create a client: %v", err)
	}

	// Register an account
	ak1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	acc, err := cl.NewAccount(ak1, false, true)
	if err != nil {
		log.Fatalf("Couldn't register an account: %v", err)
	}

	// Change the account's key.
	ak2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	updatedAcc, err := cl.AccountKeyChange(acc, ak2)
	if err != nil {
		log.Fatalf("Couldn't change the account key: %v", err)
	}

	// Here's where things seem to go wrong:
	// 1. Deactivating the account with the new key should succeed, but doesn't
	if _, err := cl.DeactivateAccount(updatedAcc); err != nil {
		log.Printf("Could not deactivate account using new key: %v", err)
	}

	// 2. Deactivating the account with the old key should fail, but doesn't.
	if _, err := cl.DeactivateAccount(acc); err != nil {
		log.Printf("Could not deactivate account using old key: %v", err)
	} else {
		log.Println("Managed to deactivate the account using the old key. :(")
	}
}
$ go run ./cmd/repro-key-rollover-race/main.go
2022/04/14 08:33:34 Could not deactivate account using new key: acme: error code 400 "urn:ietf:params:acme:error:malformed": JWS verification error
2022/04/14 08:33:34 Managed to deactivate the account using the old key. :(

Running the same program on config works just fine, presumably because the account cache is not enabled there.

8 Likes

Thanks for the second tag. I had seen this but it had fallen back off my radar. I think you're right that there's a cache invalidation issue here. We'll have to think about how best to solve it.

7 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.