PEM Conversion (PKCS7) to DER - AKA Base64 C++ Problems - c++

I've just finished up writing some OpenSSL/PKCS7 digital signature code and now have a working PEM encoded PKCS7 file. So after little battle, I need to convert that PEM to DER format. This is proving tougher than I hoped.
There are some convenience methods in OpenSSL such as "PEM_write_bio_PKCS7_stream" to write your PKCS7 object. But after some extensive googling and browsing some header files, I can't seem to find anything to take a PKCS7 object and write it to anything (BIO, FILE, char*) in DER format.
So feeling defeated there, I turned to parsing out the header & footer in the PEM file and Base64 decoding the contents. As a check, I did this with Java & BouncyCastle and got exactly what I want.
Here's my code for that. With almost every Base64 decoder I try I turn something like this...
MIIHmwYJKoZIhvcNAQcCoIIHjDCCB4gCAQExCzAJBgUrDgMCGgUAMIIBrQYJKoZI
hvcNAQc ... Lots More stuff
... +8L5ad45D/7ZGJWafaSw==
into...
0\202\233 *\367\367
\240\202\2140\202\21010 +
Here's that code...
string PKCS7String(starting_point);
string PEM_PKCS7_HEADER("-----BEGIN PKCS7-----\n");
string PEM_PKCS7_FOOTER("\n-----END PKCS7-----");
string::size_type pos = 0;
while ( (pos = PKCS7String.find(PEM_PKCS7_HEADER, pos)) != string::npos ) {
PKCS7String.replace( pos, PEM_PKCS7_HEADER.length(), "" );
pos++;
}
pos = 0;
while ( (pos = PKCS7String.find(PEM_PKCS7_FOOTER, pos)) != string::npos ) {
PKCS7String.replace( pos, PEM_PKCS7_FOOTER.length(), "" );
pos++;
}
//Take your pick of decoders, they all do the same thing. Here's just the most recent
auto_ptr< uint8_t > decoded = decode(PKCS7String);
uint8_t* array = decoded.get();
cout << array << endl;
Any thoughts?

A PEM file is just a Base64 encoded version of the DER file with the -----BEGIN PKCS7----- & -----END PKCS7----- header and footer lines.
So not exactly sure what you expect to see after Base64 decoding it...
As a test just:
package a certificate in PKCS#7 PEM format: $ openssl crl2pkcs7 -nocrl -out outfile.pem.p7b -certfile server.crt -outform pem
package the same certificate in PKCS#7 DER format: $ openssl crl2pkcs7 -nocrl -out outfile.der.p7b -certfile server.crt -outform der
Base64 decode the body of the PEM file (outfile.pem.p7b) with the decoder of your choice & compare the binary output with the DER file (outfile.der.p7b)
Now, I'm afraid this may be what you asked for but not what you wanted...

i2d_PKCS7_fp() and i2d_PKCS7_bio() from <openssl/pkcs7.h> will write out a PKCS7 structure in DER format to a file stream or BIO respectively.

Related

OpenSSL - digital signature generated through code does not match signature generated on command line

UPDATE
I didn't realize this question was still getting eyeballs.
I found and fixed the problem some time ago, and it was basically me not understanding how to do things properly. I wasn't using the right file type for the certificate, I was not reading the private key properly (should have been using PEM_read_RSAPrivateKey, not PEM_read_PrivateKey), I was not computing the signature properly, etc.
It just took me more time than it should have to figure it out.
Thanks for everyone who took the time to answer.
ORIGINAL
I'm working on some code to digitally sign some requests, but the signature I am generating from the code is incorrect, and it doesn't jibe with a signature generated from the command line. I suspect the problem is that I am not reading the private key in properly, but I would expect to get some errors back if that were the case, and I'm not.
This is the first time I've worked with openssl, and I'm sure I'm doing things incorrectly, but I don't yet understand enough to ask intelligent questions.
I've created a MessageSigner class to handle the libcrypto chores. The private key and certificate are attributes of the class:
class MessageSigner
{
...
private:
EVP_PKEY *private_key;
X509 *certificate;
};
I initialize these to NULL when an instance is created:
MessageSigner::MessageSigner() :
private_key( NULL ),
certificate( NULL )
{
}
I load the private key as follows:
void MessageSigner::addKeyFile( const std::string& filename, const std::string& passphrase )
{
FILE *fp = ::fopen( keyfile.c_str(), "r" );
if ( !fp )
// throw exception
private_key = PEM_read_PrivateKey( fp, NULL, NULL, passphrase.c_str() );
::fclose( fp );
if ( !private_key )
// throw exception
}
And I am generating the signature as
void MessageSigner::signMessage( const std::vector< unsigned char >& msg, std::vector< unsigned char >& signature )
{
unsigned char *msgbuf = new unsigned char [msg.size()];
std::copy( msg.begin(), msg.end(), msgbuf );
unsigned char *sigbuf;
size_t sigbuf_length;
EVP_MD_CTX *ctx = EVP_MT_CTX_create();
if ( !ctx )
// throw exception
if ( EVP_DigestSignInit( ctx, NULL, EVP_sha256(), NULL, private_key ) != 1)
// throw exception
if ( EVP_DigestSignUpdate( ctx, msgbuf, msg.size() ) != 1 )
// throw exception
if ( EVP_DigestSignFinal( ctx, NULL, &sigbuf_length ) != 1 )
// throw exception
sigbuf = (unsigned char *)OPENSSL_malloc( sizeof *sigbuf * sigbuf_length );
if ( !sigbuf )
// throw exception
if ( EVP_DigestSignFinal( ctx, sigbuf, &sigbuf_length ) != 1 )
// throw exception
std::copy( sigbuf, sigbuf + sigbbuf_length, std::back_inserter( signature ) );
EVP_MD_CTX_destroy( ctx );
OPENSSL_free( sigbuf ); // yes, there's potential for a memory leak, but I'm just trying to get this bastard to work.
}
Again, the signature generated from this code does not jibe with the result of
openssl rsautl -sign -inkey keyfile.pem -keyform PEM -in msg.txt -out signature
I'm convinced the problem is with how I'm loading the private key, but all the examples I've seen so far indicate that this should work.
I've been staring at this and pulling my hair our for two days. Any suggestions, hints, guidance, rude remarks, etc., will be much appreciated.
Thanks.
EDIT
Some examples should illustrate what I'm up against.
Here's the SignedInfo generated for a given message:
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="alws soapenv"/>
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="#TS-5b171864-232b-11e9-846f-00505695541c">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2006/12/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsse alws soapenv"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestValue>eeLn6ak1glbbbWE48q7olsxO0CO/fL85bZ+8hzcjrvE=
</ds:DigestValue>
<ds:DigestMethod Algorithm="https//www.w3.org/2001/04/xmlenc#sha256"/>
</ds:Reference>
</ds:SignedInfo>
The digest value is calculated from the timestamp:
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="TS-5b171864-232b-11e9-846f-00505695541c">
<wsu:Created>2019-01-28T18:34:33Z</wsu:Created>
<wsu:Expires>2019-01-28T18:34:38Z</wsu:Expires>
</wsu:Timestamp>
When I calculate the digest for that timestamp from the command line as so:
$ openssl dgst -sha256 -binary timestamp > timestamp.dgst
I get the same digest value:
$ openssl base64 -in timestamp.dgst
eeLn6ak1glbbbWE48q7olsxO0CO/fL85bZ+8hzcjrvE=
So far so good. That hash is added to SignedInfo, then I take the hash of SignedInfo:
cQaWLGHi8D/c1kXPG9i49xzAupeBuypvMvuMQlzA/wo=
And use it to generate the signature. Now, here's where things go completely off the rails. When I generate the signature using the above code, I get:
HtQ4LkYq4Eao4bMOpV4SBpMxHi2a+0ilxDXS9jIQZWdCC8HCNlpvVU4rWMZG2Zd/
LplCWmUHIaB35FKv6uKjCjJPVDAJT2agyp7FnSKxaBI44Y/YsdvKyxJTAMiAlF8i
dd1MB8ljYsfayrzq5e76kt2cbHlYkT/RM3SvwJtjZiYsNpfcXD0Bi6JhRshHxQ8s
6/errruOe7jUqbKh7UOPJokadCX0OTSSwRgcs+sm7VjnS9MYILaGzFFT3Js9xI6d
TL4B6A/JGIkEqLO+GA1lrokAeIBr9OVUu7OEzaBb7DaiP9Gv1diu0j1sbZ4uT5Cf
CjYJPYU72Xx8F+MKdSJteg==
When I use the command-line tool, I get:
PvfCDqPl86/8USbFU0XR5r1Dhl5JbWd2va3L4W1IW1zw6xdes04F4lYjol6gMKio
jyr8DdmWBquroVlo4vW8kmhr6760qMcpK6mfsZ26ftu7XRC+Z4b9ge6ICOemsGlE
04Yoh9EpECP+ei5yS4E1sbntteiSoQcjotmVcIbPaEG5DIDcd4JKfoCWmsnuZESs
qctIJAQy4YY9HJsVGJ2JG7QashFcEQJabtInFgYeKuxla0ZSXBfOBkwHZT/cSv+k
n/NqPMCyEl4B2LiPBVa36GaTUd6fx0SXnIh0Fm+jw6b6j3EjU0QfMJ/JBAlL+oWZ
fXO/pS5L7W+OWk8Fh//iKA==
When I verify the signature generated on the command line, I get the original hash back:
$ openssl base64 -d -in signature.b64 > signature.reversed
$ openssl rsautl -verify -inkey cert.pem -certin -in signature.reversed > signature.reversed.dgst
$ openssl base64 -in signature.reversed.dgst
cQaWLGHi8D/c1kXPG9i49xzAupeBuypvMvuMQlzA/wo=
When I try to verify the signature generated by my code, I do not get the original hash back:
$ openssl base64 -d -in badsig.b64 > badsig
$ openssl rsautl -verify -inkey cert.pem -certin -in badsig > badsig.dgst
$ openssl base64 -in badsig.dgst
MDEwDQYJYIZIAWUDBAIBBQAEIHEGlixh4vA/3NZFzxvYuPccwLqXgbsqbzL7jEJc
wP8K
What's especially frustrating is that I've since added code to verify the signature after I create it, and that code passes. But it's obviously wrong - the remote service rejects our requests because the hashes don't match, and it doesn't match with what's generated by the openssl command line tool. I've verified that both the command line tool and the library I'm using are the same version (1.0.1).
I've added code to dump the private key to compare against what's in the keyfile, and it matches, so I'm reading the key in correctly.
There appear to be no less than 3 different ways to sign a message using libcrypto routines - EVP_Sign(), EVP_DigestSign(), EVP_PKEY_sign() - and I'm not sure which one is the correct one to use in my case. All three give me bad signatures (meaning, when decrypted against the public key, do not result in the original hash value).
So, again, if anyone can point to anything I'm doing that's obviously wrong, I would appreciate it.
MORE EDIT
Also attempting to verify using dgst as follows, again using the SignedInfo block above:
$ openssl dgst -sha256 -binary -sign fx-realtime.fundsxpress.com.pem -out signature signedinfo
$ openssl dgst -sha256 -binary -verify publickey.pem -signature signature signedinfo
Verified OK
Reversing the signature my code generates:
$ echo -n 'XfgP1A08UTwz3sUHIVvvV+fq1n3act6+lVBZ8ieDtgh28k1r1/M0tm9MntvK+Hm4
> Be+LjguX2gxhZ4PvVcoCBCugDIsrhxplDeB4bYeY2PEedQL6+IZFX+kFrz6o3RQa
> W7sXK7czogxWpdLAmKnhDJOk2BmKFihkRMTjo9D4z/qylZI9nnX29HNdg3uV2BYw
> zHh8GvYO8fy1ugqfFW80na+hLBAtBP6fwTTv10DS2L8n+ixQcnxlKW5pyBOXlR/r
> mZEqwU+A996G0573HkGFeFvXzArlRFg/7mkKoyUHyqyDzkf5eC+vTnpEy1CP75Yc
> lvd7ldSrwREisPnyxu47sg=='> computed_signature.b64
$ openssl base64 -e -in computed_signature.b64 > computed_signature
$ openssl dgst -sha256 -binary -verify publickey.pem -signature computed_signature signedinfo
Verification Failure
The code is fine and the signature generated is fine. What you are missing is that every time you generate a signature it will be different each time. If you run the signatures through the verify they will all work. I don’t known the reason why but I assume there is a security reason why this is done this way.
Its not clear from the details here how the signed data (input) is treated.
the openssll commands will read these as binary (not text) and there may be newline data that is changed if you use text reads (ie on windows 2 chars CR/LF -> transition to 1 CR) before you sign the input.
not signing the same input will create a diff digest (obviously).

Errors decrypting message using OpenSSL RSA functions in C++

I've been struggling with this for a while now. When I run my program, sometimes I see these errors:
bad decrypt
140380701197976:error:0606506D:digital envelope
routines:EVP_DecryptFinal_ex:wrong final block length:evp_enc.c:518:
Sometimes, I see these errors:
RSA operation error
139986632922776:error:0407109F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error:rsa_pk1.c:273:
139986632922776:error:04065072:rsa
routines:RSA_EAY_PRIVATE_DECRYPT:padding check failed:rsa_eay.c:602:
Error reading password from BIO
Error getting password
And sometimes, I don't see any errors at all! I'm running on GalliumOS, a flavor of Ubuntu made for Chrome hardware.
What am I missing? I've looked all over, and I can't find anything that's very relevant. For your reference, I've attached my code below. I think I've narrowed down the error to one of the last two commands, but I'm not positive.
#include <cstdlib>
#include <string>
using namespace std;
int main()
{
string bob_keys, alice_plaintext, alice_encrypted, bob_decrypted;
// ----- USER INPUT -----
// TODO: switch to user input
bob_keys = "bob_keys.pem";
bob_decrypted = "bob_decrypted.txt";
alice_encrypted = "alice_encrypted.txt";
alice_plaintext = "alice_plaintext.txt";
// ----- CONFIDENTIALITY: MESSAGE ENCRYPTION -----
// generate session key
system("openssl rand -base64 64 -out key.bin");
// encrypt message using session key
system(("openssl enc -aes-128-cbc -salt -in " + alice_plaintext
+ " -out alice_plaintext.txt.enc -pass file:./key.bin").c_str());
// encrypt session key using Bob's public key
system(("openssl rsautl -encrypt -inkey " + bob_keys
+ " -pubin -in key.bin -out key.bin.enc").c_str());
// write encrypted message and encrypted session key to file
system(("cat alice_plaintext.txt.enc > " + alice_encrypted).c_str());
system(("echo >> " + alice_encrypted).c_str());
system(("cat key.bin.enc >> " + alice_encrypted).c_str());
// ----- CONFIDENTIALITY: MESSAGE DECRYPTION -----
// get encrypted message and encrypted session key from file (and remove newlines)
system(("head -1 " + alice_encrypted + " > message.bin.enc").c_str());
system("tr -d '\n' < message.bin.enc > temp.bin && mv temp.bin message.bin.enc");
system(("tail -1 " + alice_encrypted + " > key.bin.enc").c_str());
// decrypt the key using Bob's private key
system(("openssl rsautl -decrypt -inkey " + bob_keys
+ " -in key.bin.enc -out key.bin").c_str());
// decrypt the message using the decrypted key
system(("openssl enc -d -aes-128-cbc -in message.bin.enc -out "
+ bob_decrypted + " -pass file:./key.bin").c_str());
return 0;
}
You are using ˋheadˋ and ˋtailˋ on binary files. Before that you'e managed to escape the many traps where you could have interpreted binary as text, but here it finally fails. It is perfectly possible that your ciphertext contains newline characters, and if those are found then your ciphertext will be incorrect. You will either have to use just binary or you should base 64 encode your ciphertext.
Note that it is great that you manage to circumvent the use of C/C++ the way that you do, but the last laugh is on you. You are using passwords instead of keys and the result will not be identical to just using an AES key. Be smart and use the C library instead. Or, if you like C++ more, use Crypto++ or Bothan. And learn how to actually program instead of hacking your way out of evertything. Because you - in the end - will be the person who suffers the most from your own hacking.

How to create AWS signed cookies with elixir?

I've been trying to set up CloudFront with signed cookies using elixir. From what I learned the signature must use sha1 encoding. According to AWS in the terminal that would look like this:
cat policy.json | openssl sha1 -sign CloudFront_Key.pem |base64 |tr '+=/' '-_~'
I can't figure out how to do that in elixir. I've been looking into this dependency and tried using :sha1 instead in it's sign method. I did read through here and did see sha, not sha1. Is that how erlang calls it? (I have close to no knowledge about erlang). Tried :sha but I do not get the same key as in the terminal and it does not seem to work. I also noticed that base64 usually adds a few extra chars at the end than the terminal. Not sure what to do.. Resort to try using System.cmd ?
This is the AWS doc I'm following: create & verify signed cookies and create signature for signed cookie
You can use :public_key.sign/3 for this.
For reference,
$ cat pk.pem
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAknEbD26dg4w2JlaN2pmtz7sEBSEjFcfsgcUuV100L60Lz36s
EDySjiTMbhfhKRa46gBvGSEzb8G1l4iD1O5M5mfzEVg/shSOgwVfyvq5XShJBaz6
PcKJ4hsLh/aUIkvnfYWg5zdcPj5FBYT0I+jLnWvNevsolfGwN4rY5S/PuBCUwtVC
m1L3p5bVHvR33tjI4qenHkuRJUnJh4TMLCOTCGHdmZ+yyWJ2rLcYq7yciI0WlRh5
cCI1eYEehUsbcifOyazKNOAmO/9DzXZlo9S0TSYjxrG626mt+j9yD473t/FSfUeg
6Hn67wfGBWAk0HezJ3QGRl9jBAeYhSLbaX7DHQIDAQABAoIBAHo+Blu8d6oe+fjI
2cM389pq/7EUd0gwSmINamCtQenmZux/jjxDhAc5+piQQHlfKV7Um+j7SQeqSN7E
q1+syO6wqTu6UflipZADhXJYFzIHdeVR/tZdNWJUNyz5DbEPcZ7bVHSORucCbfVs
hawQISA4pB9b1wZL6VCEDAhM//ViRgBA3k6tBu5jNi+I1WXE9nrat4DSDV3uklfo
f5KfHLhbHgrl7R7jnAyWBya6O6Pw0r/3FeWrjo6W0wht5BprX4HSgJvpWtqjhodA
iucWjmWV7Gt++HBRX0NEQozajQ7fdfnCOpSD2g8QzVw/5whRBidYngJWLBNt1YQV
UMi7toECgYEAzw4i+m0PnqiExMOSLsvPcEQu0wScOe3vwXTBOcfOS0Uds0kWzZP2
BHQL7IATtN+OAMdtFi1xyN+auikTsZpy1hezUtamfeo75btFhgSrtRXQki5o3pX8
HgDh+31+Ox1RM/Qhm4DSPQ/YImw+HQD+lK9sw3yNZlmPmXwyCARxkCECgYEAtQ75
M5HcBG+zPbPHc4mrOgtQzaUTEs1onu/zDCuFZwYtyBB0RKSLLmacgh/Qcdu13Lli
fdTqego5asKztQfsB88aqkYtdY51dnm+H2sxQ2ef+1D2gbV+sdrsM7gUuVvxBMwv
itFizQ1rgZn/Vee4zfFXEoURjkKfTKT27Hn7A30CgYEAx4ugOiiRPR67lcXFREQ3
jsKnPcbbqRieT5rt/XmKXxAlJ3vw9f76wh/0veBRHae1exq3DwCNAEI/I9oimK94
rMv6joM/wWnUf/qTbi1iLgrwD3Gar6lsaJ4BLBYtaVs/vwowuWTVOPPkIIig8+LZ
dwH5mAyZWWJG+myu6vsdVwECgYAJqT/g4ZKU5gTxcOteneT2Fu572qgW48EGYhVc
++GFas38k+wwUXtfwXfudZYgzTF6EqZPwpG0a2E+8h62tTKCBCoPFemNEUnxRXPA
p26cgyYFOf+9UhrtkJnz9Imejmpg8ChFRwD3ohSveLEoO1IgIxWbVmBmb+WiKFdI
rQWY3QKBgA6Gw/8KB7LxqR3N9/1xgW5b02WVhG7gvFNlSYZMmdd8H3w8MdXCdk22
kJBfrIfkDS6nbF6w4+8q309NPwOqdkt2QNPq6Il1ugWuxFMiNEGHjjT21PrsgEJS
q6K6c+znrnI7O/wijdvEC2n+q+S9h8yv1bT6QfC1Vx88SC9GCBuq
-----END RSA PRIVATE KEY-----
$ cat policy.json
{"Statement":[{"Resource":"https://mycloudfront.net/a.png","​Condition":{"DateLes​sThan":{"AWS:EpochTi​me":1512086400}}}]}
First, read the key:
iex(1)> key = File.read!("pk.pem") |> :public_key.pem_decode |> hd |> :public_key.pem_entry_decode
...
Then sign the data using :public_key.sign/3:
iex(2)> File.read!("policy.json") |> :public_key.sign(:sha, key) |> Base.encode64
"QjLmx3LASRb1zt9eW/EMywGMXB1SwX/0JrTnLOFulYjcRJ1dpacUZBB/AYI1zwaXPEQTgQ8crNDFgje6fqbLKoNwgcpE9mOK/RdDKi963ztJnD6EmtM60YbROSpjQ/LDupEYgipPNZbjCnRCJcqDX43BadbVR75G3B5mFmAwtRSPdslJ5irVnt9PjoDMdi9DYe1wGhgQkoym1tiKEyaTrH5lyrw+KPdAi1tpzuZ60ZEcQFJJbKqYYdA0SslbUFL71mdLLkQ9xz95JPNpsSY3ZJyJsKpRGFJuaL1aMsdNLxlLD91PpNW15FitBpBnAwuiiEfPrwU14zIxsfFszaM6KA=="
The output is identical to openssl:
$ cat policy.json | openssl sha1 -sign pk.pem | base64
QjLmx3LASRb1zt9eW/EMywGMXB1SwX/0JrTnLOFulYjcRJ1dpacUZBB/AYI1zwaXPEQTgQ8crNDFgje6fqbLKoNwgcpE9mOK/RdDKi963ztJnD6EmtM60YbROSpjQ/LDupEYgipPNZbjCnRCJcqDX43BadbVR75G3B5mFmAwtRSPdslJ5irVnt9PjoDMdi9DYe1wGhgQkoym1tiKEyaTrH5lyrw+KPdAi1tpzuZ60ZEcQFJJbKqYYdA0SslbUFL71mdLLkQ9xz95JPNpsSY3ZJyJsKpRGFJuaL1aMsdNLxlLD91PpNW15FitBpBnAwuiiEfPrwU14zIxsfFszaM6KA==

SSL_CTX_use_PrivateKey_file() failed

I'm writing a client application on Windows that establishes an SSL connection to a server, and the server requests client certificate for authentication. The server provides me a .pfx file, then I use openssl command line tool to get the certificate and the private key like this:
openssl pkcs12 -in filename.pfx -clcerts -nokeys -out cert.pem
openssl pkcs12 -in filename.pfx -nocerts -out key.pem
after that, I try to load the certificate and the private key with functions from openssl as below, but SSL_CTX_use_PrivateKey_file() always failed, the error message is "error:0906D06C:PEM routines:PEM_read_bio:no start line", I can't figure it out why, can anyone give me some enlightenment?
Here is the code.
#include "openssl/ssl.h"
#include "openssl/err.h"
#include <stdio.h>
#include <string>
int InitClientCtx()
{
OpenSSL_add_ssl_algorithms();
SSL_CTX* m_pClientCtx;
m_pClientCtx = SSL_CTX_new(SSLv23_method());
if(!m_pClientCtx)
{
return -1;
}
::SSL_CTX_set_options(m_pClientCtx, SSL_OP_ALL); //for well-known bugs
int nRet = 0;
std::string sCertFilePath = "C:\\cert.pem";
nRet = SSL_CTX_use_certificate_chain_file(m_pClientCtx, sCertFilePath.c_str());
std::string sKeyPassWord = "123456";
SSL_CTX_set_default_passwd_cb_userdata(m_pClientCtx, (void*)(sKeyPassWord.c_str()));
std::string sKeyFilePath = "C:\\key.pem";
// this method returned 0, which means it failed.
nRet = SSL_CTX_use_PrivateKey_file(m_pClientCtx, sKeyFilePath.c_str(), SSL_FILETYPE_PEM);
SSL_load_error_strings();
unsigned long n = ERR_get_error();
char buf[1024];
printf("%s\n", ERR_error_string(n, buf));
nRet = SSL_CTX_check_private_key(m_pClientCtx);
if (nRet <= 0)
{
return -1;
}
/*std::string sCACertFilePath;
nRet = SSL_CTX_load_verify_locations(m_pClientCtx, sCACertFilePath.c_str(), NULL);*/
return 0;
}
int main()
{
InitClientCtx();
return 0;
};
In my case the error was because the PEM file did not contain both a key and a certificate.
Make sure your file contains both sections:
-----BEGIN PRIVATE KEY----- jhajk838383jks.....
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE----- yoe55wjcxnshre.....
-----END CERTIFICATE KEY-----
I catenated .key and .crt files I already had in my Apache configuration to make a .pem file.
The 'no start line' error is certainly misleading as you can have a perfectly good "BEGIN" line in your PEM file and still get the error.
The error error:0906D06C:PEM routines:PEM_read_bio:no start line is because in both the cert.pem as well as key.pem, don't start off with -----BEGIN CERTIFICATE----- and -----BEGIN ENCRYPTED PRIVATE KEY-----.
If you open up your cert.pem and key.pem in a text editor and yank off whatever is there before the BEGIN markers, you should be good.
When you create a certificate and a key pair using Certificate Signing Request, you won't get this additional information.
I've solved this problem myself. I generated the key.pem using OpenSSL for Windows, when the CMD prompts me to type in the pass phrase, I just typed a Enter since I needn't a pass phrase, but the key.pem was invalid(neither BEGIN nor END markers). When I generate the private key in Linux, the terminal prompts I must type a pass phrase and I do. Then I remove the key pass phrase using this command:
openssl rsa -in key.pem -out newkey.pem
After that, I open the key.pem in a text editor, it starts off with -----BEGIN RSA PRIVATE KEY----- and end up with -----END RSA PRIVATE KEY-----. And SSL_CTX_use_PrivateKey_file() just works fine!
Under openssl directory demo/openssl there is a client example.
Change:
meth = SSLv2_client_method();
in your case to:
meth = SSLv23_client_method();
Try this example and see where it fails.
Are you sure that the key password is 123456 ?
I had the same problem in NGINX while installing the SSL certificate and I resolved using the following step:
Go to the folder where you have your certificate and pem files
Open your ca-bundle.crt file and copy everything, sudo nano your fileName select all and copy. the file looks like
-----BEGIN CERTIFICATE----- MIIE0DCCA7igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
..........
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE----- MIIEfTCCA2WgAwIBAgIDG+cVMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNVBAYTAlVT
MSEwHwYDVQQKExhUaGUgR28gRGFkZHkgR3JvdXAsIEluYy4xMTAvBgNVBAsTKEdv
..........
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE----- MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
..........
-----END CERTIFICATE-----
Open your pem file using sudo nano your fileName.pem
You will see some code like
-----BEGIN CERTIFICATE----- MIIGLzCCBRegAwIBAgIIWI4ndTQi3MwwDQYJKoZIhvcNAQELBQAwgbQxCzAJBgNV
MAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA4GA1UdDwEB/wQEAwIF
oDA4BgNVHR8EMTAvMC2gK6AphidodHRwOi8vY3JsLmdvZGFkZHkuY29tL2dkaWcy
-----END CERTIFICATE-----
After -----END CERTIFICATE----- paste all the code copied from ca-bundle.crt file and save it
Restart your Nginx server using following command sudo service nginx restart
The above steps worked for me in my Nginx and SSL configuration

How I can create a DomainKey-Signature and DKIM-Signature by C++(or VC++) help of OpenSSL, Is it possible?

I have a public/private key pair, I submitted my public key into the server DNS TXT record(in SPF record), and I have a private key file.. I want to create DKIM-Signature and DomainKey-Signature for email, so I want to encrypt text message by private key to send it to email servers.. I want to use SHA-256 as the cryptographic hash and RSA as the public key encryption scheme.. I want to use OpenSSL and C++ to make DKIM-Signature and DomainKey-Signature for email..
This is my Public key in server TXT record :
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnHVS+q65lvG2xocltTYgPGt9F
aysGZTrcOwHedo8tX1dyPrcx2I8x/cvB9nmfdAkt65aGFAlBZrofbPCr2Mq4wDdv
IZ31KSuyMQI4T68ylWNT89GewQF6AOkpY1E2bW+oDXc+MpbtpYXY1rUJAS/Abt5v
Xi7gwKN9FSJ3mm9bjQIDAQAB
-----END PUBLIC KEY-----
This is my Privet key :
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCnHVS+q65lvG2xocltTYgPGt9FaysGZTrcOwHedo8tX1dyPrcx
2I8x/cvB9nmfdAkt65aGFAlBZrofbPCr2Mq4wDdvIZ31KSuyMQI4T68ylWNT89Ge
wQF6AOkpY1E2bW+oDXc+MpbtpYXY1rUJAS/Abt5vXi7gwKN9FSJ3mm9bjQIDAQAB
AoGACXuxnmxRpjZOJ0FeE9TNfsXwm5jcRS2jbHHwxjYGI/YAGVyTusFmRtj3Iheh
iTnld3SiAxPJ/qscrsHY2nef8Up60V7RF8bs+sbICnHbdz8ZoKxN9dEFCwJkIl55
dv0GHjox6UgWUUkUAiHCM2EgG5eOOQ8PsuXY8LpPWLvfuyUCQQDbkFG9y/Q/3lmb
CdYfBpsP3qvma+fdgCw9lRXTowhu0rKen/CC3rFkHMeHfSW9GHuR8QbPYdVA6d9H
Y7KxYa7LAkEAwtjTEo/zAVexH/+YgiqL6w89BHlTAmwIEkXpqtAnE86kDDCekYdE
fRIdGK1zHDOUddMAhwoJQjJzy/NJreQ8BwJBAMoJ6U3vKZjD8Ex8Jq5yE6nsyt3D
mZ73XL5mO6l9sjrYY0kX/+dNKIro+KoyfNGef8bxtcSLUALlsnIsybf0HTUCQQCR
DD4cvGJHJpOp4WkTxT6Bjsd6lCKyU9+yUq8/RFNC0HqYxHzWkx7uCFT2sPBXFyK2
j4v9+v+ncs13DzZTJ+tzAkBI6UWHtsn839nrAT32M8PEPF/TuDmqebMhFOaTl4an
W0Jr8w8iGk2gvQS2cXEPNh4XT9AgcTKDQkhui4RgxK4F
-----END RSA PRIVATE KEY-----
I tried some code help of OpenSSL, but I am unable to create DKIM-Signature and DomainKey-Signature for email..
This is code :
#include <stdio.h>
#include <stdlib.h>
#include <openssl/bio.h>
#include <openssl/pkcs7.h>
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
/ * Arguments: to be signed email, PKCS#12 certificate with private key
*/
int main(int argc, char** argv) {
if (argc < 3) exit(1);
OpenSSL_add_all_algorithms();
BIO *data = BIO_new_file(argv[1], "rb");
if (!data) exit(1);
BIO *p12file = BIO_new_file(argv[2], "rb");
if (!p12file) exit(1);
PKCS12 *p12 = d2i_PKCS12_bio(p12file, NULL);
if (!p12) exit(1);
X509 *signcert;
EVP_PKEY *pkey;
STACK_OF(X509) *ca;
int ret = PKCS12_parse(p12, "", &pkey, &signcert, &ca);
if (!ret) exit(1);
PKCS7 *p7 = PKCS7_sign(signcert, pkey, ca, data, 0);
if (!p7) exit(1);
BIO *out = BIO_new_file("email.eml", "wb");
if (!out) exit(1);
ret = SMIME_write_PKCS7(out, p7, data, 0);
if (!ret) exit(1);
BIO_free(data);
BIO_free(p12file);
PKCS12_free(p12);
PKCS7_free(p7);
return (EXIT_SUCCESS);
}
Any one can help me how I will use my Privet key with text to create this "DKIM-Signature and DomainKey-Signature"????
This is not working : BIO *p12file = BIO_new_file(argv[2], "rb");
I dont have "PKCS#12 certificate", so how I will use only my Privet key with email's text message to create "DKIM-Signature and DomainKey-Signature" ????
Your private and public key are in PEM format. You can use OpenSSL to convert it to PKCS#12.
openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt
Having said this, this will not help you. The source code that you presented is for signing e-mails using S/MIME. This uses personal certificates that are signed by an official Certification Authority, not using self-signed certificates. This also provides for the ability to encrypt e-mails, provided the recipient has your public key in their key chain.
What you are looking for is DKIM and DomainKeys. This is signed and then hashed using SHA256 and SHA1, respectively. It is computationally less expensive than S/MIME, but requires canonicalization (Google it) of the header and body before hashes can be calculated.
I recommend you have a look at PHPMailer projet on Github. Although this is PHP source code, it should be readily adaptable to work in C. It only covers the DKIM implementation, but DKIM supersedes DomainKeys, so I wouldn't bother implementing DomainKeys.