RSA PKCS1 V1.5 padding encryption in Node js - javascript

I wanna encrypt the symmetric session key (which I'm using for AES encryption) with the RSA algorithm with Padding PKCS1 V1.5 but somehow I'm not getting any library that provides this padding (except node-forge). But in node-forge, I guess the padding is not working correctly, It's breaking on the server side and Saying "Padding is not proper etc".
I tried so many options, I'm referring to the python code(which is working correctly), in that, it provides PKCS1 V1.5 padding properly.
encryptData(sessionKey, publicKey):
sessionKeyBytes = bytes(sessionKey, 'utf-8')
key = RSA.import_key(publicKey)
cipher = PKCS1_v1_5.new(key)
cipherTextBytes = cipher.encrypt(sessionKeyBytes)
cipherText = b64encode(cipherTextBytes).decode()
Any library or something that provides this PKCS1 V1.5 padding properly?
Node Js (node-forge) example code
const forge = require("node-forge");
const fs = require("fs");
const Data = "This is a test";
const publicKeyReading = fs.readFileSync("./publicKeyFinal.pem");
const privateKeyReading = fs.readFileSync("./privateKeyFinal.pem");
const publicKeyFromCert = forge.pki.certificateFromPem(publicKeyReading);
const publicKeyToPem = forge.pki.publicKeyToPem(publicKeyFromCert.publicKey);
const publicKeyFromPem = forge.pki.publicKeyFromPem(publicKeyToPem);
const privateKeyFromPem = forge.pki.privateKeyFromPem(privateKeyReading);
const RSAEncryption = publicKeyFromPem.encrypt(Buffer.from(Data));
const RSADecryption = privateKeyFromPem.decrypt(RSAEncryption);
console.log("RSAEncryption", forge.util.encode64(RSAEncryption));
console.log(RSADecryption);
This decryption is working properly here in Node JS but somehow it's breaking on the server side which is written in C#. I tried standard Node Js crypto too but I guess it doesn't provide PKCS1 V1.5 padding for the encryption (I guess that padding is only for signing and verifying in the Node JS standard lib).
I'm a newbie to asking questions on StackOverflow, so let me know if you need any code or extra info for a better understanding of the problem. I've been working on this project for the past 5 weeks and it's been stuck here for the past 2 weeks.
Thank you in Advance.
Update :
As I got know, That Node JS does provide PKCS1 V1.5 padding and I used that but still it's breaking on the server side and I guess that's because of the oaepHash thing in publicEncrypt method. We're using fOEP as false in c# and I'm not able to fix that value as a false in Node Js(and its default value is 'sha1'), So is there any way that I can set the oaepHash value as false or something?
Note: I can't change the backend code as per Node JS cause it's working on Java, Python and .net too. I've to figure it out according to the backend code.
C# code
public string DecryptData(EncryptionDecryptionParameterDto encryptionDecryptionParams)
{
ValidateDecryptData(encryptionDecryptionParams);
byte[] numArray = Convert.FromBase64String(encryptionDecryptionParams.CipherText);
string str = string.Empty;
var tuple = LoadCertificateAndPrivateKey(encryptionDecryptionParams.PrivateKey, encryptionDecryptionParams.PrivateKeyPassword);
using (RSACryptoServiceProvider privateKey = new RSACryptoServiceProvider())
{
privateKey.ImportParameters(tuple.Item2);
byte[] numArray1 = null;
numArray1 = privateKey.Decrypt(numArray, encryptionDecryptionParams.IsOAEPWithSHA1AndMGF1Padding);
str = Encoding.UTF8.GetString(numArray1);
}
return str;
}
Node Js
const crypto = require("crypto");
const cert = new
X509Certificate(Buffer.from(A66ClientRequest.PublicKey));
const encryptedData = crypto.publicEncrypt(
{
key: cert.publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING,
},
Buffer.from(sessionKey),
);
return encryptedData.toString('base64');
LoadCertificateAndPrivateKey() in C#
internal Tuple<Org.BouncyCastle.X509.X509Certificate, RSAParameters> LoadCertificateAndPrivateKey(byte[] privateKey,string pswd)
{
using (MemoryStream ms = new MemoryStream(privateKey))
{
Pkcs12Store pkcs12 = new Pkcs12Store(ms, pswd.ToArray());
string keyAlias = pkcs12.Aliases.Cast<string>().FirstOrDefault(p => pkcs12.IsKeyEntry(p));
return new Tuple<Org.BouncyCastle.X509.X509Certificate, RSAParameters>(pkcs12.GetCertificate(keyAlias).Certificate, DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)pkcs12.GetKey(keyAlias).Key));
}
}
ValidateDecryptData() in C#
protected virtual void ValidateDecryptData(EncryptionDecryptionParameterDto encryptionDecryptionParams, bool checkDataLength = false)
{
if (!encryptionDecryptionParams.CipherText.HasContent())
{
throw new ApplicationException("Cipher Text is missing for RSA decryption.");
}
if (encryptionDecryptionParams.PrivateKey == null)
{
throw new ApplicationException("Private Key is missing for RSA decryption.");
}
if (!encryptionDecryptionParams.PrivateKeyFileName.HasContent())
{
throw new ApplicationException("Private Key File Name is missing for RSA decryption.");
}
if (encryptionDecryptionParams.IsPrivateKeyPasswordPresent && !encryptionDecryptionParams.PrivateKeyPassword.HasContent())
{
throw new ApplicationException("Private Key Password is missing for RSA decryption.");
}
if (checkDataLength && encryptionDecryptionParams.DataLength == 0)
{
throw new ApplicationException("Data length is missing for RSA decryption.");
}
if (encryptionDecryptionParams.SkipMachineKeyFileDeletion ? false : !encryptionDecryptionParams.MachineKeyPath.HasContent())
{
throw new ApplicationException("MachineKeyPath is missing");
}
}
EncryptionDecryptionParameterDto() in C#
public class EncryptionDecryptionParameterDto
{
public string SymmetricCode { get; set; }
public string AsymmetricCode { get; set; }
public CipherMode CipherMode { get; set; }
public string CipherText { get; set; }
public int DataLength { get; set; }
public string InitailVector { get; set; }
public string MachineKeyPath { get; set; }
public string PlainText { get; set; }
public byte[] PrivateKey { get; set; }
public string PrivateKeyFileName { get; set; }
public string PrivateKeyPassword { get; set; }
public byte[] PublicKey { get; set; }
public string PublicKeyFileName { get; set; }
public string SharedKey { get; set; }
public bool SkipMachineKeyFileDeletion { get; set; }
public bool IsOAEPWithSHA1AndMGF1Padding { get; set; }
public bool IsPrivateKeyPasswordPresent { get; set; }
public string Signature { get; set; }
public EncryptionDecryptionParameterDto()
{
CipherMode = CipherMode.ECB;
}
}
Update 2:
I try to decrypt Python encrypted code with Node Js (to figure out which padding and all working for it) and I found out that python encrypted data was not able to decrypt in Node JS with RSA_PKCS1_PADDING but in python code, they mentioned PKCS1 V1.5 padding and I used same in Node JS (as #Topac said Node Js does provide the padding PKCS1 V1.5) and it's throwing me error rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error. I didn't get this where I'm using the same padding but not be able to decrypt in the Node JS with the same padding and key.
I did replace the python example code with the original python code (which I'm referring to)
"

So the answer was very simple but kinda tricky to figure out but #Topaco was very helpful.
It's just that I was loading pfx files(public and private keys in pfx format) in c# and loading Pem files(Pem formatted keys) in Node JS. I used Pem formatted keys instead of pfx in the c# decryption implementation and it worked like a charm.
Thank you #jdweng and #topaco.

Related

How do i get RSACryptoServiceProvider to verify a message using public key and signature

I generated a private and public key in javascript like this.
import crypto from "crypto";
/*export const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
modulusLength: 2048,
});*/
const pair = crypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
export const privateKey = pair.privateKey.export({
type: "pkcs1",
format: "pem",
});
export const publicKey = pair.publicKey.export({
type: "pkcs1",
format: "pem",
});
Then i use the private key to create a signature for a jsonfile like this, and the public key to verify it before i return the signature.
//Lav signatur
const signate = crypto.createSign("SHA384");
signate.update(Buffer.from(licenseRelationship, "utf-8"));
const signature = signate.sign(privateKey, "hex");
const verifier = crypto.createVerify("SHA384");
// verificer signature, besked
verifier.update(Buffer.from(licenseRelationship, "utf-8"));
const verificationResult = verifier.verify(publicKey, signature, "hex");
This works perfectly, and then i return the json and the signature as a http response.
I recieve it in c# code and store the two components so im can use them later on request.
Upon request i fetch the two components and want to use the signature to check if the json has been tampered with.
I also has the public key in this code.
I do it like this.
string licenseRelationshipJson = licenseRelationshipDAO.getLicenseRelationshipWithoutSignatureAsJson(licenseRelationship);
byte[] signature = Encoding.UTF8.GetBytes(licenseRelationship.signature);
byte[] licenseRelationshipJsonAsArray = Encoding.UTF8.GetBytes(licenseRelationshipJson);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048);
result = rsa.VerifyData(licenseRelationshipJsonAsArray, signature,
HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1);
if (result)
{
log.write("Message verified ", null);
} else
{
log.write("Message not Verified ", null);
}
All debug code and exception handling removed.
I'm a crypto virgin, and am trying to understand this. But i must have misunderstood something serious.
I have the public key as a string (not base64 encoded)
Ive checked the json, and it is the exact same bytes when signed in Javascript as when being verified in c#
The public key is not used in this process. That has to be wrong i think ?
How do i get the public key into the RWACryptoServiceProvider ?
Im sure im using RWACryptoServiceProvider wrong.
EDIT....:
Ive tried this instead, but still to no avail.
string licenseRelationshipJson = licenseRelationshipDAO.getLicenseRelationshipWithoutSignatureAsJson(licenseRelationship);
byte[] signature = Encoding.UTF8.GetBytes(licenseRelationship.signature);
byte[] licenseRelationshipJsonAsArray = Encoding.UTF8.GetBytes(licenseRelationshipJson);
byte[] asBytes = Encoding.ASCII.GetBytes(DataStorage.Instance.PUBLIC_KEY);
char[] publicKeyAsArray = Encoding.ASCII.GetChars(asBytes);
ReadOnlySpan<char> publicKeyChars = publicKeyAsArray;
RSA rsa = RSA.Create();
try
{
rsa.ImportFromPem(publicKeyChars);
result = rsa.VerifyData(licenseRelationshipJsonAsArray, signature, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1);
} catch (CryptographicException cex)
{
log.write("Something went wrong with the crypto verification process", cex);
}
.
.
.
Thankyou for your time.

Crypto.js SHA1 vs MessageDigest java SHA1: why different results?

I need to encrypt a string. The initial version was written in Java, but it needs to be rewritten in javascript now. But i have different results.
Here is the code in java:
Java Version:
private static String getEncrypt(String input, String salt) throws NoSuchAlgorithmException {
byte[] raw_salt = Base64.getDecoder().decode(salt);
byte[] raw_data = input.getBytes(StandardCharsets.UTF_8);
byte[] test_data = new byte[raw_salt.length + raw_data.length];
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
System.arraycopy(raw_salt, 0, test_data, 0, raw_salt.length);
System.arraycopy(raw_data, 0, test_data, raw_salt.length, raw_data.length);
mdTemp.update(test_data);
byte[] bytes = mdTemp.digest();
return new String(Base64.getEncoder().encode(bytes));
}
input: 123456789
salt: pMm6kWsoWjR18sWKnoG4Az==
output: 6u/VAXS9ZKmLEbHw/OZ1AVarth4=
JS Version: (use crypto.js)
const crypto = require("crypto");
function getEncrypt(input, salt){
const sha1 = crypto.createHash('sha1');
const beforeCrypto = salt + input;
const afterCrypto = sha1.update(beforeCrypto).digest('base64');
return afterCrypto;
}
input: 123456789
salt: pMm6kWsoWjR18sWKnoG4Az==
output: ie/3j+92nxvcNT5i+3WUJbWsEAg=
The MessageDigest method in java requires me to enter input in byte[] format. While in javascript, I use ·string· type input.
They also use salt to make the encryption more safe, but it brings more different that i cannot rewrite it in javascript.
I try many ways to solve it. But I still cannot get the same result.
The following snippet also gives me the correct answer:
98O8HYCOBHMq32eZZczDTKeuNEE=
There must be some detail in your code that is different.
One thing I did was to use the UTF8 standard charset, to avoid any mishaps with the way "utf-8", vs. "UTF8" etc is specified.
Your code does not compile (missing return type on getEncrypt, for example) so there might be something else that is different.
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Main {
private static String getEncrypt(String input) throws NoSuchAlgorithmException {
byte[] raw_data = input.getBytes(StandardCharsets.UTF_8);
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(raw_data);
byte[] bytes = mdTemp.digest();
return new String(Base64.getEncoder().encode(bytes));
}
public static void main(String[] args){
try {
System.out.println(getEncrypt("123456789"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}

Generate HMAC SHA Algorithm using URI and Key

I wrote a Java program which generates HMAC SHA hash code, But due to some reason I have to write the same code in NodeJs/JavaScript. I tried googling around but did not get anything. In this Java code, I am passing URI and Key as arguments, to generate the hash code, where URI contains Timestamp.
The java code is as :
public static String calcMAC(String data, byte[] key) throws Exception {
String result=null;
SecretKeySpec signKey = new SecretKeySpec(key, SecurityConstants.HMAC_SHA1_ALGORITHM);
Mac mac = Mac.getInstance(SecurityConstants.HMAC_SHA1_ALGORITHM);
mac.init(signKey);
byte[] rawHmac;
try {
rawHmac = mac.doFinal(data.getBytes("US-ASCII"));
result = Base64.encodeBase64String(rawHmac);
} catch (Exception e) {
e.printStackTrace();
}
return result.trim();
}
public static void main(String args[]) {
String timestamp = args[0];
String key = "d134hjeefcgkahvg32ajkdbaff84ff180";
String out = null;
try {
out = calcMAC("/req?app_id=47ca34" + timestamp + "=2018-05-22T12:02:15Z",
key.getBytes());
System.out.println(URLEncoder.encode(out, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
Is it possible to achieve the same goal in NodeJs/JavaScript?
Note:: I have to call this script from Postman pre-request script.
The crypto module should do this for you, you can substitute the 'data' variable with whatever you want to hash:
const crypto = require('crypto');
const data = 'The fault dear Brutus lies not in our stars';
const key = Buffer.from('d134hjeefcgkahvg32ajkdbaff84ff180', 'utf8');
const hash = crypto.createHmac('sha1', key).update(data).digest('base64');
const uriEncodedHash = encodeURIComponent(hash);
console.log('Hash: ' + uriEncodedHash);
Hashing the data in both Java and Node.js gives me the result (URI Encoded) of:
TJJ3xj93m8bfVpGoucluMQqkB0o%3D
The same Java code would be:
public static void main(String args[]) {
String data = "The fault dear Brutus lies not in our stars";
String key = "d134hjeefcgkahvg32ajkdbaff84ff180";
String out = null;
try {
out = calcMAC(data, key.getBytes());
System.out.println(URLEncoder.encode(out, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
Again, we can put anything into 'data' we want.

Different behaviors from Decryption method in java

i am using decryption alogirthm RSA with public and private keys from certification .
var encrypt = new JSEncrypt();
var publicKey = "example";
encrypt.setPublicKey(publicKey);
var data = encrypt.encrypt(value);
console.log(data);
return data;
public static String getDecrypted(String data, String Key)
throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
Cipher cipher = Cipher.getInstance("RSA");
PrivateKey pk = KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(Key.getBytes())));
cipher.init(Cipher.DECRYPT_MODE, pk);
// byte[] encryptedbytes =
// cipher.doFinal(Base64.encodeBase64(data.getBytes()));
return new String(cipher.doFinal(Base64.decodeBase64(data)));
}
i am using front end encryption and in main method encryption . the encrypted values from two sides are getting correctly . but when i call it from web
i get another values like that
EAQ�žÃqÈ›#Á¢éz¡?�4zsHD-U€�
KÉ¢a`Õ³=jö`›=šúFhU;••˜ü®2¿¶žñÛ¤lÉê×)§?]¾–`n_üÙ&1ï)ðeÈž‹x¯ø¬#;ýp& Ê*í~ý¾´çVõF¯±ë©yṉ̃©w_f'úH⬒#™G™|¦ý¹j"Ìç8=ŽRÉž[££4™s#àâ
and the value at the end

Sending content of a text file to a WCF webservice

I am working on a project where it involves calling a WCF web service to upload content of a given file to a WCF web service. client is a iPad application written using Titanium Studio. But i am allowed to send files which are less than 8KB in size. but files i am sending can be large in size than 8KB. when i send files which are larger than 8KB, web service return following error message.
The server encountered an error processing the request
Given below is the client code which call
Data is sent to the web service using JSON format.
var readFile = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory,'log.txt');
modifiedDate = readFile.modificationTimestamp();
var payload ={dateTime: modifiedDate, fileName:'log.txt' , text:readFile};
var xhrLog = Titanium.Network.createHTTPClient();
var serviceUrl = 'http://192.168.134.134/webservice/viewerservice.svc/submitLogData/';
xhrLog.open("POST", serviceUrl);
xhrLog.setRequestHeader("Content-Type", "");
xhrLog.setRequestHeader("Authentication-Token", "605b32dd");
xhrLog.onload = function() {
Ti.API.info(this.responseText);
},
xhrLog.onerror = function() {
}
xhrLog.send(JSON.stringify(sendData));
Following is the service contract and data contract used in the WCF web service to retrive data.
[OperationContract]
[WebInvoke(
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/SubmitLogData/")]
bool SubmitLogData(List<LogData> log);
Data Contract
[DataContract]
public class LogData
{
[DataMember]
public string dateTime { get; set; }
[DataMember]
public string fileName { get; set; }
[DataMember]
public string text { get; set; }
}
You probably need to increase all the quotas - example of messagesize - would be helpfull if you could enable the WCF server-side tracing and see the exact error message!
UPDATE: example for a wsHttpBinding:
<binding name="wsHttp" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647" >
</binding>

Categories

Resources