I have been struggling with a weird problem with RSA_verify. I am trying to RSA_sign using C and RSA_verify using C++. I have generated the private key and certificate using OpenSSL commands.
message = "1.2.0:08:00:27:2c:88:77"
When I use the message above, generate a hash and use RSA_sign to sign the digest, I get a signature of length 256 (strlen(signature)) and also the length returned from RSA_sign is 256. I use this length to verify and verification succeeds.
But when I use a message = "1.2.0:08:00:27:2c:88:08", the signature length is 60 and RSA_sign returns 256. When I use this length 60 to verify it fails. It fails to verify with length 256 as well. Also for some messages (1.2.0:08:00:27:2c:88:12) the signature generated is zero.
I am using SHA256 to hash the message and NID_SHA256 to RSA_sign and RSA_verify this digest. I have used -sha256 while generating the keys using the OpenSSL command.
I am forming the message by parsing an XML file reading some of the tags using some string operation.
Kindly suggest.
Below is the code used to sign.
int main(void)
{
int ret;
RSA *prikey;
char *data ;
unsigned char* signature;
int slen = 0;
FILE * fp_priv = NULL;
char* privfilepath = "priv.pem";
unsigned char* sign = NULL;
ERR_load_crypto_strings();
data = generate_hash();
printf("Message after generate hash %s: %d\n", data, strlen(data));
fp_priv = fopen(privfilepath, "r");
if (fp_priv == NULL)
{
printf("Private key path not found..");
return 1;
}
prikey = RSA_new();
prikey = PEM_read_RSAPrivateKey(fp_priv, &prikey, NULL, NULL);
if (prikey == NULL)
{
printf("Private key returned is NULL\n");
return 1;
}
signature = (unsigned char*)malloc(RSA_size(prikey));
if( signature == NULL )
return 1;
if(RSA_sign(NID_sha256, (unsigned char*)data, strlen(data),
signature, &slen, prikey) != 1) {
ERR_print_errors_fp(stdout);
return 1;
}
printf("Signature length while signing... %d : %d : %d ",
strlen(signature), slen, strlen(data));
FILE * sig_bin = fopen("sig_bin", "w");
fprintf(sig_bin, "%s", signature);
fclose(sig_bin);
system("xxd -p -c256 sig_bin sig_hex");
RSA_free(prikey);
if(signature)
free(signature);
return 0;
}
One very, very important thing to learn about C is it has two distinct types with the same name.
char*: This represents the beginning of a character string. You can do things like strstr or strlen.
You should never strstr or strlen, but rather strnstr and strnlen, but that's a different problem.
char*: This represents the beginning of a data blob (aka byte array, aka octet string), you can't meaningfully apply strlen/etc to it.
RSA_sign uses the latter. It returns "data", not "a message". So, in your snippet
printf("Signature length while signing... %d : %d : %d ",
strlen(signature), slen, strlen(data));
FILE * sig_bin = fopen("sig_bin", "w");
fprintf(sig_bin, "%s", signature);
fclose(sig_bin);
data came from a function called generate_hash(); it's probably non-textual, so strlen doesn't apply. signature definitely is data, so strlen doesn't apply. fprintf also doesn't apply, for the same reasons. These functions identify the end of the character string by the first occurrence of a zero-byte (0x00, '\0', etc). But 0x00 is perfectly legal to have in a signature, or a hash, or lots of "data".
The length of the output of RSA_sign is written into the address passed into the 5th parameter. You passed &slen (address-of slen), so once the function exits (successfully) slen is the length of the signature. Note that it will only very rarely match strlen(signature).
To write your signature as binary, you should use fwrite, such as fwrite(sig_bin, sizeof(char), signature, slen);. If you want it as text, you should Base-64 encode your data.
Related
I am new to socket programming, so be kind :)
I am writing a client-server application in C++ and using OpenSSL. Till now I have generated the public-private keys for the client and server and have exchanged it over the network. Now is the part where I want to encrypt my client's message using the server's public key. But my public_encrypt function returns gibberish. I know the methods which I am using are deprecated and there are better methods but the purpose is to get the hands dirty only.
Below is the function that invokes the encryption API. (Ignore the if part, it's for sending the clients public key)
#define RSA_SIZE 256
void sendMessage(int clientFD, uint16_t type, char *data, serverState *server){
uint16_t length = strlen(data);
unsigned char message[MESSAGE_SIZE];
if (server->state == 0)
{
memcpy(message, (char *)&length, sizeof(length));
memcpy(message + 2, (char *)&type, sizeof(type));
memcpy(message + 4, data, length);
send(clientFD, message, 4 + length, 0);
server->state = 1;
}
else
{
unsigned char encrypted[RSA_SIZE] = {0};
length = public_encrypt(reinterpret_cast<unsigned char *>(data), length, server->key, encrypted);
assert(length != -1);
printf("%s\n", encrypted);
memcpy(message, (char *)&length, sizeof(length));
memcpy(message + 2, (char *)&type, sizeof(type));
memcpy(message + 4, encrypted, length);
send(clientFD, message, 4 + length, 0);
}}
This is the code for the encryption
int padding = RSA_PKCS1_OAEP_PADDING;
RSA *createRSA(unsigned char *key, int pub){
RSA *rsa = NULL;
BIO *keybio;
keybio = BIO_new_mem_buf(key, -1);
if (keybio == NULL)
{
printf("Failed to create key BIO");
return 0;
}
if (pub)
{
rsa = PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL);
}
else
{
rsa = PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL);
}
if (rsa == NULL)
{
printf("Failed to create RSA");
}
return rsa;}
int public_encrypt(unsigned char *data, int data_len, unsigned char *key, unsigned char *encrypted){
printf("Data:%s\n:", data);
printf("Data Length:%d\n:", data_len);
printf("Server's Key:\n%s\n:", key);
RSA *rsa = createRSA(key, 1);
int result = RSA_public_encrypt(data_len, data, encrypted, rsa, padding);
return result;}
Please check out the link https://i.stack.imgur.com/WJn7e.png to see my output.
PS: Sorry for such a long post.
The output of RSA is a random value between 0 and the modulus of the RSA private key, encoded as an unsigned big endian octet string (octet string is just another name for byte array, a char[] in C / C++). It contains bytes with any value, and it is therefore certainly not ASCII. If you want ASCII you have to base 64 encode the ciphertext.
However, quite often ciphertext is "stringified" for no good reason at all, so only do this if this is necessary within your protocol / system. Python strings are made somewhat readable for you by the Python runtime. I'm not sure if that's a good thing or not - it's certainly not a good idea to copy that string as it is only Python proprietary.
C is not as forgiving, if you treat the binary array as text you'll run into trouble, as it can contain any character, including control characters and the NUL character (00), which can play merry hell with functions such as strlen and many others that expect a textual string instead of an array of bytes (both are usually based on char in C/C++).
I would like to generate a random string with OpenSSL and use this as a salt in a hashing function afterwards (will be Argon2). Currently I'm generating the random data this way:
if(length < CryptConfig::sMinSaltLen){
return 1;
}
if (!sInitialized){
RAND_poll();
sInitialized = true;
}
unsigned char * buf = new unsigned char[length];
if (!sInitialized || !RAND_bytes(buf, length)) {
return 1;
}
salt = std::string (reinterpret_cast<char*>(buf));
delete buf;
return 0;
But a std::cout of salt doesn't seem to be a proper string (contains control symbols and other stuff). This is most likely only my fault.
Am I using the wrong functions of OpenSSL to generate the random data?
Or is my conversion from buf to string faulty?
Random data is random data. That's what you're asking for and that's exactly what you are getting. Your salt variable is a proper string that happens to contain unprintable characters. If you wish to have printable characters, one way of achieving that is using base64 encoding, but that will blow up its length. Another option is to somehow discard non-printable characters, but I don't see any mechanism to force RAND_bytes to do this. I guess you could simply fetch random bytes in a loop until you get length printable characters.
If encoding base64 is acceptable for you, here is an example of how to use the OpenSSL base64 encoder, extracted from Joe Linoff's Cipher library:
string Cipher::encode_base64(uchar* ciphertext,
uint ciphertext_len) const
{
DBG_FCT("encode_base64");
BIO* b64 = BIO_new(BIO_f_base64());
BIO* bm = BIO_new(BIO_s_mem());
b64 = BIO_push(b64,bm);
if (BIO_write(b64,ciphertext,ciphertext_len)<2) {
throw runtime_error("BIO_write() failed");
}
if (BIO_flush(b64)<1) {
throw runtime_error("BIO_flush() failed");
}
BUF_MEM *bptr=0;
BIO_get_mem_ptr(b64,&bptr);
uint len=bptr->length;
char* mimetext = new char[len+1];
memcpy(mimetext, bptr->data, bptr->length-1);
mimetext[bptr->length-1]=0;
BIO_free_all(b64);
string ret = mimetext;
delete [] mimetext;
return ret;
}
To this code, I suggest adding BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL), because otherwise you'll get a new line character inserted after every 64 characters. See OpenSSL's -A switch for details.
I have a native program that takes a password that is passed in on command line. That password is showing up in server logs so I want to obfuscate it by encrypting it before putting on the command line. I would then decrypt it in the program and use it as before. The idea is I use powershell to create a SecureString with the password, then make it a printable text string using ConvertFrom-SecureString. That string is then passed in on the command line to the native c++ program. From there, I decode it back to a binary excrypted form, and then decrypt it back to the original plain text password. Easy right?
From scant documentation, I think the ConvertFrom-SecureString does a Base64 encoding to make the binary SecureString inot printable text. Can anyone confirm that?
I recover the binary bytes using ATL::Base64Decode(). This appears to work when comparing the first 20 bytes from orignal and decoded.
After that I'm trying to decrypt the SecureString bytes. Again some documentation appears to imply that the SecureString Encryption is done using Machine Key (or User Session Key). Based on this, I'm trying to decrypt using the DPAPI CryptUnprotectData method. Here, though I get an decrupt failure with "(0x8007000d) The data is invalid". Does this sound like it will work? If so any idea where I'm off course?
Heres the decrypt method ...
// Decrypts an encoded and encrypted string with DPAPI and Machine Key, returns the decrypted string
static HRESULT Decrypt(CStringW wsEncryptedBase64, OUT CStringW& wsPlainText)
{
HRESULT hr = S_OK;
DATA_BLOB dbIn = { 0 };
DATA_BLOB dbOut = { 0 };
const wchar_t *pos = wsEncryptedBase64.GetBuffer(wsEncryptedBase64.GetLength());
dbIn.cbData = wsEncryptedBase64.GetLength() / 2;
dbIn.pbData = (byte*)malloc(dbIn.cbData * sizeof(byte));
int num = 0;
for (size_t i = 0; i < dbIn.cbData; i += 1)
{
swscanf_s(pos, L"%2hhx", &num);
dbIn.pbData[i] = num;
pos += sizeof(wchar_t);
}
if (::CryptUnprotectData(&dbIn, NULL, NULL, NULL, NULL,
CRYPTPROTECT_UI_FORBIDDEN, &dbOut))
{
wsPlainText = CStringW(reinterpret_cast< wchar_t const* >(dbOut.pbData), dbOut.cbData / 2);
}
else
{
hr = HRESULT_FROM_WIN32(::GetLastError());
if (hr == S_OK)
{
hr = SEC_E_DECRYPT_FAILURE;
}
}
return hr;
}
From what I can tell looking at the binary in dotPeek, ConvertFrom-String is using SecureStringToCoTaskMemUnicode to convert the secure string payload to an array of bytes. That array of bytes is returned in hex form e.g. byte.ToString("x2).
This assumes that you are using DPAPI as you say and not use the Key or SecureKey parameters on ConvertFrom-SecureString.
So in your C++ program do not use Base64Decode, just parse every two chars as a hex byte. Then call CryptUnprotectData on the resulting byte array (stuffed into the DATA_BLOB).
this is my first post. i am currently taking a networking class and i am required to write a client program that can download all emails from imap.gmail.com:993 to text files. i am required to write this program using winsock and openssl. I was able to connect to the server and fetch the emails. For emails with small data, i had no problem receiving it. But for emails with large data such as an images that is base64-decoded, i was able to download only a part of it.
so my question is How can i tell the client to wait until it received all the data from the server?
Here is what i have done so far:
void fetchMail(SSL *sslConnection,int lowerLimit, int UpperLimit)
{
SYSTEMTIME lt;
ofstream outfile;
GetLocalTime(<);
char szFile[MAX_PATH + 1];
char szPath[MAX_PATH+1];
char message[BUFSIZE];
char result[BUFSIZE];
::GetModuleFileName( NULL, szPath, MAX_PATH );
// Change file name to current full path
LPCTSTR psz = strchr( szPath, '\\');
if( psz != NULL )
{
szPath[psz-szPath] = '\0';
}
char szMailBox[MAX_PATH+1];
memset( szMailBox, 0, sizeof(szMailBox));
wsprintf( szMailBox, "%s\\inbox", szPath );
// Create a folder to store emails
::CreateDirectory( szMailBox, NULL );
for(int i = lowerLimit; i < UpperLimit; ++i)
{
// Create a folder to store emails
memset( szFile, 0, sizeof(szFile));
memset( result, 0, sizeof(result));
memset( message, 0, sizeof(message));
::sprintf(szFile,"%s\\%d%d%d%d%d%d.txt", szMailBox, lt.wHour, lt.wMinute,lt.wMinute,lt.wSecond, lt.wMilliseconds,i);
string Result;//string which will contain the result
stringstream convert; // stringstream used for the conversion
const char * num;
convert << i;//add the value of Number to the characters in the stream
Result = convert.str();//set Result to the content of the stream
num = Result.c_str();
strcpy(result, "tag FETCH ");
strcat(result, num);
strcat(result, " (BODY[TEXT])\r\n");
int n = 0;
cout << "\nFETCHING : \n";
SSL_write(sslConnection, result, strlen(result));
outfile.open(szFile );
SSL_read(sslConnection, message, sizeof(message)-1);
outfile <<message ;
outfile.close();
}
}
First of all some points on your code:
You use strcpy, strcat and all those unchecked, unsafe C functions. You might easily get buffer overflows and other kinds of errors. Consider to use C++ strings, vectors, arrays.
You do a lot of different things in that function, on different levels of abstraction. AFAICS only the two SSL_* function calls are really about fetching that mail. Consider to break out some functions to improve readability.
Now to your problem: Googling a bit about SSL_read, you will see that it returns an int, denoting how many bytes were actually read. You should use that return value - not only for this issue but also for error handling. If the mail data is longer than your buffer, the function will read until the buffer is filled and return its size. You should continue to call the function until all bytes have been read.
I am using this simple function for decrypting a AES Encrypted string
unsigned char *aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext, int *len)
{
int p_len = *len, f_len = 0;
unsigned char *plaintext = (unsigned char*)malloc(p_len + 128);
memset(plaintext,0,p_len);
EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL);
EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len);
EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len);
*len = p_len + f_len;
return plaintext;
}
The problem is that len is returning a value that does not match the entire decoded string. What could be the problem ?
When you say "string", I assume you mean a zero-terminated textual string. The encryption process is dependent on a cipher block size, and oftentimes padding. What's actually being encoded and decoded is up to the application... it's all binary data to the cipher. If you're textual string is smaller than what's returned from the decrypt process, your application needs to determine the useful part. So for example if you KNOW your string inside the results is zero-terminated, you can get the length doing a simple strlen. That's risky of course if you can't guarantee the input... probably better off searching the results for a null up to the decoded length...
If you are using cipher in ECB, CBC or some other chaining modes, you must pad plain text to the length, which is multiple of cipher block length. You can see a PKCS#5 standard for example. High-level functions like in OpenSSL can perform padding transparently for programmer. So, encrypted text can be larger than plain text up to additional cipher block size.