Self sign certificate programatically - c++

I'm trying to build a fake HTTPS server (like mitmproxy), so I made an SSL "sign" function. after generating and installing the certificate on my system I got the error: "NET::ERR_CERT_AUTHORITY_INVALID"/ "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT".
The code looks like
static bool generateX509(const std::shared_ptr<CA>& ca, SSL *ssl, const char * domain)
{
auto ca_cert = ca->ca_crt.get();
auto ca_pkey = ca->ca_key.get();
// --- cert generation ---
std::unique_ptr<X509, void (*)(X509 *)> cert { X509_new(), X509_free };
X509_set_notBefore(cert.get(), X509_get_notBefore(ca_cert));
X509_set_notAfter(cert.get(), X509_get_notAfter(ca_cert));
X509_set_serialNumber(cert.get(), X509_get_serialNumber(ca_cert));
X509_set_pubkey(cert.get(), X509_get_pubkey(ca_cert));
X509_set_version(cert.get(), 2);
const unsigned char country[] = "RU";
const unsigned char company[] = "MyCompany, PLC";
X509_name_st* name = X509_get_subject_name(cert.get());
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, country, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, company, -1, -1, 0);
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (const unsigned char *)domain, -1, -1, 0);
X509_set_issuer_name(cert.get(), name);
X509_EXTENSION *Extension;
char Buffer[512];
// Format the value
sprintf (Buffer, "DNS:%s", domain);
Extension = X509V3_EXT_conf_nid (nullptr, nullptr, NID_subject_alt_name, Buffer);
if (Extension) {
X509_add_ext (cert.get(), Extension, -1);
X509_EXTENSION_free (Extension);
}
X509_sign(cert.get(), ca_pkey, EVP_sha256()); // some hash type here
SSL_use_certificate(ssl, cert.get());
SSL_use_PrivateKey(ssl, ca_pkey);
return true;
}
What i missing?
How to resolve this error

Related

Create a CSR with OpenSSL without signing

I'm working with Openssl on C++ and getting in trouble with my CSR generation. What i'm wanted to do is creating a CSR without using the private key. I want to put my details and of course the public key in it, but because of security management i can't call the sign function.
Instead i want to collect all the "data which should be signed" and transmit it to another application which creates the signature in a secure area. When my application is receiving the signature i want to manually set the value in the X509_REQ struct and write the CSR to PEM format.
My problem is, how to get the "data to be signed"? I know that the CSR is Asn1 Decoded, do you know any function to get the asn1 data as a string or something?
This is from my test prog:
std::string subjectCN = "Test123";
std::string subjectL = "Test456";
std::string subjectSN = "1234";
std::string szProvinceStr = "test";
std::string szOrganization = "My Organization";
std::string szCountry = "DE";
int nVersion = 1;
EVP_PKEY *test = NULL;
//RSA just for test validation
std::string mKey = "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCDT4ntP3Fqj73RQW32B6hCDHMG64GtxeQDZ5BcQnQSRB3S/EwM\ngpZwuGYwTb7E65pdAAQ0e5na2d7yIGZX4MoDRGaDbYgdxr49J430cVLRU1r9EW+O\nqZQERyGYefxWOqBaNZL2PBclS/qf+AxRh1WnD8aY5V5zNItgVV4Bv9w4YQIDAQAB\nAoGAMd6xaXNg5bG1y5Vn57q8wmjr/sLOu2qe0FQy1Xc/kfhgw1Kip1WpQSInXU0J\nmMxKEewBrNUMd7urGpYHiHSNA+QXiIp3qxGy7PrmZrPSrJImPxAE02YaUGDoh+6o\nZJc7xXCw2bwX8Fth8Duj2KNcIDuWuieybfzwTYKKJG3J04ECQQDxSa4gq/0SiiZ2\nc8YTn9wCTwqezmdI8SWsWXRnpXt1BhejokvLFbqpfQZ6m9LLYvpUsihZ2QkBXUl/\n1/uNu+aJAkEAi1Ey/7fjJJSJalNUgF3lKQdTqUlYI/9O9/98zPOcDmXcKlLNpf+f\nTV3nhK3vsewYqsx3Tu9bMKBVTE0dv+/NGQJAHfYyQyhYMpcpE4hozkMJhNffz7x9\notcfAHnTNJOd8vggs1cR5lP6a9V0moEC+fJ+d0nwLMgAkETPParKN91fUQJAEWMB\n3V4ir+cFu0pJCngtaFBsxXzMzjlHrrWo6p8gg798mZ+Z4LSlOe+VPD7E4kyXy4EX\nBrfihpAL9SjOpKyVyQJBAPD3E4Z7THZCQI/2u4eRXz3qbJAmPYLPTn/AxuX4VssW\n1WJAxZeCFHWL6+/84zoDWwzXN0xQFzO0ZspxxQNFqCI=\n-----END RSA PRIVATE KEY-----";
BIO* bo = BIO_new( BIO_s_mem() );
BIO_write( bo, mKey.c_str(),mKey.length());
EVP_PKEY* pkey = 0;
PEM_read_bio_PrivateKey( bo, &pkey, 0, 0 );
BIO_free(bo);
RSA* rsa = EVP_PKEY_get1_RSA( pkey );
x509Req = X509_REQ_new();
ret = X509_REQ_set_version(x509Req, nVersion);
x509Name = X509_REQ_get_subject_name(x509Req);
ret = X509_NAME_add_entry_by_txt(x509Name,"C", MBSTRING_ASC, (const unsigned char*)szCountry.c_str(), -1, -1, 0);
ret = X509_NAME_add_entry_by_txt(x509Name,"ST", MBSTRING_ASC, (const unsigned char*)szProvince.c_str(), -1, -1, 0);
ret = X509_NAME_add_entry_by_txt(x509Name,"L", MBSTRING_ASC, (const unsigned char*)subjectL.c_str(), -1, -1, 0);
ret = X509_NAME_add_entry_by_txt(x509Name,"O", MBSTRING_ASC, (const unsigned char*)szOrganization.c_str(), -1, -1, 0);
ret = X509_NAME_add_entry_by_txt(x509Name,"CN", MBSTRING_ASC, (const unsigned char*)subjectCN.c_str(), -1, -1, 0);
test = EVP_PKEY_new();
EVP_PKEY_assign_RSA(test, rsa);
ret = X509_REQ_set_pubkey(x509Req, test);
PEM_write_bio_X509_REQ(basicInputOutput, x509Req);
BIO_get_mem_ptr(basicInputOutput, &pointerToBuffer);
std::string csrTemp = std::string(pointerToBuffer->data, pointerToBuffer->length);
//now i need all the data to create the signature
//i cant't call
//ret = X509_REQ_sign(x509Req, test, EVP_sha256());

Verify signature in C++

I have a function that verifies signature in C++ using Openssl functions.
the verify does not work.
int verify(char *msg, int msg_len, char *signature, int signature_len, char *cert_str, int cert_len) {
X509* cert = d2i_X509(NULL, &cert_str, cert_len);
EVP_PKEY* key = X509_get_pubkey(cert);
EVP_MD_CTX* ctx = EVP_MD_CTX_create();
EVP_MD_CTX_init(ctx);
X509_ALGOR *palg = NULL;
ASN1_OBJECT *paobj = NULL;
X509_get0_signature(NULL, &palg, cert);
TRACE_ASSERT(T_PLUGIN, palg);
X509_ALGOR_get0(&paobj, NULL, NULL, palg);
int alg = OBJ_obj2nid(paobj);
const EVP_MD* md = EVP_get_digestbynid(alg);
EVP_VerifyInit_ex(ctx, md, NULL);
EVP_VerifyUpdate(ctx, msg, msg_len);
return EVP_VerifyFinal(ctx, signature, signature_len, key) == 1);
for some unknown reason the verify return 0 on things that should pass verification.
the msg, signature and cert_str are bytearrays converted to char*.
the conversions work fine as far as I've checked.
Am I missing anything, did something wrong?
Thanks.

LogonUserW logon fails for a non-ascii username

I have C++ code that tries to authenticate a local user to windows:
BOOL result = ::LogonUserW(localAdminUserName_.c_str(), L".", localAdminPassword_.c_str(),
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT);
This works fine for ASCII character-set based usernames.
But doesn't work for a user named, say userああ
If I print the variable localAdminUserName_ in the log, It's printing the username just fine.
Is .c_str() messing it up somehow?
Should I encode the username/password in someway before making this API call?
The following console application I made to test this scenario, is working fine!
_declspec(dllimport)
BOOL
__stdcall
LogonUserW(
__in LPCWSTR lpszUsername,
__in_opt LPCWSTR lpszDomain,
__in LPCWSTR lpszPassword,
__in DWORD dwLogonType,
__in DWORD dwLogonProvider,
__deref_out PHANDLE phToken
);
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE hToken = NULL;
BOOL returnValue = LogonUserW(
L"userああ",
L".",
L"pa$$w0rd",
LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT,
&hToken);
if (returnValue == false) {
std::cout<<"Error!";
} else {
std::cout<<"Success!";
}
return 0;
}
Problem got solved after the original string is converted to multi byte string and then used the MultiByteToWideChar method in windows.h to convert it to wide char:
//convert the credentials to a multi-byte string first
std::string MbLocalAdminUserName = MbString(localAdminUserName_.c_str());
std::string MbLocalAdminPassowrd = MbString(localAdminPassowrd_.c_str());
//convert this multi-byte format to wide char that windows is expecting
//username
int len_MbLocalAdminUserName = MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminUserName.c_str() , -1, NULL, 0);
wchar_t* WcLocalAdminUserName = new wchar_t[len_MbLocalAdminUserName + 1];
MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminUserName.c_str(), -1, WcLocalAdminUserName, len_MbLocalAdminUserName + 1);
//password
int len_MbLocalAdminPassowrd = MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminPassowrd.c_str() , -1, NULL, 0);
wchar_t* WcLocalAdminPassowrd = new wchar_t[len_MbLocalAdminPassowrd + 1];
MultiByteToWideChar(CP_UTF8, 0, MbLocalAdminPassowrd.c_str(), -1, WcLocalAdminPassowrd, len_MbLocalAdminPassowrd + 1);
BOOL result = ::LogonUser(WcLocalAdminUserName, L".", WcLocalAdminPassowrd,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &hToken);
delete[] WcLocalAdminUserName;
delete[] WcLocalAdminPassowrd;
Where MbString is :
MbString::MbString(const wchar_t* src)
: buf_(0) {
const size_t count = 1 + sizeof(wchar_t) * wcslen(src);
buf_ = new char[count];
// The 3rd parameter specifies the size of multi-bytes string buffer.
wcstombs(buf_, src, count);
}

Reading DSA private key protected with 3DES

Here is how I manage to create the key pair and store in files successfully. The problem comes when reading the private key protected with password where I always get NULL. Any tip to solve this issue? Thanks.
FILE *dsa_privatekey_file;
FILE *dsa_publickey_file;
const char *pkeykey = "password";
int result = 0;
DeleteFile("dsapub.pem");
DeleteFile("dsapriv.pem");
dsa_publickey_file = fopen("dsapub.pem", "r");
dsa_privatekey_file = fopen("dsapriv.pem", "r");
if (dsa_privatekey_file == NULL || dsa_publickey_file == NULL)
{
if (dsa_privatekey_file != NULL)
{
fclose(dsa_privatekey_file);
}
if (dsa_publickey_file != NULL)
{
fclose(dsa_publickey_file);
}
dsa_publickey_file = fopen("dsapub.pem", "w");
dsa_privatekey_file = fopen("dsapriv.pem", "w");
DSA* dsa = DSA_new();
result = DSA_generate_parameters_ex(dsa, 2048, NULL, 0, NULL, NULL, NULL);
result = DSA_generate_key(dsa);
result = PEM_write_DSAPrivateKey(dsa_privatekey_file, dsa, EVP_des_ede3_cbc(), NULL, 0, NULL, (void *)pkeykey);
//result = PEM_write_DSAPrivateKey(dsa_privatekey_file, dsa, EVP_des_ede3_cbc(), (unsigned char *) pkeykey, strlen(pkeykey), NULL, NULL);
//result = PEM_write_DSAPrivateKey(dsa_privatekey_file, dsa, NULL, NULL, 0, NULL, NULL);
result = PEM_write_DSA_PUBKEY(dsa_publickey_file, dsa);
fclose(dsa_privatekey_file);
fclose(dsa_publickey_file);
DSA_free(dsa);
dsa_publickey_file = fopen("dsapub.pem", "r");
dsa_privatekey_file = fopen("dsapriv.pem", "r");
}
DSA *dsa_sign = PEM_read_DSAPrivateKey(dsa_privatekey_file, NULL, default_set_password, (void *)pkeykey);
DSA *dsa_verify = PEM_read_DSA_PUBKEY(dsa_publickey_file, NULL, NULL, NULL);
Here's the simple function I use to hard insert the password, but anyway it seems not to be called.
static int default_set_password(char *buf, int size, int rwflag, void *descr){ memcpy(buf, descr, size); return size; }
A call to
OpenSSL_add_all_algorithms();
was needed to work as expected.

How can I fix this code?

This code does not work as expected and I don't know what's wrong.
szTeam should change, but doesn't.
Could anyone explain this?
-----------------------------------------------------
WCHAR szTeam[MAX_PATH] = L"\u7F57\u5207\u8FBE\u5C14\u6D41\u6D6A";
char szMsg[MAX_PATH];
sprintf(szMsg , "%s" , WideStringToMultiByte(szTeam));
swprintf( szTeam , L"%s" , MultiByteToWideString(szMsg));
......
WCHAR* MultiByteToWideString(const char* szSrc)
{
int iSizeOfStr = MultiByteToWideChar(CP_ACP, 0, szSrc, -1, NULL, 0);
wchar_t* wszTgt = new wchar_t[iSizeOfStr];
if(!wszTgt)
return (NULL);
MultiByteToWideChar(CP_ACP, 0, szSrc, -1, wszTgt, iSizeOfStr);
return(wszTgt);
}
char* WideStringToMultiByte(const wchar_t* wszSrc)
{
int iSizeOfStr = WideCharToMultiByte(CP_UTF8, 0, wszSrc, -1, NULL, 0, NULL, NULL);
char* szTgt = new char[iSizeOfStr];
if(!szTgt)
return(NULL);
WideCharToMultiByte(CP_UTF8, 0, wszSrc, -1, szTgt, iSizeOfStr, NULL, NULL);
return szTgt;
}
-----------------------------------------------------
Erm, no szTeam does change. Into something unrecognizable, mojibake. You start out with "罗切达尔流浪" and convert it from its utf-16 encoding to utf-8. That works fine. The debugger won't show you anything recognizable because it neither knows nor cares that szMsg is encoded in utf-8.
You then go wrong though, you are converting that utf-8 string with CP_ACP. Which says that the string is encoded in the default system code page. It is not, it was encoded in utf-8.
Fix your problem with:
WCHAR* MultiByteToWideString(const char* szSrc)
{
int iSizeOfStr = MultiByteToWideChar(CP_UTF8, 0, szSrc, -1, NULL, 0);
// etc..
}
And now szTeam won't change because the string got properly converted back.