I am trying to "decode" this following Base64 string:
OBFZDTcPCxlCKhdXCQ0kMQhKPh9uIgYIAQxALBtZAwUeOzcdcUEeW0dMO1kbPElWCV1ISFFKZ0kdWFlLAURPZhEFQVseXVtPOUUICVhMAzcfZ14AVEdIVVgfAUIBWVpOUlAeaUVMXFlKIy9rGUN0VF08Oz1POxFfTCcVFw1LMQNbBQYWAQ==
This is what I know about the string itself:
The original string is first passed through the following code:
private static string m000493(string p0, string p1)
{
StringBuilder builder = new StringBuilder(p0);
StringBuilder builder2 = new StringBuilder(p1);
StringBuilder builder3 = new StringBuilder(p0.Length);
int num = 0;
Label_0084:
while (num < builder.Length)
{
int num2 = 0;
while (num2 < p1.Length)
{
if ((num == builder.Length) || (num2 == builder2.Length))
{
MessageBox.Show("EH?");
goto Label_0084;
}
char ch = builder[num];
char ch2 = builder2[num2];
ch = (char)(ch ^ ch2);
builder3.Append(ch);
num2++;
num++;
}
}
return m0001cd(builder3.ToString());
}
The p1 part in the code is supposed to be the string "_p0lizei.".
It is then converted to a Base64 string by the following code:
private static string m0001cd(string p0)
{
string str2;
try
{
byte[] buffer = new byte[p0.Length];
str2 = Convert.ToBase64String(Encoding.UTF8.GetBytes(p0));
}
catch (Exception exception)
{
throw new Exception("Error in base64Encode" + exception.Message);
}
return str2;
}
The question is, how do I decode the Base64 string so that I can find out what the original string is?
Simple:
byte[] data = Convert.FromBase64String(encodedString);
string decodedString = Encoding.UTF8.GetString(data);
The m000493 method seems to perform some kind of XOR encryption. This means that the same method can be used for both encoding and decoding the text. All you have to do is reverse m0001cd:
string p0 = Encoding.UTF8.GetString(Convert.FromBase64String("OBFZDT..."));
string result = m000493(p0, "_p0lizei.");
// result == "gaia^unplugged^Ta..."
with return m0001cd(builder3.ToString()); changed to return builder3.ToString();.
// Decode a Base64 string to a string
public static string DecodeBase64(string value)
{
if(string.IsNullOrEmpty(value))
return string.Empty;
var valueBytes = System.Convert.FromBase64String(value);
return System.Text.Encoding.UTF8.GetString(valueBytes);
}
I have base64 encoded binary data(~2.5MB). The data is consisting of c double value array. In c language it comes with no cost using this array - you just read the whole data into memory and use double* address to access any element.
However, now I need to use this data in javascript. I'm looking for most efficient way to be able to read this array from js code.
You could use this function in Node:
function doublesFromBase64(base64) {
let buff = Buffer.from(base64, 'base64');
let result = [];
for (let i = 0; i < buff.length; i += 8) {
result.push(buff.readDoubleLE(i));
}
return result;
}
To test it, I produced a sample base64 encoded string from the following piece of C code, which uses a function base64_encode found in this answer:
double arr[] = {15.9234, 2.05e30, -19};
size_t output_length;
char * result = base64_encode((unsigned char *) arr, 3* sizeof(double), &output_length);
printf("%s", result);
This outputs:
uECC4sfYL0Cjw3dB6N85RgAAAAAAADPA
I passed this string to the function at the top:
function doublesFromBase64(base64) {
let buff = Buffer.from(base64, 'base64');
let result = [];
for (let i = 0; i < buff.length; i += 8) {
result.push(buff.readDoubleLE(i));
}
return result;
}
let result = doublesFromBase64("uECC4sfYL0Cjw3dB6N85RgAAAAAAADPA");
console.log(result);
This outputs:
[ 15.9234, 2.05e+30, -19 ]
I have a simple node.js script
var text = "Hello!";
var serverSeed = "d8818b38a14e7461e87301ad4b9809b558bcbca816b650cd470452e018ada255";
var crypto = require('crypto');
var hash = crypto.createHmac('sha512', serverSeed).update(text).digest('hex');
console.log(hash);
I also have the C# program
using System;
using System.Text;
using System.Security.Cryptography;
public class Program
{
public static byte[] StringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
public static void Main()
{
var serverSeed = StringToByteArray("d8818b38a14e7461e87301ad4b9809b558bcbca816b650cd470452e018ada255");
using (var sha = new HMACSHA512(serverSeed))
{
var hash = sha.ComputeHash(Encoding.ASCII.GetBytes("Hello!"));
Console.WriteLine(ByteArrayToString(hash));
}
}
}
(runable version here)
I get from the node.js program
99e3b20acaa9c7674f074da950945ee897876b0afc02121d5a89fa581081465f3e01a084e9b05bed729b7fbdc1d485fb38af7d6f501cbc258b6c66add54410ba
And from the C# program
73250817a927f394b0912afcece47b8c12aeaed31892c64116ae9dd0d407f6e31d5c062d65f68a3cae09a8acb14a7cef1f6afd99f5a22f2b73e46a991fcd079a
What am I doing wrong to cause this difference?
Your C# code is converting the hex characters in your seed to a byte array based on the what the characters represent in hexadecimal format.
But your node code is passing the seed as a string which is converting the characters to a byte.
So for example, your C# code is converting a to a byte with the value of 10 but your node code would be converting a to a byte with the value of 97
Your node code either needs to convert the hex in your string to a Buffer based on their hex values as you are doing in C#.
var text = "Hello!";
var serverSeed = "d8818b38a14e7461e87301ad4b9809b558bcbca816b650cd470452e018ada255";
var crypto = require('crypto');
var buff = new Buffer(seed, "hex")
var hash = crypto.createHmac('sha512', buff).update(text).digest('hex');
console.log(hash);
Or in C# instead of converting hex to bytes you can get a byte array representing the actually characters the seed using GetBytes from a System.Text.Encoding instance.
var serverSeed = Encoding.ASCII.GetBytes("d8818b38a14e7461e87301ad4b9809b558bcbca816b650cd470452e018ada255");
using (var sha = new HMACSHA512(serverSeed))
{
var hash = sha.ComputeHash(Encoding.ASCII.GetBytes("Hello!"));
Console.WriteLine(ByteArrayToString(hash));
}
Most likely you intended to pass the serverSeed as a buffer in the node.js code.
I'm trying to encrypt text using WebCrypto. I convert the result to a utf8 string, then convert that to hex. The encryption/decryption works. However, I want to convert the data to hex. When I try to convert to hex and back, the result is different.
Here's the fiddle (use Chrome): https://jsfiddle.net/yxp01v5g/
The test code is here:
var text = "hello world";
var key = App.crypto.generateKey(16);
App.crypto.encrypt(text, key, function(encryptedText, iv){
console.log("encrypted text:", encryptedText, "iv", iv);
var encryptedTextHex = convertUtf8StringToHex(encryptedText);
console.log("encrypted text hex", encryptedTextHex);
var backToUtf8 = convertHexToUtf8(encryptedTextHex);
console.log("Back to utf8", backToUtf8);
console.assert(encryptedText == backToUtf8);
})
As you can see, I'm taking the result, converting it to hex, then converting it back to utf8, hoping for it to be equal to the original result. However, it's not.
Can anyone tell me what on earth I'm doing wrong?
I didn't dig too deep into the call-chain used in the fiddle to do the conversion, but it appears to try converting UTF-16/UCS-2 to byte size instead of an actually hex representation of the buffer content itself.
Here is one approach to convert the byte buffer content to hex string representation, and from hex string back to binary data.
It takes each byte in the buffer and produced a two-digit hex representation of its value, and concatenates it to a string.
The reverse takes two chars from the string representation and converts it back to a byte value representation.
// some bytes as Uint8Array
var random = crypto.getRandomValues(new Uint8Array(16)), i, str = "", allOK = true;
// convert to HEX string representation
for(i = 0; i < random.length; i++) str += pad2(random[i].toString(16));
console.log(str);
// convert back to byte buffer
var buffer = new Uint8Array(random.length);
for(i = 0; i < buffer.length; i++) buffer[i] = parseInt(str.substr(i<<1, 2), 16);
// check if same content
for(i = 0; i < buffer.length; i++) if (buffer[i] !== random[i]) allOK = false;
console.log("All OK?", allOK)
function pad2(s) {return s.length < 2 ? "0" + s : s}; // helper: pad to 2 digits
I am using RSA in javascript from: http://www-cs-students.stanford.edu/~tjw/jsbn/
And on their demo page, it is able to generate the keys required: http://www-cs-students.stanford.edu/~tjw/jsbn/rsa2.html
But what I don't understand, is how to turn their key, which is a few variables, of hex strings, into a string that looks like a private/public key string.
Their output looks like this...
Modulus (hex):
a32464be9bef16a6186a7f29d5ebc3223346faab91ea10cc00e68ba26322a1b0
3dc3e1ec61832fca37ed84018db73ae6b79bd8f3fa2945c8606766402658d0c1
Public exponent (hex, F4=0x10001):
10001
Private exponent (hex):
c3f5730d81402e7453df97df2895884e0c49b5cf5ff54737c3dd28dc6537b3fd
8b0eb2fd82148ebbf81fe16128ec1ebf39fd9a6e62d42aad245b172f4ed8661
P (hex):
f542cdc91f73747ecc20076962a2ed91749b8e0af66693ba6f67dd92f99b1533
Q (hex):
aa491917d8b05a75db7a5c1f6b592b0f0a9bb30a530cbef44ae233b9bf3d5a3b
D mod (P-1) (hex):
e5bfbea639201e70e926d7ca90ebaf4022cbd533cfbe2784edf78e48b029e6a1
D mod (Q-1) (hex):
3e3e17d8fa9083903ed83be21427f4b03bcd6ba523742e3c373ef56f38b2e14f
1/Q mod P (hex):
d93a8828e23458c2115fc1dd003274485af1483e8ec35866d5cffd214c5f853e
I expect a private key to look something like this...
-----BEGIN RSA PRIVATE KEY-----
MIIBOQIBAAJBALH5w2R5Zl3hHoH7zDF6i5IbO7W1Ry9bT2bxTy2sJa7mZC+p3rQG
YNrX+M/KuOUuVqFaohrWLwU28+VlFjFHG5MCAwEAAQJAFZCi+Viwa62saamd+1zS
7pg4KvNVNcrFmz6gDnOueTujcBdaNfxOzWy39SxSOtPe62qYHPculci/QiJfLvOf
MQIhAOqxj69QPRqervf6ARMI1zV0+UAGA1tWgOeU7NH0U9vLAiEAwiIJu+986PKT
qmjAKDFx3OJxUuEay8kJ5apDjC6s1lkCIBVRdazGDBbb7SbHRcu11N6dNnrTUQC9
9c2TYIOdvvRLAiB7bbDKsKW2ZiTEv/0MkQNX8REkJMMothV41BxGUJbLYQIgF/yA
54gqfRtj3aPB6lDETMOZbZIWgNpn8hU2kFGdlIs=
-----END RSA PRIVATE KEY-----
The private and public keys you are familiar with are in a format called PEM. PEM refers to the header and footer wrapping base64 encoded data.
The encoded binary data is in the DER (Distinguished Encoding Rules) format of ASN.1 (Abstract Syntax Notation One).
Here's a javascript ASN.1 decoder.
http://lapo.it/asn1js/
The public and private keys contain the modulus and either the public or private exponent respectively.
The following code came from a couple authors (listed in the comments), compiled on Ohloh Code (http://code.ohloh.net/file?fid=gxO4OHIDEgc0cAHG4K2iptOVOUY&cid=b0rvFixqcq4&s=&fp=285256&mp=&projSelected=true#L0). All credit due to them for their hard work. But the compilation did the trick for me. Add this as a .js library and then you can call it from your main routine like this:
publicPem = publicPEM();
privatePemUnencrypted = privatePEM(); //you will probably want to encrypt this
The output is in PEM format.
Codez:
//From git://github.com/titanous/pem-js.gitmaster
var ASNIntValue, ASNLength, int2hex;
function privatePEM() {
var encoded;
encoded = '020100';
encoded += ASNIntValue(this.n, true);
encoded += ASNIntValue(this.e, false);
encoded += ASNIntValue(this.d, false);
encoded += ASNIntValue(this.p, true);
encoded += ASNIntValue(this.q, true);
encoded += ASNIntValue(this.dmp1, true);
encoded += ASNIntValue(this.dmq1, false);
encoded += ASNIntValue(this.coeff, false);
encoded = '30' + ASNLength(encoded) + encoded;
return "-----BEGIN RSA PRIVATE KEY-----\n" + encode64(chars_from_hex(encoded)) + "\n-----END RSA PRIVATE KEY-----";
};
function publicPEM() {
var encoded;
encoded = ASNIntValue(this.n, true);
encoded += ASNIntValue(this.e, false);
encoded = '30' + ASNLength(encoded) + encoded;
encoded = '03' + ASNLength(encoded, 1) + '00' + encoded;
encoded = '300d06092a864886f70d0101010500' + encoded;
encoded = '30' + ASNLength(encoded) + encoded;
return "-----BEGIN PUBLIC KEY-----\n" + encode64(chars_from_hex(encoded)) + "\n-----END PUBLIC KEY-----";
};
RSAKey.prototype.parsePEM = function(pem) {
pem = ASN1.decode(Base64.unarmor(pem)).sub;
return this.setPrivateEx(pem[1].content(), pem[2].content(), pem[3].content(), pem[4].content(), pem[5].content(), pem[6].content(), pem[7].content(), pem[8].content());
};
ASNIntValue = function(integer, nullPrefixed) {
integer = int2hex(integer);
if (nullPrefixed) {
integer = '00' + integer;
}
return '02' + ASNLength(integer) + integer;
};
ASNLength = function(content, extra) {
var length;
if (!(typeof extra !== "undefined" && extra !== null)) {
extra = 0;
}
length = (content.length / 2) + extra;
if (length > 127) {
length = int2hex(length);
return int2hex(0x80 + length.length / 2) + length;
} else {
return int2hex(length);
}
};
int2hex = function(integer) {
integer = integer.toString(16);
if (integer.length % 2 !== 0) {
integer = '0' + integer;
}
return integer;
};
/* CryptoMX Tools - Base64 encoder/decoder
* http://www.java2s.com/Code/JavaScriptDemo/Base64EncodingDecoding.htm
*
* Copyright (C) 2004 - 2006 Derek Buitenhuis
* Modified February 2009 by TimStamp.co.uk
* GPL v2 Licensed
*/
function encode64(a){a=a.replace(/\0*$/g,"");var b="",d,e,g="",h,i,f="",c=0;do{d=a.charCodeAt(c++);e=a.charCodeAt(c++);g=a.charCodeAt(c++);h=d>>2;d=(d&3)<<4|e>>4;i=(e&15)<<2|g>>6;f=g&63;if(isNaN(e))i=f=64;else if(isNaN(g))f=64;b=b+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(h)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(d)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(i)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)}while(c<
a.length);a="";b=b.split("");for(c=0;c<b.length;c++){if(c%64==0&&c>0)a+="\n";a+=b[c]}b.join();b=a%4;for(c=0;c<b;c++)a+="=";return a}
function decode64(a){var b="",d,e,g="",h,i="",f=0;a=a.replace(/[^A-Za-z0-9\+\/\=\/n]/g,"");do{d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(f++));e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(f++));h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(f++));i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(f++));d=d<<2|e>>4;e=(e&15)<<4|h>>2;g=(h&3)<<
6|i;b+=String.fromCharCode(d);if(h!=64)b+=String.fromCharCode(e);if(i!=64)b+=String.fromCharCode(g)}while(f<a.length);return b=b.replace(/\0*$/g,"")};
/* JavaScript ASCII Converter
* http://www.vortex.prodigynet.co.uk/misc/ascii_conv.html
*
* TPO 2001/2004
* Modified Feb 2009 by Tim Stamp (timstamp.co.uk)
* License Unknown
*/
function chars_from_hex(a){var c="";a=a.replace(/^(0x)?/g,"");a=a.replace(/[^A-Fa-f0-9]/g,"");a=a.split("");for(var b=0;b<a.length;b+=2)c+=String.fromCharCode(parseInt(a[b]+""+a[b+1],16));return c};
Using the link suggested by #borkencode (http://lapo.it/asn1js/) I have been able to successfully extract all the components of the public key, and also the relevant components from private keys.
Code to test extraction of numerical portions of keys
// copy and paste into console whilst on `http://lapo.it/asn1js/`
// so that dependancies are met
var getParts, pri, pub;
getParts = function(key) {
var end, current, output, crypt, slice, sa, so, start, tree, _i, _len;
// strip out the BEGIN/END tags from the input keys
crypt = key.replace(/^-.+/mg, '');
// decode the input text into ASN1 object
tree = ASN1.decode(Base64.decode(crypt));
// set root object based on type of key
if (key.match(/PUBLIC KEY-/)) {
root = tree.sub[1].sub[0].sub;
} else if (key.match(/PRIVATE KEY-/)) {
root = tree.sub;
} else {
console.error("Not happening");
return;
}
// initialize empty array to populate with results
output = [];
// loop through the root ASN1 object
for (_i = 0, _len = root.length; _i < _len; _i++) {
// get the current object
current = root[_i];
// set the start and end positions in the stream
start = current.stream.pos + current.header;
end = start + current.length;
// cut the required compnent out of the stream
slice = current.stream.enc.slice(start, end);
// format the slice in HEX, joining as string
// and replacing leading `00`s
str = slice.map(function(i) {
return ("0" + i.toString(16)).replace(/.(..)/, "$1");
}).join('').replace(/^(00)+/, '');
// add to output array
output.push(str);
}
// send the results!
return output;
};
pri = "-----BEGIN RSA PRIVATE KEY-----\n"+
"MIIBOgIBAAJBAMpZrx0gTluJEu6+fop1e60lwbnlBD6kHvoRx85GBhUgD8SQknjc\n"+
"LcU2qqM/pV9ZX8MV8x49h2mzrmRyH7kDmpcCAwEAAQJAYf2GYMt5Rrids4IKk5CL\n"+
"IPFs3FH8eT1PRvh/UvP0FBwDMZu/Q4m+3PNTM3ARQhFuCvWgCalMmZkyVx0HYRLe\n"+
"4QIhAOaaQm+b/bSoHqolvVTcyfBL09rrLFZhgGkETX3R6cVRAiEA4KLdUm97YBxP\n"+
"T6/jkn/P7K8SUWEO9o9u8Bif1UKQB2cCIETqoSQ92EqfW9q5wKWV/nvkDYKFehCu\n"+
"vvOjp40MqPKhAiA2sPBZpbLQD5Rvvk8V1/Bzm5xGG+9csEc+RYCEl5QheQIhAKgi\n"+
"Xb3zY9lqtpX/mgTIrW6RPB3GocviJOibqtpfNxRU\n"+
"-----END RSA PRIVATE KEY-----";
pub = "-----BEGIN PUBLIC KEY-----\n"+
"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMpZrx0gTluJEu6+fop1e60lwbnlBD6k\n"+
"HvoRx85GBhUgD8SQknjcLcU2qqM/pV9ZX8MV8x49h2mzrmRyH7kDmpcCAwEAAQ==\n"+
"-----END PUBLIC KEY-----";
console.dir(getParts(pri));
console.dir(getParts(pub));
Output to console:
0: ""
1: "ca59af1d204e5b8912eebe7...d8769b3ae64721fb9039a97"
2: "010001"
3: "61fd8660cb7946b89db3820...94c999932571d076112dee1"
4: "e69a426f9bfdb4a81eaa25b...c56618069044d7dd1e9c551"
5: "e0a2dd526f7b601c4f4fafe...ef68f6ef0189fd542900767"
6: "44eaa1243dd84a9f5bdab9c...a10aebef3a3a78d0ca8f2a1"
7: "36b0f059a5b2d00f946fbe4...f5cb0473e45808497942179"
8: "a8225dbdf363d96ab695ff9...1cbe224e89baada5f371454"
------------------------------------------------------
0: "ca59af1d204e5b8912eebe7...d8769b3ae64721fb9039a97"
1: "010001"
I have tested the values, using them as inputs to encrypt, and decrypt messages with my javascript library. What I really want to be able to do now, is to create the .pem and .pub files by going the other way, from numbers, to formatted text.
The closest I have found so far, is a PHP function, which I might translate to javascript. Details here: http://pumka.net/2009/12/19/reading-writing-and-converting-rsa-keys-in-pem-der-publickeyblob-and-privatekeyblob-formats/
//Encode key sequence
$modulus = new ASNValue(ASNValue::TAG_INTEGER);
$modulus->SetIntBuffer($Modulus);
$publicExponent = new ASNValue(ASNValue::TAG_INTEGER);
$publicExponent->SetInt($PublicExponent);
$keySequenceItems = array($modulus, $publicExponent);
$keySequence = new ASNValue(ASNValue::TAG_SEQUENCE);
$keySequence->SetSequence($keySequenceItems);
//Encode bit string
$bitStringValue = $keySequence->Encode();
$bitStringValue = chr(0x00) . $bitStringValue; //Add unused bits byte
$bitString = new ASNValue(ASNValue::TAG_BITSTRING);
$bitString->Value = $bitStringValue;
//Encode body
$bodyValue = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" . $bitString->Encode();
$body = new ASNValue(ASNValue::TAG_SEQUENCE);
$body->Value = $bodyValue;
//Get DER encoded public key:
$PublicDER = $body->Encode();
For answer to your question, please have a look at the RSA algorithm for the actual formulae . For the complexity part, its better to leave the transformation to the JS
In case it helps, I used this code for my application and it worked wonders :)