I'm creating a low level HTTP server and I want to validate that my responses are correct.
Some of them are fine, but others fail. I have saved some responses as bytes and viewing them in HxD, and they look correct, but they obviously aren't in some way.
Here's the start of one HTTP response formatted as hex:
48 54 54 50 2F 31 2E 31 20 32 30 30 20 4F 4B 0D 0A 43 4F 4E 54 45 4E
54 2D 54 59 50 45 3A 20 69 6D 61 67 65 2F 70 6E 67 0D 0A 4C 41 53 54
2D 4D 4F 44 49 46 49 45 44 3A 20 53 75 6E 2C 20 30 31 20 4D 61 79 20
32 30 32 32 20 32 32 3A 32 34 3A 32 34 20 47 4D 54 0D 0A 41 43 43 45
50 54 2D 52 41 4E 47 45 53 3A 20 62 79 74 65 73 0D 0A 45 54 41 47 3A
20 22 37 35 37 35 35 33 36 61 61 35 64 64 38 31 3A 30 22 0D 0A 53 45
52 56 45 52 3A 20 4D 69 63 72 6F 73 6F 66 74 2D 49 49 53 2F 31 30 2E
30 0D 0A 58 2D 50 4F 57 45 52 45 44 2D 42 59 3A 20 41 53 50 2E 4E 45
54 0D 0A 44 41 54 45 3A 20 57 65 64 2C 20 30 34 20 4D 61 79 20 32 30
32 32 20 30 39 3A 30 31 3A 34 38 20 47 4D 54 0D 0A 43 4F 4E 54 45 4E
54 2D 4C 45 4E 47 54 48 3A 20 39 38 37 35 37 0D 0A 43 6F 6E 6E 65 63
74 69 6F 6E 3A 20 63 6C 6F 73 65 0D 0A 0D 0A 0D 0A 89 50 4E 47
How can I validate that the response I'm creating is valid according to RFC 2616?
I'm working in .NET but I don't mind if it's an online tool or a different platform.
Edit:
I've solved my issue with this particular example (3 crlfs instead of 2 between head and body) but I'd still like to know how I can validate these requests automatically.
I ended up achieving this with this nuget package and the following code:
if (System.Diagnostics.Debugger.IsAttached)
{
using (var handler = new HttpParserDelegate())
using (var parser = new HttpCombinedParser(handler))
{
parser.Execute(arr);
}
if (!handler.HttpRequestResponse.IsEndOfMessage)
{
System.Diagnostics.Debugger.Break();
throw new InvalidOperationException($"An HTTP response was serialized but it is not a valid response");
}
}
I have this string:
x <- "W3siY29kZSI6IkIxMTgyIiwiZGVzY3JpcHRpb24iOiJUaXJlIHByZXNzdXJlIG1vbml0b3IgbW9kdWxlIiwiZmF1bHRJbmZvcm1hdGlvbnMiOlt7ImRlc2NyaXB0aW9uIjoiLS0tIn0seyJkZXNjcmlwdGlvbiI6IlRSVUUiLCJkZXNjcmlwdGlvbl9lbmciOiJUUlVFIiwiZGVzY3JpcHRpb25fZnJlIjoiVFJVRSIsImRlc2NyaXB0aW9uX2dlciI6IlRSVUUiLCJkZXNjcmlwdGlvbl9zcGEiOiJUUlVFIiwiZGVzY3JpcHRpb25faXRhIjoiVFJVRSIsImRlc2NyaXB0aW9uX3R1ciI6IlRSVUUifV0sImZyZWV6ZUZyYW1lcyI6W10sImVuaGFuY2VkRHRjSW5mb3MiOltdLCJzdGF0dXMiOiJBQ1RJVkUiLCJzeXN0ZW0iOiJNVUxUSUZVTkNUSU9OIiwic3lzdGVtTWV0YSI6eyJzeXN0ZW1OYW1lIjpudWxsLCJzdWJTeXN0ZW1OYW1lIjpudWxsfSwiZGVzY3JpcHRpb25fZW5nIjoiVGlyZSBwcmVzc3VyZSBtb25pdG9yIG1vZHVsZSIsImRlc2NyaXB0aW9uX2ZyZSI6Ik1vZHVsZSBkZSBzdXJ2ZWlsbGFuY2UgZGUgbGEgcHJlc3Npb24gZGVzIHBuZXVzIiwiZGVzY3JpcHRpb25fZ2VyIjoiUmVpZmVuZHJ1Y2vDvGJlcndhY2h1bmciLCJkZXNjcmlwdGlvbl9zcGEiOiJNw7NkdWxvIGRlIGNvbnRyb2wgZGUgbGEgcHJlc2nDs24gZGUgbG9zIG5ldW3DoXRpY29zIiwiZGVzY3JpcHRpb25faXRhIjoiTW9kdWxvIG1vbml0b3IgZGkgcHJlc3Npb25lIHBuZXVtYXRpY28iLCJkZXNjcmlwdGlvbl90dXIiOiJMYXN0aWsgYmFzxLFuY8SxIGl6bGVtZSBtb2TDvGzDvCJ9LHsiY29kZSI6IkIxMjREIiwiZGVzY3JpcHRpb24iOiJTZW5zb3IsIHR5cmUgcHJlc3N1cmUiLCJmYXVsdEluZm9ybWF0aW9ucyI6W3siZGVzY3JpcHRpb24iOiItLS0ifSx7ImRlc2NyaXB0aW9uIjoiVFJVRSIsImRlc2NyaXB0aW9uX2VuZyI6IlRSVUUiLCJkZXNjcmlwdGlvbl9mcmUiOiJUUlVFIiwiZGVzY3JpcHRpb25fZ2VyIjoiVFJVRSIsImRlc2NyaXB0aW9uX3NwYSI6IlRSVUUiLCJkZXNjcmlwdGlvbl9pdGEiOiJUUlVFIiwiZGVzY3JpcHRpb25fdHVyIjoiVFJVRSJ9XSwiZnJlZXplRnJhbWVzIjpbXSwiZW5oYW5jZWREdGNJbmZvcyI6W10sInN0YXR1cyI6IkFDVElWRSIsInN5c3RlbSI6Ik1VTFRJRlVOQ1RJT04iLCJzeXN0ZW1NZXRhIjp7InN5c3RlbU5hbWUiOm51bGwsInN1YlN5c3RlbU5hbWUiOm51bGx9LCJkZXNjcmlwdGlvbl9lbmciOiJTZW5zb3IsIHR5cmUgcHJlc3N1cmUiLCJkZXNjcmlwdGlvbl9mcmUiOiJDYXB0ZXVyLCBwcmVzc2lvbiBkZXMgcG5ldXMiLCJkZXNjcmlwdGlvbl9nZXIiOiJTZW5zb3IgUmVpZmVuZHJ1Y2siLCJkZXNjcmlwdGlvbl9zcGEiOiJTZW5zb3IgZGUgcHJlc2nDs24gZGVsIG5ldW3DoXRpY28iLCJkZXNjcmlwdGlvbl9pdGEiOiJTZW5zb3JlLCBwcmVzc2lvbmUgcG5ldW1hdGljaSIsImRlc2NyaXB0aW9uX3R1ciI6IlNlbnPDtnIsIGxhc3RpayBoYXZhIGJhc8SxbmPEsSJ9XQ=="
I know it has been encoded using base64. Doing a quick search on internet, I found that in R there are some packages to encode/decode base64.
Package jsonlite with the function base64dec()
Package base64decode with the function base64decode().
In both cases, when running...
base64dec(x)
base64decode(x)
I get this result, which is a binary output:
[1] 5b 7b 22 63 6f 64 65 22 3a 22 42 31 31 38 32 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 22 3a 22 54 69 72 65 20 70 72 65 73 73 75 72 65 20 6d 6f 6e 69 74 6f 72
[54] 20 6d 6f 64 75 6c 65 22 2c 22 66 61 75 6c 74 49 6e 66 6f 72 6d 61 74 69 6f 6e 73 22 3a 5b 7b 22 64 65 73 63 72 69 70 74 69 6f 6e 22 3a 22 2d 2d 2d 22 7d 2c 7b
[107] 22 64 65 73 63 72 69 70 74 69 6f 6e 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 65 6e 67 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69
[160] 70 74 69 6f 6e 5f 66 72 65 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 67 65 72 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69
[213] 6f 6e 5f 73 70 61 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 69 74 61 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f
[266] 74 75 72 22 3a 22 54 52 55 45 22 7d 5d 2c 22 66 72 65 65 7a 65 46 72 61 6d 65 73 22 3a 5b 5d 2c 22 65 6e 68 61 6e 63 65 64 44 74 63 49 6e 66 6f 73 22 3a 5b 5d
[319] 2c 22 73 74 61 74 75 73 22 3a 22 41 43 54 49 56 45 22 2c 22 73 79 73 74 65 6d 22 3a 22 4d 55 4c 54 49 46 55 4e 43 54 49 4f 4e 22 2c 22 73 79 73 74 65 6d 4d 65
[372] 74 61 22 3a 7b 22 73 79 73 74 65 6d 4e 61 6d 65 22 3a 6e 75 6c 6c 2c 22 73 75 62 53 79 73 74 65 6d 4e 61 6d 65 22 3a 6e 75 6c 6c 7d 2c 22 64 65 73 63 72 69 70
[425] 74 69 6f 6e 5f 65 6e 67 22 3a 22 54 69 72 65 20 70 72 65 73 73 75 72 65 20 6d 6f 6e 69 74 6f 72 20 6d 6f 64 75 6c 65 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e
[478] 5f 66 72 65 22 3a 22 4d 6f 64 75 6c 65 20 64 65 20 73 75 72 76 65 69 6c 6c 61 6e 63 65 20 64 65 20 6c 61 20 70 72 65 73 73 69 6f 6e 20 64 65 73 20 70 6e 65 75
[531] 73 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 67 65 72 22 3a 22 52 65 69 66 65 6e 64 72 75 63 6b c3 bc 62 65 72 77 61 63 68 75 6e 67 22 2c 22 64 65 73 63 72
[584] 69 70 74 69 6f 6e 5f 73 70 61 22 3a 22 4d c3 b3 64 75 6c 6f 20 64 65 20 63 6f 6e 74 72 6f 6c 20 64 65 20 6c 61 20 70 72 65 73 69 c3 b3 6e 20 64 65 20 6c 6f 73
[637] 20 6e 65 75 6d c3 a1 74 69 63 6f 73 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 69 74 61 22 3a 22 4d 6f 64 75 6c 6f 20 6d 6f 6e 69 74 6f 72 20 64 69 20 70 72
[690] 65 73 73 69 6f 6e 65 20 70 6e 65 75 6d 61 74 69 63 6f 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 74 75 72 22 3a 22 4c 61 73 74 69 6b 20 62 61 73 c4 b1 6e 63
[743] c4 b1 20 69 7a 6c 65 6d 65 20 6d 6f 64 c3 bc 6c c3 bc 22 7d 2c 7b 22 63 6f 64 65 22 3a 22 42 31 32 34 44 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 22 3a 22 53
[796] 65 6e 73 6f 72 2c 20 74 79 72 65 20 70 72 65 73 73 75 72 65 22 2c 22 66 61 75 6c 74 49 6e 66 6f 72 6d 61 74 69 6f 6e 73 22 3a 5b 7b 22 64 65 73 63 72 69 70 74
[849] 69 6f 6e 22 3a 22 2d 2d 2d 22 7d 2c 7b 22 64 65 73 63 72 69 70 74 69 6f 6e 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 65 6e 67 22 3a 22
[902] 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 66 72 65 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 67 65 72 22 3a 22 54 52 55
[955] 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 73 70 61 22 3a 22 54 52 55 45 22 2c 22 64 65 73 63 72 69 70 74 69 6f 6e 5f 69 74 61 22 3a
However, when I copy that string into this web, I get this result:
[{"code":"B1182","description":"Tire pressure monitor module","faultInformations":[{"description":"---"},{"description":"TRUE","description_eng":"TRUE","description_fre":"TRUE","description_ger":"TRUE","description_spa":"TRUE","description_ita":"TRUE","description_tur":"TRUE"}],"freezeFrames":[],"enhancedDtcInfos":[],"status":"ACTIVE","system":"MULTIFUNCTION","systemMeta":{"systemName":null,"subSystemName":null},"description_eng":"Tire pressure monitor module","description_fre":"Module de surveillance de la pression des pneus","description_ger":"Reifendrucküberwachung","description_spa":"Módulo de control de la presión de los neumáticos","description_ita":"Modulo monitor di pressione pneumatico","description_tur":"Lastik basıncı izleme modülü"},{"code":"B124D","description":"Sensor, tyre pressure","faultInformations":[{"description":"---"},{"description":"TRUE","description_eng":"TRUE","description_fre":"TRUE","description_ger":"TRUE","description_spa":"TRUE","description_ita":"TRUE","description_tur":"TRUE"}],"freezeFrames":[],"enhancedDtcInfos":[],"status":"ACTIVE","system":"MULTIFUNCTION","systemMeta":{"systemName":null,"subSystemName":null},"description_eng":"Sensor, tyre pressure","description_fre":"Capteur, pression des pneus","description_ger":"Sensor Reifendruck","description_spa":"Sensor de presión del neumático","description_ita":"Sensore, pressione pneumatici","description_tur":"Sensör, lastik hava basıncı"}]
This is actually the expected output. What's wrong with the code?
Looks like the output is raw encoded. So you'll need to use rawToChar.
library(base64)
rawToChar(base64_decode(x))
[1] "[{\"code\":\"B1182\",\"description\":\"Tire pressure monitor module\",\"faultInformations\":[{\"description\":\"---\"},{\"description\":\"TRUE\",\"description_eng\":\"TRUE\",\"description_fre\":\"TRUE\",\"description_ger\":\"TRUE\",\"description_spa\":\"TRUE\",\"description_ita\":\"TRUE\",\"description_tur\":\"TRUE\"}],\"freezeFrames\":[],\"enhancedDtcInfos\":[],\"status\":\"ACTIVE\",\"system\":\"MULTIFUNCTION\",\"systemMeta\":{\"systemName\":null,\"subSystemName\":null},\"description_eng\":\"Tire pressure monitor module\",\"description_fre\":\"Module de surveillance de la pression des pneus\",\"description_ger\":\"Reifendrucküberwachung\",\"description_spa\":\"Módulo de control de la presión de los neumáticos\",\"description_ita\":\"Modulo monitor di pressione pneumatico\",\"description_tur\":\"Lastik basıncı izleme modülü\"},{\"code\":\"B124D\",\"description\":\"Sensor, tyre pressure\",\"faultInformations\":[{\"description\":\"---\"},{\"description\":\"TRUE\",\"description_eng\":\"TRUE\",\"description_fre\":\"TRUE\",\"description_ger\":\"TRUE\",\"description_spa\":\"TRUE\",\"description_ita\":\"TRUE\",\"description_tur\":\"TRUE\"}],\"freezeFrames\":[],\"enhancedDtcInfos\":[],\"status\":\"ACTIVE\",\"system\":\"MULTIFUNCTION\",\"systemMeta\":{\"systemName\":null,\"subSystemName\":null},\"description_eng\":\"Sensor, tyre pressure\",\"description_fre\":\"Capteur, pression des pneus\",\"description_ger\":\"Sensor Reifendruck\",\"description_spa\":\"Sensor de presión del neumático\",\"description_ita\":\"Sensore, pressione pneumatici\",\"description_tur\":\"Sensör, lastik hava basıncı\"}]"
Edit
To better understand why this is necessary we can check the class of the output of base64_decode().
> class(base64_decode(x))
#[1] "raw"
The ?raw helpfully tells us:
The raw type is intended to hold raw bytes. It is possible to extract subsequences of bytes, and to replace elements (but only by elements of a raw vector). ... A raw vector is printed with each byte separately represented as a pair of hex digits. If you want to see a character representation (with escape sequences for non-printing characters) use rawToChar.
So we just need to convert the raw bytes to character.
Data
x <- "W3siY29kZSI6IkIxMTgyIiwiZGVzY3JpcHRpb24iOiJUaXJlIHByZXNzdXJlIG1vbml0b3IgbW9kdWxlIiwiZmF1bHRJbmZvcm1hdGlvbnMiOlt7ImRlc2NyaXB0aW9uIjoiLS0tIn0seyJkZXNjcmlwdGlvbiI6IlRSVUUiLCJkZXNjcmlwdGlvbl9lbmciOiJUUlVFIiwiZGVzY3JpcHRpb25fZnJlIjoiVFJVRSIsImRlc2NyaXB0aW9uX2dlciI6IlRSVUUiLCJkZXNjcmlwdGlvbl9zcGEiOiJUUlVFIiwiZGVzY3JpcHRpb25faXRhIjoiVFJVRSIsImRlc2NyaXB0aW9uX3R1ciI6IlRSVUUifV0sImZyZWV6ZUZyYW1lcyI6W10sImVuaGFuY2VkRHRjSW5mb3MiOltdLCJzdGF0dXMiOiJBQ1RJVkUiLCJzeXN0ZW0iOiJNVUxUSUZVTkNUSU9OIiwic3lzdGVtTWV0YSI6eyJzeXN0ZW1OYW1lIjpudWxsLCJzdWJTeXN0ZW1OYW1lIjpudWxsfSwiZGVzY3JpcHRpb25fZW5nIjoiVGlyZSBwcmVzc3VyZSBtb25pdG9yIG1vZHVsZSIsImRlc2NyaXB0aW9uX2ZyZSI6Ik1vZHVsZSBkZSBzdXJ2ZWlsbGFuY2UgZGUgbGEgcHJlc3Npb24gZGVzIHBuZXVzIiwiZGVzY3JpcHRpb25fZ2VyIjoiUmVpZmVuZHJ1Y2vDvGJlcndhY2h1bmciLCJkZXNjcmlwdGlvbl9zcGEiOiJNw7NkdWxvIGRlIGNvbnRyb2wgZGUgbGEgcHJlc2nDs24gZGUgbG9zIG5ldW3DoXRpY29zIiwiZGVzY3JpcHRpb25faXRhIjoiTW9kdWxvIG1vbml0b3IgZGkgcHJlc3Npb25lIHBuZXVtYXRpY28iLCJkZXNjcmlwdGlvbl90dXIiOiJMYXN0aWsgYmFzxLFuY8SxIGl6bGVtZSBtb2TDvGzDvCJ9LHsiY29kZSI6IkIxMjREIiwiZGVzY3JpcHRpb24iOiJTZW5zb3IsIHR5cmUgcHJlc3N1cmUiLCJmYXVsdEluZm9ybWF0aW9ucyI6W3siZGVzY3JpcHRpb24iOiItLS0ifSx7ImRlc2NyaXB0aW9uIjoiVFJVRSIsImRlc2NyaXB0aW9uX2VuZyI6IlRSVUUiLCJkZXNjcmlwdGlvbl9mcmUiOiJUUlVFIiwiZGVzY3JpcHRpb25fZ2VyIjoiVFJVRSIsImRlc2NyaXB0aW9uX3NwYSI6IlRSVUUiLCJkZXNjcmlwdGlvbl9pdGEiOiJUUlVFIiwiZGVzY3JpcHRpb25fdHVyIjoiVFJVRSJ9XSwiZnJlZXplRnJhbWVzIjpbXSwiZW5oYW5jZWREdGNJbmZvcyI6W10sInN0YXR1cyI6IkFDVElWRSIsInN5c3RlbSI6Ik1VTFRJRlVOQ1RJT04iLCJzeXN0ZW1NZXRhIjp7InN5c3RlbU5hbWUiOm51bGwsInN1YlN5c3RlbU5hbWUiOm51bGx9LCJkZXNjcmlwdGlvbl9lbmciOiJTZW5zb3IsIHR5cmUgcHJlc3N1cmUiLCJkZXNjcmlwdGlvbl9mcmUiOiJDYXB0ZXVyLCBwcmVzc2lvbiBkZXMgcG5ldXMiLCJkZXNjcmlwdGlvbl9nZXIiOiJTZW5zb3IgUmVpZmVuZHJ1Y2siLCJkZXNjcmlwdGlvbl9zcGEiOiJTZW5zb3IgZGUgcHJlc2nDs24gZGVsIG5ldW3DoXRpY28iLCJkZXNjcmlwdGlvbl9pdGEiOiJTZW5zb3JlLCBwcmVzc2lvbmUgcG5ldW1hdGljaSIsImRlc2NyaXB0aW9uX3R1ciI6IlNlbnPDtnIsIGxhc3RpayBoYXZhIGJhc8SxbmPEsSJ9XQ=="
I'm hoping that someone can help me diagnose this odd behavior that I'm experiencing. I'm programming for the Arduino Due and using the Adafruit FONA modem. The code in question submits an array of bytes to be sent over the modem. sendData is taking an unsigned char[] which I cast to void*. data_len is the number of bytes written to the buffer rather than the size of the buffer itself.
The board I'm working with doesn't use printf/sprintf to print to stdout. Instead this is done using Serial.print and Serial.println. Serial here is an Arduino class, not the standard C++ class. sprintf_ByteArray I wrote to examine the contents of a byte buffer, with the results reflected as hex bytes.
As I mentioned my ultimate goal here is to write data to the modem. This is done in the fonaSerial->println and fonaSerial->write calls. The first issues a modem command with the size of the data to be written. The second writes the data.
But I'm experiencing something screwy. The result of printing data and String message is displayed below. Inexplicably to me, message only contains the last 119 bytes of data. At first, this didn't surprise me as data includes null bytes and I assumed that those were somehow to blame. But, message actually includes null bytes! And, I would have expected message to represent the first N bytes of data, up until the first null byte.
Lastly: I end by casting data to a uint8_t* and passing to fonaSerial->write. Initially I had been passing a String but after I had trouble casting I decided to try this alternative tack. I then send this data to a server I'm running. The server only gets a payload of 119 bytes too.
For the record, earlier I successfully sent a smaller payload. I have no idea what I'm doing now that is causing the problem.
bool sendData(void *data, size_t data_len) {
char tmp[1024] = {0};
Serial.print("data_len=");Serial.println(data_len);
sprintf_ByteArray(tmp, (unsigned char*) data, data_len);
Serial.print("Payload (void*)=");Serial.println(tmp);
String message = (const char*) data;
Serial.print("Payload (String)=");Serial.println(message);
fonaSerial->print(F("AT+CIPSEND="));
fonaSerial->println(data_len);
fona.readline(3000);
fonaSerial->write((uint8_t*) data, data_len);
fonaSerial->flush();
fona.readline(3000); // wait up to 3 seconds to send the data
Serial.print (F("\t<--- ")); Serial.println(fona.replybuffer);
return (strcmp(fona.replybuffer, "SEND OK") == 0);
}
Payload (void*)= 8a a5 62 69 64 00 69 63 6f 6e 64 69 74 69 6f 6e 01 67 62 61 74 74 65 72 79 18 1d 65 73 74 61 74 65 02 69 74 69 6d 65 73 74 61 6d 70 19 0f 95 a5 62 69 64 01 69 63 6f 6e 64 69 74 69 6f 6e 01 67 62 61 74 74 65 72 79 18 42 65 73 74 61 74 65 62 69 74 69 6d 65 73 74 61 6d 70 19 0f 97 a5 62 69 64 02 69 63 6f 6e 64 69 74 69 6f 6e 00 67 62 61 74 74 65 72 79 18 29 65 73 74 61 74 65 06 69 74 69 6d 65 73 74 61 6d 70 19 0f 98 a5 62 69 64 03 69 63 6f 6e 64 69 74 69 6f 6e 01 67 62 61 74 74 65 72 79 18 26 65 73 74 61 74 65 01 69 74 69 6d 65 73 74 61 6d 70 19 0f 99 a5 62 69 64 04 69 63 6f 6e 64 69 74 69 6f 6e 01 67 62 61 74 74 65 72 79 18 2f 65 73 74 61 74 65 62 69 74 69 6d 65 73 74 61 6d 70 19 0f 9a a5 62 69 64 05 69 63 6f 6e 64 69 74 69 6f 6e 01 67 62 61 74 74 65 72 79 18 1d 65 73 74 61 74 65 06 69 74 69 6d 65 73 74 61 6d 70 19 0f 9c a5 62 69 64 18 42 69 63 6f 6e 64 69 74 69 6f 6e 18 75 67 62 61 74 74 65 72 79 18 69 65 73 74 61 74 65 18 6c 69 74 69 6d 65 73 74 61 6d 70 1a 67 6e 69 64 a5 62 69 64 18 20 69 63 6f 6e 64 69 74 69 6f 6e 18 6c 67 62 61 74 74 65 [72 79 18 6f 65 73 74 61 74 65 18 74 69 74 69 6d 65 73 74 61 6d 70 1a 00 6e 39 20 a5 62 69 64 62 69 63 6f 6e 64 69 74 69 6f 6e 00 67 62 61 74 74 65 72 79 18 40 65 73 74 61 74 65 74 69 74 69 6d 65 73 74 61 6d 70 19 0f a0 a5 62 69 64 74 69 63 6f 6e 64 69 74 69 6f 6e 00 67 62 61 74 74 65 72 79 18 43 65 73 74 61 74 65 74 69 74 69 6d 65 73 74 61 6d 70 19 0f a1]
length 471
Payload (String)= 72 79 18 6f 65 73 74 61 74 65 18 74 69 74 69 6d 65 73 74 61 6d 70 1a 00 6e 39 20 a5 62 69 64 62 69 63 6f 6e 64 69 74 69 6f 6e 00 67 62 61 74 74 65 72 79 18 40 65 73 74 61 74 65 74 69 74 69 6d 65 73 74 61 6d 70 19 0f a0 a5 62 69 64 74 69 63 6f 6e 64 69 74 69 6f 6e 00 67 62 61 74 74 65 72 79 18 43 65 73 74 61 74 65 74 69 74 69 6d 65 73 74 61 6d 70 19 0f a1
length 119
try this 'to_string' converter:
String to_string(unsigned char *data,unsigned int len){
String memtmp="";
unsigned char tmpdata;
for(unsigned int i=0;i<len;i++){
tmpdata=*(data+i);
memtmp+=String(tmpdata,HEX);
memtmp+=" ";
}
return memtmp;
}
I hope this can help. Good luck!
I am trying to decrypt the PaymentToken returned by ApplePay with OpenSSL. The decryption works but I am seeing an extra 16 bytes appended to the plaintext as well as an error during EVP_DecryptFinal_ex. My first thought was I needed to turn off padding but that does not seem to be the issue. Any help is appreciated!
Apple's docs say:
Use the symmetric key to decrypt the value of the data key using AES–256 (id-aes256-GCM 2.16.840.1.101.3.4.1.46), with an initialization vector of sixteen null bytes and no associated authentication data.
The java sample provided by Apple uses BouncyCastle and initializes the Cipher with "AES/GCM/NoPadding"
My code looks like this:
static const unsigned char gcm_iv[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
/* Create the context */
EVP_CIPHER_CTX *decrypt_ctx;
decrypt_ctx = EVP_CIPHER_CTX_new();
/* Select cipher */
EVP_DecryptInit_ex(decrypt_ctx, EVP_aes_256_gcm(), NULL, NULL, NULL);
/* Set IV length */
EVP_CIPHER_CTX_ctrl(decrypt_ctx, EVP_CTRL_GCM_SET_IVLEN, sizeof(gcm_iv), NULL);
/* Specify key and IV */
EVP_DecryptInit_ex(decrypt_ctx, NULL, NULL, (const unsigned char *)gcm_key, gcm_iv);
/* Turn off padding */
EVP_CIPHER_CTX_set_padding(decrypt_ctx, 0);
unsigned char outbuf[4096];
int outlen = sizeof(outbuf);
int rv;
int plaintext_len;
/* Decrypt plaintext */
rv = EVP_DecryptUpdate(decrypt_ctx, outbuf, &outlen, (const unsigned char *)data, data_length);
if(!rv)
{
printf("\nOpenSSL Error after DecryptUpdate: %s\n", ERR_error_string(ERR_get_error(), NULL));
err = 1;
}
/* Output decrypted block */
printf("\nPlaintext after DecryptUpdate (%d bytes):\n\n", outlen);
/* Remember plaintext length */
plaintext_len = outlen;
BIO_dump_fp(stdout, (const char *)outbuf, plaintext_len);
rv = EVP_DecryptFinal_ex(decrypt_ctx, outbuf + outlen, &outlen);
if(!rv)
{
printf("\nOpenSSL Error after DecryptFinal_ex: %s\n", ERR_error_string(ERR_get_error(), NULL));
err = 1;
}
/* Output decrypted block */
printf("\nPlaintext after DecryptFinal(%d bytes):\n\n", outlen);
BIO_dump_fp(stdout, (const char *)outbuf, plaintext_len);
And the output looks like this (plaintext altered for security):
Plaintext after DecryptUpdate (319 bytes):
0000 - 7b 22 61 70 70 6c 69 63-61 74 69 6f 6e 50 72 69 {"applicationPri
0010 - 6d 61 72 79 41 63 63 6f-75 6e 74 4e 75 6d 62 65 maryAccountNumbe
0020 - 72 22 3a 22 34 30 38 38-xx xx xx xx xx xx xx xx r":"4088xxxxxxxx
0030 - 30 30 32 32 22 2c 22 61-70 70 6c 69 63 61 74 69 0022","applicati
0040 - 6f 6e 45 78 70 69 72 61-74 69 6f 6e 44 61 74 65 onExpirationDate
0050 - 22 3a 22 32 30 30 32 32-38 22 2c 22 63 75 72 72 ":"200228","curr
0060 - 65 6e 63 79 43 6f 64 65-22 3a 22 38 34 30 22 2c encyCode":"840",
0070 - 22 74 72 61 6e 73 61 63-74 69 6f 6e 41 6d 6f 75 "transactionAmou
0080 - 6e 74 22 3a 33 30 30 2c-22 64 65 76 69 63 65 4d nt":300,"deviceM
0090 - 61 6e 75 66 61 63 74 75-72 65 72 49 64 65 6e 74 anufacturerIdent
00a0 - 69 66 69 65 72 22 3a 22-xx xx xx xx xx xx xx xx ifier":"xxxxxxxx
00b0 - xx xx xx xx 22 2c 22 70-61 79 6d 65 6e 74 44 61 xxxx","paymentDa
00c0 - 74 61 54 79 70 65 22 3a-22 33 44 53 65 63 75 72 taType":"3DSecur
00d0 - 65 22 2c 22 70 61 79 6d-65 6e 74 44 61 74 61 22 e","paymentData"
00e0 - 3a 7b 22 6f 6e 6c 69 6e-65 50 61 79 6d 65 6e 74 :{"onlinePayment
00f0 - 43 72 79 70 74 6f 67 72-61 6d 22 3a 22 xx xx xx Cryptogram":"xxx
0100 - xx xx xx xx xx xx xx xx-xx xx xx xx xx xx xx xx xxxxxxxxxxxxxxxx
0110 - xx xx xx xx xx xx xx xx-xx 22 2c 22 65 63 69 49 xxxxxxxxx","eciI
0120 - 6e 64 69 63 61 74 6f 72-22 3a 22 35 22 7d 7d 0b ndicator":"5"}}.
0130 - ce 56 54 48 49 1c 73 1a-b3 a4 89 e8 b2 11 f7 .VTHI.s........
OpenSSL Error after DecryptFinal_ex: error:00000000:lib(0):func(0):reason(0)
Plaintext after DecryptFinal(0 bytes):
0000 - 7b 22 61 70 70 6c 69 63-61 74 69 6f 6e 50 72 69 {"applicationPri
0010 - 6d 61 72 79 41 63 63 6f-75 6e 74 4e 75 6d 62 65 maryAccountNumbe
0020 - 72 22 3a 22 34 30 38 38-xx xx xx xx xx xx xx xx r":"4088xxxxxxxx
0030 - 30 30 32 32 22 2c 22 61-70 70 6c 69 63 61 74 69 0022","applicati
0040 - 6f 6e 45 78 70 69 72 61-74 69 6f 6e 44 61 74 65 onExpirationDate
0050 - 22 3a 22 32 30 30 32 32-38 22 2c 22 63 75 72 72 ":"200228","curr
0060 - 65 6e 63 79 43 6f 64 65-22 3a 22 38 34 30 22 2c encyCode":"840",
0070 - 22 74 72 61 6e 73 61 63-74 69 6f 6e 41 6d 6f 75 "transactionAmou
0080 - 6e 74 22 3a 33 30 30 2c-22 64 65 76 69 63 65 4d nt":300,"deviceM
0090 - 61 6e 75 66 61 63 74 75-72 65 72 49 64 65 6e 74 anufacturerIdent
00a0 - 69 66 69 65 72 22 3a 22-xx xx xx xx xx xx xx xx ifier":"xxxxxxxx
00b0 - xx xx xx xx 22 2c 22 70-61 79 6d 65 6e 74 44 61 xxxx","paymentDa
00c0 - 74 61 54 79 70 65 22 3a-22 33 44 53 65 63 75 72 taType":"3DSecur
00d0 - 65 22 2c 22 70 61 79 6d-65 6e 74 44 61 74 61 22 e","paymentData"
00e0 - 3a 7b 22 6f 6e 6c 69 6e-65 50 61 79 6d 65 6e 74 :{"onlinePayment
00f0 - 43 72 79 70 74 6f 67 72-61 6d 22 3a 22 xx xx xx Cryptogram":"xxx
0100 - xx xx xx xx xx xx xx xx-xx xx xx xx xx xx xx xx xxxxxxxxxxxxxxxx
0110 - xx xx xx xx xx xx xx xx-xx 22 2c 22 65 63 69 49 xxxxxxxxx","eciI
0120 - 6e 64 69 63 61 74 6f 72-22 3a 22 35 22 7d 7d 0b ndicator":"5"}}.
0130 - ce 56 54 48 49 1c 73 1a-b3 a4 89 e8 b2 11 f7 .VTHI.s........
I'm trying to create HTTP request to XML service and I'm getting 400 errors from IIS server.
This request WORKS:
T 192.168.0.10:52584 -> 193.189.144.141:80 [AP]
50 4f 53 54 20 2f 73 63 72 69 70 74 73 2f 58 4d POST /scripts/XM
4c 5f 49 6e 74 65 72 66 61 63 65 2e 64 6c 6c 20 L_Interface.dll
48 54 54 50 2f 31 2e 30 0d 0a HTTP/1.0..
###
T 192.168.0.10:52584 -> 193.189.144.141:80 [AP]
48 6f 73 74 3a 20 77 77 77 31 2e 67 6e 74 2e 6c Host: www1.gnt.l
74 0d 0a 43 6f 6e 74 65 6e 74 2d 74 79 70 65 3a t..Content-type:
20 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 77 application/x-w
77 77 2d 66 6f 72 6d 2d 75 72 6c 65 6e 63 6f 64 ww-form-urlencod
65 64 0d 0a 43 6f 6e 74 65 6e 74 2d 6c 65 6e 67 ed..Content-leng
74 68 3a 20 37 33 0d 0a 43 6f 6e 6e 65 63 74 69 th: 73..Connecti
6f 6e 3a 20 63 6c 6f 73 65 0d 0a 0d 0a 4d 66 63 on: close....Mfc
49 53 41 50 49 43 6f 6d 6d 61 6e 64 3d 44 65 66 ISAPICommand=Def
61 75 6c 74 26 55 53 45 52 4e 41 4d 45 3d 73 65 ault&USERNAME=se
63 72 65 74 26 50 41 53 53 57 4f 52 44 3d 73 65 cret&PASSWORD=se
63 72 65 74 26 43 48 45 43 4b 3d 73 65 63 72 65 cret&CHECK=secre
74 26 58 4d 4c 3d 0d 0a 0d 0a t&XML=....
Although this one DOESN'T WORK:
T 192.168.0.10:52592 -> 193.189.144.141:80 [AP]
50 4f 53 54 20 2f 73 63 72 69 70 74 73 2f 58 4d POST /scripts/XM
4c 5f 49 6e 74 65 72 66 61 63 65 2e 64 6c 6c 20 L_Interface.dll
48 54 54 50 2f 31 2e 30 0d 0a 43 6f 6e 74 65 6e HTTP/1.0..Conten
74 2d 54 79 70 65 3a 20 61 70 70 6c 69 63 61 74 t-Type: applicat
69 6f 6e 2f 78 2d 77 77 77 2d 66 6f 72 6d 2d 75 ion/x-www-form-u
72 6c 65 6e 63 6f 64 65 64 0d 0a 43 6f 6e 74 65 rlencoded..Conte
6e 74 2d 4c 65 6e 67 74 68 3a 20 37 32 0d 0a 48 nt-Length: 72..H
6f 73 74 3a 20 77 77 77 31 2e 67 6e 74 2e 6c 74 ost: www1.gnt.lt
0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 50 79 ..User-Agent: Py
74 68 6f 6e 2d 75 72 6c 6c 69 62 2f 31 2e 31 37 thon-urllib/1.17
0d 0a 0d 0a 58 4d 4c 3d 26 55 53 45 52 4e 41 4d ....XML=&USERNAM
45 3d 73 65 63 72 65 74 26 50 41 53 53 57 4f 52 E=secret&PASSWOR
44 3d 73 65 63 72 65 74 26 4d 66 63 49 53 41 50 D=secret&MfcISAP
49 43 6f 6d 6d 61 6e 64 3d 44 65 66 61 75 6c 74 ICommand=Default
26 43 48 45 43 4b 3d 63 68 65 63 6b &CHECK=check
Exact error message:
HTTP/1.1 400 Bad Request.
Server: Microsoft-IIS/6.0.
X-Powered-By: ASP.NET.
Date: Wed, 13 Jul 2011 20:53:07 GMT.
Connection: close.
.
Any ideas where's the issue?
Resolved by changing order of arguments.
Made MfcISAP=Default the first argument