After another day of trying to get all - or at least most - json signatures to verify I'm declaring defeat. My summary is that this method of verifying ActivityPub post authenticity is too complex, too easy to mess with and under ordinary conditions also leaks IP addresses.
In order for a json post to be signed it first needs to be organized into a standardized form, known as "normalization". That process involves downloading schemas which are defined within the context field of the post, and it looks like such schemas can be recursive and may also reference http-only sites which are amenable to injection attacks. So the schema that you download might not be the same as the one on the server.
Making your signing process depend on remote schemas effectively gives them veto power over whether you can verify incoming posts. Caching schemas gets around this, and in practice I've hardcoded the most common ones, such as the one for activitystreams. But even while doing that, in tests it looks like there is sufficient exoticism out there in the wild that it becomes difficult to say that you have any highly reliable way for signing posts which doesn't have all sorts of unknowable corner cases. It's not even clear that the normalization algorithm checks for circular references.
So at present for compatibility I'll continue using json signatures on outgoing posts, using the activitystreams context which has a hardcoded schema so that it's not needing to download anything from remote sites, but I'll deactivate checking of json signatures on incoming posts unless you set the administrator option "verify all signatures".
I could devise an alternative post signing system which would be fully consistent and not reference any external schemas, but the main difficulty in any such endeavour is getting the requisite amount of consensus between ActivityPub server developers for it to be useful.