Should signature verification work using two different RSA libraries? I am new to the forum, so forgive me if this question is out of place or lacks adequate detail.

I am working on a project that involves allowing discreet clients to upload content to a central host. The host is expected to verify ownership and data integrity by testing the signature of the content against a user's supplied public RSA key. For some reason, signatures provided by a test client I have written can not be verified by the host. Signatures created by the host for the sake of testing pass with no problem. (That is the host uses the client's private key to create a signature based on the same content, and test's that signature against the client's public key).

The host is written in Go and uses Go's native rsa package to verify a PKCS1v15 signature of a SHA256 sum. The client is written in Node.JS and uses a third party package, forge, to sign the data, also using PKCS1v15 and a SHA256 sum. I have also tried PSS in place of PKCS1v15 on both sides. The results appear to be the same. Can this sort of pattern (create signature on platform-A and verify it on platform-B) be implemented practically, or is RSA public key encryption open to the interpretation of the stack implementer? 

NodeJS Implementation

The following is the client written in NodeJS. Here, the user's private key is read from the file "test-client-rsa." The variable "hex" is the hex-encoded SHA256 sum of the test file being used, hard-coded here for simplicity.

var fs = require('fs');
var forge = require('node-forge')

fs.readFile('test-client-rsa', (err, data) =&gt; {
   if (err) throw err;

    var key = forge.pki.privateKeyFromPem(data);
    var hex = 'dac1f3853c1e4c6579692f59af0a30aacb86820d5ad331c792987c4dae047595';
    var md = forge.md.sha256.create();
    md.update(hex, 'utf8');

    var signature = key.sign(md);
    var buf = new Buffer(signature);

    console.log(buf.toString('base64'));   
});


The client's output (encoded to Base64) resembles the following:


  JShQKsKAVcKow4HCnMKMw7MXwp3DhhAPXGhGPsOow4U9DQg3acOmIEjDoFR6eCUCwpZ4wq7CmBB7dsOrSMKOw6fDlDfCs8OKwonCm8KVLjMrIsKSwqTDkmnCgcKlRsO4QQrCiWDDjBbClcOTwqfDn8KDw57DgG0Qw7gcHsKSwr57w4rClyrDqkgrwo5owoAHBMOGwq/DpMKDwoVhw5bDtD9zw4NYCxwLwrLCoTp7wo3CtsOqPjFKwrdWw63CpMKQFcOcwpnCpMKhwrDCisOkBCZrw4wkwoYVwotWw7/DrBYSIsKjbXF9wqfDnRTCk0kTE8KTVMOSw6wicElvw45rwolbwpNgS8O+bSwhwqFHw5ICTcKRZcO0w7YOwoTCncOow4pBwo1ow7bCisKYwrRRMiTDpTLDnsKjQcOBwpgPw7rCokRtOcKTP8KiwppLwp3Dn2QYwqZvDEsRw4LCuiYbM8K5w6fClF98EW/CmUTCjcOjMcOHwqLDqsKpwqfDvcKgNA==


Go Lang Implementation

The following is the server implementation, written in Go. The code has been simplified to keep the example focused, and so lacks proper error handling and logging routines. In the method, the object "u" represents the user's identity, or RSA credentials in this example. The variable "u.pkey" is the user's RSA public key represented as bytes, while the input parameters "sig" and "data" represent the signature provided by the test client for the test file, and the content's of the test file itself respectively.

func (u User) VerifySignature(sig []byte, data []byte) bool {
    if !u.IsValid() {
        return false
    }

    hash := sha256.Sum256(data)

    if err := rsa.VerifyPKCS1v15(u.pkey, crypto.SHA256, hash[:], sig); err == nil {
        return true
    } 

    return false    
}


The server's output is consistently false with the call to "rsa.VerifyPKCS1v15" returning the error "crypto/rsa: verification error."

I've run through both layers of code several times now, and I am not able to spot the problem in the logic. Is there something incorrect in this pattern? Any help that can be provided would be greatly appreciated.
Yes, RSA signature generation and verification should work identically on different libraries. PKCS#1 - the dominant standard for signature generation and verification - has a complete and formal description of how the configuration / input is transformed into the output.

This includes:


the key specifications (and possible encoding);
the hashing;
the padding and hash indicators;
the modular exponentiation;
the transformation into octets of the result.


There are test vectors available from NIST as well, so it is possible to (partially) verify the correctness of an implementation against well known test vectors.



That doesn't mean that there may not be differences in implementation between libraries, so some libraries:


require a key in a specific format (and error handling if the format is incorrect is not always up to par);
require CRT (Chinese Remainder Theorem) parameters for a key;
require a pre-calculated hash value, some libraries an initialized hash function and some libraries just need an indication of the hash function (and some just assume a specific hash function, often SHA-1);
offer implicit character encoding for when the input is a text string (this may also be implicit for the programming language / runtime);
allow for online calculation of the hash / signature by offering update / final functionality  or streaming;
implicitly or explicitly perform the choice of the padding mode (PKCS#1, PSS, ISO 9797, RAW, etc.);
offer to encode or even implicitly encode the result into a text string.


Less common, but still possible is the way bytes are ordered for lower level functions. You may also have to configure implementation specific parameters, such as a random number generator for protecting the implementation or performing PSS signature generation. PSS may also require parameters - a second hash function for instance - for Mask Generation Function (MGF-1).

These are all design choices that may influence the outcome of the signature generation or verification. So a developer still needs to study the API to known what to expect, despite the well written PKCS#1 standard.



Fortunately, once the algorithm works it is unlikely to fail unless you manage to bugger up the character encoding of the message or the encoding / decoding of the signature.



It seems you got stuck on (3). Double calculation of the hash is a common mistake. Note that PKCS#1 signature describes the signing of messages and includes the hashing within the signature generation description, so having the signature also generate the hash is common among cryptographic APIs.