I was trying to understand CryptEncode() in order to send and receive strings between mt4 indicator and PHP server.
But when a string is encrypted on mql4 side it's totally different then when it's encrypted on the PHP side. Same key and same encryption algorithm is used on both ends, but still the encryption is different on both sides.
And when I use an online tool to decrypt the PHP encrypted string it successfully decrypts the sting, whereas when I use the mql4 generated encrypted string it says final block is not properly padded.
Code:
string text="this is the text to be encoded";
string keystr="d41d8cd98f00b204e9800998ecf8427e";
uchar src[],dst[],key[];
StringToCharArray(text,src);
StringToCharArray(keystr,key);
int encd=CryptEncode(CRYPT_AES256,src,key,dst);
encd=CryptEncode(CRYPT_BASE64,dst,key,src);
if(encd>0)
{
PrintFormat("Encoded data: size=%d, string='%s'",ArraySize(src),CharArrayToString(src));
}
else
Print("Error in CryptEncode. Error code=",GetLastError());
Why is it that mql4 generated encrypted string different from a string generated by any other language? How to avoid this issue? Is there any alternative encryption library available to avoid this issue?
It is not different, it is just that MQL4 only supports a very specific implementation of AES encryption and unless you use the correct settings in your other code you will not achieve compatibility between the two platforms.
Specifically you need to ensure the following are implemented:
Padding Mode: Zeros
Cipher Mode: ECB (so no IV)
KeySize: 256
BlockSize: 128
You can try the online AES encryption/decryption tool to verify your results available here: The online toolbox
// PUTTING ALL THINGS TOGETHER
<?php
$plaintext = '123456';
$key = 'MyKey';
$encryptedMessage = EncryptMQL($plaintext, $key);
echo ($encryptedMessage);
echo nl2br("\n");
$decryptedMessage = DecryptMQL($encryptedMessage, $key);
echo ($decryptedMessage);
//--------------------------------------------------
function EncryptMQL($plaintext, $key){
$data .= "\0"; // string of MQL is terminated by null character
$key .= "\0";
$hash = hash('sha256', $key, true); // true (raw binary) | false (lowercase hexits)
$result = openssl_encrypt(padZero($plaintext), "AES-256-ECB", $hash, OPENSSL_ZERO_PADDING);
// note: result = AES (base64 encoded)
return $result;
}
function DecryptMQL($encryptedtext, $key) {
$key .= "\0";
$hash = hash('sha256', $key, true); // true (raw binary) | false (lowercase hexits)
$decrypted = openssl_decrypt($encryptedtext, "AES-256-ECB", $hash, OPENSSL_ZERO_PADDING);
return(unpadZero($decrypted));
}
//--------------------------------------------------
function padZero($data, $blocksize = 16) {
$pad = $blocksize - (strlen($data) % $blocksize);
return $data.str_repeat("\0", $pad);
}
function unpadZero($data) {
return rtrim($data, "\0");
}
?>
Related
Got a weird question for you. Recently upleveled my old project from java 7(jdk1.7.0_10) to java 8(1.8.0.91.x86_64). In java 7 it printed the post script file with no issues and now it is printing the postscript file as plain text instead of converting the file. This is on a redhat linux environment. Simply I am trying to print a string containing a post script file of a file itself.
Here is my original code
DocFlavor flavor = DocFlavor.INPUT_STREAM.POSTSCRIPT;
PrintService pService = PrintServiceLookup.lookupDefaultPrintService();
// In a field environment, send to the printer
if (System.getenv("USER_DEFINED_RELTOP") == null || pfr.exists()) {
if (pService.getName().isEmpty()) {
LOGGER.error("No printer selected");
} else {
LOGGER.info("Printing to " + pService.getName());
DocPrintJob pj = pService.createPrintJob();
try {
InputStream is = new ByteArrayInputStream(data.getBytes("UTF8"));
Doc doc = new SimpleDoc(is, flavor, null);
PrintJobWatcher pjw = new PrintJobWatcher(pj);
pj.print(doc, null);
pjw.waitForDone();
is.close();
} catch (PrintException | IOException e) {
LOGGER.error(e);
} // try block
} // no printer selected
// Otherwise, send to a file
} else {
That worked fine in java 7, I updated it to the oracle spec found here for java 8.
https://docs.oracle.com/javase/8/docs/api/javax/print/PrintService.html#createPrintJob--
https://docs.oracle.com/javase/8/docs/technotes/guides/jps/spec/printing.fm6.html
DocFlavor psFlavor = DocFlavor.INPUT_STREAM.POSTSCRIPT;
PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
attrs.add(MediaSizeName.ISO_A4);
PrintService[] pservices = PrintServiceLookup.lookupPrintServices(psFlavor,
attrs);
File pfr = new File(PFR_INDICATOR);
// In a field environment, send to the printer
if (System.getenv("USER_DEFINED_RELTOP") == null || pfr.exists()) {
//Check we have a printer capable of post script
if (pservices.length > 0) {
LOGGER.info("Printing to " + pservices[0].getName());
DocPrintJob pj = pservices[0].createPrintJob();
try {
InputStream fis = new ByteArrayInputStream(data.getBytes("UTF8"));
//byte[] ba =data.getBytes("UTF8");
Doc doc = new SimpleDoc(fis, psFlavor, null);
LOGGER.info("Doc Flavor " + doc.getDocFlavor());
PrintJobWatcher pjw = new PrintJobWatcher(pj);
LOGGER.info("PrintJob Attributes : " + pj.getAttributes());
pj.print(doc, attrs);
pjw.waitForDone();
fis.close();
} catch (IOException e) {
LOGGER.error(e);
NotificationDialog.show(NotificationDialog.NOTICE_TYPE.ERROR, PRINT_ERROR);
} catch (PrintException e) {
LOGGER.error(e);
}
} else { // no printer selected
This gives me an error java.awt.print.PrinterIOException: java.io.IOException: /usr/bin/lpr: where it looks to not find lpr.
If I keep it the way it was originally (not write to file) it prints the postscript as plain text even if adding the check to check if the printer is post script capable. If I use the new way of printing file I get a lpr not found error. If I print the PS document using the command lpr it converts it as expected and prints fine. If I use lpr -l that doesn't format it prints it document as plain text as well.
Any suggestion/help would be great. I am lost on what to do. I really don't want to convert it to an image and print that.
At a guess I'd say that your printer is an HP or at least PCL + PS printer, not a pure PostScript-only printer.
In that case you generally need to prepend the PostScript with a language selection PJL string. If you don't do this then it usually defaults to PCL and if you don't send any PCL commands (which all begin with 0x1B) then everything is treated as plain ASCII text. That would explain why both your application and lpr -l end up writing text, but lpr itself doesn't (presumably it adds the PJL).
You could try prepending the PostScript file with something like:
%-12345X#PJL JOB
#PJL ENTER LANGUAGE=POSTSCRIPT
NB the first byte there, before the % should be a 0x1b ESC character, but I can't readily paste binary....
Try sending the file with lpr -l if that works then you could try your old printing method.
I am working on a telegram bot which sends telephone numbers to my telegram account. The problem is, that a '+' is converted to a ' ' blank. So every telephone number is wrong.
E.g. '+4915733000000' turns into '4915733000000'. I've tried to use the HTML code + the unicode version \u002B and the url encoding caracter %2B and none of them work.
https://api.telegram.org/botTOKEN/sendMessage?chat_id=MYID&text=Test:\u2031 Unicode:\u002B HTML:+ URL:%2B
Result: Test:‱ Unicode: HTML:
Do you know any possiblility to send a plus sign?
Thanks!
In case someone is using VBA to send Telegram messages with + in them you can replace your string like that:
Dim URL as String
Dim reURL as String
URL = "https://www.webpage.com/product+name/specifics+number" 'etc....
reURL = replace(URL, "+, "%2B")
'send message to telegram code here
For more Encoding info you can visit: https://www.w3schools.com/tags/ref_urlencode.ASP
It is possible to send the plus sign using POST method.
Here's the sample Google App Script code (can be easily adapted to JavaScript).
var options = {
method : "post",
payload: {
method: "sendMessage",
chat_id: "<chat_id_here>",
text: "+something",
parse_mode: "HTML"
}
};
var response = UrlFetchApp.fetch("https://api.telegram.org/bot<YOUR_TOKEN>/", options);
Plus sign can also be easily sent with parse_mode="Markdown".
Just checked (this time on Python using telebot library) that both options work:
bot.send_message(CHAT_ID, "Phone number: +1234567890", parse_mode='Markdown')
bot.send_message(CHAT_ID, "Phone number: +1234567890", parse_mode='HTML')
I had the same problem. I was using Java and Spring's WebClient. The only way to make it work is building WebClient using DefaultUriBuilderFactory and set encoding mode to NONE.
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(url);
factory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
WebClient webClient = WebClient.builder().uriBuilderFactory(factory).filter(logRequest()).build();
Default is EncodingMode.TEMPLATE_AND_VALUES so if you replace + with %2B the resulting URL is %252B. Setting the encoding mode to NONE doesn't replace any especial characters so I had to replace them manually.
private String replaceUrlSpecialCharacters(String message) {
return message.replace("%", "%25").replace("+", "%2B").replace(" ", "%20").replace("|", "%7C").replace(System.lineSeparator(), "%0A");
}
And now the + sign is shown in my messages.
I'am using PHP and this case was solved with rawurlencode. Below is the code:
public function send_message($tg_msg)
{
$tg_token = ''; // Bot Token
$chat_id = ''; // Chat ID
$url = 'https://api.telegram.org/bot' . $tg_token . '/sendMessage?parse_mode=markdown&chat_id=' . $chat_id;
$curlopt_url = $url . '&text=' . rawurlencode($tg_msg);
$ch = curl_init();
$optArray = array(
CURLOPT_URL => $curlopt_url,
CURLOPT_RETURNTRANSFER => true
);
curl_setopt_array($ch, $optArray);
curl_exec($ch);
curl_close($ch);
}
$msg = 'The message';
send_message($msg);
And now the + sign is shown in my messages.
I got that solved by just using this php function:
utf8_encode(text_to_send)
I have a receipt in PKCS7 that I obtained from my iOS app. Apple says this is a PKCS7 structure, and within that, is information regarding past recurring purchases.
I have the raw receipt here, encoded in Base64.
I've sent this payload, with my secret key, to Apple and got this response. Based on WWDC videos, and documentation, I believe I should be able to read this receipt directly, and without sending it to apple.
I'm guessing that PEMReader in BC is the correct starting point parse it, however I'm not sure how to actually use it. I've scanned the BC source code for the strings "PKCS", and looked at unit tests, however all I ever see are casts from PEMReader into another format.
using (var stream1 = new MemoryStream(receipt.Data))
using (var stream2 = new StreamReader(stream1))
{
var pp = new PemReader(stream2);
pp.ReadObject();
}
Question
How do I use Bouncy Castle to verify a raw receipt payload generated from Apple Store?
Note to self: I intend to use this to inspect the actual binary to see if ApplicationUsername is included in the receipt, yet for some reason isn't returned in the JSON result when posting the server. (Bug on Apple's side?)
I've made this using Java 7 and BouncyCastle 1.56.
For the code below, consider that pemString is the PEM string you provided. But I had to make some modifications:
format (break lines for every 64 characters) - I've made a small program to do that
include BEGIN and END headers
So my PEM looks like:
-----BEGIN PKCS7-----
MIIv5gYJKoZIhvcNAQcCoIIv1zCCL9MCAQExCzAJBgUrDgMCGgUAMIIfhwYJKoZI
hvcNAQcBoIIfeASCH3Qxgh9wMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQEC
AQEEAwIBADALAgELAgEBBAMCAQAwCwIBDwIBAQQDAgEAMAsCARACAQEEAwIBADAL
....
gdTu2uzkTyT+vcBlaLHK1ZpjKozsBds7ys6Q4EFp7OLxtJTj7saEDYXCNQtXBjwl
UfSGvQkXeIbsaqSPvOVIE83K3ki5i64gccA=
-----END PKCS7-----
For the code below, I followed the definition in Apple's doc:
ReceiptAttribute ::= SEQUENCE {
type INTEGER,
version INTEGER,
value OCTET STRING
}
Payload ::= SET OF ReceiptAttribute
Code:
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.DLSet;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
String pemString = // PEM String as described above
PemReader reader = new PemReader(new StringReader(pemString));
PemObject pemObject = reader.readPemObject();
reader.close();
CMSSignedData s = new CMSSignedData(pemObject.getContent());
byte[] content = (byte[]) s.getSignedContent().getContent();
ASN1InputStream in = new ASN1InputStream(content);
// Payload: a SET of ReceiptAttribute
DLSet set = (DLSet) DLSet.fromByteArray(in.readObject().getEncoded());
int size = set.size();
for (int i = 0; i < size; i++) {
// ReceiptAttribute is a SEQUENCE
DLSequence seq = (DLSequence) set.getObjectAt(i);
// value is the third element of the sequence
DEROctetString oct = (DEROctetString) seq.getObjectAt(2);
ASN1Object obj = readObject(oct.getOctets()); // *** see comments below ***
}
in.close();
// readObject method
public ASN1Object readObject(byte[] b) throws IOException {
ASN1InputStream in = null;
try {
in = new ASN1InputStream(b);
return in.readObject();
} catch (Exception e) {
// if error occurs, just return the octet string
return new DEROctetString(b);
} finally {
in.close();
}
}
Variable obj will be the content of the ReceiptAttribute, and it can vary a lot - I've seen DERIA5String, DERUTF8String, ASN1Integer and many others. As I don't know all possible values of this field, I think it's up to you to check each value.
I am trying to encrypt something on the iOS side and decrypt it on my node.js server. On the server, I am using the library forge. I was able to encrypt something and decrypt it all on node.js, and that worked. I encrypted like this: const encryptedPassword = publicKey.encrypt(password, 'RAW'); and decrypted like this: const password = privateKey.decrypt(encryptedPassword, 'RAW');.
Now, instead of encrypting in the server, I would like to encrypt on my iOS app, but still decrypt using the same way. I found this library, swift-rsautils. https://github.com/btnguyen2k/swift-rsautils/blob/master/Swift-RSAUtils/RSAUtils.swift It has this function called encryptWithRSAKey, which is what I am using. Since it is raw encryption, I tried to pass in padding SecPaddingNone. However, unfortunately it doesn't work and I am unable to decrypt on the the server. The error message is invalid length, and the length of the base64 data does seem a lot bigger. Does anyone know how I can fix this problem?
Here is my iOS code:
let dataString = text.dataUsingEncoding(NSUTF8StringEncoding)
let certificateLabel = "certificate"
let certificateRef = self.getCertificateFromKeyChain(certificateLabel)
let certificateData = self.getDataFromCertificate(certificateRef)
let cryptoImportExportManager = CryptoExportImportManager()
let publicKeyRef = cryptoImportExportManager.importPublicKeyReferenceFromDERCertificate(certificateData)
let encryptedData = self.encryptWithRSAKey(data, rsaKeyRef: publicKeyRef!, padding: SecPadding.None)
let base64EncryptedString = encryptedData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
I am then sending this base64 encrypted string to the server and trying to decrypt using the private key. It doesn't work unfortunately.
This isn't the answer to your exact question, since I haven't used that specific library, but I have played a little with encryption in javascript and node.js.
I was able to implement the eccjs library which is the Stanford Javascript Crypto Library (SJCL) built with asymmetric elliptical curve support.
On the node.js side:
var ecc = require('eccjs');
var cryptoKeys = ecc.generate(ecc.ENC_DEC); //crypto_keys.enc is the pubic key for encoding. crypto_keys.dec is the private key for decoding.
//send the public key to the client
app.get('/PublicKey', function(req, res){
res.setHeader('Cache-Control', 'private, no-cache, no-store, must-revalidate');
res.setHeader('Expires', '-1');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Content-type', 'text/plain');
res.send('var publicKey = ' + JSON.stringify(cryptoKeys.enc) + ';');
});
//authenticate a user name and a password (encrypted by client) against the domain controller
app.get('/Authenticate', function(req, res){
res.setHeader('Content-type', 'text/plain');
var url = "ldap://na-us-dc01.am.corp.airliquide.com";
var userPrincipalName = req.query.username + "#US-AIRLIQUIDE";
try
{
var cipherMessage = JSON.parse(req.query.encryptedPassword);
var password = ecc.decrypt(cryptoKeys.dec, cipherMessage);
//... Authentication goes here ...
}
catch(err)
{
console.log("Error with authentication: ",err);
res.send("Error with authentication: " + JSON.stringify(err,null,' '));
}
});
In the client:
<script src="ecc.js"></script>
<script src="../PublicKey"></script> <!-- This returns the variable publicKey which has been set equal to the server's public key -->
<script>
function login() {
var plainTextPassword = document.getElementById('password').value;
var cipherTextPassword = ecc.encrypt(publicKey, plainTextPassword);
var username = document.getElementById('name').value;
console.log(ecc, publicKey, cipherTextPassword);
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = (function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
document.getElementById('result').innerHTML = xhttp.responseText;
console.log("Response: " + xhttp.responseText);
}
}).bind(this);
xhttp.open("GET", "../Authenticate?username=" + username + "&encryptedPassword=" + JSON.stringify(cipherTextPassword), true);
xhttp.send();
}
</script>
I'm sure this solution is not perfectly secure and I ended up not using it, and instead implemented HTTPS. However this should provide you with the necessary pieces to do your own asymmetric encryption if that's your ultimate goal.
SecPadding.None has been removed from Swift3 and the code of Swift-RSAUtils has changed so I cannot reproduce your problem.
However I am able to encrypt and then decrypt data with the following code:
let data = "Data to be encrypted".data(using: String.Encoding.utf8)!
let e = RSAUtils.encryptWithRSAKey(data, rsaKeyRef: publicSecKeyRef, padding: SecPadding())
let d = try! RSAUtils.decryptWithRSAPrivateKey(encryptedData: e!, privkeyBase64: privkey)
Can you try again with latest version of Swift-RSAUtils at https://github.com/btnguyen2k/swiftutils ?
Edit: I noticed that you got error "invalid message length". Be noted that RSA cannot encrypt a very large amount of data in one go. It can encrypt a message up to key's size - 11 length.
To workaround that limitation, Swift-RSAUtils splits the long data into small chunks, encrypts each chunk and merges them all together. So, at server side, you should do similarly: split the encrypted data into chunks of key's size, decrypts each one and merges them to the final result.
I've been trying to get the new iOS Game Center GKPlayer method, generateIdentityVerificationSignatureWithCompletionHandler, working so we can securely rely on the Game Center credentials for authentication. We're using Node.js as the backend server, and I've been trying to verify the signature but to no avail.
Here is the code on the server side that I have - if there's anyone who can chime in on what's missing, that'd be appreciated. The question has been answered somewhat here: How to authenticate the GKLocalPlayer on my 'third party server'?, but Node.js hasn't specifically been tackled. Note that the code below doesn't ensures the validity of the certificate with a signing authority (yet).
//Client sends the payload below
//json.playerId - UTF-8 string
//json.bundleId - UTF-8 string
//json.timestamp - Hex string
//json.salt - base64 encoded
//json.publicKeyURL - UTF-8 string
//json.signature - base64 encoded
var json = JSON.parse(req.body);
console.log(JSON.stringify(json));
//get the certificate
getCertificate(json.publicKeyURL, function(cert){
//read file from fs for now, since getCertificate returns cert in DER format
fs = require('fs');
fs.readFile('/gc-sb.pem', 'utf8', function (err,data) {
if (err) {
console.log(err);
} else {
console.log(data);
var verifier = crypto.createVerify("sha1WithRSAEncryption");
verifier.write(json.playerId, "utf8");
verifier.write(json.bundleId, "utf8");
verifier.write(json.hexTimestamp, "hex");
verifier.write(json.salt, "base64");
var isValid = verifier.verify(data, json.signature, "base64");
console.log("isvalid: " + isValid);
}
});
});
One thing I've found using the crypto module in node.js is that it seems to want the certificate in PEM format, and I believe the format retrieved from Apple is DER. Until I figure out how to convert the DER file to PEM, I've temporarily converted it using
openssl x509 -in gc-sb.cer -inform der -outform pem -out gc-sb.pem
The main thing for me is being able to validate the signature first. Conversion of the certificate and verifying it against a signing authority will come later :)
EDIT: I've figured it out - I was hashing the playerId, bundleId, timestamp and salt, and then using the hashed value as information to verify. I needed to just put those pieces of information into the verifier to verify without the SHA-1 hash (since the verifier will be taking care of it). I've modified the code above to "make it work". Hope this helps anyone that comes across this.
Here is how you can validate gamecenter identity using nodejs. It convert also the der certificate format to pem on the fly.
var crypto = require('crypto');
var request = require('request');
var ref = require('ref');
var token = require('./test.json');
request({url: token.publicKeyURL, encoding: null}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var verifier = crypto.createVerify("sha1");
verifier.update(token.playerId, "utf8");
verifier.update(token.bundleId, "utf8");
var buf = ref.alloc('uint64');
ref.writeUInt64BE(buf, 0, token.timestamp.toString());
verifier.update(buf);
verifier.update(token.salt, 'base64');
var pmd = '-----BEGIN CERTIFICATE-----';
var base64 = body.toString('base64');
var size = base64.length;
for (var i = 0; i < size; i = i + 64) {
var end = i + 64 < size ? i + 64 : size;
pmd = pmd + '\n' + base64.substring(i, end);
}
pmd = pmd + '\n-----END CERTIFICATE-----';
var valid = verifier.verify(pmd, token.signature, "base64");
console.log(valid);
}
});
It's seems there's a npm package for it.
https://github.com/maeltm/node-gamecenter-identity-verifier