M2MQTT Argument Exception: unsupported HMAC - amazon-web-services

I am trying to setup an MQTT client in Unity (C#) which would communicate with AWS IoT Core. Here are the steps I've taken:
1)Generated a .pfx file using the certificate files from AWS:
openssl pkcs12 -export -in certificate.pem.crt -inkey private.pem.key -out certificate.cert.pfx -certfile AmazonRootCA1.pem
2)Placed the .pem and .pfx file in the Resources folder of my project. The path for the device certification in the code would be: deviceCertPath="Assets/Resources/certificate.cert.pfx"
3)Added M2Mqtt.net dll to Plugins folder (Downloaded from NuGet)
4)My code is as follows:
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using UnityEngine;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
// Variables
private void Start()
{
caCert = X509Certificate2.CreateFromCertFile(caCertPath);
deviceCert = new X509Certificate2(deviceCertPath);
client = new MqttClient(broker, port, true, caCert, deviceCert, MqttSslProtocols.TLSv1_2);
client.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
client.MqttMsgSubscribed += Client_MqttMsgSubscribed;
//Connect
client.Connect(clientId);
Debug.Log($"Connected to AWS IoT with client id: {clientId}.");
}
// Message Methods
When I try to run the code I get an error that happens when trying to create the deviceCert (line 16 in code snippet corresponds to line 32 mentioned in the error log):
ArgumentException: unsupported HMAC
Mono.Security.X509.PKCS12.Decode (System.Byte[] data) (at <b2e147cb24644c1580a142ea3d6c249e>:0)
Mono.Security.X509.PKCS12..ctor (System.Byte[] data, System.String password) (at <b2e147cb24644c1580a142ea3d6c249e>:0)
System.Security.Cryptography.X509Certificates.X509Certificate2ImplMono.ImportPkcs12 (System.Byte[] rawData, System.String password) (at <6d7c4c8dd3624dc596686fb7270ae1e6>:0)
System.Security.Cryptography.X509Certificates.X509Certificate2ImplMono.ImportPkcs12 (System.Byte[] rawData, Microsoft.Win32.SafeHandles.SafePasswordHandle password) (at <6d7c4c8dd3624dc596686fb7270ae1e6>:0)
System.Security.Cryptography.X509Certificates.X509Certificate2ImplMono..ctor (System.Byte[] rawData, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) (at <6d7c4c8dd3624dc596686fb7270ae1e6>:0)
Mono.X509PalImpl.ImportFallback (System.Byte[] data, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) (at <6d7c4c8dd3624dc596686fb7270ae1e6>:0)
Mono.X509PalImplMono.Import (System.Byte[] data, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) (at <6d7c4c8dd3624dc596686fb7270ae1e6>:0)
Mono.SystemCertificateProvider.Import (System.Byte[] data, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags, Mono.CertificateImportFlags importFlags) (at <6d7c4c8dd3624dc596686fb7270ae1e6>:0)
Mono.SystemCertificateProvider.Mono.ISystemCertificateProvider.Import (System.Byte[] data, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags, Mono.CertificateImportFlags importFlags) (at <6d7c4c8dd3624dc596686fb7270ae1e6>:0)
System.Security.Cryptography.X509Certificates.X509Helper.Import (System.Byte[] rawData, Microsoft.Win32.SafeHandles.SafePasswordHandle password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) (at <6073cf49ed704e958b8a66d540dea948>:0)
System.Security.Cryptography.X509Certificates.X509Certificate..ctor (System.String fileName, System.String password, System.Security.Cryptography.X509Certificates.X509KeyStorageFlags keyStorageFlags) (at <6073cf49ed704e958b8a66d540dea948>:0)
System.Security.Cryptography.X509Certificates.X509Certificate..ctor (System.String fileName) (at <6073cf49ed704e958b8a66d540dea948>:0)
System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromSignedFile (System.String filename) (at <6073cf49ed704e958b8a66d540dea948>:0)
MQTT.Start () (at Assets/Circulate/Scripts/Networking/MQTT/MQTT.cs:32)
The current .pfx file does not have a password, although I've attempted to use one with a password and got the same error. When I check the details of the certificates, both have a signature hash algorithm of sha256. I'm not sure why I am receiving this error and I haven't been able to find much information regarding unsupported HMAC. Any help is appreciated, thank you!

Your exception, based on the source is that the PFX MAC algorithm ID is not 1.3.14.3.2.26 (SHA-1).
This seems surprising, since OpenSSL still defaults to HMAC-SHA1 for the PFX MAC, but maybe your build of OpenSSL has been modified. You should be able to force it to use HMAC-SHA1 by adding -macalg sha1 onto your export command (e.g. openssl pkcs12 -export -in certificate.pem.crt -inkey private.pem.key -out certificate.cert.pfx -certfile AmazonRootCA1.pem -macalg sha1).
You can verify the MAC algorithm ID by openssl asn1parse, such as
$ openssl asn1parse -inform der -i -in test.pfx
0:d=0 hl=4 l=1716 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :03
7:d=1 hl=4 l=1658 cons: SEQUENCE
11:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-data
22:d=2 hl=4 l=1643 cons: cont [ 0 ]
26:d=3 hl=4 l=1639 prim: OCTET STRING [HEX DUMP]:3082066...<snip />
1669:d=1 hl=2 l= 49 cons: SEQUENCE
1671:d=2 hl=2 l= 33 cons: SEQUENCE
1673:d=3 hl=2 l= 9 cons: SEQUENCE
1675:d=4 hl=2 l= 5 prim: OBJECT :sha1
1682:d=4 hl=2 l= 0 prim: NULL
1684:d=3 hl=2 l= 20 prim: OCTET STRING [HEX DUMP]:9E2270B998C4A69898F29634EC0F4823E47879A0
1706:d=2 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:E540B4FDBC03B6AF
1716:d=2 hl=2 l= 2 prim: INTEGER :0800
The OBJECT :sha1 5 lines from the bottom is the MAC algorithm identifier. (The particular offset for that line (e.g. 1675) will depend on your particular PFX, but that line will always be 5 from the bottom)

Related

How can I verify a pem_cert and signiture (RSA-SHA1)?

I'm trying to check if a signature is valid, in Node.js I would use
https://nodejs.org/api/crypto.html#crypto_verify_verify_object_signature_signatureencoding
I can't seem to find anything for crystal that provides this functionality?
This is currently not implemented in the standard library, but we can find a shard that does it: openssl_ext. This shards closely resembles the Ruby API, so let's get going!
First we need a key:
$ openssl genpkey -algorithm rsa -out key.pem
$ openssl rsa -in key.pem -pubout -out pub.pem
Then we need to create a shard and add the dependency:
$ shards init
$ $EDITOR shards.yml
dependencies:
openssl_ext:
github: randomstate/openssl_ext
$ shards
A small program to sign something:
$ $EDITOR sign.cr
require "openssl_ext"
require "base64"
private_key = OpenSSL::RSA.new File.read(ARGV[0])
digest = OpenSSL::Digest.new("SHA1")
data = ARGV[1]
signature = private_key.sign digest, data
puts Base64.encode signature
$ crystal build sign.cr
And a small program to verify a signature:
$ $EDITOR verify.cr
require "openssl_ext"
require "base64"
public_key = OpenSSL::RSA.new File.read(ARGV[0]), is_private: false
digest = OpenSSL::Digest.new("SHA1")
data = ARGV[1]
signature = Base64.decode STDIN.gets_to_end
puts public_key.verify(digest, signature, data) ? "Valid" : "Invalid"
$ crystal build verify.cr
Let's test our work:
$ ./sign key.pem Hello | ./verify pub.pem Hello
Valid
$ ./sign key.pem Hello | ./verify pub.pem Bye
Invalid

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.

OpenSSL: server cannot verify client certificate

I am working with a legacy OpenSSL wrapper, which I intend to extend with client certificates. This wrapper contains both server and client parts, but the issue is related only to server handshake part. The problem I am facing is that the server cannot get the certificate from client.
Here are the commands which I used to generate a pair of certificates for client and server:
1) Generate Certificate Authority:
openssl req -out ca.pem -new -x509
2) Generate server certificate:
openssl genrsa -out server.key 1024
openssl req -key server.key -new -out server.req
echo "00" > file.srl
openssl x509 -req -in server.req -CA ca.pem -CAkey privkey.pem -CAserial file.srl -out server.pem
3) Generate client certificate:
openssl genrsa -out client.key 1024
openssl req -key client.key -new -out client.req
openssl x509 -req -in client.req -CA ca.pem -CAkey privkey.pem -CAserial file.srl -out client.pem
All of the certificates are placed in one folder
When I am trying to connect to server implemented by Wrapper using s_client, I got the following output:
$ openssl s_client -connect localhost:40912 -CAfile ca.pem -cert client.pem -key client.key -state
CONNECTED(00000003)
SSL_connect:before/connect initialization
SSL_connect:unknown state
SSL_connect:SSLv3 read server hello A
depth=1 C = RO, ST = Rootville, L = Rootbirgem, O = CA Limited, OU = Issuer, CN = Autorida, emailAddress = ca#ca.mil
verify return:1
depth=0 C = SR, ST = Serverstan, L = Serversaintsburg, O = Servers advanced productions, OU = Acceptors factory, CN = Servstian, emailAddress = server#internet.org
verify return:1
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server certificate request A
SSL_connect:SSLv3 read server done A
SSL_connect:SSLv3 write client certificate A
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write certificate verify A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL3 alert read:fatal:unknown CA
SSL_connect:failed in SSLv3 read server session ticket A
3073885884:error:14094418:SSL routines:SSL3_READ_BYTES:tlsv1 alert unknown ca:s3_pkt.c:1262:SSL alert number 48
3073885884:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177:
---
Certificate chain
0 s:/C=SR/ST=Serverstan/L=Serversaintsburg/O=Servers advanced productions/OU=Acceptors factory/CN=Servstian/emailAddress=server#internet.org
i:/C=RO/ST=Rootville/L=Rootbirgem/O=CA Limited/OU=Issuer/CN=Autorida/emailAddress=ca#ca.mil
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDMzCCAhsCAQIwDQYJKoZIhvcNAQELBQAwgYkxCzAJBgNVBAYTAlJPMRIwEAYD
VQQIDAlSb290dmlsbGUxEzARBgNVBAcMClJvb3RiaXJnZW0xEzARBgNVBAoMCkNB
IExpbWl0ZWQxDzANBgNVBAsMBklzc3VlcjERMA8GA1UEAwwIQXV0b3JpZGExGDAW
BgkqhkiG9w0BCQEWCWNhQGNhLm1pbDAeFw0xNTEwMjcxNTA3MjlaFw0xNTExMjYx
NTA3MjlaMIG4MQswCQYDVQQGEwJTUjETMBEGA1UECAwKU2VydmVyc3RhbjEZMBcG
A1UEBwwQU2VydmVyc2FpbnRzYnVyZzElMCMGA1UECgwcU2VydmVycyBhZHZhbmNl
ZCBwcm9kdWN0aW9uczEaMBgGA1UECwwRQWNjZXB0b3JzIGZhY3RvcnkxEjAQBgNV
BAMMCVNlcnZzdGlhbjEiMCAGCSqGSIb3DQEJARYTc2VydmVyQGludGVybmV0Lm9y
ZzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy6j9ZHv/xCOqrRUXbWTE2Xht
PRPf/70Rytri4McCyjT6AO/GrDlAfD815yZPqTssRWy13sJGczoM4CPOffR7ufQR
4oMfugbX0W6kqlN7K1VpPlqtm8fb2jei5K0+tXOb7UHox13b69y0kVvArfhcUqmE
SGllcB++/Se2IIdcCOECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA5Eot8mv6QXvG
jd8oeX/JmJzzPSOn6kGMRsyR06OnF4tALHo5EdHaTF4EBgylVuAIp7dBRrdHzU4y
XnsY+X8lPlFEUFsSEuheJmISJgKZ8GtlbjHkV2cgXFSsFeiiI6Gjzlj2aOzYQQHD
DtpOL4InjHgeDTv7JxtMZXRaQqSvIejVXki0d+FOP42H3GyqleYABDtPl7o3gOmh
b8I7aZaOj/qxR8J//nGjXmodGHmL+RKK3vJ3thgD5UWREdE87qygJ2sZEUvXsnhG
CTulJEdWShGw27xuYw26wCt2yo0VXLcYsMgJLOjUAEdW3k+TowTi4XmZN4MaudAp
KFMlb2y7jg==
-----END CERTIFICATE-----
subject=/C=SR/ST=Serverstan/L=Serversaintsburg/O=Servers advanced productions/OU=Acceptors factory/CN=Servstian/emailAddress=server#internet.org
issuer=/C=RO/ST=Rootville/L=Rootbirgem/O=CA Limited/OU=Issuer/CN=Autorida/emailAddress=ca#ca.mil
---
Acceptable client certificate CA names
/C=RO/ST=Rootville/L=Rootbirgem/O=CA Limited/OU=Issuer/CN=Autorida/emailAddress=ca#ca.mil
---
SSL handshake has read 1101 bytes and written 2149 bytes
---
New, TLSv1/SSLv3, Cipher is AES256-GCM-SHA384
Server public key is 1024 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : AES256-GCM-SHA384
Session-ID:
Session-ID-ctx:
Master-Key: 4CA603347F386EB0A723BCF1F07040E0C47FAD52AE9CBCC5DCB3D329D69C8E3FB31E994DC24FBA9E8312A476A8F08905
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
Start Time: 1445977139
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
At the same time on server part SSL_accept returns -1, SSL_get_error then returns SSL_ERROR_SSL and ERR_print_errors_fp gives me a message
3080665920:error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned:s3_srvr.c:3279:
I don't have any experience with cryptography or OpenSSL, so all of these errors seems really confusing for me. OpenSSL's documentation doesn't make things clear at all.
As far as I can see from s_client log, the problems is with certificates themselves, although I don't understand what exactly goes wrong. At the same time, I can successfully use these certificates with s_server application, so this is reproducible only with my implementation of server. It seems that my implementation is incomplete in some way, but I don't see a way how I can resolve the situation from here, most of the documents I found on the web avoid client certificates topic at all, and all of the error messages are cryptic.
Here's a a simplified code of the server (with omitted error checking, logging, timeouts, etc):
ctx_ = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_use_certificate_file(ctx_, config.cert_file.c_str(), SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(ctx_, config.private_key.c_str(), SSL_FILETYPE_PEM);
if( !SSL_CTX_check_private_key(ctx_) )
{ /* never happens */ }
ssl_ = SSL_new(ctx_);
STACK_OF(X509_NAME)* list;
list = SSL_load_client_CA_file(config.client_ca_file.c_str());
SSL_set_client_CA_list(ssl_, list);
SSL_set_verify(ssl_, SSL_VERIFY_PEER, 0);
int flags = fcntl(fd_, F_GETFL, 0);
flags |= O_NONBLOCK;
if( fcntl(fd_, F_SETFL, flags) < 0 )
SSL_set_fd(ssl_, fd_);
while( /* not timeout */ )
{
int res = SSL_accept(ssl_);
if( res > 0) {break;}
ec = WaitAfterError(res, /* timeout */);
if( !ec.isSucceeded() )
{
return; // Returns here
}
}
Wait after error have the following implementation:
ErrorCode OpenSslWrapper::WaitAfterError( int res, long* ptimeout )
{
ErrorCode ec;
int ret = SSL_get_error(ssl_, res);
switch( ret )
{
case SSL_ERROR_WANT_READ:
WaitForFd(fd_, Consts::k_WaitRead, ptimeout);
return ec;
case SSL_ERROR_WANT_WRITE:
case SSL_ERROR_WANT_CONNECT:
case SSL_ERROR_WANT_ACCEPT:
WaitForFd(fd_, Consts::k_WaitWrite, ptimeout);
return ec;
case SSL_ERROR_SYSCALL:
if( !errno ) {return ec;}
case SSL_ERROR_SSL:
// Got message here:
ERR_print_errors_fp(stderr);
ec = ErrorCode::Fail;
return ec;
default:
ec = ERR_reason_error_string(ret);
return ec;
}
}
WaitForFd is just a simple wrapper over select.
The documentation for SSL_CTX_set_client_CA_list() says:
The CAs listed do not become trusted (list only contains the names, not the complete certificates); use SSL_CTX_load_verify_locations to additionally load them for verification.

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

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.