I'm having some troubles matching the value returned from RSA signing
a Base64 SHA1 hash in the actionscript as3crypto library with the result returned in c#.
I'm passing in a Base64 hash decoded as a byte array to the sign()
function provided in as3crypto and base64 encoding the result.
However, this result never matches the returned result from a c#
function which performs the same task. Does it matter that the
function takes in and returns hex even though it works at the byte
array level?
Please see my below signing function to check i haven't missed
anything!
private function signHash(hashInBase64:String):String
{
var src:ByteArray = Base64.decodeToByteArray(hashInBase64);
var key:RSAKey = getRSAKey();
var dst:ByteArray = new ByteArray();
key.sign(src, dst, src.length);
return Base64.encodeByteArray(dst);
}
Anyone had much experience with the AS3Crypto library?
Any help would be great!!!
Thanks,
Jon
I assume that your C# version is using RSA PKCS #1 version 1.5. The standard computes signatures by doing an RSA private key operation over a byte string composed as
0x00 0x01 || 0xff* || 0x00 || OID || hash
Looking at the as3crypto code shows that the RSAKey class does not add any OID during the sign operation. Hence if you don't do it you'll get incorrect results.
Looking at the code also shows that as3crypto is vulnerable to this attack, because it does not verify the padding properly. This attack is more than 3 years old. Hence it seems like a good to use a different library than as3crypto.
Now there is an ActionScript crypto library compatible with .NET. Here it is: http://code.google.com/p/flame. Looks like it supports RSA exactly the way .NET does.
Related
I am using eccrypto library in javascript for encryption using the ECIES algorithm (curve- secp256k1). The cipher generated by encryption in JS code could not be decrypted in Kotlin.
Here is the Javascript code.
var eccrypto = require("eccrypto");
eccrypto.encrypt(publicKeyA, Buffer.from("Sic Mundus Creatus Est")).then(function(encrypted) {
val ciphertext = encrypted.ciphertext
//the hex encoded ciphertext is then sent to the server
}
Here is the decryption code for kotlin
val cipherBytes = DatatypeConverter.parseHexBinary(ciphertext)
val cipher: Cipher = Cipher.getInstance("ECIES", "BC")
cipher.init(Cipher.DECRYPT_MODE, privateKeyA)
print( cipher.doFinal(cipherBytes) )
With this code for decryption, I get an Bad Block Exception.
However, if I just do encryption and decryption using Java, there is no problem. Also, encryption and decryption both in Javascript also work fine.
Is there anything I am missing?
I found the solution (or say figured out the actual issue). Hope it helps future devs:
The mismatch in encryption/decryption between javascript and java implementation is because those implementations are using different parameters of the hash algorithm and AES encryption.
ECIES implementation in Java using BouncyCastle has a crappy implementation. It uses AES with 128 bits, does not use a secure hash algorithm, no robust check for MAC, and has very little test cases.
As a workaround, I now use a custom written ECIES implementation for Java which uses SHA-512 for the hash, AES encryption (using 256 bits key and AES/CBC/PKCS7Padding mode). This new implementation is what eccrypto javascript library engine uses under the hood. Now, they work fine!
Your comment 'Encrypting the same message using the same public key in both environment gives different cipher messages' does not prove a difference between values. I encrypted 10 times the same message '123' with my public key and got 10 times a different encrypted value.
This is due to a random element in the (internal) encryption function:
ciphertext: 0444a9e31cf1f8f7cabcf2a6562622ce2ee1c38bcdf9938fa48401b34fbeae7ea70c9ad1bd16343a3632ef9011ba6081a7df47ed2ca9904bf0f97febdd18f1b5da9315f744a68c87deee353b481cfbe90a49462403550e3a
ciphertext: 04e1970f415cccb62dc61de534bd61ce9627e9b0e857f6270c20b202f3e62a789979d7c9ca893d85f65f00f5462a29cb986764e34fefb8f5c5ce8f0e9700ddf777b616539ec4e860bad4cac04f8cd3b29c61513cb68a1c9b
ciphertext: 04b07bfbfa53e17446ac8ebaf6af53056274ff4e104bfe26da6176aab390d521429971c151f31a1af4b0240703d4b75a81136b22695798b1ad1cf272e842f5e60ba931972e6868ca54301ec6585ff503cfab76f69ad3beb0
ciphertext: 041d90bcfa34af06559c5f482d06a684492001bb3bd52dc2e9f2eb31cd2de27e595fad3178c0f65d3cd160e0444ae6d9cbfbd2a1e12e21525057d79b8ea24fed572426e3fdeba4a298c17fc481acb66db7b0c8f1d0d0701d
ciphertext: 04a440a994dc0a5f712b1190e1dc0bfff15c053e4a03dc3c74c0cfc965a2da5ed6d668a52b4ae1e320b5ed068fc338e2076f2bc68f156fb79d67daf74ab21c4036a23bebcca007417d5c8ed486750dd37b3f495de92312aa
ciphertext: 04033bfaa2fb90c667ca25e77abba6cdc8e6c601ebb5bf836456c810abe54a4d426b3e52477a8b97f1115905d0babbdda572d9294532d7eda032f6fc98a588c77b00b7bebfe1fe1f4319f3aa0b1776c6da02d9f24baa98c2
ciphertext: 047a1aa7e82084af63cdf76ad0bdd21865d38fdee7fa8902dbf5c0e4840c7209c9cb249698802313c83d22fc8e18b376012888ee0878d4e8c186c241b648180613b1ee460bf9eef5c9a6fab15541ae4bc16dd9f98a10c940
ciphertext: 04dbababe30bd8013bd74f846ed2f1bd818e5b826b59b9b6c6336a62e2e373e82e7f80d2a2fbc3094435e61a53325aba6585047af6bfa593260afc6e5ee737783a8dbbc60d3f99277eea95ed2671d2a81d369602388b7cb0
ciphertext: 04eb52c8d946bfd1d84b27858f78d174bf77509058d10d6bfe11ee5f6553b1c571b3c9c7389b08e596735342584c2be43f5ef4e6952d3db7b5239d00b2c7d22f1ca9e588a8f6dc5c8274d97d18999c4a88702d12b9d56ea6
ciphertext: 04808530ee927d5445a1e8f7a06b6bd8c1457cbf89ea6a75e2a28fd8899e06e8ccc5fab8b45536610a79a50ebf3ca1ed5fafb782fe483165fdc483c7e2e3c3724409d539cdbf586f090b783647d791f33ddfa8e2ba29e328
We are developing an application that has to work with data that is enycrpted by LoraWan (https://www.lora-alliance.org)
We have already found the documentation of how they encrypt their data, and have been reading through it for the past few days (https://www.lora-alliance.org/sites/default/files/2018-04/lorawantm_specification_-v1.1.pdf) but currently still can't solve our problem.
We have to use AES 128-bit ECB decryption with zero-padding to decrypt the messages, but the problem is it's not working because the encrypted messages we are receiving are not long enough for AES 128 so the algorithm returns a "Data is not a complete block" exception on the last line.
An example key we receive is like this: D6740C0B8417FF1295D878B130784BC5 (not a real key). It is 32 characters long, so 32 bytes, but if treat it as hexadecimal, then it becomes 16 bytes long, which is what is needed for AES 128-bit. This is the code we use to convert the Hex from String:
public static string HextoString(string InputText)
{byte[] hex= Enumerable.Range(0, InputText.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(InputText.Substring(x, 2), 16))
.ToArray();
return System.Text.Encoding.ASCII.GetString(hex);}
(A small thing to note for the above code is that we are not sure what Encoding to use, as we could not find it in the Lora documentation and they have not told us, but depending on this small setting we could be messing up our decryption (though we have tried all possible combinations, ascii, utf8, utf7, etc))
An example message we receive is: d3 73 4c which we are assuming is also in hexadecimal. This is only 6 bytes, and 3 bytes if we convert it from hexa to normal, compared to the 16 bytes we'd need minimum to match the key length.
This is the code for Aes 128 decrypt we are using:
private static string Aes128Decrypt(string cipherText, string key){
string decrypted = null;
var cipherPlainTextBytes = HexStringToByteArray(cipherText);
//var cipherPlainTextBytes = ForcedZeroPadding(HexStringToByteArray(cipherText));
var keyBytes = HexStringToByteArray(key);
using (var aes = new AesCryptoServiceProvider())
{
aes.KeySize = 128;
aes.Key = keyBytes;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.Zeros;
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(cipherPlainTextBytes, 0, cipherPlainTextBytes.Length))
{
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
decrypted = sr.ReadToEnd();
}
}
}
}
return decrypted;}
So obviously this is going to return "Data is an incomplete block" at sr.ReadToEnd().
As you can see from the example, in that one commented out line, we have also tried to "Pad" the text to the correct size with a full zero byte array of correct length (16 - cipherText), in which case the algorithm runs fine, but it returns complete gibberish and not the original text.
We already have tried all of the modes of operation and messed around with padding modes as well. They are not providing us with anything but a cipherText, and a key for that text. No Initialization vector either, so we are assuming we are supposed to be generating that every time (but for ECB it isn't even needed iirc)
What's more is, they are able to encrypt-decrypt their messages just fine. What is most puzzling about this is that I have been googling this for days now and I cannot find a SINGLE example on google where the CIPHERTEXT is shorter than the key during decryption.
Obviously I have found examples where the message they are Encrypting is shorter than what is needed, but that is what padding is for on the ENCRYPTION side (right?). So that when you then receive the padded message, you can tell the algorithm what padding mode was used to make it correct length, so then it can seperate the padding from the message. But in all of those cases the recieved message during decryption is of correct length.
So the question is - what are we doing wrong? is there some way to decrypt with ciphertexts that are shorter than the key? Or are they messing up somewhere by producing ciphers that are too short?
Thanks for any help.
In AES-ECB, the only valid ciphertext shorter than 16-byte is empty. That 16-byte limit is the block (not key) size of AES, which happens to match the key size for AES-128.
Therefore, the question's
An example message we receive is: d3 73 4c
does not show an ECB encrypted message (since a comment tells that's from a JSON, that can't be bytes that happen to show as hex). And that's way too short to be a FRMPayload (per this comment) for a Join-Accept, since the spec says of the later:
1625 The message is either 16 or 32 bytes long.
Could it be that whatever that JSON message contains is not a full FRMPayload, but a fragment of a packet, encoded as hexadecimal pair with space separator? As long as it is not figured out how to build a FRMPayload, there's not point in deciphering it.
Update: If that mystery message is always 3 bytes, and if it is always the same for a given key (or available a single time per key), then per Maarten Bodewes's comment it might be a Key Check Value. The KCV is often the first 3 bytes of the encryption of the all-zero value with the key per the raw block cipher (equivalently: per ECB). Herbert Hanewinkel's javascript AES can work fully offline (which is necessary to not expose the key), and be used to manually validate an hypothesis. It tells that for the 16-byte key given in the question, a KCV would be cd15e1 (or c076fc per the variant in the next section).
Also: it is used CreateDecryptor to craft a gizmo in charge of the ECB decryption. That's likely incorrect in the context of decryption of a LoraWan payload, which requires ECB encryption for decryption of some fields:
1626 Note: AES decrypt operation in ECB mode is used to encrypt the join-accept message so that the end-device can use an AES encrypt operation to decrypt the message. This way an end device only has to implement AES encrypt but not AES decrypt.
In the context of decryption of a LoraWan packets, you want to communicate with the AES engine using byte arrays, not strings. Strings have an encoding, when LoraWan ciphertext and corresponding plaintext does not. Others seems to have managed to coerce the nice .NET do-it-all crypto API to get a low-level job done.
In the HextoString code, I vaguely get that the intention and perhaps outcome is that hex becomes the originally hex input as a byte array (fully rid of hexadecimal and other encoding sin; in which case the variable hex should be renamed to something on the tune of pure_bytes). But then I'm at loss about System.Text.Encoding.ASCII.GetString(hex). I'd be surprised if it just created a byte string from a byte array, or turned the key back to hexadecimal for later feeding to HexStringToByteArray in Aes128Decrypt. Plus this makes me fear that any byte in [0x80..0xFF] might turn to 0x3F, which is not nice for key, ciphertext, and corresponding LoraWan payload. These have no character encoding when de-hexified.
My conclusion is that if HexStringToByteArray does what its name suggests, and given the current interface of Aes128Decrypt, HextoString should simply remove whitespace (or is unneeded if HexStringToByteArray removes whitespace on the fly). But my recommendation is to change the interface to use byte arrays, not strings (see previous section).
As an aside: creating an ICryptoTransform object from its key is supposed to be performed once for multiple uses of the object.
SAP servers are capable of encrypting and hashing data. But there doesn't appear to be a suitable API to call. SAP Note 1456433 talks about the class CL_SEC_SXML_XENCRYPTION. The signature of basic encryption is clearly geared towards SSF and unsuitable to basic private key encryption/decryption. I don't want/need envelopes and user certificates. Just private keys.
I found an AES library on GitHub AES library in ABAP and tweaked that to suit us. But it is very slow. I would like to use the encryption libraries SAP has. Clearly, the libraries are there but find a suitably exposed API seems the issue.
Does anybody know how to use basic encryption in SAP?
In SAP ABAP stack, using ABAP.
Eg (a call to use AES-CBC 128, with PKCS7 padding
where only a private key and data to encrypt is required. As example:
public static method encrypt_xstring
importing i_key type xstring
i_data type xstring
i_initialization_vector type xstring optional
i_padding_standard type char10 optional
i_encryption_mode type char10 optional
exporting e_data type xstring
Use case is encrypting data on clients with a private key and sending the data to SAP system. The source supports private keys and libraries like AES-CBC.
And we have encrypted data interchange working.
Next step is to use a supported and faster library.
EDIT: In case anyone needs to encryption / decryption properly in abap
And is looking at the answer. Use class CL_SEC_SXML_WRITER.
CL_SEC_SXML_WRITER was exactly what i was looking for
BUT SAP didnt expose it properly. It is only useful for encryption no decryption.
When interacting with external libraries. Where PKCS7 padding is used and SALTs
or Initialization vectors are required.
SAP offer an ENCRYPT_IV but no Decrypt_IV. Why ????
So you cant use the tool and remain compliant. :(
It is not considered safe to use AES-CBC without IV.
Why would SAP do that ?
ENCRYPT_IV instead of ENCRYPT but no DECRYPT_IV
The offer an Add Padding but no remove padding. OK roll your own padding removal, no big deal. Its like the must be another library for the other direction.
So i can use the tool to encrypt but not decrypt.
My main problem was decrypting quickly strings sent from a mobile device.
So still need to use the old ABAP code for that :(
I have similar requirements and I found the cl_sec_sxml_writer class. Please have a look at the following example. Note that the writer requires XSTRING parameters which is why I'm using conversion classes.
REPORT zged_aes.
DATA lv_message_string TYPE string.
" create message
DATA(lr_conv_sec) = cl_abap_conv_out_ce=>create( ).
lr_conv_sec->write( data = 'This is my secret' ).
" create key
DATA(lr_conv_key) = cl_abap_conv_out_ce=>create( ).
lr_conv_key->write( data = 'MySymmetricKey' ).
" encrypt using AES256
cl_sec_sxml_writer=>encrypt(
EXPORTING
plaintext = lr_conv_sec->get_buffer( )
key = lr_conv_key->get_buffer( )
algorithm = cl_sec_sxml_writer=>co_aes256_algorithm_pem
IMPORTING
ciphertext = DATA(lv_message) ).
" decrypt message
cl_sec_sxml_writer=>decrypt(
EXPORTING
ciphertext = lv_message
key = lr_conv_key->get_buffer( )
algorithm = cl_sec_sxml_writer=>co_aes256_algorithm_pem
IMPORTING
plaintext = DATA(lv_message_decrypted) ).
" convert xstring to string for output
cl_abap_conv_in_ce=>create( input = lv_message_decrypted)->read( IMPORTING data = lv_message_string ).
" output secret message
WRITE lv_message_string.
I tested it on a NetWeaver 7.50 SP 6 system.
I got the ENCRYPT_IV method to work alongside method DECRYPT of class CL_SEC_SXML_WRITER.
The caveat here is that I didn't generate the Symmetric Key and IV by making use of Class cl_abap_conv_out_ce.
I already had my keys and IV from a Java implementation test.
The only thing I needed was to create the Key and IV as an XSTRING and initializing them with the Hex format of my Java implementation (they were in Byte format.
Because of this, I first converted them to Hex and passed those values to the ABAP Xstring types).
I need a piece of code that defines functions which can encrypt and decrypt a piece of string. What I basically want is that the string should not be visible to third-party users, so that when the string originates in one file, it is converted to, say, an integer value using the encrypt function and then it is passed as parameter to another file. There the decrpyt function then decrypts it back and uses the string to perform actions on it.
Any suggestions or already available codes will be just fine!
Please help me out. Thanks!
Install tcllib. There are several standard encryption algorithms implemented in tcllib.
The following encryption algorithms are available:
blowfish: http://tcllib.sourceforge.net/doc/blowfish.html
aes: http://tcllib.sourceforge.net/doc/aes.html
des (including triple des): http://tcllib.sourceforge.net/doc/des.html
rc4: http://tcllib.sourceforge.net/doc/rc4.html
The des package in Tcllib should do what you want. It's pretty easy to use:
package require des
set key "12345678"; # Must be 8 bytes long
set msg "abcde"
##### ENCRYPTION
set encryptedMsg [DES::des -dir encrypt -key $key $msg]
# $encryptedMsg is a bunch of bytes; you'll want to send this around...
##### DECRYPTION
set decryptedMsg [DES::des -dir decrypt -key $key $encryptedMsg]
puts "I got '$decryptedMsg'"
Note that DES will pad the message out to a multiple of 8 bytes long.
Please visit the TCL/TK homepage e.g
here:http://wiki.tcl.tk/900
That's just one way of doing it. There will be much more, I'm sure.
I run a rather large site where my members add thousands of images every day. Obviously there is a lot of duplication and i was just wondering if during an upload of an image i can somehow generate a signature or a hash of an image so i can store it. And every time someone uploads the picture i would simply run a check if this signature already exists and fire an error stating that this image already exists. Not sure if this kind of technology already exists for asp.net but i am aware of tineye.com which sort of does it already.
If you think you can help i would appreciate your input.
Kris
A keyword that might be of interest is perceptual hashing.
You use any derived HashAlgorithm to generate a hash from the byte array of the file. Usually MD5 is used, but you could subsitute this for any of those provided in the System.Security.Cryptography namespace. This works for any binary, not just images.
Lots of sites provide MD5 hashes when you download files to verify if you've downloaded the file properly. For instance, an ISO CD/DVD image may be missing bytes when you've received the whole thing. Once you've downloaded the file, you generate the hash for it and make sure it's the same as the site says it should be. If all compares, you've got an exact copy.
I would probably use something similar to this:
public static class Helpers
{
//If you're running .NET 2.0 or lower, remove the 'this' keyword from the
//method signature as 2.0 doesn't support extension methods.
static string GetHashString(this byte[] bytes, HashAlgorithm cryptoProvider)
{
byte[] hash = cryptoProvider.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
Requires:
using System.Security.Cryptography;
Call using:
byte[] bytes = File.ReadAllBytes("FilePath");
string filehash = bytes.GetHashString(new MD5CryptoServiceProvider());
or if you're running in .NET 2.0 or lower:
string filehash = Helpers.GetHashString(File.ReadAllBytes("FilePath"), new MD5CryptoServiceProvider());
If you were to decide to go with a different hashing method instead of MD5 for the miniscule probability of collisions:
string filehash = bytes.GetHashString(new SHA1CryptoServiceProvider());
This way your has method isn't crypto provider specific and if you were to decide you wanted to change which crypto provider you're using, you just inject a different one into the cryptoProvider parameter.
You can use any of the other hashing classes just by changing the service provider you pass in:
string md5Hash = bytes.GetHashString(new MD5CryptoServiceProvider());
string sha1Hash = bytes.GetHashString(new SHA1CryptoServiceProvider());
string sha256Hash = bytes.GetHashString(new SHA256CryptoServiceProvider());
string sha384Hash = bytes.GetHashString(new SHA384CryptoServiceProvider());
string sha512Hash = bytes.GetHashString(new SHA512CryptoServiceProvider());
Typically you'd just use MD5 or similar to create a hash. This isn't guaranteed to be unique though, so I'd recommend you use the hash as a starting point. Identify if the image matches any known hashes you stored, then individually load the ones that it does match and do a full byte comparison on the potential collisions to be sure.
Another, simpler technique though is to simply pick a smallish number of bits and read first part of the image... store that number of starting bits as if they were a hash. This still gives you a small number of potential collisions that you'd need to check, but has much less overhead.
Look in the System.Security.Cryptography namespace. You have your choice of several hashing algorithms/implementations. Here's an example using md5, but since you have a lot of these you might want something bigger like SHA1:
public byte[] HashImage(Stream imageData)
{
return new MD5CryptoServiceProvider().ComputeHash(imageData);
}
I don't know if it already exists or not, but I can't think of a reason you can't do this yourself. Something similar to this will get you a hash of the file.
var fileStream = Request.Files[0].InputStream;//the uploaded file
var hasher = System.Security.Cryptography.HMACMD5();
var theHash = hasher.ComputeHash(fileStream);
System.Security.Cryptography