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) => {
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.
md
comes with a method allowing to know the hash used,forge.pki.privateKeyFromPem.sign
can't know which hash was used, even from its size (that could be SHA-512/256). This could be an indication that it uses no padding, or equivalently PKCS1v1 padding mode 0, allowing certain attacks, and breaking compatibility with RSASSA-PKCS1-v1_5. It could also be that your signer code hashes a message twice as long as the verifier does, consisting of the rendition in (lowercase) hexadecimal of the actual message. Check if changinghex
to uppercase changesmd
. $\endgroup$