SwiftyRSA && Python's cryptography for signature verification - python

I am trying to sign a piece of text using a private key using SwiftyRSA, which I do successfully. Then send the signature to the python server which holds the public key for verification. the But I keep receiving the InvalidSignature exception.
I have tried different hashing algorithms, bit sizes, but still the same InvalidSignature exception. I am sure that the key and signature are related b/c they are generated at the same time!
My issue is not with the libraries themselves--which I think are functioning properly. I think it has to do with a padding discrepancy between the two libraries. Cryptography is using PSS, while I cant find the padding/salt that SwiftyRSA uses.
Key Verification Script
#app.route('/', methods=['POST'])
def verify():
record = json.loads(request.data)
signature = record['sig']
public_key = record['public_key']
hash_local_token = record['hash_local_token']
input_string = record['input_string']
verification_response = auth.verify_signature(signature=signature, pub_key=public_key, input_string=input_string)
print(verification_response)
return jsonify({"Verification": verification_response})
def verify_signature(signature, pub_key, input_string):
# Load the public key
# Url Safe Base64 Decoding
derdata = base64.b64decode(pub_key)
public_key = load_der_public_key(derdata, default_backend())
signature_decoded = base64.urlsafe_b64decode(signature)
# Perform the verification.
try:
public_key.verify(
signature_decoded,
str.encode(input_string),
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH,
),
hashes.SHA256(),
)
return "SUCCESS: Signature Verified!"
except cryptography.exceptions.InvalidSignature as e:
return 'FAILED: Payload and/or signature files failed verification'
Key Generation Script
func moreTesting() {
do {
let keyPair = try SwiftyRSA.generateRSAKeyPair(sizeInBits: 4096)
let privateKey = keyPair.privateKey
let publicKey = keyPair.publicKey
let inputString = "test_string"
let clear = try ClearMessage(string: inputString, using: .utf8)
let signature = try clear.signed(with: privateKey, digestType: .sha256)
let base64Signature = signature.base64String
let parameters: [String: String] = [
"sig": base64Signature,
"input_string": inputString,
"public_key": try publicKey.base64String()
]
AF.request("http://127.0.0.1:5000/", method: .post, parameters: parameters, encoder: JSONParameterEncoder.default).responseJSON { response in
debugPrint(response)
}
}
catch {
print(Error.self)
}
}

It appears that SwiftyRSA's signing APIs use only PKCS1 and not PSS. See: https://github.com/TakeScoop/SwiftyRSA/blob/master/Source/Signature.swift#L20-L27
So to resolve this you'll want to switch your padding from PSS to PKCS1v15 on the Python side.

Related

Failed Validation while setting up LinkedIn Webhook

#app.route('/webhooks/linkedin', methods=['GET'])
def webhook_challenge_linkedIn():
# creates HMAC SHA-256 hash from incoming token and your consumer secret
sha256_hash_digest = hmac.new(bytes({api_secret},'utf-8'), msg=bytes(request.args.get('challengeCode'),'utf-8'), digestmod=hashlib.sha256)
# construct response data with base64 encoded hash
print((sha256_hash_digest))
val = {
"challengeCode" : request.args.get('challengeCode'),
"challengeResponse" : sha256_hash_digest.hexdigest()
}
return json.dumps(val)
When I try to set up the server and send an authentication request from LinkedIn for webhook to this endpoint it says Failed Validation
all i need to do for the verification is return the challenge code in
challengeResponse = Hex-encoded(HMACSHA256(challengeCode, clientSecret))
I think this is what I did but the still validation gets failed.
I don't seem to see the issue.
Try the following:
#app.route('/webhooks/linkedin', methods=['GET'])
def webhook_challenge_linkedIn():
challenge_code = request.args.get('challengeCode')
# creates HMAC SHA-256 hash from incoming token and your consumer secret
sha256_hash_digest = hmac.new(api_secret.encode(), challenge_code.encode(), hashlib.sha256).hexdigest()
# construct response data with base64 encoded hash
# print(sha256_hash_digest)
val = {
"challengeCode" : challenge_code,
"challengeResponse" : sha256_hash_digest
}
return json.dumps(val)
See this answer from How to use SHA256-HMAC in python code?

Unable to verify signature in Java signed using Python

Stuck at this point for last few days. A file is signed in python using pss(RSA) approach.
Below is the link for reference:
https://pycryptodome.readthedocs.io/en/latest/src/signature/pkcs1_pss.html
At Java client side, while verification, below mentioned approach is used:
https://pycryptodome.readthedocs.io/en/latest/src/faq.html#are-rsassa-pss-signatures-compatible-with-java-or-openssl
Signing Approach:
A hash value has been generated from the payload and the hash value is signed using pem private key. The signature bytes are then encoded to Base64 and sent to client for verification.
Verification Approach:
Initially, a hash value is generated from the payload and then the signature bytes as received from the server side is decoded from Base64 and used for verification.
Both the hash values, viz, sent from Server side and generated at Client side, both are same. However, still the signature is unable to be verified.
Code snippet:
Signing in python:
def sign_file(rsa, byte_buffer):
mgf = lambda x, y: pss.MGF1(x, y, SHA256)
h = SHA256.new(byte_buffer.getbuffer())
print("Hash value: " ,h.hexdigest())
signer = pss.new(rsa, mask_func=mgf, salt_bytes=len(h.digest()))
print("Salt length: ", signer._saltLen)
signature = b64encode(signer.sign(h))
return signature
# SIGN
rsa_key = get_key(priv_key_file)
signature = sign_file(rsa_key, encrypted)
Verification in Java:
public static String verify(String signedData, String signedHash, boolean debug, PublicKey publickey) {
boolean successFlg = false;
try {
Security.addProvider(new BouncyCastleProvider());
Signature ss = Signature.getInstance("SHA256withRSA/PSS");
AlgorithmParameters pss1 = ss.getParameters();
PSSParameterSpec pssParameterSpec = new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 32, 1);
ss.setParameter(pssParameterSpec);
ss.initVerify(publickey);
ss.update(signedHash.getBytes());
successFlg = ss.verify(Base64.getDecoder().decode(signedData.getBytes()));
if (debug) {
System.out.println( "signature matched: " + successFlg);
}
} catch (Exception e) {
e.printStackTrace();
}
return String.valueOf(successFlg);
}
Salt length: 32
Generated hash value:
7ff8ce3dab21c615764682394c07fbc858a5713002f68c1fce520a3a93adbecd
Signed Data(as received from Server side)(Base64 encoded)
E459mLf2PeAIMPFy4c3md+BT+AFMtDBsoyP1GjDgbMFc72Ccuvy9Y97vrjYJxilfaOtdaCdo8xQWURnaWjEdHRT3VL/A5bfqEata2L2v8DxnD7hPtSr5tRYVRvsTDm5ByJHR9uh0VwQTz19GOjvG/slHMH0uJJXSOpuMB/brZggMUspY87oGDDcL+z1JuI6elVQtw+weP2kWWQ5gIG/+WbT2MXiKptaq0cqq6sG5oQNni42KCUj2+HADqypqdlVuyk02CgYDe69WQwnb90qILdqiqnyIbRlhoT8Du/DHY+N15ShTUBght6oeqj/cfuUeMtVgIdIP3g+gH/JMdl2sdw==
Please help in the case.

need to convert following Signnature verification python code to Android?

I create to python3 application that generate the RSA key pairs.
from Crypto.PublicKey import RSA
print("--Private Key Generate--")
key = RSA.generate(2048)
private_key = key.export_key()
file_out = open("key/private.pem", "wb")
file_out.write(private_key)
file_out.close()
print("--Public Key Generate--")
public_key = key.publickey().export_key()
file_out_1 = open("key/receiver.pem", "wb")
file_out_1.write(public_key)
file_out_1.close()
print("key Generated")
I sign some data using python and create a signature. It is also verified using python successfully.
def sign(data):
private_key = RSA.import_key(open('key/private.pem').read())
h = SHA256.new(data)
signature = base64.b64encode(pss.new(private_key).sign(h))
print("signature generate")
verify(data,signature)
return signature
def verify(recive_Data ,signature):
public_key = RSA.import_key(open('key/receiver.pem').read())
h = SHA256.new(recive_Data)
verifier = pss.new(public_key)
try:
verifier.verify(h, base64.b64decode(signature))
print("The signature is authentic")
except (ValueError, TypeError):
print ("The signature is not authentic.")
But actually, my verification implementation in Android(min SDK 23, target SDK 29). So, I need to convert this verification code to Android. I tried using the following code, but not verification success. need some expert help to do it.
public class SecurityHelper {
private static String getKey(InputStream filename) throws IOException {
// Read key from file
String strKeyPEM = "";
BufferedReader br = new BufferedReader(new InputStreamReader(filename));
String line;
while ((line = br.readLine()) != null) {
strKeyPEM += line + "\n";
}
br.close();
// System.out.println(strKeyPEM);
return strKeyPEM;
}
public static PublicKey getPublicKey(InputStream filename) throws IOException, GeneralSecurityException {
String publicKeyPEM = getKey(filename);
return getPublicKeyFromString(publicKeyPEM);
}
public static PublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
String publicKeyPEM = key;
publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
System.out.println(publicKeyPEM);
byte[] encoded = Base64.decode(publicKeyPEM ,Base64.CRLF);
// System.out.println(encoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pubKey = kf.generatePublic(new X509EncodedKeySpec(encoded));
System.out.println(pubKey);
return pubKey;
}
public static boolean verify(PublicKey publicKey, String message, String signature) throws SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initVerify(publicKey);
sign.update(message.getBytes("UTF-8"));
System.out.println(message);
return sign.verify(Base64.decode(signature,Base64.CRLF));
}
}
There are different paddings used, in the Python code PSS and in the Android code Pkcs#1 v1.5, see for the difference RFC 8017. Replace in the Android code SHA256withRSA with SHA256withRSA/PSS.
Update:
Although according to the Android documentation SHA256withRSA/PSS is supported from API level 23+, an InvalidKeyException (No provider supports the provided key) is thrown for API level 23, for API level 24+ it works as specified.
A possible workaround for API level 23 is to use BouncyCastle, which then has to be included as a dependency in the Android project (details depend on the IDE, e.g. here for Android Studio):
implementation 'org.bouncycastle:bcprov-jdk15on:1.64'
Before adding the BC Provider, the pre-installed version must be removed. The schema to be used is SHA256withRSAandMGF1 (see section Signature Algorithms):
Security.removeProvider("BC");
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Signature sign = Signature.getInstance("SHA256withRSAandMGF1");
// Go ahead as for schema SHA256withRSA/PSS...
Note: SpongyCastle would be an alternative possibility. Here the pre-installed BC Provider doesn't have to be removed. The schema is SHA256withRSA/PSS.

Cannot find a way to decrypt a JWE token in python (but created in ASP.Net) using jwcrypto

I am having difficulties decrypting my JWE token in python once it has been encrypted using ASP.Net.
Here is my C# code (fake passwords):
var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("ae743683f78d498a9026d0c87020b9d3"));
var secret = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MbPeShVmYq3t6w9z"));
var signingCreds = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
var encryptingCreds = new EncryptingCredentials(secret, SecurityAlgorithms.Aes128KW, SecurityAlgorithms.Aes128CbcHmacSha256);
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.CreateJwtSecurityToken(
_issuer,
_audience,
new ClaimsIdentity(claimsList),
DateTime.Now,
_expires,
DateTime.Now,
signingCreds,
encryptingCreds);
var token = handler.WriteToken(jwtSecurityToken);
The token looks like this when using the encrypting credentials (should typ not be JWE?):
{
{
"alg": "A128KW",
"enc": "A128CBC-HS256",
"typ": "JWT"
}.{
"userId": "151aedd5-76c3-4eb2-8b73-a16004315731",
"prop1": "test1",
"prop2": "test2",
"nbf": 1549894420,
"exp": 1550240017,
"iat": 1549894420,
"iss": "https://localhost:56880/",
"aud": "https://localhost:56880/"
}
}
This is the token when encrypted:
eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwidHlwIjoiSldUIn0.4w4MZv5WFALGbXhmaYqtTv1VGrUpQpDJ0jN8VmLpQwDRU0j16RbPyg.hGt_z5j8THCiNEhpVvlJmw.WehLBKdyB_eYtDRvHxJHgYwa4GA7f8oKYf3GgIqAAih1eVqU084kHu1lIhC8ibxxziwmFZ4IhBFT-nWgWQrH9thhgqndF4ojtGRBgdHtW3GDAgYV6fgI11h6meyBBuLBs7mkQC5PX8EYsMTDiNE9iNTH4pWtDElc07CGGXlHsm6ntuq7G3sinagZtZMchy1shTY73NTS40FqW37C9HTIPDbrTdsm-USHcGaBMLSmjF5eOZ9Po3p4fhRT42l_gwJc9tlurttYBucvIiO1r_3NB8xGeORizYW1P_P9XGusAFy4L8h8XU9P0FctsMjUFy64LOIK8Qv8YZVq4q82vv-r9uGH6bApUdpCIcYFfGu86w63t1QLQcDT_OYMCqwo9ZmZP5Gd07lB1ypNZbP6hQTgkosp3js3i4K4bFQY7CiSXB_pSTH623TMLHNfUXWMRMIBHmXGr-zTZiKj5vkVUZLjNg.sdBUYvadnwMhkCXP8sABgA
I have tried a couple of different python packages, including 2 versions of jose (python-jose and jose), but could not get jose to work with encryption (did not seem to support the A128KW algorithm).
I am now trying jwcrypto but it seems to expect me to generate a new key rather than use my existing one (the one used to encrypt the JWT in ASP.Net):
from jwcrypto import jwk, jwe
encrypted_token = request.cookies.get(cookie_name)
private_key = "MbPeShVmYq3t6w9z"
jwk_key = jwk.JWK()
# not sure how to use my existing "private_key" value,
# and no support for "A128KW" with jwcrypto despite
# the documentation saying there is support
key = jwk_key.import_key(alg='A128KW', kty="A256CBC-HS512")
jwe_token = jwe.JWE()
jwe_token.deserialize(encrypted_token)
jwe_token.decrypt(key) # decrypt requires an instance of JWK
decrypted_payload = jwe_token.payload
How can I get this to work? Thanks for any advice you can give.
It looks like python-jose DO NOT support JWE. In their online documentation or in the source code I cannot find any line of code related to the JWE parsing or encryption/decryption.
Hopefully in the library list from jwt.io, I've found jwcrypto that should support such encrypted token (see this example dealing with A256KW) and in the srouce code we can see A128KW is listed.
You can give it a try.
from jwcrypto import jwk, jwe
encrypted_token = request.cookies.get(cookie_name)
key = jwk.JWK.from_json('{"kty":"oct","k":"TWJQZVNoVm1ZcTN0Nnc5eg"}')
jwe_token = jwe.JWE()
jwe_token.deserialize(encrypted_token)
jwe_token.decrypt(key)
decrypted_payload = jwe_token.payload

Which Python JOSE library supports nested JWT (signed+encrypted)?

I looked at python-jose and jose but neither seem to support encrypting a signed JWT. For example, "jose" library supports signing and encrypting separately, without nesting them.
Am I missing something, like perhaps it's fairly easy to nest JWTs outside the library? If so, please share tips on achieving this so the result is well formatted.
jwcrypto supports nested JWS and JWE.
To sign and then encrypt:
# Load your RSA pub and private keys
pubKey = jwk.JWK().from_pyca(serializedPublicKey)
privateKey = jwk.JWK().from_pyca(serializedPrivateKey)
# your JWT claims go here
claims = {
# JWT claims in JSON format
}
# sign the JWT
# specify algorithm needed for JWS
header = {
u'alg' : 'RS256',
'customSigHeader':'customHeaderContent'
}
# generate JWT
T = jwt.JWT(header, claims)
# sign the JWT with a private key
T.make_signed_token(privateKey)
# serialize it
signed_token = T.serialize(compact=True)
# JWE algorithm in the header
eprot = {
'alg': "RSA-OAEP",
'enc': "A128CBC-HS256",
'customEncHeader':'customHeaderContent'
}
E = jwe.JWE(signed_token, json_encode(eprot))
# encrypt with a public key
E.add_recipient(pubKey)#
# serialize it
encrypted_signed_token = E.serialize(compact=True)
To decrypt and verify signature:
#Decrypt and Verify signature
E = jwe.JWE()
# deserialize and decrypt
E.deserialize(encrypted_signed_token, key=privateKey)
raw_payload = E.payload
# verify signature
S = jws.JWS()
S.deserialize(raw_payload, key=pubKey)
final_payload = S.payload

Categories

Resources