Parse error reading JWS: failed to unmarshal JWK: illegal base64 data at input byte

response error message

{
"type": "urn:ietf:params:acme:error:malformed",
"detail": "Parse error reading JWS: failed to unmarshal JWK: illegal base64 data at input byte 53: \"{\\\"e\\\":\\\"AQAB\\\",\\\"kty\\\":\\\"RSA\\\",\\\"n\\\":\\\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAviNNH9WCD+rHcrQMl0YYKnzkCzH+8sxiVT3+Bum7AYTMNxcnCWIb8WUElRkkO4VJaCLP2w0+Z1QT3M5y2ZE6Y6CCZASeGMMuHx6i5K8HYqhVwS3GwmussO1nSJW04ReXIoxNHEkpG7HMMD6qg8uEnUP926hI2et2RxgJceaRkYiHchcP4b9UEZPhEf2znn7yeteCAflcKIg+jbV+Z5QQhTq6y1fnBvEodt+c1UWKYcbwsJ5MOZTqvsmmIkc97imyfD9vF0ojymZZVcnLuu+fKcSVxwz3EcPYHeJGq4maxhNW8C0i/TBm9wkgVnaKvp8SduxgDW7fE/8bWg5h2GeCrwIDAQAB\\\"}\"",
"status": 400
}

request json

{
  "protected": "eyJub25jZSI6Ijk3dUt5YTVvVWc1WlJEM2E3NUF2bDJhSzVvc1RIZDVjNGoxWXc0VWlqTmRRekt4UUV0TSIsInVybCI6Imh0dHBzOi8vbG9jYWxob3N0OjE0MDAwL3NpZ24tbWUtdXAiLCJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6IlJTQSIsIm4iOiJNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXZpTk5IOVdDRCtySGNyUU1sMFlZS256a0N6SCs4c3hpVlQzK0J1bTdBWVRNTnhjbkNXSWI4V1VFbFJra080VkphQ0xQMncwK1oxUVQzTTV5MlpFNlk2Q0NaQVNlR01NdUh4Nmk1SzhIWXFoVndTM0d3bXVzc08xblNKVzA0UmVYSW94TkhFa3BHN0hNTUQ2cWc4dUVuVVA5MjZoSTJldDJSeGdKY2VhUmtZaUhjaGNQNGI5VUVaUGhFZjJ6bm43eWV0ZUNBZmxjS0lnK2piVitaNVFRaFRxNnkxZm5CdkVvZHQrYzFVV0tZY2J3c0o1TU9aVHF2c21tSWtjOTdpbXlmRDl2RjBvanltWlpWY25MdXUrZktjU1Z4d3ozRWNQWUhlSkdxNG1heGhOVzhDMGkvVEJtOXdrZ1ZuYUt2cDhTZHV4Z0RXN2ZFLzhiV2c1aDJHZUNyd0lEQVFBQiJ9fQ",
  "payload": "eyJ0ZXJtc09mU2VydmljZUFncmVlZCI6dHJ1ZSwiY29udGFjdCI6WyJtYWlsdG86IGhvbnl1d0BxcS5jb20iXX0",
  "signature": "EdioaexRBB7OBQ4KHxeIJ29NmpjP1gkB215RjUgnbDev_40yBpLfV1mDE5dvXMrM2nPt5yO_w2GhPIdcGNZrJf08kYtHiV7u4M-L_0OVCjEcgv2ac4wQY_I32wD_8-0RZdQLfhB7N7Fr5T-2AuwbktNshp4QBzLeEwjlCUV5aoTFL0to4Nd70UOQdherVTluYVQAp0dreTpW25PXEaWNJZ8kVoMxKRg7WAdvhofBsMe2jSAV6Gz-lx95WyQjkjEmYD9xGp3wpMLSIBA6Ei4HRAHkqzvQXtbpOycVarols14TeJh3Ni5uFZewmZtETb9SfZIk5_IrLBlFwDU3frn2hQ"
}

base64 protected source

{
	"nonce": "97uKya5oUg5ZRD3a75Avl2aK5osTHd5c4j1Yw4UijNdQzKxQEtM",
	"url": "https://localhost:14000/sign-me-up",
	"alg": "RS256",
	"jwk": {
		"e": "AQAB",
		"kty": "RSA",
		"n": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuNRwrgyLeyzcSXiUICvy92+iJMJOlvpzThBURoub2538KdJH+ABE7HywM7xyURtjvLGQhiEC1b1SWjGR/jl4mH+8yeFNlGhTTVk/TCTuagjKwcxl7+deLY0si1s0YAdC7BmdApMkTakx1tLJ/reWi51uCy/MAlnDepZwFstrB//TZFi61bW1mFlEdYKSPtUeLVLUA0PCH0dQ6hzYMbxsY9PxNB3dHp/y5d8Z+P4z+DDgUSoAj+ZUToCWnOOWxZEjA1cKd5YJnpDx+Bhy/nEqJvgsO26gOEBbvN3kBRWwT2QPnBJIqYHu2RnC6j8PVymC84jOWrXqiuaEn4Gz/dykOwIDAQAB"
	}
}

Hi @max-end,

It looks to me like you're missing some base64 padding bytes at the end of your protected, payload, and signature strings: a double equals sign (==) at the end of the protected and signature, and a single equals sign (=) at the end of the payload.

Your base64 encoding library should generate these padding characters whenever they are required (this depends on the length of the input string to the base64 encoding process). You should make sure that whatever you're doing isn't somehow removing the = characters (where present) from the end of these base64 strings.

For comparison, in Python

import json
import base64

d = json.loads("[your complete JSON object here]")
print(base64.urlsafe_b64decode(d["protected"]+"=="))
print(base64.urlsafe_b64decode(d["payload"]+"="))
print(base64.urlsafe_b64decode(d["signature"]+"=="))

If you remove any of the = characters here, you'll get an exception instead of valid output.

1 Like

Doesn't JWS use base64url? Which doesn't use padding IIRC.

1 Like

Good catch, @Osiris. Thank you. I spoke too soon!

As a correction, the problem now seems to me to be twofold:

(1) n in the JWK public key is encoded with base64 rather than urlsafebase64. You can see a few differences between the encodings here:

>>> base64.b64encode(pubkey)
b'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuNRwrgyLeyzcSXiUICvy92+iJMJOlvpzThBURoub2538KdJH+ABE7HywM7xyURtjvLGQhiEC1b1SWjGR/jl4mH+8yeFNlGhTTVk/TCTuagjKwcxl7+deLY0si1s0YAdC7BmdApMkTakx1tLJ/reWi51uCy/MAlnDepZwFstrB//TZFi61bW1mFlEdYKSPtUeLVLUA0PCH0dQ6hzYMbxsY9PxNB3dHp/y5d8Z+P4z+DDgUSoAj+ZUToCWnOOWxZEjA1cKd5YJnpDx+Bhy/nEqJvgsO26gOEBbvN3kBRWwT2QPnBJIqYHu2RnC6j8PVymC84jOWrXqiuaEn4Gz/dykOwIDAQAB'
>>> base64.urlsafe_b64encode(pubkey)
b'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuNRwrgyLeyzcSXiUICvy92-iJMJOlvpzThBURoub2538KdJH-ABE7HywM7xyURtjvLGQhiEC1b1SWjGR_jl4mH-8yeFNlGhTTVk_TCTuagjKwcxl7-deLY0si1s0YAdC7BmdApMkTakx1tLJ_reWi51uCy_MAlnDepZwFstrB__TZFi61bW1mFlEdYKSPtUeLVLUA0PCH0dQ6hzYMbxsY9PxNB3dHp_y5d8Z-P4z-DDgUSoAj-ZUToCWnOOWxZEjA1cKd5YJnpDx-Bhy_nEqJvgsO26gOEBbvN3kBRWwT2QPnBJIqYHu2RnC6j8PVymC84jOWrXqiuaEn4Gz_dykOwIDAQAB'

And the given version is using the former version rather than the latter version.

(2) Probably more importantly (?), the supposed n value is actually an entire PEM-encoded public key (!!) rather than just the modulus n. If you base64-decode it and run openssl rsa -inform der -pubin on it, you get the whole key object, not just the modulus n. You can see related to this that the given string ends in AQAB, which is duplicative of the modulus e given elsewhere in the JWK (but it's not sufficient to just chop that off, the entire encoding of n as DER is probably mistaken here).

3 Likes

I know I am a broken record on this bit but...

The error messages above is a payload from an ACME Server.

The best way to develop ACME clients is:

  1. Write functions that can property serialize and deserialize the payloads, and cover these with unit tests.

  2. When the tests pass, start testing against a local server like Pebble or Boulder. Write integrated tests that cover a full certificate order and authorization process.

  3. When the local tests pass, try testing against production systems like the Staging API.

5 Likes

Thank you, I have resolved this issue. It is due to certificate issues

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