I hope that today your code is written quickly and without bugs :) Let me explain the context a bit.
I have a two projects which will communicate to each other.
Project A - written in PHP, and it will send requests to project B with DigitalSignature provided in Headers
Project B - written in python, and it will receive requests from project A, verify DigitalSignature and save recieved payload in MQ.
The problem is, that project B, always returns that Digital Signature created by project A is invalid, although if you check the same created digital signature on php or nodejs, everything is correct and valid.
Will try to be short and precise.
I created prototypes in 3 languages( nodejs, php and python )
PHP and nodejs - all works as expected and return the same output. Python - generates different output
Sign code in PHP
function encryptNotification($strNotification, $strPrivateKey) {
$strHash = hash('sha256', $strNotification);
openssl_private_encrypt($strHash, $strCrypt, $strPrivateKey);
return bin2hex($strCrypt);
}
Verify code in PHP
function validateNotification($strNotification, $strDigitalSignature, $strPublicKey) {
$strValidate = hash('sha256', $strNotification);
$strCrypt = hex2bin($strDigitalSignature);
openssl_public_decrypt($strCrypt, $strDecrypt, $strPublicKey);
return $strValidate == $strDecrypt;
}
Sign code in NODEJS
function signNotification(strNotification, strPrivateKeyPath) {
const strHash = crypto.createHash('sha256').update(strNotification).digest("hex");
const privateKey = fs.readFileSync(strPrivateKeyPath, 'utf8');
const strSign = crypto.privateEncrypt(
{
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PADDING
},
strHash
);
return strSign.toString('hex');
}
Verify code in NODEJS
function validateNotification(strNotification, strDigitalSignature, strPublicKey) {
const strValidate = crypto.createHash('sha256').update(strNotification).digest('hex');
const strCrypt = Buffer.from(strDigitalSignature, 'hex');
const publicKey = fs.readFileSync(strPublicKey, 'utf8');
const strDecrypt = crypto.publicDecrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING
}, strCrypt).toString('utf8');
return strValidate === strDecrypt;
}
Sign code in python ( do not work as expected )
def sign_notification(notification, private_key_path):
with open(private_key_path, "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(notification.encode())
hash = digest.finalize()
signature = private_key.sign(
hash,
padding.PKCS1v15(),
hashes.SHA256()
)
return signature.hex()
Verify code in python ( works only if digital signatrue was created in python )
def validate_notification(str_notification, str_digital_signature, str_public_key_pem):
# Decode the public key from PEM format
public_key = serialization.load_pem_public_key(
str_public_key_pem.encode(),
backend=default_backend()
)
# Hash the notification string using SHA-256
notification = str_notification.encode()
# Convert the digital signature from hex to binary
signature = bytes.fromhex(str_digital_signature)
# Verify the signature
try:
public_key.verify(
signature,
notification,
padding.PKCS1v15(),
hashes.SHA256()
)
return True
except InvalidSignature:
return False
except Exception as e:
print(f"An unexpected error occurred: {e}")
return False
Generated DigitalSignature in nodejs and php
46e0f90929609f626edc48b57c5f6e0f2852b0c554265a1dfae9008a08fe318b04758af53f0dd35bacb904fd508f9e7466701f7010abd0fb90b2304f59944a19acafc29b05287b07cb8fea360de274bed8f3ae199c2e40b29fd746bac0ee4ee991d9c793a818cc2d0fe8fc8396d9d5bdb3429935fe6d2ce4bba8df6dc9c0c902deb29dc70869c7c2573d68935dcccd8be0e60dc359d0f0d3cedcf40291d8318e6315e9451e5b9610f9264ea4ecb6f8a6bc5ca0d2909e725417cc33f29e247e3dc62a9cf2d9f3fa8e50f7355a09264dbdf33f4be0819ee19f0cb97a31ed02c0d8bc82cd120451bab10d78d399ba9bc5a0fd0618649630335b708f39862f6bc0f4a15e4e1d75e3076a6dbe53814babe6e4bcb146f835fa43e9e19d1fca970591a71d65dd41be374ae979da96f06ea96fa1369d5fc1975a063a04625b3b0f975b9d5664843a3b73138def8c351ce0cd0907fd90378c4f441ab98a325de6f5ee96b85c4898ff3a51c4b0c8c7cb82a91186983197f0cb706690bd0d9d07c3be908f022696e2bedb60523679ea002ee63f7882c4732d681c82e03d190ec0b87c2f94182fadbe3a54ff87921035606969c9ed3de3145b69e3df6cff55cb674bb70de75ddf26ac79cb9fdcf4e7bb5f257e66dece6c4f0d36d174764dec0306ace55855eda83fe2115a8fb93012f3d5491ae055ea921104e89ccfd316fe15a63769237f75
Used message body
hello
Private key
-----BEGIN PRIVATE KEY-----
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
0JQBQOys4Qcga615l0FipiZu9wLI
-----END PRIVATE KEY-----
Public key
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu1DduyRsNJvbUWMBXh25
w2MLJZ/tdJp1mI6DTfyGOCj+yeJIXJtdavrAWIqLvvIIFnSkuddSZLOb3Zx58KhB
GWpEVIBH+Vw4d4EnSuxNzA/U14ENfqf84L5avURgAIfZuI1XO3sOllpeBl7RbS+L
BEv0ruxUXBopeYCyOBYL+We/4JBP+nSS1cBox8ywGHIssYqvrt5FEu3PWdcH3x+D
GGKQ11dGvrCJAkAKY2MSKvGPB6GHQ8M/5MUR7RP9h3xVB4ZBb3fvEkdapK/G4rCn
ozqYZi3dPszQvqbtn1xR9ztNrU+h1/LjNB2Z2zbO5C7GEM/3Nw0pDflxeBLQ0OW1
4sz/kZYKZQ2bq/8QoylSJofIARh+jOvTtRS1Oyx9DsXmt2rwg02NIz54Q3dAqKav
CIJf/516D2V8C8OmRNb9eWJqweqiEMeI7fXPQ4K1HfMAQ9LFIrxTwgKkKJuKtBze
QhKpNyWgScLAia7TJTY1LX+cqlUHbJtuj39Duws50Mc+z6C2+kkyic8z7JKknBlI
n8sNhJV6dEDZ7J4EGBL/aJPySorhGr0hyrH/TS43ue2sgvPsPVRavbFCV83KRxrY
Ql0bJtfKl5oeMse3NPCTECBaZXrnNPjl7FIuVFuJZ4rMFU5FSi3WjM8a+P9Lyaoq
aosbK6oRnlPP636/SR1ZWMECAwEAAQ==
-----END PUBLIC KEY-----
I am sure that private, public keys are the same for each example. And I am sure that padding alg also is PKCS1v15. I tried several approaches in python, and also asked a lot of questions to chat gpt, but still - nothing helps. Mby some python guru can help me.
The question is, how to verify generated digital signature in python, which was created by PHP, mby I am missing something? Thank you
hash = notification), but you should change the namehashtomsgor similar. In addition, the three lines regarding hashing should be removed, as the hash (digest.finalize()) is not used at all. The reason for your problem is that the Python code generates RFC8017 compliant signatures, the other two do not. Can the PHP and NodeJS code be changed?DigestInfovalue). Although both codes explicitly hash, they do not add the digest ID, so that the signature is not compliant with RFC8017. The Python code, on the other hand, is compliant, which is why the verification of the PHP/NodeJS signatures with the Python code fails.openssl_sign(), on the NodeJS side e.g.crypto.sign(). If you can't change the NodeJS/PHP side so that the Python code has to be adapted, you need a corresponding Python functionality. The Python crypto libraries known to me adhere to the standard, so you need a custom implementation, see e.g. here. However, I would clearly recommend RFC8017-compliant signatures.