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:
Related
I try to sign some data by Java with private key and then verify it by C++ with public key. I user Java as client and C++ as server.
Java run on Windows, C++ on Ubuntu
in Java I use
key = "MIIEowIBAAKCAQ......s8mFoA2"; //private key
byte[] b1 = Base64.decodeBase64(key);
this.Sign = hmacSha256Base64("test", b1);
/**************/
public static String hmacSha256Base64(String message, byte[] secretKey) throws
NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, NoSuchProviderException {
Mac hmacSha256;
try {
hmacSha256 = Mac.getInstance("HmacSHA256", "BC");
} catch (NoSuchAlgorithmException nsae) {
hmacSha256 = Mac.getInstance("HMAC-SHA-256");
}
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256");
hmacSha256.init(secretKeySpec);
// Build and return signature
return Base64.encodeBase64String(hmacSha256.doFinal(message.getBytes("UTF-8")));
}
and on C++, to verify I real try different code, for example:
int verify_it(const unsigned char *msg, size_t mlen, const unsigned char *val, size_t vlen, EVP_PKEY *pkey)
{
/* Returned to caller */
int result = 0;
EVP_MD_CTX* ctx = NULL;
unsigned char buff[EVP_MAX_MD_SIZE];
size_t size;
int rc;
if (!msg || !mlen || !val || !vlen || !pkey)
return 0;
ctx = EVP_MD_CTX_new();
if (ctx == NULL) {
printf("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
goto err;
}
rc = EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey);
if (rc != 1) {
printf("EVP_DigestSignInit failed, error 0x%lx\n", ERR_get_error());
goto err;
}
rc = EVP_DigestSignUpdate(ctx, msg, mlen);
if (rc != 1) {
printf("EVP_DigestSignUpdate failed, error 0x%lx\n", ERR_get_error());
goto err;
}
size = sizeof(buff);
rc = EVP_DigestSignFinal(ctx, buff, &size);
if (rc != 1) {
printf("EVP_DigestSignFinal failed, error 0x%lx\n", ERR_get_error());
goto err;
}
result = (vlen == size) && (CRYPTO_memcmp(val, buff, size) == 0);
err:
EVP_MD_CTX_free(ctx);
return result;
}
RSA* createPublicRSA(std::string TermId, bool is_local) {
RSA *rsa = NULL;
BIO *keybio;
FILE * fp = fopen((SettingsConfig["UserKeys"] + "user_public/" + TermId).c_str(), "rb");
if (fp != 0)
{
rsa = PEM_read_RSA_PUBKEY(fp, &rsa, NULL, NULL);
fclose(fp);
}
return rsa;
}
size_t calcDecodeLength(const char* b64input) {
size_t len = strlen(b64input), padding = 0;
if (b64input[len - 1] == '=' && b64input[len - 2] == '=') //last two chars are =
padding = 2;
else if (b64input[len - 1] == '=') //last char is =
padding = 1;
return (len * 3) / 4 - padding;
}
void Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) {
BIO *bio, *b64;
int decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char*)malloc(decodeLen + 1);
(*buffer)[decodeLen] = '\0';
bio = BIO_new_mem_buf(b64message, -1);
b64 = BIO_new(BIO_f_base64());
bio = BIO_push(b64, bio);
*length = BIO_read(bio, *buffer, strlen(b64message));
BIO_free_all(bio);
}
std::string test = "XChhsTE....NkE="; //Sign from Java
std::string msg = "test";
RSA* publicRSA = createPublicRSA("#1.pem", false); //public key
EVP_PKEY* pubKey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pubKey, publicRSA);
unsigned char* encMessage;
size_t encMessageLength;
Base64Decode(test.c_str(), &encMessage, &encMessageLength);
int result_sign = verify_it((unsigned char*)msg.c_str(), msg.length(), encMessage, encMessageLength, pubKey);
std::cout << std::to_string(result_sign) << std::endl; //return 0
And any others examples return false. I don't know what is problem, please help! Thanks!
p.s. private key:
MIIEowIBAAKCAQEAra2jau89VIfcunyOth5O08EZqFVSgVzk9Tv0ELG+zH89D/s0DMLSkACXUSYq2EFRXUS05doajB55ZVoD2qYiUjJPrZDnPS+H3f/9tqRf+o2bbb4DWRd9MJbMt2E2Q8auIN3M49XvlQnZ2+dSvplLepYv6H+fbILBsYfQUxh4RX5B+qvk1JdbMh1rhgLV6y9/lYkF3UlL8W5EBA2A1YQvgrwl/nBjXTTk3PVv+OmWGFRFE0BGuf7oYEuoX86732gAtLkImqLNeNNhgUVVhFiDUOOyWjybxH9UiH28eYBZqzJlyY9D3xeC3ZUkTvfJOURK5t8vagS/t8Vu3xsMHWQ7DwIDAQABAoIBAHbNlkGp0Uwne6fdWEnfxZA4QPLTGpL/FmdiUXux+pAsYXqzHVG1Ww/CN7/82cYAOEYSn6OzZAGBPw1DW+uPRV7wp2xU+Ljz8H69g7ISEs1zXGTfW67v0GUSYor2ZoZKPAajcmpPh4ltqacxP3q9pdH/NlpWIpm5gAGOo8STsoHl0PItHpxYbWXRylzWIgysalYPRERicT/ibQlJ4w8jhdk1lqYZAyEg2trJXDXxiNGx19OxEfRoqDVumK+W7Pn38ye9zgjuR8TYRAMPJ1WcQ9HZPLZKbVBvjztLSvUk/Q+Z8PoomIN9s+Ggev1y6+ccOiRWpPQLp45483k5fHHXTpECgYEA4KJsRwGTw3yomIAN+k0eFSL/+bJJBimQXjRcc0qw+NbeLoytfVrnSCBD85QYamcB8tMg+CvcCdJve46ByOsmYN6jXLdUmai4Nt/kJfUU6bWpPwBdtUOGKb9mYH4xLGnnJqyUhCJ+vhY6WrOUBXu1KfkQZUEc/r/EWyEo09UNsCsCgYEAxe3IQ2tXI1zJ91xu0R309eToH/ZhfVSKR6UBoCKptEweBBPlCj8tQcKS6Ao9Oyk28rSoAYM8thy1V9+XItku97L+fP1JSZMkGq3g/r4DHuklshDoR3xAYOSZ6/89BxsV0O9a92bb0CV472wM7HHH0KAMtODwRqw8IpC5qlMHiq0CgYEAxYTMJJt0bF3+eSmQINkSbI97+PkVUL/XW5469H1mo0d70f6Mxj7aQwdr+I/t8BFnGzceNFmMf25z7HbgE+UAuAjMKEhjsUEzybyQhfe8TcwYZ3dQ7oPTQn4z7QDJCD6Oq+jwJkeWnlo5MWvZ6gBeyetgyUe50R6Z72920NzzzkUCgYBeY/V7YXde3+NZWfVnONgXZCDnDUKU2HpRjHln+t/foeU2oJ478sEMeVRB4JAu5IrV2B2/Cu0rFCnPTEvxTI2/htcimFAZDFjNeFqyYb9vQFS/xJxhavnwu1REXaam+t2+lEdXcPAnJZe05lyLbf+SmKE2qYcszPqoqUhB1/LiyQKBgGDXVyw05oSvR9GGKfMKIghRimeF97+EZhS718zcuDqXJ8Qmn+S+qrrwvn1X7TZbZ3bnM6JSnC5FcgLLVTWulLShjIo2ctsqaZUPnUJTBPoCJMkmGCR8H6XaVuFlfElT/jXglqwS+UkMMM2WPkDubnLzuTuslH1DJnrBBs8mFoA2
public key:
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAra2jau89VIfcunyOth5O
08EZqFVSgVzk9Tv0ELG+zH89D/s0DMLSkACXUSYq2EFRXUS05doajB55ZVoD2qYi
UjJPrZDnPS+H3f/9tqRf+o2bbb4DWRd9MJbMt2E2Q8auIN3M49XvlQnZ2+dSvplL
epYv6H+fbILBsYfQUxh4RX5B+qvk1JdbMh1rhgLV6y9/lYkF3UlL8W5EBA2A1YQv
grwl/nBjXTTk3PVv+OmWGFRFE0BGuf7oYEuoX86732gAtLkImqLNeNNhgUVVhFiD
UOOyWjybxH9UiH28eYBZqzJlyY9D3xeC3ZUkTvfJOURK5t8vagS/t8Vu3xsMHWQ7
DwIDAQAB
-----END PUBLIC KEY-----
message: 12105333071
signaturee from Java: XChhsTE+Yr4wkiibvTFiLTMhJ8tLqYo7WQs///VtNkE=
Just using HMACSHA256 is not the same as Private/Public Key signature. The full name of HMACSHA256 is "Hash-based Message Authentication Code" and you "sign" and "verify" this with the same "key" that is just a byte array and has nothing to do with Private or Public Key.
Of course you can take the encoded bytes of the Private/Public key as input, but when doing so (I do NOT recommend this)
you need to pass the same key to the verification part.
I setup two small programs to show how it works. For Java I'm using your code except of using Bouncy Castle as "native" Java
should have this build in. As well I left out the apache-Base64-conversion as it's build in as well. The C#-part is the same program but has a "verification" output.
Both code samples do not have any exceptional handling and are for educational purposes only.
Result of Java-code:
HMAC SHA256 sign on Java, Verify on C++ private-public keys
hmacSha256 (Base64): /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
Result of C#-code:
HMAC SHA256 sign on Java, Verify on C++ private-public keys
HMACSHA256 in C#: /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
HMACSHA256 Java : /1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0=
Hashes are equal: True
Java-code:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
public class Org {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
System.out.println("HMAC SHA256 sign on Java, Verify on C++ private-public keys");
String message = "12105333071";
String key = "12345678901234567";
String result = hmacSha256Base64(message, key.getBytes(StandardCharsets.UTF_8));
System.out.println("hmacSha256 (Base64): " + result);
}
public static String hmacSha256Base64(String message, byte[] secretKey) throws
NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
Mac hmacSha256;
try {
hmacSha256 = Mac.getInstance("HmacSHA256");
} catch (NoSuchAlgorithmException nsae) {
hmacSha256 = Mac.getInstance("HMAC-SHA-256");
}
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, "HmacSHA256");
hmacSha256.init(secretKeySpec);
// Build and return signature
return Base64.getEncoder().encodeToString(hmacSha256.doFinal(message.getBytes("UTF-8")));
}
}
C#-code:
using System;
using System.Text;
using System.Security.Cryptography;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("HMAC SHA256 sign on Java, Verify on C++ private-public keys");
string message = "12105333071";
string key = "12345678901234567";
string expectedHashBase64 = "/1qkanJi8onWOxVe02MO/Wf1922aKzSTSfJk6E7o1x0="; // from Java
// generate HMACSHA256
string hmacSha256DigestBase64 = HmacSha256DigestBase64(key, message);
Console.WriteLine("HMACSHA256 in C#: " + hmacSha256DigestBase64);
Console.WriteLine("HMACSHA256 Java : " + expectedHashBase64);
Console.WriteLine("Hashes are equal: " + hmacSha256DigestBase64.Equals(expectedHashBase64, StringComparison.OrdinalIgnoreCase));
//Console.ReadLine();
}
private static string HmacSha256DigestBase64(string secret, string message)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] keyBytes = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(message);
System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);
byte[] bytes = cryptographer.ComputeHash(messageBytes);
return Convert.ToBase64String(bytes);
}
}
Golang code to complete the collection (tested to produce the exactly same result as the java code form Michael Fehr:
package main
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
b64 "encoding/base64"
)
func main() {
secret := "12345678901234567"
data := "12105333071"
fmt.Printf("Secret: %s Data: %s\n", secret, data)
// Create a new HMAC by defining the hash type and the key (as byte array)
h := hmac.New(sha256.New, []byte(secret))
// Write Data to it
h.Write([]byte(data))
// Get result and base64 encode the string
sha := b64.StdEncoding.EncodeToString(h.Sum(nil))
fmt.Println("Result: " + sha)
}
I have the need to crypt big files (multi GB) with crypto++. I managed to find an example on the documentation that helped me create the 2 followings functions :
bool AESEncryptFile(const std::string& clearfile, const std::string& encfile, const std::string& key) {
try {
byte iv[CryptoPP::AES::BLOCKSIZE] = {};
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor;
encryptor.SetKeyWithIV((unsigned char*)key.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH, iv);
CryptoPP::StreamTransformationFilter filter(encryptor);
CryptoPP::FileSource source(clearfile.c_str(), false);
CryptoPP::FileSink sink(encfile.c_str());
source.Attach(new CryptoPP::Redirector(filter));
filter.Attach(new CryptoPP::Redirector(sink));
const CryptoPP::word64 BLOCK_SIZE = 4096;
CryptoPP::word64 processed = 0;
while (!EndOfFile(source) && !source.SourceExhausted()) {
source.Pump(BLOCK_SIZE);
filter.Flush(false);
processed += BLOCK_SIZE;
}
filter.MessageEnd();
return true;
} catch (const CryptoPP::Exception& ex) {
return false;
}
}
bool AESDecryptFile(const std::string& encfile, const std::string& clearfile, const std::string& key) {
try {
byte iv[CryptoPP::AES::BLOCKSIZE] = {};
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
decryptor.SetKeyWithIV((unsigned char*)key.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH, iv);
CryptoPP::StreamTransformationFilter filter(decryptor);
CryptoPP::FileSource source(encfile.c_str(), false);
CryptoPP::FileSink sink(clearfile.c_str());
source.Attach(new CryptoPP::Redirector(filter));
filter.Attach(new CryptoPP::Redirector(sink));
const CryptoPP::word64 BLOCK_SIZE = 4096;
CryptoPP::word64 processed = 0;
while (!EndOfFile(source) && !source.SourceExhausted()) {
source.Pump(BLOCK_SIZE);
filter.Flush(false);
processed += BLOCK_SIZE;
}
.
filter.MessageEnd();
return true;
} catch (const CryptoPP::Exception& ex) {
return false;
}
}
This is working great. On 8 GB files i'm using very little memory.
But as you can see the IV is (empty for now) hardcoded and i would like to :
While encrypting , put it a the end of the file.
While decrypting : get the IV from the file to init the decryptor.
Is there a way to do that with crypto++ or should i handle it manually after/before the enc/decryption process ?
Thanks to all the differents comments here is what i managed to do. As suggested by #Sam Mason i put the iv at the beginning of the file :
So before starting to encrypt i 'm putting the iv at the beginning of the file:
CryptoPP::ArraySource(iv, sizeof(iv), true,
new CryptoPP::Redirector(sink)
);
// Encrypt
And then when decrypting i'm getting the IV back like this :
unsigned char iv[CryptoPP::AES::BLOCKSIZE];
CryptoPP::ArraySink ivSink(iv, sizeof(iv));
source.Attach(new CryptoPP::Redirector(ivSink));
source.Pump(CryptoPP::AES::BLOCKSIZE);
// Decrypt
Note for future reader : Don't use an empty IV like show in my OP , instead generate one randomly , for example :
CryptoPP::AutoSeededRandomPool prng;
unsigned char iv[CryptoPP::AES::BLOCKSIZE];
prng.GenerateBlock(iv, sizeof(iv));
I try pull from remote master to local master. In remote master only one not synchronized commit.
Error in method git_annotated_commit_lookup():
Git Error -3 : object not found - no match
for id (08f4a8cc00400100f083caccd755000020299210)
In callback fetchhead_ref_cb never exevute code in "if" block.
int fetchhead_ref_cb(const char *name, const char *url,
const git_oid *oid, unsigned int is_merge, void *payload)
{
qDebug() << "fetchhead_ref_cb";
if (is_merge)
{
qDebug() << "Is merge";
git_oid_cpy((git_oid *)payload, oid);
}
return 0;
}
bool pullBranch()
{
int error;
git_remote *remote;
git_oid branchOidToMerge;
/* lookup the remote */
error = git_remote_lookup(&remote, repo, "origin");
if (!checkForError(error, "Remote lookup")) {
git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
options.callbacks.credentials = cred_acquire_cb;
error = git_remote_fetch(remote,
NULL, /* refspecs, NULL to use the configured ones */
&options, /* options, empty for defaults */
"pull"); /* reflog mesage, usually "fetch" or "pull", you can leave it NULL for "fetch" */
if (!checkForError(error, "Remote fetch")) {
git_repository_fetchhead_foreach(repo, fetchhead_ref_cb, &branchOidToMerge);
git_merge_options merge_options = GIT_MERGE_OPTIONS_INIT;
git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
git_annotated_commit *commit;
error = git_annotated_commit_lookup(&commit, repo, &branchOidToMerge);
if (!checkForError(error, "Annotated commit lookup")) {
error = git_merge(repo, (const git_annotated_commit **)commit, 1, &merge_options, &checkout_options);
if (!checkForError(error, "Merge")) {
git_annotated_commit_free(commit);
git_repository_state_cleanup(repo);
git_remote_free(remote);
return true;
}
}
}
}
git_remote_free(remote);
return false;
}
Solution for fast-forward merge:
GitPullStatus GitWizard::pullBranch()
{
git_remote *remote;
int error = git_remote_lookup(&remote, repo, "origin");
if (!checkForError(error, "Remote lookup")) {
git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
options.callbacks.credentials = cred_acquire_cb;
error = git_remote_fetch(remote,
NULL, /* refspecs, NULL to use the configured ones */
&options, /* options, empty for defaults */
"pull"); /* reflog mesage, usually "fetch" or "pull", you can leave it NULL for "fetch" */
if (!checkForError(error, "Remote fetch")) {
git_oid branchOidToMerge;
git_repository_fetchhead_foreach(repo, fetchhead_ref_cb, &branchOidToMerge);
git_annotated_commit *their_heads[1];
error = git_annotated_commit_lookup(&their_heads[0], repo, &branchOidToMerge);
checkForError(error, "Annotated commit lookup");
git_merge_analysis_t anout;
git_merge_preference_t pout;
qDebug() << "Try analysis";
error = git_merge_analysis(&anout, &pout, repo, (const git_annotated_commit **) their_heads, 1);
checkForError(error, "Merge analysis");
if (anout & GIT_MERGE_ANALYSIS_UP_TO_DATE) {
qDebug() << "up to date";
git_annotated_commit_free(their_heads[0]);
git_repository_state_cleanup(repo);
git_remote_free(remote);
return GitPullStatus::GIT_UP_TO_DATE;
} else if (anout & GIT_MERGE_ANALYSIS_FASTFORWARD) {
qDebug() << "fast-forwarding";
git_reference *ref;
git_reference *newref;
const char *name = QString("refs/heads/").append(mCurrentBranch).toLocal8Bit().data();
if (git_reference_lookup(&ref, repo, name) == 0)
git_reference_set_target(&newref, ref, &branchOidToMerge, "pull: Fast-forward");
git_reset_from_annotated(repo, their_heads[0], GIT_RESET_HARD, NULL);
git_reference_free(ref);
git_repository_state_cleanup(repo);
}
git_annotated_commit_free(their_heads[0]);
git_repository_state_cleanup(repo);
git_remote_free(remote);
return GitPullStatus::GIT_PULL_OK;
}
}
git_remote_free(remote);
return GitPullStatus::GIT_PULL_ERROR;
}
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.
I have this C++ function that downloads S3 files as istreams using the AWS SDK C++:
std::istream& s3read(std::string bucket, std::string key) {
Aws::Client::ClientConfiguration aws_conf;
aws_conf.region = Aws::Environment::GetEnv("AWS_REGION");
aws_conf.caFile = "/etc/pki/tls/certs/ca-bundle.crt";
Aws::S3::S3Client s3_client(aws_conf);
Aws::S3::Model::GetObjectRequest object_request;
object_request.WithBucket(bucket.c_str()).WithKey(key.c_str());
auto get_object_outcome = s3_client.GetObject(object_request);
if (get_object_outcome.IsSuccess()) {
std::istream& res = get_object_outcome.GetResult().GetBody();
return res;
} else {
...
};
};
I call it from main.cpp and try to parse it with Jsoncpp:
std::istream& stream = s3read(bucket, key);
Json::Value json;
Json::Reader reader;
reader.parse(stream, json);
However, I keep getting segmentation fault. Why?
I think that the problem is that reader.parse needs binary data and the istream isn't. But, if I'm right, how can I parse the stream as binary?
The issue you'r have is classical returning reference to temporary
You can re-design your code a little, to avoid this. For example:
static Json::Value parse_json(std::istream& src) {
Json::Value ret;
Json::Reader reader;
reader.parse(src, ret);
return ret;
}
// Aws::String is actually same thing to std::string except the allocator
// in case of Android, otherwise this is std::string as it is.
// You can use function like s3read("foo","bar");
Json::Value s3read_json(const Aws::String& bucket,const Aws::String& key) {
static constexpr const char *FILE_NAME = "/etc/pki/tls/certs/ca-bundle.crt";
Aws::Client::ClientConfiguration aws_conf;
aws_conf.region = Aws::Environment::GetEnv("AWS_REGION");
aws_conf.caFile = FILE_NAME;
Aws::S3::S3Client s3_client(aws_conf);
Aws::S3::Model::GetObjectRequest object_request;
object_request.WithBucket( bucket ).WithKey( key );
auto object_outcome = s3_client.GetObject(object_request);
if (object_outcome.IsSuccess()) {
auto result = object_outcome.GetResult();
// destructor of object_outcome is not yet called
return parse_json( result.GetBody() );
} else {
...
// throw std::runtime_error("S3 connection failed");
};
};