How to create ocsp request using openssl in c++? - c++

I am trying to send a ocsp request to an ocsp server using C++, but I can't find anything to prepare the request. In the documentation I found the following functions
long SSL_get_tlsext_status_ocsp_resp(ssl, unsigned char **resp);
long SSL_set_tlsext_status_ocsp_resp(ssl, unsigned char *resp, int len);
How can I add the certificate and set the nonce for the request?

Want you are trying to do is generate C++ code for the openssl OCSP command:
openssl ocsp -issuer issuer.pem -cert alice.pem -cert bob.pem -reqout
ocspreq.der
The main OPENSSL API's you need are:
PEM_read_bio_X509 - reading certificates
OCSP_REQUEST_new - generating the OCSP request
OCSP_request_add0_id - adding certificate to request
i2d_OCSP_REQUEST_bio - write request out in DER format
The API's used may vary depending on certificate formats you wish to read in and write out.
An example of the above openssl command turned in simple C++ code is:
template<typename T, typename D>
std::unique_ptr<T, D> make_handle(T* handle, D deleter)
{
return std::unique_ptr<T, D>{handle, deleter};
}
bool generate_ocsp_request()
{
// load issuer certificate
auto file = make_handle(BIO_new_file("issuer.pem", "r"), BIO_free);
if(!file) return false;
auto const issuer = make_handle(PEM_read_bio_X509(file.get(), nullptr, nullptr, nullptr), X509_free);
if(!issuer) return false;
// setup OCSP request
auto const request = make_handle(OCSP_REQUEST_new(), OCSP_REQUEST_free);
if(!request) return false;
auto const cert_id_md = EVP_sha1();
// add alice certificate to OCSP request
file = make_handle(BIO_new_file("alice.pem", "r"), BIO_free);
if(!file) return false;
auto cert = PEM_read_bio_X509(file.get(), nullptr, nullptr, nullptr);
auto id = OCSP_cert_to_id(cert_id_md, cert, issuer.get());
if (id == nullptr) return false;
if (!OCSP_request_add0_id(request.get(), id)) return false;
// add bob certificate to OCSP request
file = make_handle(BIO_new_file("bob.pem", "r"), BIO_free);
if(!file) return false;
cert = PEM_read_bio_X509(file.get(), nullptr, nullptr, nullptr);
id = OCSP_cert_to_id(cert_id_md, cert, issuer.get());
if (id == nullptr) return false;
if (!OCSP_request_add0_id(request.get(), id)) return false;
// write the request out in DER format
file = make_handle(BIO_new_file("ocspreq.der", "wb"), BIO_free);
if(!file) return false;
// the below doesn't compile in C++ :(
// return i2d_OCSP_REQUEST_bio(file.get(), request.get()) != 0;
// go around the macro's that cause the problem in C++ because it will not automatically convert void* to unsigned char* like in C
return ASN1_i2d_bio(reinterpret_cast<i2d_of_void *>(i2d_OCSP_REQUEST), file.get(), reinterpret_cast<unsigned char*>(request.get())) != 0;
}
UPDATE:
Reading the response gets a little involved.
The main apis used to process a response would be:
d2i_OCSP_RESPONSE_bio - loading response in DER format
OCSP_response_get1_basic - extract response information
OCSP_check_nonce - check the response is for the request (optional)
OCSP_basic_verify - verify the response is valid (optional)
There is no "text" that can be extracted, you need to extract specifically what you want from the response.
The below code is a example of this command basically
openssl ocsp -respin ocspresp.der -reqin ocspreq.der -issuer
issuer.pem -cert alice.pem -cert bob.pem
bool read_ocsp_response()
{
// load ocsp request (der format)
auto file = make_handle(BIO_new_file("ocspreq.der", "rb"), BIO_free);
if(!file) return false;
auto const request = make_handle(d2i_OCSP_REQUEST_bio(file.get(), nullptr), OCSP_REQUEST_free);
if(!request) return false;
// load ocsp response (der format)
file = make_handle(BIO_new_file("ocspresp.der", "rb"), BIO_free);
if(!file) return false;
auto const response = make_handle(d2i_OCSP_RESPONSE_bio(file.get(), nullptr), OCSP_RESPONSE_free);
if(!response) return false;
file.reset();
// was the server response ok?
if(OCSP_response_status(response.get()) != OCSP_RESPONSE_STATUS_SUCCESSFUL) return false;
// verify response
auto const basic_response = make_handle(OCSP_response_get1_basic(response.get()), OCSP_BASICRESP_free);
if(!basic_response) return false;
// check that the response is for the expected request
auto const nonce_check_result = OCSP_check_nonce(request.get(), basic_response.get());
if(nonce_check_result <= 0)
{
if(nonce_check_result == -1)
{
puts("WARNING: no nonce in response");
}
else
{
return false;
}
}
// verify the response against the issuer certificate
auto const issuers_certificate_stack = make_handle(sk_X509_new_null(), [](auto handle){ sk_X509_pop_free(handle, X509_free); });
if(!issuers_certificate_stack) return false;
file = make_handle(BIO_new_file("issuer.pem", "r"), BIO_free);
if(!file) return false;
auto const issuer = PEM_read_bio_X509(file.get(), nullptr, nullptr, nullptr);
if(!issuer) return false;
file.reset();
sk_X509_push(issuers_certificate_stack.get(), issuer);
// load default certificate store
auto const store = make_handle(X509_STORE_new(), X509_STORE_free);
if(!store) return false;
auto const lookup = X509_STORE_add_lookup(store.get(), X509_LOOKUP_file());
if(lookup == nullptr) return false;
if(OCSP_basic_verify(basic_response.get(), issuers_certificate_stack.get(), store.get(), OCSP_TRUSTOTHER) != 1) return false;
// check that all the certificates have a status ok results
if(OCSP_resp_count(basic_response.get() == 0) return false;
for (auto i = 0; i < OCSP_resp_count(basic_response.get()); i++)
{
auto const single_response = OCSP_resp_get0(basic_response.get(), i);
if(single_response == nullptr) return false;
if(OCSP_single_get0_status(single_response, nullptr, nullptr, nullptr, nullptr) != V_OCSP_CERTSTATUS_GOOD) return false;
}
return true;
}
If you want to find the status for a specific certificate like alice.pem, then you use OCSP_CERTID returned from OCSP_cert_to_id (see generating request) and use it with OCSP_resp_find_status API to find the status for that certificate rather that enumerating all the certificates like I'm doing in the code above.
If you are going to query the certificate(s) on a regular basis, you may like to use the next update time stamps returned from the status to schedule when to do the next check call.

Related

using a bytes field as proxy for arbitrary messages

Hello nano developers,
I'd like to realize the following proto:
message container {
enum MessageType {
TYPE_UNKNOWN = 0;
evt_resultStatus = 1;
}
required MessageType mt = 1;
optional bytes cmd_evt_transfer = 2;
}
message evt_resultStatus {
required int32 operationMode = 1;
}
...
The dots denote, there are more messages with (multiple) primitive containing datatypes to come. The enum will grow likewise, just wanted to keep it short.
The container gets generated as:
typedef struct _container {
container_MessageType mt;
pb_callback_t cmd_evt_transfer;
} container;
evt_resultStatus is:
typedef struct _evt_resultStatus {
int32_t operationMode;
} evt_resultStatus;
The field cmd_evt_transfer should act as a proxy of subsequent messages like evt_resultStatus holding primitive datatypes.
evt_resultStatus shall be encoded into bytes and be placed into the cmd_evt_transfer field.
Then the container shall get encoded and the encoding result will be used for subsequent transfers.
The background why to do so, is to shorten the proto definition and avoid the oneof thing. Unfortunately syntax version 3 is not fully supported, so we can not make use of any fields.
The first question is: will this approach be possible?
What I've got so far is the encoding including the callback which seems to behave fine. But on the other side, decoding somehow skips the callback. I've read issues here, that this happened also when using oneof and bytes fields.
Can someone please clarify on how to proceed with this?
Sample code so far I got:
bool encode_msg_test(pb_byte_t* buffer, int32_t sval, size_t* sz, char* err) {
evt_resultStatus rs = evt_resultStatus_init_zero;
rs.operationMode = sval;
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
/*encode container*/
container msg = container_init_zero;
msg.mt = container_MessageType_evt_resultStatus;
msg.cmd_evt_transfer.arg = &rs;
msg.cmd_evt_transfer.funcs.encode = encode_cb;
if(! pb_encode(&stream, container_fields, &msg)) {
const char* local_err = PB_GET_ERROR(&stream);
sprintf(err, "pb_encode error: %s", local_err);
return false;
}
*sz = stream.bytes_written;
return true;
}
bool encode_cb(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
evt_resultStatus* rs = (evt_resultStatus*)(*arg);
//with the below in place a stream full error rises
// if (! pb_encode_tag_for_field(stream, field)) {
// return false;
// }
if(! pb_encode(stream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
//buffer holds previously encoded data
bool decode_msg_test(pb_byte_t* buffer, int32_t* sval, size_t msg_len, char* err) {
container msg = container_init_zero;
evt_resultStatus res = evt_resultStatus_init_zero;
msg.cmd_evt_transfer.arg = &res;
msg.cmd_evt_transfer.funcs.decode = decode_cb;
pb_istream_t stream = pb_istream_from_buffer(buffer, msg_len);
if(! pb_decode(&stream, container_fields, &msg)) {
const char* local_err = PB_GET_ERROR(&stream);
sprintf(err, "pb_encode error: %s", local_err);
return false;
}
*sval = res.operationMode;
return true;
}
bool decode_cb(pb_istream_t *istream, const pb_field_t *field, void **arg) {
evt_resultStatus * rs = (evt_resultStatus*)(*arg);
if(! pb_decode(istream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
I feel, I don't have a proper understanding of the encoding / decoding process.
Is it correct to assume:
the first call of pb_encode (in encode_msg_test) takes care of the mt field
the second call of pb_encode (in encode_cb) handles the cmd_evt_transfer field
If I do:
bool encode_cb(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) {
evt_resultStatus* rs = (evt_resultStatus*)(*arg);
if (! pb_encode_tag_for_field(stream, field)) {
return false;
}
if(! pb_encode(stream, evt_resultStatus_fields, rs)) {
return false;
}
return true;
}
then I get a stream full error on the call of pb_encode.
Why is that?
Yes, the approach is reasonable. Nanopb callbacks do not care what the actual data read or written by the callback is.
As for why your decode callback is not working, you'll need to post the code you are using for decoding.
(As an aside, Any type does work in nanopb and is covered by this test case. But the type_url included in all Any messages makes them have a quite large overhead.)

Trying to understand a parsing error when making an HTTP request with C++ on ESP32

I'm attempting to do an HTTPS PUT from C++ on an ESP-EYE. I started with the C esp_https_example code and had that working with the same PEM and URL. I started to transition to using this from C++ as part of a project that is primarily written in C++. My call looks like:
static const char *URL = "https://signal.unexpectedeof.casa/on-air";
void https_with_url(void)
{
esp_http_client_config_t* config = (esp_http_client_config_t*)calloc(sizeof(esp_http_client_config_t), 1);
config->url = URL;
config->cert_pem = unexpectedeof_casa_root_cert_pem_start;
config->event_handler = _http_event_handler;
esp_http_client_handle_t client = esp_http_client_init(config);
esp_http_client_set_method(client, HTTP_METHOD_PUT);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTPS Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_close(client);
esp_http_client_cleanup(client);
}
I believe the URL I'm providing isn't being copied or initialized correctly resulting in a url parsing error. When the function https_with_url is called I get this error:
E (13593) esp-tls: couldn't get hostname for :signal.unexpectedeof.casa:
E (13593) esp-tls: Failed to open new connection
E (13603) TRANS_SSL: Failed to open a new connection
E (13603) HTTP_CLIENT: Connection failed, sock < 0
E (13613) HTTPS_HANDLING: Error perform http request ESP_ERR_HTTP_CONNECT
I (13623) HTTPS_HANDLING: HTTP_EVENT_DISCONNECTED
I (13623) HTTPS_HANDLING: HTTP_EVENT_DISCONNECTED
Since I'm using C++ but the parse happens in the esp-idf C code I thought maybe I'm not passing the data correctly, but haven't made much progress. Switching from an inline string for the URL to the character array shown didn't make a difference.
esp-idf version 4.1.
Ended up being that I didn't handle the wifi event properly before the request was sent.
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
s_retry_num++;
ESP_LOGI(LOGTAG, "retry to connect to the AP");
}
ESP_LOGI(LOGTAG,"connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(LOGTAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
void wifi_init_sta(void)
{
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL));
wifi_config_t wifi_config = { };
strcpy((char*)wifi_config.sta.ssid, CONFIG_ESP_WIFI_SSID);
strcpy((char*)wifi_config.sta.password, CONFIG_ESP_WIFI_PASSWORD);
ESP_LOGI(LOGTAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(LOGTAG, "wifi_init_sta finished.");
}
Examples of this are in the esp-idf github repo under examples/protocols/wifi.

CertGetCertificateChain with a supporting memory store and Certificate Trust List

I need to mark a custom self-signed root certificate as trusted during certificate chain validation and, overall, I want to rely on the system API as much as possible.
I create a temporary memory store.
HCERTSTORE certStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, 0, 0);
Then I place the custom root certificate into the store.
CertAddCertificateContextToStore(certStore, rootCertContext, CERT_STORE_ADD_REPLACE_EXISTING, 0);
The CertGetCertificateChain MSDN documentation says
hAdditionalStore A handle to any additional store to search for supporting certificates and certificate trust lists (CTLs).
As far as I understand if I create a CTL with the root certificate and place it to the store, CertGetCertificateChain will trust it. So, I create the root certificate CTL entry in an allocated buffer and then copy it to std::vector ctlEntries
CertCreateCTLEntryFromCertificateContextProperties(rootCertContext, 0, nullptr, CTL_ENTRY_FROM_PROP_CHAIN_FLAG, nullptr, ctlEntry, &size);
Then I create the CTL itself.
const std::wstring ctlID(L"TrustedRoots");
// I do not know what OIDs to use here. I tried different options.
std::vector<LPSTR> usageList;
usageList.push_back(szOID_SORTED_CTL);
usageList.push_back(szOID_PKIX_KP_CLIENT_AUTH);
usageList.push_back(szOID_PKIX_KP_SERVER_AUTH);
CTL_INFO ctlInfo;
ZeroMemory(&ctlInfo, sizeof(ctlInfo));
ctlInfo.dwVersion = CTL_V1;
ctlInfo.SubjectUsage.cUsageIdentifier = static_cast<DWORD>(usageList.size());
ctlInfo.SubjectUsage.rgpszUsageIdentifier = usageList.data();
ctlInfo.ListIdentifier.cbData = static_cast<DWORD>((ctlID.size() + 1) * sizeof(wchar_t));
ctlInfo.ListIdentifier.pbData = static_cast<BYTE*>(static_cast<void*>(const_cast<wchar_t*>(ctlID.data())));
ctlInfo.SubjectAlgorithm.pszObjId = szOID_OIWSEC_sha1;
ctlInfo.cCTLEntry = static_cast<DWORD>(ctlEntries.size());
ctlInfo.rgCTLEntry = const_cast<PCTL_ENTRY>(ctlEntries.data());
// From MSDN:
// The message can be encoded without signers if the cbSize member of the structure is set to the
// size of the structure and all of the other members are set to zero.
CMSG_SIGNED_ENCODE_INFO encode;
ZeroMemory(&encode, sizeof(encode));
encode.cbSize = sizeof(encode);
DWORD size = 0, flags = CMSG_ENCODE_SORTED_CTL_FLAG | CMSG_ENCODE_HASHED_SUBJECT_IDENTIFIER_FLAG;
if (CryptMsgEncodeAndSignCTL(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &ctlInfo, &encode, flags, nullptr, &size) == TRUE)
{
std::string data;
data.resize(size);
BYTE* p = static_cast<BYTE*>(static_cast<void*>(&data.front()));
if (CryptMsgEncodeAndSignCTL(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &ctlInfo, &encode, flags, p, &size) == TRUE)
{
PCCTL_CONTEXT ctlContext = CertCreateCTLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, p, size);
if (ctlContext)
{
if (CertAddCTLContextToStore(certStore, ctlContext, CERT_STORE_ADD_REPLACE_EXISTING, nullptr) == TRUE)
{
// success
}
}
}
}
All API calls above finish successfully but when I call CertGetCertificateChain, it still returns CERT_TRUST_IS_UNTRUSTED_ROOT in TrustStatus.dwErrorStatus.
Potential Workaround
If I get the CERT_TRUST_IS_UNTRUSTED_ROOT error, I just extract the CTL from the certificate store and check if the root from the result chain (returned by CertGetCertificateChain) is in the CTL. It works but is still not fully acceptable for me. I would like to rely on CertGetCertificateChain.
What is wrong with the solution?
What specific Certificate Trust List OIDs must I use?
Is any requirement (like specific extensions) for the root certificate to be trusted in this case?
p.s. The test certificates are created using this instruction https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309
UPD: 2020-01-31
CertModifyCertificatesToTrust did not help. It finishes successfully but the chain is still reported as having an untrusted root. Probably, the issue is in different area.
PCCERT_CONTEXT copiedCert = nullptr;
BOOL result = CertAddCertificateContextToStore(certStore,
cert, CERT_STORE_ADD_REPLACE_EXISTING, &copiedCert);
CertFreeCertificateContext(cert);
if (result)
{
// Save the certificate to create a CTL entry later
trustedRoots.push_back(copiedCert);
}
...
// Creating the CTL entries
...
std::vector<LPSTR> usageList;
usageList.push_back(szOID_CTL); // I really do not know what IDs I must use here
...
CTL_INFO ctlInfo;
ZeroMemory(&ctlInfo, sizeof(ctlInfo));
ctlInfo.dwVersion = CTL_V1;
ctlInfo.SubjectUsage.cUsageIdentifier = static_cast<DWORD>(usageList.size());
ctlInfo.SubjectUsage.rgpszUsageIdentifier = usageList.data();
...
// Should I use any of the flags?
DWORD size = 0, flags = 0; /*CMSG_ENCODE_SORTED_CTL_FLAG | CMSG_ENCODE_HASHED_SUBJECT_IDENTIFIER_FLAG;*/
if (CryptMsgEncodeAndSignCTL(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &ctlInfo, &encode, flags, nullptr, &size) == TRUE)
...
if (CertAddCTLContextToStore(certStore, ctlContext, CERT_STORE_ADD_REPLACE_EXISTING, nullptr) == TRUE)
{
// Check that the CTL is in the store and the root certificate is in the CTL
CTL_FIND_USAGE_PARA usagePara;
ZeroMemory(&usagePara, sizeof(usagePara));
usagePara.cbSize = sizeof(usagePara);
usagePara.SubjectUsage.cUsageIdentifier = 0;
usagePara.ListIdentifier.cbData = static_cast<DWORD>((ctlID.size() + 1) * sizeof(wchar_t));
usagePara.ListIdentifier.pbData = static_cast<BYTE*>(static_cast<void*>(const_cast<wchar_t*>(ctlID.data())));
PCCTL_CONTEXT foundCTLContext = CertFindCTLInStore(certStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CTL_FIND_USAGE,
static_cast<void*>(&usagePara), nullptr);
if (foundCTLContext != nullptr)
{
PCTL_ENTRY ctlEntry = CertFindSubjectInCTL(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
CTL_CERT_SUBJECT_TYPE, const_cast<void*>(*trustedRoots.begin()), foundCTLContext, 0);
if (ctlEntry != nullptr)
{
// It means the root certificate has been correctly added to the CTL and the CTL is in the store.
std::cout << "Found the certificate in the CTL" << std::endl;
}
}
// Make the certificate trusted via CertModifyCertificatesToTrust
HMODULE module = LoadLibrary(L"CryptDlg.dll");
if (module)
{
CertModifyCertificatesToTrustPfn pfn =
(CertModifyCertificatesToTrustPfn)GetProcAddress(hModule, "CertModifyCertificatesToTrust");
if (pfn != nullptr)
{
CTL_MODIFY_REQUEST req;
// Only one certificate is in the trustedRoots store curretly
req.pccert = static_cast<PCCERT_CONTEXT>(*trustedRoots.begin());
req.dwOperation = CTL_MODIFY_REQUEST_ADD_TRUSTED;
req.dwError = 0;
HRESULT hr = pfn(1, &req, szOID_CTL, NULL, certStore, nullptr);
if (hr == S_OK)
{
// Success
std::cout << "Modified" << std::endl;
}
}
}
}
You can try to use the following api: CertModifyCertificatesToTrust
And note that
This function has no associated import library. You must use the
LoadLibrary and GetProcAddress functions to dynamically link to
CryptDlg.dll.
Set the CTL_MODIFY_REQUEST.dwOperation to the flagCTL_MODIFY_REQUEST_ADD_TRUSTED to add the certificate to the CTL. The certificate is explicitly trusted.

How to compose a PKCS#7 signature file correctly?

I'm trying to use the Botan library to generate a detached signature file. The resulting signature file is not validated by OpenSSL (no other checks). Prompt in what there can be an error of formation of the signature file.
A couple of keys for signing and the certificate is stored in the HSM, it was not difficult to get them. For tests I use RSA keys and SoftHSM, later another key format and physical HSM will be used. PKCS#11 is used to communicate with HSM.
For create PKCS#7:
static const Botan::BigInt CMSVersion(1ull);
std::vector<uint8_t> createAttributes(std::vector<uint8_t> &digestData)
{
std::chrono::time_point<std::chrono::system_clock> time = std::chrono::system_clock::now();
Botan::OID dataOID("1.2.840.113549.1.7.1");
Botan::Attribute contentType(Botan::OIDS::str2oid("PKCS9.ContentType"),
dataOID.BER_encode());
Botan::X509_Time timeASN1(time);
std::vector<uint8_t> attributesData;
Botan::DER_Encoder attrib(attributesData);
attrib.start_cons(Botan::ASN1_Tag(0),
Botan::ASN1_Tag(Botan::ASN1_Tag::CONTEXT_SPECIFIC));
attrib.encode(contentType)
.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OID("1.2.840.113549.1.9.5"))
.start_cons(Botan::ASN1_Tag::SET).encode(timeASN1).end_cons()
.end_cons()
.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OIDS::str2oid("PKCS9.MessageDigest"))
.start_cons(Botan::ASN1_Tag::SET)
.encode(digestData, Botan::ASN1_Tag::OCTET_STRING,
Botan::ASN1_Tag::OCTET_STRING, Botan::ASN1_Tag::UNIVERSAL)
.end_cons()
.end_cons();
attrib.end_cons();
return attributesData;
}
std::vector<uint8_t> createCMS(const Botan::AlgorithmIdentifier &digestAlg,
Botan::X509_Certificate &cert,
const Botan::AlgorithmIdentifier &keyAlg,
std::vector<uint8_t> &sigData,
std::vector<uint8_t> &signedAttributes)
{
Botan::secure_vector<uint8_t> msgData;
Botan::DER_Encoder encoder(msgData);
encoder.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(CMSVersion)
.start_cons(Botan::ASN1_Tag::SET).start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(digestAlg.get_oid()).end_cons().end_cons();
Botan::OID dataOID("1.2.840.113549.1.7.1");
encoder.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(dataOID).end_cons();
encoder.start_cons(Botan::ASN1_Tag::UNIVERSAL, Botan::ASN1_Tag::PRIVATE)
.encode(cert).end_cons();
encoder.start_cons(Botan::ASN1_Tag::SET);
Botan::secure_vector<uint8_t> signerInfoData;
Botan::DER_Encoder signerInfo(signerInfoData);
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE);
signerInfo.encode(CMSVersion);
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(cert.issuer_dn())
.encode(Botan::BigInt(cert.serial_number())).end_cons();
signerInfo.start_cons(Botan::ASN1_Tag::SEQUENCE).encode(digestAlg.get_oid())
.end_cons();
signerInfo.raw_bytes(signedAttributes);
signerInfo.encode(keyAlg)
.encode(sigData, Botan::ASN1_Tag::OCTET_STRING,
Botan::ASN1_Tag::OCTET_STRING, Botan::ASN1_Tag::UNIVERSAL);
signerInfo.end_cons();
encoder.raw_bytes(signerInfoData).end_cons().end_cons();
std::vector<uint8_t> resulData;
Botan::DER_Encoder result(resulData);
result.start_cons(Botan::ASN1_Tag::SEQUENCE)
.encode(Botan::OID("1.2.840.113549.1.7.2"))
.start_cons(Botan::ASN1_Tag::UNIVERSAL, Botan::ASN1_Tag::PRIVATE)
.raw_bytes(msgData).end_cons().end_cons();
return resulData;
}
To calculate the hash and signature using PKCS#11, as follows:
QFile input(m_content->text()), output(m_sigFile->text());
if(!input.open(QFile::ReadOnly))
{
QMessageBox::critical(this, tr("Error"),
tr("Content file '%1' not open.\n"
"Error message: %2").arg(m_content->text())
.arg(input.errorString()));
return;
}
Botan::PKCS11::PKCS11_X509_Certificate *cert = nullptr;
Botan::Private_Key *key = nullptr;
// извлечение ключа и сертификата из токена
while(!input.atEnd())
{
static const qint64 maxLen = 1024;
QByteArray data = input.read(maxLen);
(*module)->C_DigestUpdate(session->handle(),
reinterpret_cast<uchar*>(data.data()),
data.size(), &rv);
if(rv != Botan::PKCS11::ReturnValue::OK)
{
QMessageBox::critical(this, tr("Error"),
tr("Digest not run.\nError code: 0x%3")
.arg(static_cast<int>(rv), 0, 16));
delete key;
delete cert;
delete session;
delete slot;
delete module;
return;
}
}
digest.resize(102400);
ulong digestLen;
(*module)->C_DigestFinal(session->handle(), digest.data(), &digestLen, &rv);
if(rv != Botan::PKCS11::ReturnValue::OK)
{
QMessageBox::critical(this, tr("Error"),
tr("Digest not start.\nError code: 0x%3")
.arg(static_cast<int>(rv), 0, 16));
delete key;
delete cert;
delete session;
delete slot;
delete module;
return;
}
digest.resize(digestLen);
{
Botan::PKCS11::PKCS11_RNG rng(*session);
std::unique_ptr<Botan::PK_Ops::Signature> signer =
key->create_signature_op(rng,
"EMSA3(SHA-256)",
"");
signer->update(digest.data(), digest.size());
std::vector<uint8_t> attr = createAttributes(digest);
auto signData = signer->sign(rng);
for(uint8_t i : signData)
signature.push_back(i);
Botan::AlgorithmIdentifier digAlg("SHA-256", {});
auto fileData = createCMS(digAlg, *cert, key->algorithm_identifier(),
signature, attr);
output.write(reinterpret_cast<const char*>(fileData.data()),
fileData.size());
output.close();
}
When checking the received signature file, OpenSSL says
Verification failure
140365848428992:error:04091068:rsa routines:int_rsa_verify:bad signature:../crypto/rsa/rsa_sign.c:220:
140365848428992:error:2E09A09E:CMS routines:CMS_SignerInfo_verify_content:verification failure:../crypto/cms/cms_sd.c:842:
140365848428992:error:2E09D06D:CMS routines:CMS_verify:content verify error:../crypto/cms/cms_smime.c:393:

How to check if a download with Aws::Transfer::Transfermanager was a success?

I do a download from AWS S3 and at the end of the method I want to return the success of it but sadly I don't know how to.
I tried to do this with the TransferHandle I get but there does not seem to be an option for this.
bool downloadObject(const std::shared_ptr<Aws::S3::S3Client> & client, const Aws::String &key_name)
{
const auto executor = Aws::MakeShared<Aws::Utils::Threading::PooledThreadExecutor>("Downloading-File", 4);
auto transferInitCallback = [&](const Aws::Transfer::TransferManager*, const std::shared_ptr<const Aws::Transfer::TransferHandle>& handle) {};
Aws::Transfer::TransferManagerConfiguration transferConfig(executor.get());
transferConfig.s3Client = client;
transferConfig.transferInitiatedCallback = transferInitCallback;
const auto transmanager = Aws::Transfer::TransferManager::Create(transferConfig);
const auto handle = transmanager->DownloadFile("bucketName", key_name, "C:/Development/test.gz");
handle->WaitUntilFinished(); // block calling thread until download complete
//if download == success -> return true;
}
You can use GetStatus to check for success, e.g.:
return handle->GetStatus() == Aws::Transfer::TransferStatus::COMPLETED