HOWTO Verify Messages Between JavaScript and Java with a Graphene KeyPair

in #steemdev7 years ago (edited)

I'm working on a project where the authentication system only requires browser-side access to a user's STEEM Posting Key (similarly to how STEEM, DTube, and DSound interact with the blockchain).

The main difference, however, is that I also need to verify messages (ie. user-specific requests) between a client and server that are not directly verified by the STEEM blockchain. For example, how can I confirm on my back-end server (written in Java) that a particular STEEM user is really who they say they are in the browser?

When you're logged into STEEMIT or DTube, only the front-end really knows you're logged in. And that's just fine, because any transactions such as upvotes that require validation will be verified and encoded by one of the STEEM witnesses into the next block. And if the transaction is accepted, the latest "state" request will reflect any changes effected by all newly accepted transactions collectively.

In my case, however, I need to extend that functionality off-chain, and as such, it would be up to my Java server to validate and verify that the request is a current, non-duplicate "transaction" being requested by a true holder of a valid private posting key vis-a-vis a particular STEEM userID.

At first, I thought this would be a relatively easy problem to solve, because every graphene library that can sign transactions for the blockchain has the required code to perform this signing function. However, most libraries tend to obfuscate their "inner workings". For example, most users of the steemjs library are interested in methods such as:

steem.broadcast.vote(wif, voter, author, permlink, weight, function(err, result) {
console.log(err, result);
});

How that's actually accomplished is a bit less obvious, and the methods used are generally hidden from the library's end users as not to confuse them further. And while I was able to figure out the sign/verify process relatively easily in either javascript or Java, I found myself having an awfully tough time doing it in a way that was inter-operable between the two!

Making it more confusing is that the various code examples I came across were slightly different depending on which chain was being targeted. For example, some libraries seemed to use the bitcoin version, which would perform Sha256Hash.twiceOf(). For graphene chains, the payload is only Sha256Hashed once. Also, while many libraries are designed to sign transactions, they don't focus so much on actually verifying signatures, since that task is normally left up to the witnesses or miners before they're encoded to the blockchain.

And, despite all my googling around and asking others if they had attempted any "off-chain" signing of messages in a compatible way, while some may have attempted it at some point or another, none had a working solution that could perform this seemingly simple extension of a widely performed blockchain operation.

What to do, what to do...?! 😩

Well, as I've often done in the past, if you can't find it readily available, dig, dig, dig, test, test, test, and then dig some more, 😱 pull out a few more hairs from your head 😱, and then test again...! 😩

As such, I traced through the finer details of how these libraries were performing their sign and verify operations. In this case, the main libraries I'm using are steemjs on the javascript side, and steemJ on the Java side.

For those who understand what's involved in a crypto signature, what made this even trickier to solve (which I still don't quite get) is that the signature objects on each side (javascript versus java) would return different r and s combinations, although I later discovered that both were still validating correctly! If anyone has more insight into why this is, please feel free to share in the comments...

To spare you more of my "rambling", the good news is that I finally came up with a solution, which I then rolled up into a relatively simple Java class for anyone to use, called GrapheneUtils.java, which I've now released in my github crypto-playpen repo.

Understanding the Code

GrapheneUtils.java contains both the Java class, and the javascript functions and examples (wrapped in the comments). Also, in order to access the required methods in steemJS, I had to expose a few additional classes, such as the ecc and buffer classes. That updated version of the steemJS library is also available in my github crypto-playpen repo.

In order for the cross-compatible sign/verify to work, I basically made sure that the messages were being processed in the same way, and the signatures were in a compatible format that both sides would understand. Each signature consists of 32-byte R and S integer components, which can be directly serialized to both java's BigInteger type, and javascript's "bigi" BigInteger object. An additional header byte is included for compatibility with other implementations, though when deserializing to javascript, the first byte has to be 32 (base10) to indicate the number of BigInteger bytes. The final format is 1 x 32 x 32, or 1 header byte, 32-bytes for R, and another 32-bytes for S, for a total of 65-bytes. This byte buffer is then converted to its base64 representation, and we're set to go!

In javascript, the functions to achieve this (using my custom-rolled steem.min.js) are as follows:

For the sake of clarity, the above signing call is equivalent to:

const sigObj = steem.ecc.Signature.signBufferSha256(steem.ecc.hash.sha256(msg), privateWif);

In Java, the equivalent functions to encode and decode the same content are as follows:

I've also included a few additional support functions that convert keys between their bytecode and textual (base58) representations. It turns out this was also less intuitive than you would at first expect, and I also had to dig around various libraries and examples to find the correct way to do that as well.

And once you've set this all up, you too will now be empowered with the ability to verify the authenticity of a signed message versus a particular STEEM or other graphene user!

Here's an example from the javascript side:

One last thing though, if you do plan to use this technique to verify messages, you'll also want to include a token and/or a timestamp as well. For example, what if someone intercepts one of the signed packets shown above? Anyone with access to the packet could potentially send that packet again and the server will verify it as valid.

However, if you first send the user a new token before each signed request, that would force the user to generate a freshly signed packet with the correct signing key upon each new login (or any other requested operation, for that matter). If the token encoded into the message doesn't match the one expected by the server, the transaction is simply rejected as invalid.

{"sig":"ABC/7qvUhfvwcGr/cLjR6PXM6kP/olu53FBct6JNgQPfYs2dgBJQS5hFjyvKBcGXFzUsLOTkLVdTnWsVgYhUJIs=","message":["login",{"steem_id":"someSteemUser","token":"7prta-JSla0"}]}

Taking this a step further, you don't even have to include the token in the message. You can simply add it to the signing and verification calls as such:

Wrapping it up...

I'm quite pleased I managed to pull this together, since it opens the doors to using STEEM (and other graphene-based blockchains) as an off-chain authentication system for trusted hybrid blockchain projects where the keys are still maintained locally, but an app can still securely hook into the system to extend functionality beyond the current capabilities of a particular chain.

For example, several services now require a user to link their STEEM userIDs to another account by sending a small transfer of 0.001 SBD or STEEM. Using this technique, the user could simply sign a message along with a token issued by the server, and the server can then validate the signature versus the user's public key pulled directly from the STEEM blockchain.

And finally, for those who've made it this far, but aren't really into blockchain programming, kudos to ya for lasting this long! I can only hope this post provides a bit more insight and understanding into the revolutionary tech that's now taking us... Beyond Bitcoin! 😀

As always, I appreciate your upvote, your follow and all your comments!

GitHub Link: GrapheneUtils.java on github
GitHub Repo: crypto-playpen repo on github

GitHub Repo: steemJ: Steem api wrapper for Java, by @dez1337
GitHub Repo: crypto-core: Swiss Army knife for Blockchain related projects, also by @dez1337

Sort:  

this is a waaaaaaaaaaaaaaaaaaay delayed comment...

But with regards to "the signature objects on each side (javascript versus java) would return different r and s combinations, although I later discovered that both were still validating correctly! If anyone has more insight into why this is, please feel free to share in the comments..."

It's because ECDSA signatures are generated using a secret number k which is NEVER the same (leading to different r and s). See http://nlitsme.github.io/posts/2014-06-19-ecdsa-explanation/

as an off-chain authentication system for trusted hybrid blockchain projects

This sounds very very exciting. I have been asked to do a certain sort of double blinded verification (almost AAA) and this seems to the approach to follow.

though you made me remember DIAMETER and adventures with RADIUS auth sigh!

yeah, you're right, there are indeed quite a few similarities there... lol

what if someone intercepts one of the signed packets shown above?

Yea but what are the odds of that happening if dont' mind asking? all these come out a bit difficult but it's def worth the time to learn about it.

  • Thanks in advance!* 🙏

there are many cases actually, for example if the data is not encrypted (ie. http versus https), or if packets are sent via email. and while it may be less likely to happen on an encrypted site, why risk leaving such a large potential attack vector open?

As such, the graphene blockchain takes it to a whole other level than even what I've described. If you'd like a better understanding of how STEEM validates transactions, @xeroc does a great job in explaining it in his post, "Steem transaction signing in a nutshell":

We notice that our operation is now part of the transaction (as part of the operations array) and that we now have a field for our signatures and an expiration. The expiration allows for transactions to expire if they are not included into a block by that time. Usually that date is about 30 seconds in the future.

Let's discuss the ref_block_* parameters a little: The ref_block_num indicates a particular block in the past by referring to the block number which has this number as the last two bytes. The ref_block_prefix on the other hand is obtain from the block id of that particular reference block.

The purpose of these two parameters is to prevent replay attacks in the case of a fork. Once two chains have forked, the two parameters identify two different blocks. Applying a signed transaction of one chain at another chain will invalidate the signature.

Informative post.Thanks for sharing it

your post is all the great post sir.I like your post.Thank you very much.

Hey.)
I will be glad to meet you
my profile pays rewards from 50-70% for app)
subscribe and I will be glad to communicate with everyone

Sounds like you're preparing to build with Smart Media Tokens.

Congratulations! This post has been upvoted from the communal account, @minnowsupport, by alexpmorris from the Minnow Support Project. It's a witness project run by aggroed, ausbitbank, teamsteem, theprophet0, someguy123, neoxian, followbtcnews, and netuoso. The goal is to help Steemit grow by supporting Minnows. Please find us at the Peace, Abundance, and Liberty Network (PALnet) Discord Channel. It's a completely public and open space to all members of the Steemit community who voluntarily choose to be there.

If you would like to delegate to the Minnow Support Project you can do so by clicking on the following links: 50SP, 100SP, 250SP, 500SP, 1000SP, 5000SP.
Be sure to leave at least 50SP undelegated on your account.

It's really informative post...thanks for sharing the post...carry on
..

The post is very good, very inspiring and information for us the steemians,,