but the code looks suspiciously complex just for the purpose of removing the lock.
That's because you are thinking of it as a simple lock someone added by chance / or laziness, instead of a factor that was considered in the whole session provider concept. That code looks simple enough for the task at hand / replacing the whole existing provider with your own / and while doing so lock in a different way than it is intended.
I suggest you read a bit more about how the session concept works in asp.net. Consider that the way it was designed involves reading the Whole Session once in the request, and then writing the changed session once.
Also note that while its not your scenario, code might depend on reading separate session values to process something and can write separate values as well / locking individually can get you into the same considerations that can cause a dead lock in databases.
The designed way contrasts with your intent of reading/writing+locking as each separate item is loaded/stored.
Also note that you might be updating the values of an object you retrieved from the session and not setting it back to it, yet the session was updated. As far as I know, the providers write everything in the session back at a certain point in the life cycle, so its not straightforward if you want to separate read vs. write locks at an item level.
Finally, if you are finding a high level of resistance from the framework session providers model and the way you intent to modify it will prevent you from using some features of it (as switching to a diff provider, since those would have the same issue); then I think you should just stay away from it and roll your own for your very specific needs. That's without even using asp.net Cache, since you want to it to be your way, you control the lifetime & locks of what's stored in there.
It just appears to me that you need a different mechanism altogether, so I don't see the benefit of trying to bend the framework to do so. You might reuse some very specific pieces, like generating a session id / and cookie vs. cookie-less, but other than that its a different beast.