Great question — answering as the UphillTech team:
The solution was to have the URL passed into RequestValidator.validate() exactly match what's in the <ConversationRelay url="..." ... /> - in my case there were 2 problems with it: (a) You must use wss:// instead of https:// (even though I had ran into some older examples somewhere that said otherwise; but that's apparently simply plain wrong, at least now). (b) You must be very careful about trailing slashes after the hostname in the URL! In my case, I used https://4ea9be067e20.ngrok-free.app/ in the code on my client, but on the server-side Twilio calculated the signature based on <ConversationRelay url="https://4ea9be067e20.ngrok-free.app" ... /> (which I had manually typed into a TwiML...
Full answer by vorburger: https://stackoverflow.stackexchange.com/a/79833843 (CC BY-SA 4.0).
Need it done for your business? UphillTech.