Generating an ONVIF authentication digest using Crypto++? - c++

The ONVIF authentication spec section 6.1.1.3 has what looks like a straight forward description of how to generate a digest. However, when using Crypto++ for Base64 and SHA1 operations, I cannot generate the same hash as the specification. I must be doing something wrong but cannot see what.
std::string nonce = "LKqI6G/AikKCQrN0zqZFlg==";
std::string dt = "2010-09-16T07:50:45Z";
std::string pwd = "userpassword";
{
// result should be tuOSpGlFlIXsozq4HFNeeGeFLEI=
// as per spec. This approach is also used here:
// https://github.com/agsh/onvif/blob/master/lib/cam.js
CryptoPP::Base64Decoder decoder;
decoder.Put((byte*)nonce.data(), nonce.size());
std::vector<uint8_t> bytes(decoder.MaxRetrievable(),0);
decoder.Get(&bytes[0],bytes.size());
//
CryptoPP::SHA1 hash;
byte digest[CryptoPP::SHA1::DIGESTSIZE];
hash.Update(bytes.data(), bytes.size());
hash.Update((const byte*)dt.c_str(), dt.size());
hash.Update((const byte*)pwd.c_str(), pwd.size());
hash.Final(digest);
CryptoPP::Base64Encoder encoder;
encoder.Put(digest, CryptoPP::SHA1::DIGESTSIZE);
std::string hash64(encoder.MaxRetrievable(), 0);
encoder.Get((byte*)hash64.data(), hash64.size());
// generates woEIuU+ryXxcwkTZ9ktbKGeQ
std::cout << hash64 << std::endl;
}
Any thoughts on this one much appreciated.
[edit: remove C# references]

CryptoPP::Base64Decoder decoder;
decoder.Put((byte*)nonce.data(), nonce.size());
std::vector<uint8_t> bytes(decoder.MaxRetrievable(),0);
decoder.Get(&bytes[0],bytes.size());
Call MessageEnd:
Base64Decoder decoder;
decoder.Put((byte*)nonce.data(), nonce.size());
decoder.MessageEnd();
vector<uint8_t> bytes(decoder.MaxRetrievable(),0);
decoder.Get(&bytes[0],bytes.size());
Ditto:
Base64Encoder encoder;
encoder.Put(digest, 20);
encoder.MessageEnd();
string hash64(encoder.MaxRetrievable(), 0);
encoder.Get((byte*)hash64.data(), hash64.size());
Also see Base64Encoder | Missing Data and Base64Decoder | Missing Data on the Crypto++ wiki.
Nor indeed can I duplicate the result using any other approaches, like a full C# test bed using all of the .NET crypto resources.
I don't know C# as well as Crypto++, so I can't help with a C# example that works as expected with ONVIF authentication.
Here's the result I get:
$ g++ test.cxx -I. ./libcryptopp.a -o test.exe
$ ./test.exe
tuOSpGlFlIXsozq4HFNeeGeFLEI=
And the cat test.cxx:
#include <iostream>
#include <string>
#include <vector>
#include "base64.h"
#include "sha.h"
std::string nonce = "LKqI6G/AikKCQrN0zqZFlg==";
std::string dt = "2010-09-16T07:50:45Z";
std::string pwd = "userpassword";
int main(int argc, char* argv[])
{
CryptoPP::Base64Decoder decoder;
decoder.Put((byte*)nonce.data(), nonce.size());
decoder.MessageEnd();
std::vector<uint8_t> bytes(decoder.MaxRetrievable(),0);
decoder.Get(&bytes[0],bytes.size());
CryptoPP::SHA1 hash;
byte digest[CryptoPP::SHA1::DIGESTSIZE];
hash.Update(bytes.data(), bytes.size());
hash.Update((const byte*)dt.c_str(), dt.size());
hash.Update((const byte*)pwd.c_str(), pwd.size());
hash.Final(digest);
CryptoPP::Base64Encoder encoder;
encoder.Put(digest, 20);
encoder.MessageEnd();
std::string hash64(encoder.MaxRetrievable(), 0);
encoder.Get((byte*)hash64.data(), hash64.size());
std::cout << hash64 << std::endl;
return 0;
}

Related

How to convert HEX to PEM openssl c++

I am trying to convert a hex string to pem format using openssl, I can do it using the openssl command but would like to do in c++ rather then rely on breaking out to the console
Example Hex String:
308204A33082038BA00302010202100AED96BA818D647A58A925ED9DB78884300D06092A864886F70D01010B05003081B9310B30090603550406130247423110300E06035504080C07456E676C616E643120301E060355040A0C1746616C636F6E20456C656374726963204C696D6974656431143012060355040B0C0B46616C636F6E436C6F7564312E302C06035504030C2546616C636F6E20456C65637472696320435020496E7465726D6564696174652043412076323130302E06092A864886F70D01090116216365727469666963617465734066616C636F6E656C6563747269632E636F2E756B301E170D3233303230373136323335325A170D3238303230363136323335325A3081A2310B300906035504060C024742310F300D06035504080C064C6F6E646F6E310F300D06035504070C064C6F6E646F6E31163014060355040A0C0D536978205761747473204C746431143012060355040B0C0B436861726765506F696E743115301306035504030C0C44384230335743525350514B312C302A06092A864886F70D010901161D63657274696669636174657340706C756773747265616D2E636F2E756B30820122300D06092A864886F70D01010105000382010F003082010A0282010100E2FBE3385753EB2A7EFD72AFE4B1344619F1FF5A31FEEE7118461E24795E540C498E384B81A2B56490CB1A203B12FCEC2BBFBC89C73801EA3CED5377A71E4E43B5B1AF2AD0AD6DCF126109B611A98379FE0E4CAA1DFE4230E44CF1100EFAC470FA4531D61B3F2CBC3AFA088E40F418B8CA918A44467E0F8C10D6C7BBD6B469B228CDE89E989AFA53E0D38CB86B23881C763744504D4BF1D0045847BCB24182B658319BC4B77C4C61D9025384BA3162C3C05A2F1EDF26B1807D7880A121729D5A2268A4264B3359789EE7E619CD33473CC0FB3693D76803529EA2BC383896A5D308797A375219FB10D935CD01F94C0CB4E37E72F86B9CE85005508CECDA801A7D0203010001A381BB3081B830090603551D1304023000301106096086480186F8420101040403020780303306096086480186F842010D042616244F70656E53534C2047656E65726174656420436C69656E74204365727469666963617465301D0603551D0E04160414D7A4917FCFA7906645D0ACBF316AB9677ECE958D301F0603551D23041830168014141E12E19554AF8E5D660604DDAC378781981197300E0603551D0F0101FF0404030205E030130603551D25040C300A06082B06010505070302300D06092A864886F70D01010B050003820101003927F4A0B5A5AED55FBA6243CCF9A7A644AFBE7A143375620A8672B943F85B8F603AF9FD59024FDA60C48E5C5A4BA778037D0A6956E9DB81EA1517FC9BADB86ECAF28B8B8B15B856BC5FFE648C61186CE79F8D9C68BEED02650CC1CAF550BF844A7CD38161D3518196BC5A28DC06B0DEDC3C9DDF61F20AF73EE0FC3ADC2AA537E36D46097B9B930A8C7C1366D3F6F4C42280D6AE32CD50F9E88850A110721CB1BB3FC3EACA2F5F8A5C079E2BDAC19A4CB796B722F5F88B36F7A6BAC3DE2AB33CDCFE3877BCC3B84935FEC029CA4D26B4583DB3AC7F4FD48D06F74308DA5A931C2FE165A8352FC6E89691761FC39242ED1D506C53FF9826FD28F3138E6DDAB9FA
I am writing the above to a file and then converting using xxd and openssl to pem formated cert
cat example.txt | xxd -r -p | openssl x509 -inform der -out example.cert which produces something like. Truncated to reduce size of the post.
-----BEGIN CERTIFICATE-----
MIIEozCCA4ugAwIBAgIQCu2WuoGNZHpYqSXtnbeIhDANBgkqhkiG9w0BAQsFADCB
...
B54r2sGaTLeWtyL1+Is296a6w94qszzc/jh3vMO4STX+wCnKTSa0WD2zrH9P1I0G
90MI2lqTHC/hZag1L8bolpF2H8OSQu0dUGxT/5gm/SjzE45t2rn6
-----END CERTIFICATE-----
How can I do this in c++ using openssl?
I have also tried from Convert a plain public key to PEM
#include <stdio.h>
#include <stdlib.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
#include <openssl/pem.h>
#ifdef _WIN32
#include <openssl/applink.c>
#endif
void err (const char *label){ // for test; improve for real code
fprintf (stderr, "Error in %s:\n", label);
ERR_print_errors_fp (stderr);
exit (1);
}
int main (void) //(int argc, char**argv)
{
ERR_load_crypto_strings(); /* or SSL_load_error_strings */
//OPENSSL_add_all_algorithms_noconf(); /* for PKCS#8 */
// test data -- replace for real use
char hex [] = "308204A33082038BA00302010202100AED96BA818D647A58A925ED9DB78884300D06092A864886F70D01010B05003081B9310B30090603550406130247423110300E06035504080C07456E676C616E643120301E060355040A0C1746616C636F6E20456C656374726963204C696D6974656431143012060355040B0C0B46616C636F6E436C6F7564312E302C06035504030C2546616C636F6E20456C65637472696320435020496E7465726D6564696174652043412076323130302E06092A864886F70D01090116216365727469666963617465734066616C636F6E656C6563747269632E636F2E756B301E170D3233303230373136323335325A170D3238303230363136323335325A3081A2310B300906035504060C024742310F300D06035504080C064C6F6E646F6E310F300D06035504070C064C6F6E646F6E31163014060355040A0C0D536978205761747473204C746431143012060355040B0C0B436861726765506F696E743115301306035504030C0C44384230335743525350514B312C302A06092A864886F70D010901161D63657274696669636174657340706C756773747265616D2E636F2E756B30820122300D06092A864886F70D01010105000382010F003082010A0282010100E2FBE3385753EB2A7EFD72AFE4B1344619F1FF5A31FEEE7118461E24795E540C498E384B81A2B56490CB1A203B12FCEC2BBFBC89C73801EA3CED5377A71E4E43B5B1AF2AD0AD6DCF126109B611A98379FE0E4CAA1DFE4230E44CF1100EFAC470FA4531D61B3F2CBC3AFA088E40F418B8CA918A44467E0F8C10D6C7BBD6B469B228CDE89E989AFA53E0D38CB86B23881C763744504D4BF1D0045847BCB24182B658319BC4B77C4C61D9025384BA3162C3C05A2F1EDF26B1807D7880A121729D5A2268A4264B3359789EE7E619CD33473CC0FB3693D76803529EA2BC383896A5D308797A375219FB10D935CD01F94C0CB4E37E72F86B9CE85005508CECDA801A7D0203010001A381BB3081B830090603551D1304023000301106096086480186F8420101040403020780303306096086480186F842010D042616244F70656E53534C2047656E65726174656420436C69656E74204365727469666963617465301D0603551D0E04160414D7A4917FCFA7906645D0ACBF316AB9677ECE958D301F0603551D23041830168014141E12E19554AF8E5D660604DDAC378781981197300E0603551D0F0101FF0404030205E030130603551D25040C300A06082B06010505070302300D06092A864886F70D01010B050003820101003927F4A0B5A5AED55FBA6243CCF9A7A644AFBE7A143375620A8672B943F85B8F603AF9FD59024FDA60C48E5C5A4BA778037D0A6956E9DB81EA1517FC9BADB86ECAF28B8B8B15B856BC5FFE648C61186CE79F8D9C68BEED02650CC1CAF550BF844A7CD38161D3518196BC5A28DC06B0DEDC3C9DDF61F20AF73EE0FC3ADC2AA537E36D46097B9B930A8C7C1366D3F6F4C42280D6AE32CD50F9E88850A110721CB1BB3FC3EACA2F5F8A5C079E2BDAC19A4CB796B722F5F88B36F7A6BAC3DE2AB33CDCFE3877BCC3B84935FEC029CA4D26B4583DB3AC7F4FD48D06F74308DA5A931C2FE165A8352FC6E89691761FC39242ED1D506C53FF9826FD28F3138E6DDAB9FA";
unsigned char raw [65]; for( int i = 0; i < 65; i++ ){ sscanf(hex+2*i, "%2hhx", raw+i); }
EC_KEY *eck = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); /* or OBJ_txt2nid("prime256v1") */
if( !eck ) err("ECCnewbyname");
EC_KEY_set_asn1_flag(eck, OPENSSL_EC_NAMED_CURVE); /* needed below 1.1.0 */
const unsigned char *ptr = raw;
if( !o2i_ECPublicKey (&eck, &ptr, sizeof(raw)) ) err("o2iECPublic=point");
EVP_PKEY * pkey = EVP_PKEY_new();
if( !EVP_PKEY_assign_EC_KEY(pkey, eck) ) err("PKEYassign");
BIO *bio = BIO_new(BIO_s_mem());
if( !PEM_write_bio_PUBKEY (bio, pkey) ) err("PEMwrite");
char *pem = NULL; long len = BIO_get_mem_data (bio, &pem);
fwrite (pem, 1, len, stdout); // for test; for real use as needed
return 0;
}
But I get
Error in o2iECPublic=point:
40B790FB647F0000:error:08000066:elliptic curve routines:ossl_ec_GFp_simple_oct2point:invalid encoding:../crypto/ec/ecp_oct.c:295:
40B790FB647F0000:error:08080010:elliptic curve routines:o2i_ECPublicKey:EC lib:../crypto/ec/ec_asn1.c:1142:
What am I doing wrong?

Web assembly: Using crypto++ library with emscripten

I'm planning on using the crypto++ library for a web application and wasm seemed perfect for it.
My cpp code:
#include <string>
using std::string;
#include "cryptopp/cryptlib.h"
#include "cryptopp/rsa.h"
#include "cryptopp/files.h"
#include "cryptopp/osrng.h"
using CryptoPP::RSA;
using CryptoPP::InvertibleRSAFunction;
using CryptoPP::RSAES_OAEP_SHA_Encryptor;
using CryptoPP::RSAES_OAEP_SHA_Decryptor;
using CryptoPP::StringSink;
using CryptoPP::StringSource;
using CryptoPP::PK_EncryptorFilter;
using CryptoPP::PK_DecryptorFilter;
using CryptoPP::FileSink;
using CryptoPP::FileSource;
using CryptoPP::AutoSeededRandomPool;
using CryptoPP::DecodingResult;
using CryptoPP::SHA1;
using namespace CryptoPP;
extern "C" {
char* generateKeys() {
AutoSeededRandomPool rng;
InvertibleRSAFunction parameters;
parameters.GenerateRandomWithKeySize(rng, 1024);
RSA::PrivateKey privateKey(parameters);
RSA::PublicKey publicKey(parameters);
string pubKey;
publicKey.Save(StringSink(pubKey).Ref());
privateKey.Save(FileSink("privkey.der", true).Ref());
int n = pubKey.length();
char* char_array = new char[n + 1];
strcpy(char_array, pubKey.c_str());
return char_array;
}
}
extern "C" {
char* encrypt(string pubKey, string plain) {
AutoSeededRandomPool rng;
string cipher;
RSA::PublicKey publicKey;
publicKey.Load(StringSource(pubKey, true, NULL).Ref());
RSAES_OAEP_SHA_Encryptor e(publicKey);
StringSource(plain, true,
new PK_EncryptorFilter(rng, e,
new StringSink(cipher)
) // PK_EncryptorFilter
); // StringSource
int n = cipher.length();
char* char_array = new char[n + 1];
strcpy(char_array, cipher.c_str());
return char_array;
}
}
extern "C" {
char* decrypt(const char* filename, string cipher) {
AutoSeededRandomPool rng;
RSA::PrivateKey privateKey;
string recovered;
privateKey.Load(FileSource(filename, true).Ref());
RSAES_OAEP_SHA_Decryptor d(privateKey);
StringSource(cipher, true,
new PK_DecryptorFilter(rng, d,
new StringSink(recovered)
) // PK_EncryptorFilter
); // StringSource
int n = recovered.length();
char* char_array = new char[n + 1];
strcpy(char_array, recovered.c_str());
return char_array;
}
}
I referred emscripten documentation for using libraries in emscripten
I created cryptlib.a using the cryptlib.cpp file and compiled it using emcc like so
emcc -c -o cryptlib.o cryptlib.cpp
ar rcs cryptlib.a cryptlib.o
Finally I also created Source.o like so
emcc -c -o Source.o Source.cpp
I figured out that this would the command to get an html and js file
emcc Source.o cryptlib.a -o source.html -sEXPORTED_FUNCTIONS=_generateKeys -sEXPORTED_RUNTIME_METHODS=ccall,cwrap
I get wasm-ld errors like so
Error screenshots
What am I doing wrong? I also want to expose other functions of my source code but I was testing with just one of the functions.
Looks like you try to use the native version of crypto++, and this will only work if you build the webassembly version of the library.
EDIT: sorry... you were already trying that...
I saw your question here as I was doing it myself.
this is what I just published, which might help:
=> wasm online demo:
===> https://guillaumebouchetepitech.github.io/wasm-cryptopp-demo/dist/index.html
=> source code:
===> https://github.com/GuillaumeBouchetEpitech/wasm-cryptopp-demo
PS: I've added the code to set/get string values to/from the js/wasm code as well, it's not overly well done but it feels like you could also get started on that side as well thanks to it.
let me know.

How to modify this sample code from Crypto++ for RSA to work as it should?

I'm a noob with Crypto++, still trying to understand all the pipeline stuff etc...
The first trivial problem I have is that SavePrivateKey isn't found in the library by the compiler but when I look it up on Wiki Crypto++ the link is broken and I can't find what to #include...
The second problem is that I'd like to access the privkey and pubkey generate by the first snippet as strings in X.509/PKCS#8 format in order to manipulate them but it's not clear to me how to do that.
The last problem is that I haven't understood how to input the keys to the decrypt function as a string.
Thank you in advance if you can help me
#include <cryptopp/files.h>
#include <cryptopp/modes.h>
#include <cryptopp/osrng.h>
#include <cryptopp/rsa.h>
#include <cryptopp/sha.h>
#include "cryptopp/filters.h"
#include "cryptopp/hex.h"
#include "cryptopp/cryptlib.h"
#include <typeinfo>
int main() {
// Generate keys
AutoSeededRandomPool rng;
InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 3072);
RSA::PrivateKey privateKey(params);
RSA::PublicKey publicKey(params);
std::string plain="RSA Encryption", cipher, recovered;
// Encryption
RSAES_OAEP_SHA_Encryptor e(publicKey);
StringSource ss1(plain, true,new PK_EncryptorFilter(rng, e, new StringSink(cipher))); // StringSource
// Decryption
RSAES_OAEP_SHA_Decryptor d(privateKey);
StringSource ss2(cipher, true, new PK_DecryptorFilter(rng, d, new StringSink(recovered))); // StringSource
SavePrivateKey("rsa-private.key", privateKey);
SavePublicKey("rsa-public.key", publicKey);
std::cout << "Recovered plain text" << std::endl;
std::cout<<rsaPrivate<<std::endl<<rsaPublic<<std::endl;
return 0;
}

crptopp encryption of long string: message length exceeds the maximum for this public key

the code below implements a working example for string encryption using RSA and cryptopp. It is an adaption of the examples available from the crypto++ wiki. The problem is that an exception is thrown if the string is large than 342.
terminate called after throwing an instance of 'CryptoPP::InvalidArgument'
what(): RSA/OAEP-MGF1(SHA-1): message length of 1000 exceeds the maximum of 342 for this public key
A similar situation arises if the source/sink is a file.
There are at least 3 options to deal with that problem
make the key larger
slice the string manually before calling the encryption function
configure/call cryptopp in a way that it can handle the long string.
The latter option would be the most sensible, and I am sure it exists, but I have failed so far to figure out the correct setup.
Any ideas are highly appreciated.
#include <string>
#include <cryptlib.h>
#include <rsa.h>
#include "osrng.h"
#include <iostream>
#include <queue.h>
#include <files.h>
#include "base64.h"
using namespace std;
using namespace CryptoPP;
void b(string plain){
CryptoPP::AutoSeededRandomPool rng;
CryptoPP::InvertibleRSAFunction params;
params.GenerateRandomWithKeySize(rng, 3072);
CryptoPP::RSA::PrivateKey privateKey(params);
CryptoPP::RSA::PublicKey publicKey(params);
std::string cipher, recovered;
CryptoPP::RSAES_OAEP_SHA_Encryptor e(publicKey);
CryptoPP::StringSink sink(cipher);
CryptoPP::PK_EncryptorFilter filter(rng,e);
filter.Attach(new Redirector(sink));
CryptoPP::StringSource ss1(plain, false);
ss1.Attach(new Redirector(filter));
ss1.PumpAll();
CryptoPP::RSAES_OAEP_SHA_Decryptor d(privateKey);
CryptoPP::StringSource ss2(cipher, true,
new CryptoPP::PK_DecryptorFilter(rng, d,
new CryptoPP::StringSink(recovered)
) // PK_DecryptorFilter
); // StringSource
std::cout <<recovered << std::endl;
}
int main(){
string plain;
for(int i=0;i<1000;++i){
plain.push_back('A');
}
b(plain);
}

Decrypting using openssl c/c++ API fails

I create a file using openssl from the commandline. Let's say:
echo 'foobar' | openssl enc -aes-128-cbc -e -pass pass:testing >
sample
I can decrypt this doing
cat sample | openssl enc -aes-128-cbc -d -pass pass:testing
That works fine.
Now I want to decrypt that file using the openssl c/c++ API but I can't get this quite right. I am trying to decrypt like so
#include <fstream>
#include <memory>
#include <string>
#include <openssl/err.h>
#include <openssl/evp.h>
using EVP_CIPHER_CTX_free_ptr = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>;
int main(int argc, char* argv[])
{
ERR_load_crypto_strings();
EVP_add_cipher(EVP_aes_128_cbc());
std::string l_key{"testing"};
std::string l_ctext{};
std::ifstream l_file("sample", std::ios::binary | std::ios::in | std::ios::ate);
if(l_file.is_open())
{
std::streampos l_size = l_file.tellg();
char * lp_buffer = new char[l_size];
l_file.seekg(0, std::ios::beg);
l_file.read(lp_buffer, l_size);
l_ctext.append(lp_buffer, l_size);
delete lp_buffer;
l_file.close();
}
std::string l_rtext;
EVP_CIPHER_CTX_free_ptr ctx(EVP_CIPHER_CTX_new(), ::EVP_CIPHER_CTX_free);
if(1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cbc(), NULL,
(const unsigned char*)l_key.c_str(), NULL));
ERR_print_errors_fp(stdout);
int l_len;
if(1 != EVP_DecryptUpdate(ctx.get(), (unsigned char*)&l_rtext[0], &l_len,
(const unsigned char*)l_ctext.data(),
l_ctext.size()))
ERR_print_errors_fp(stdout);
if(1 != EVP_DecryptFinal_ex(ctx.get(), (unsigned char*)&l_rtext[0] + l_len,
&l_len))
ERR_print_errors_fp(stdout);
ERR_free_strings();
exit(0);
}
However that last call to EVP_DecryptFinal_ex fails with
140559955420968:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:596:
Most of this code is based on the examples that come with the openssl API. What am I doing wrong? I set the IV to NULL in EVP_DecryptInit_ex because as far as I found out, this is acceptable as long as I don't use the key multiple times.
All this on a Fedora machine with GCC
I kind of figured out what I did wrong.
The commandline 'openssl enc' command uses a default salt, my code above didn't look for that. So (rather than try to work with the '-nosalt' option) I remove the first 16 bytes from the encrypted string and use the last 8 of that as the salt.
I was feeding the password to the EVP methods as a 'key'. It seems 'openssl enc' doesn't do this so one needs to use the salt and the password to create a key and IV (rather than using NULL which is what I did).
There was an error in the above code in that l_rtext needs a resize() to reflect the fact that characters were added to it.