How can one validate with a public key a JWT signature generated with a private key?
I received the following explanation from a separate source.

Send:    { object | encrypt(hash(object), private_key) }
Receive: { object | signature }
Verify:  hash(object) == decrypt(signature, public_key)


This explains what I was struggling to understand.

There are two processes at work here: not just hashing, but also encryption. The actual hashing itself needs no secret parameter, it takes in only the object to be hashed (the concatenation of header + payload in the case of JWT). The resulting hash itself is then the input to the encryption, the result of which forms the signature, which can then be decrypted by the recipient to retrieve the original hash for comparison against the recipient's own computed hash.

Thus, instead of the sender and recipient performing similar calculations, with different secrets, to arrive at the same hash value:

hash(object, private_key) == signature == hash(object, public_key)


we rather have the sender and recipient performing complementary calculations to arrive back at the original input

decrypt(encrypt(hash_value, private_key), public_key) == hash_value


This makes sense with how I understand public-private key encryption.