How to create a secp256k1 key pair with OpenSSL? - c++

Since the EC_KEY functions are marked deprecated in OpenSSL 3 I tried to do it with EVP_PKEY functions.
Here is my OpenSSL 1.1 code:
void MainWindowOpenSsl::createKeyPair()
{
EC_KEY *key_pair_obj = nullptr;;
BIGNUM *priv_key;
EC_POINT *pub_key;
EC_GROUP *secp256k1_group;
char *priv_key_char;
char *pub_key_char;
char *pub_key_char_comp;
int ret_error;
// Generate secp256k1 key pair
key_pair_obj = EC_KEY_new_by_curve_name(NID_secp256k1);
ret_error = EC_KEY_generate_key(key_pair_obj);
// Get private key
priv_key = (BIGNUM *)EC_KEY_get0_private_key(key_pair_obj);
priv_key_char = BN_bn2hex(priv_key);
// Get public key
pub_key = (EC_POINT *)EC_KEY_get0_public_key(key_pair_obj);
secp256k1_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
// Uncompressed
pub_key_char = EC_POINT_point2hex(secp256k1_group, pub_key, POINT_CONVERSION_UNCOMPRESSED, nullptr);
// Compressed
pub_key_char_comp = EC_POINT_point2hex(secp256k1_group, pub_key, POINT_CONVERSION_COMPRESSED, nullptr);
EC_GROUP_free(secp256k1_group);
qDebug().noquote() << "\n";
qDebug().noquote() << Q_FUNC_INFO;
printf("Private key : %s\n\n", priv_key_char);
printf("Public key ompressed : %s\n\n", pub_key_char);
printf("Public key uncompressed: %s\n", pub_key_char_comp);
printf("Error code : %d\n", ret_error);
}
With the EVP_PKEY functions I managed to get a private key, but I couldn't figure out how to extract the public key from it. That is how I started:
void MainWindowOpenSsl::createKeyPairNew()
{
char *priv_key_char;
// Private Key
EVP_PKEY *evp_pkey = EVP_EC_gen("secp256k1");
BIGNUM *bignum_priv_key = nullptr;
EVP_PKEY_get_bn_param(evp_pkey, OSSL_PKEY_PARAM_PRIV_KEY, &bignum_priv_key);
priv_key_char = BN_bn2hex(bignum_priv_key);
printf("Private key : %s\n\n", priv_key_char);
// Extract the compressed and uncompressed public key from the private key
}
but then I got stuck.
Thank you very much for your help.

Related

PKCS#7 signing Base64 string and add signer info openssl

There is problem found while signing Nonce(Base64 string) with PKCS#7 using openssl
the problem is when i decode the signature the nonce is trimmed (get 4 char and the expected is 8 char)
Here is the code.
int main(int argc, char *argv[])
{
QString nonce = "Jd0VAO74";
QDateTime dateTime = QDateTime::fromString("2022-12-15T13:51:46Z", Qt::ISODateWithMs);
unsigned char*signature = signNonce(nonce, dateTime);
qDebug() << signature;
return 0;
}
unsigned char* signNonce(nonce, dateTime){
QContentInfo contentInfo = QContentInfo(QByteArray::fromBase64(nonce.toLatin1()));
auto signedCms = QSignedCms(contentInfo);
QOpenssl::QOpensslCertificate qOpensslCertificate(getCertificate());
QCmsSigner cmsSigner = QCmsSigner(qOpensslCertificate);
cmsSigner.setDigestType(QOpenssl::DigestType::SHA256);
cmsSigner.setPkcs9SigningTime(serverDateTime);
signedCms.computeSignatureNew(cmsSigner);
auto l_pSignedCms = PKCS7_PTR(PKCS7_new(),::PKCS7_free);
// set certificate and private key in a signer info.
QSignerInfo qsignerInfo;
PKCS7_SIGNER_INFO* signerInfo = PKCS7_SIGNER_INFO_new();
X509_PTR pX509 = cmsSigner.getCertificate().getCertificate();
EVP_PKEY_PTR pKey = cmsSigner.getCertificate().getPrivateKey();
const EVP_MD* pMD = EVP_sha256();
PKCS7_SIGNER_INFO_set(signerInfo, pX509.get(), pKey.get(), pMD);
// set signing time attribute.
ASN1_TIME* pSigningTime = ASN1_TIME_set(nullptr, cmsSigner.getPkcs9SigningTime().toTime_t());
PKCS7_add0_attrib_signing_time(signerInfo, pSigningTime);
qsignerInfo.setPkcs9SigningTime(cmsSigner.getPkcs9SigningTime());
// set message digest attribute.
QCryptographicHash::Algorithm algo = cmsSigner.getDigestType() == DigestType::SHA256
? QCryptographicHash::Algorithm::Sha256
: QCryptographicHash::Algorithm::Sha1;
QByteArray hash = QCryptographicHash::hash(m_ContentInfo.getContent(), algo);
const auto* pHash = reinterpret_cast<const unsigned char*>(hash.constData());
PKCS7_add1_attrib_digest(signerInfo, pHash, m_ContentInfo.getContent().length());
qsignerInfo.setDigestType(cmsSigner.getDigestType());
qsignerInfo.setHash(hash);
// set content type attribute.
PKCS7_add_attrib_content_type(signerInfo, OBJ_nid2obj(NID_pkcs7_data));
// sign signerinfo.
if(PKCS7_SIGNER_INFO_sign(signerInfo) <= 0) {
qCritical() << ERR_error_string(ERR_get_error(), nullptr);
return;
}
// add signer info to cms.
PKCS7_add_signer(l_pSignedCms.get(), signerInfo);
// set data to cms.
// set certificate to cms.
PKCS7_add_certificate(l_pSignedCms.get(), pX509.get());
// set certificate chain
for(const QOpensslCertificate& cert : cmsSigner.getCertificate().getCertificateChain()) {
if(!cert.isSelfSigned())
PKCS7_add_certificate(l_pSignedCms.get(), cert.getCertificate().get());
}
// set content data.
BIO_PTR pContent = BIO_PTR(BIO_new(BIO_s_mem()), ::BIO_free);
BIO_puts(pContent.get(), m_ContentInfo.getContent().constData());
m_pSignedCms = PKCS7_PTR(
PKCS7_sign(pX509.get(), pKey.get(), nullptr, pContent.get(), 0),
::PKCS7_free);
unsigned char* pSignedValue = nullptr;
int result = i2d_PKCS7(m_pSignedCms.get(), &pSignedValue);
return pSignedValue ;
}
after decodeing online decoder the signature, we found nonce in hex 0x25 DD 15
the nonce only contains 4 char Jd0V
any one has a clue ?
I try to figure out why the decoded signature only contains 4 char not 8

HMAC SHA256 sign on Java, Verify on C++ private-public keys

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)
}

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 find private key in token by using X509 certificate modulus in C++

In c++ code using pkcs#11 we are trying to find the private key and install corresponding x509 certificate in the token. But unable to find the key pair in token using modulus. Below is my code sample.
//Install certificate
const char bytes[] = "-----BEGIN CERTIFICATE-----" "\n"
....
"-----END CERTIFICATE-----" "\n";
BIO *bio_mem = BIO_new(BIO_s_mem());
BIO_puts(bio_mem, bytes);
X509 * x509 = PEM_read_bio_X509(bio_mem, NULL, NULL, NULL);
//
BIO *bio_out = BIO_new_fp(stdout, BIO_NOCLOSE);
EVP_PKEY *pkey = X509_get_pubkey(x509);
RSA *rsa_key;
DSA *dsa_key;
char *rsa_e_dec, *rsa_n_hex, *dsa_p_hex,
*dsa_q_hex, *dsa_g_hex, *dsa_y_hex;
rsa_key = pkey->pkey.rsa;
//IFNULL_FAIL(rsa_e_dec, "unable to extract rsa exponent");
CK_BYTE_PTR modulus, exponent;
modulus = (unsigned char *)malloc(256);
int mo = BN_bn2bin(rsa_key->n, modulus);
//EVP_PKEY_free(pkey);
// CK_RV result;
CK_OBJECT_HANDLE hObject;
CK_OBJECT_HANDLE hObjects[100];
CK_OBJECT_HANDLE_PTR hObject_PTR = NULL;
CK_ULONG count;
vector<CK_OBJECT_HANDLE> *handles = new vector<CK_OBJECT_HANDLE>();
//Object class attribute
CK_OBJECT_CLASS classValue = CKO_PRIVATE_KEY;
CK_OBJECT_CLASS keytype = CKK_RSA;
CK_ATTRIBUTE privKeySearchTemplate[] = {
{ CKA_CLASS, &classValue,sizeof(classValue) },
{ CKA_KEY_TYPE, &keytype,sizeof(keytype) },
{ CKA_MODULUS, &modulus, sizeof(modulus) },
};
//
//{ CKA_PUBLIC_EXPONENT, exponent},
// Read label and ID from private key handle
CK_ATTRIBUTE privKeyAttrsToRead[] =
{ { CKA_LABEL, NULL_PTR, 0 },
{ CKA_ID, NULL_PTR, 0 },
};
//WriteToLog(modulus, modulus_len11);
// Find all objects with the template specified
result = m_pPKCS11->C_FindObjectsInit(m_SessionHandle, privKeySearchTemplate, 2);
do {
// Find the next object
result = m_pPKCS11->C_FindObjects(m_SessionHandle, &hObject, 1, &count);
if (count != 0)
handles->push_back(hObject);
} while (count != 0);
result = m_pPKCS11->C_FindObjectsFinal(m_SessionHandle);
There are several bugs here:
{ CKA_MODULUS, &modulus, sizeof(modulus) }
like always, sizeof(modulus) is size of your pointer which is 4 or 8 based on your system. This should be size of your modulus which in your case is mo. In addition, use correct type here:
CK_KEY_TYPE keytype = CKK_RSA;
Another bug is here:
m_pPKCS11->C_FindObjectsInit(m_SessionHandle, privKeySearchTemplate, 2);
You are searching a template with 3 attributes, but you have set number of attributes as 2. Normally you need to write code like this to prevent such bugs:
m_pPKCS11->C_FindObjectsInit(m_SessionHandle, privKeySearchTemplate, sizeof(privKeySearchTemplate) / sizeof(CK_ATTRIBUTE));
Finally, you need to allocate enough memory for your modulus before using BN_bn2bin, unless you like to get memory exceptions. Allocating 256 bytes may not be sufficient.

Invalid ciphertext when performing RSA decryption

I use the following code to encrypt and decrypt a string making use of Crypto++ 5.6.2 library
string to_BER(string spriv,bool b)
{
string HEADER, FOOTER;
if(b)
{
HEADER = "-----BEGIN RSA PRIVATE KEY-----";
FOOTER = "-----END RSA PRIVATE KEY-----";
}
else
{
HEADER = "-----BEGIN PUBLIC KEY-----";
FOOTER = "-----END PUBLIC KEY-----";
}
size_t pos1, pos2;
pos1 = spriv.find(HEADER);
if(pos1 == string::npos)
throw std::runtime_error("PEM header not found");
pos2 = spriv.find(FOOTER, pos1+1);
if(pos2 == string::npos)
throw std::runtime_error("PEM footer not found");
// Start position and length
pos1 = pos1 + HEADER.length();
pos2 = pos2 - pos1;
string keystr = spriv.substr(pos1, pos2);
return keystr;
}
string rsa_encrypt(const string &message, const string &public_key)
{
string keystr=to_BER(public_key,false);
ByteQueue queue;
Base64Decoder decoder;
decoder.Attach(new Redirector(queue));
decoder.Put((const byte*)keystr.data(), keystr.length());
decoder.MessageEnd();
RSAES_PKCS1v15_Encryptor e;
e.AccessKey().Load(queue);
bool key_ok = e.AccessKey().Validate(rng, 3);
if(!key_ok)
{
printf("ERROR IN RSA KEY\n");
return "";
}
string encrypted_data;
StringSource ss1(message, true,
new PK_EncryptorFilter(rng, e,
new StringSink(encrypted_data)
) // PK_EncryptorFilter
); // StringSource
return encrypted_data;
}
string rsa_decrypt(const string &message, const string &private_key,bool b_Base64decode)
{
string keystr=to_BER(private_key,true);
string decoded=message;
if(b_Base64decode)
{
Base64Decoder decoder;
decoder.Put( (byte*)message.data(), message.size() );
decoder.MessageEnd();
word64 size = decoder.MaxRetrievable();
if(size && size <= SIZE_MAX)
{
decoded.resize(size);
decoder.Get((byte*)decoded.data(), decoded.size());
}
}
RSAES_PKCS1v15_Decryptor pri( rng, 1024 );
TransparentFilter privSink( new StringSink(keystr) );
pri.DEREncode( privSink );
privSink.MessageEnd();
string decrypted_data;
try
{
StringSource ss( decoded, true,
new PK_DecryptorFilter( rng, pri, new StringSink( decrypted_data )));
}
catch(Exception *e)
{
printf("ERROR DECRYPTING RSA\n");
return "";
}
return decrypted_data;
}
Then I use this code:
string enc=rsa_encrypt("hola mundo","-----BEGIN PUBLIC KEY----- MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHIiecdmRAaiTrCbbEOLBPh+fxp2KyGWyMTeWIj56NRk1CFLfBDT6vOWDceFpuTcrAFGR4Np4JLsssqM24F7bZCKv6eQCV8Xjv6GJiGofKEkI4b0zwHHEwq63p+9Rb5jYXlPZ7JIud7Yi96CHbg3foLQzZSSS9oFItGOFF0jDM7lAgMBAAE= -----END PUBLIC KEY-----");
string aaa=rsa_decrypt(
/*"YTbXg1K4OlXGY6eaLuVTFZrN4qi1zg83h0PjeLd9F3Ge3gKUHsJpoE3iLv1+Gj/PepM8ehiilb5kphxCdcELjaYH9wwYHMpUZUQSRLQUTnofOboI6nfHaKnnNV28QMguM39q1hm7X1wNc69D8R+CkWLka2rQof+BXt+41ivnMj8=",*/
enc
,"-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgHIiecdmRAaiTrCbbEOLBPh+fxp2KyGWyMTeWIj56NRk1CFLfBDT6vOWDceFpuTcrAFGR4Np4JLsssqM24F7bZCKv6eQCV8Xjv6GJiGofKEkI4b0zwHHEwq63p+9Rb5jYXlPZ7JIud7Yi96CHbg3foLQzZSSS9oFItGOFF0jDM7lAgMBAAECgYBxFL9bVHNprz4PtK3bbc2K9qmv6gxpxx88Dp/hdtm8NfoG0uclNRHALZeRa1Yjwo+Y46zPAwPCDkpGbLC+5S9zfBjtrx/+8zjTyMVb2CcGLfR0H2E/hcCjADXNxs+fmpB3+jyPhgH5ANaTmAXqGXOP56I0Fqo8xCfU/zQELCtzMQJBAN2Kq+9bQW3nKAAJEZqWQlAEjuBQfe1lrvLxc/AgVl9XLWrHre7HSlkyqcFemvqhzlZy1wz0Nv5VpOIGcAKefEMCQQCD4xSbkF1kzZyj8k6P3iUW6ezaK4krOZnpq/wDyOtj0DBAtLt3apyv3BUbe7AH1e8llJ5a8UYVHlsOdRUio+m3AkAB0LYR8wR5OxCn12sughavAyqMifxOKqwhT3sst4cdpuA3ZMV3FGj2jCS58eWBMjw3lx9N+t5MfTUpqPXX/6ZzAkBx9eTXqv3YXYZtb7GMxQI9c3Jy7k1/aS1iaXbA+nrTa5BWSRT30cqEduJSNiVcD/KuAWZ35KWPGATMUEUsAoCvAkEAuVg0OA6L3xJphKEbVYXvTwXrXcoVjjLDnNYjUJuNWrFFjBuNE4whWvdg76Panw3vMhgFc6yVr+VE5XOc7rXPvA== -----END RSA PRIVATE KEY-----"
,false
);
The problem is that rsa_decrypt fails with a "invalid ciphertext" exception that isn't caught with the try/catch code. The program crashes.
I would like to know what is wrong with my code and how can I avoid the program to crash even if the ciphertext is wrong.
I have found similar threads (crypto++ RSA and "invalid ciphertext") but none of the solutions is valid for me: I don't see any problem with a 0 at the end of the string, I check that the keys are valid, I know that private and public keys are from the same key pair and they work with other RSA libraries.
This is the line were it crashes:
StringSource ss( decoded, true,
new PK_DecryptorFilter( rng, pri, new StringSink( decrypted_data )));
The problem is that rsa_decrypt fails with a "invalid ciphertext" exception
You create a new private key, and then encode it into keystr:
RSAES_PKCS1v15_Decryptor pri( rng, 1024 );
TransparentFilter privSink( new StringSink(keystr) );
pri.DEREncode( privSink );
Instead, you should use private_key (and not the new key) and do something like you did in the encryptor:
string keystr=to_BER(private_key,true);
ByteQueue queue;
Base64Decoder decoder;
decoder.Attach(new Redirector(queue));
decoder.Put((const byte*)keystr.data(), keystr.length());
decoder.MessageEnd();
RSAES_PKCS1v15_Decryptor d;
d.AccessKey().Load(queue);
... that isn't caught...
Wrap everything in a try/catch block, and not just the PK_DecryptorFilter call.
You can see the difference (or lackof) between the public and private keys with something like:
try {
string s1("-----BEGIN PUBLIC KEY-----\n"
"MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHIiecdmRAai"
"TrCbbEOLBPh+fxp2KyGWyMTeWIj56NRk1CFLfBDT6vOWDceF"
"puTcrAFGR4Np4JLsssqM24F7bZCKv6eQCV8Xjv6GJiGofKEk"
"I4b0zwHHEwq63p+9Rb5jYXlPZ7JIud7Yi96CHbg3foLQzZSS"
"S9oFItGOFF0jDM7lAgMBAAE="
"\n-----END PUBLIC KEY-----");
string s2("-----BEGIN RSA PRIVATE KEY-----\n"
"MIICWwIBAAKBgHIiecdmRAaiTrCbbEOLBPh+fxp2KyGWyMTeWIj"
"56NRk1CFLfBDT6vOWDceFpuTcrAFGR4Np4JLsssqM24F7bZCKv6"
"eQCV8Xjv6GJiGofKEkI4b0zwHHEwq63p+9Rb5jYXlPZ7JIud7Yi"
"96CHbg3foLQzZSSS9oFItGOFF0jDM7lAgMBAAECgYBxFL9bVHNp"
"rz4PtK3bbc2K9qmv6gxpxx88Dp/hdtm8NfoG0uclNRHALZeRa1Y"
"jwo+Y46zPAwPCDkpGbLC+5S9zfBjtrx/+8zjTyMVb2CcGLfR0H2"
"E/hcCjADXNxs+fmpB3+jyPhgH5ANaTmAXqGXOP56I0Fqo8xCfU/"
"zQELCtzMQJBAN2Kq+9bQW3nKAAJEZqWQlAEjuBQfe1lrvLxc/Ag"
"Vl9XLWrHre7HSlkyqcFemvqhzlZy1wz0Nv5VpOIGcAKefEMCQQC"
"D4xSbkF1kzZyj8k6P3iUW6ezaK4krOZnpq/wDyOtj0DBAtLt3ap"
"yv3BUbe7AH1e8llJ5a8UYVHlsOdRUio+m3AkAB0LYR8wR5OxCn1"
"2sughavAyqMifxOKqwhT3sst4cdpuA3ZMV3FGj2jCS58eWBMjw3"
"lx9N+t5MfTUpqPXX/6ZzAkBx9eTXqv3YXYZtb7GMxQI9c3Jy7k1"
"/aS1iaXbA+nrTa5BWSRT30cqEduJSNiVcD/KuAWZ35KWPGATMUE"
"UsAoCvAkEAuVg0OA6L3xJphKEbVYXvTwXrXcoVjjLDnNYjUJuNW"
"rFFjBuNE4whWvdg76Panw3vMhgFc6yVr+VE5XOc7rXPvA=="
"\n-----END RSA PRIVATE KEY-----");
ArraySource as1(s1, true), as2(s2, true);
RSA::PublicKey k1;
RSA::PrivateKey k2;
PEM_Load(as1, k1);
PEM_Load(as2, k2);
AutoSeededRandomPool prng;
k1.ThrowIfInvalid(prng, 3);
k2.ThrowIfInvalid(prng, 3);
Integer i1 = k1.GetModulus() - k2.GetModulus();
Integer i2 = k1.GetPublicExponent() - k2.GetPublicExponent();
cout << i1 << " " << i2 << endl;
} catch (const Exception& ex) {
cerr << ex.what() << endl;
}
Related, the encapsulation boundaries (-----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----) should be on their own lines. Instead of a space, you should use a new line (\n). So something like:
string enc=rsa_encrypt(
"hola mundo",
"-----BEGIN PUBLIC KEY-----\nMIGeMA0G...gMBAAE=\n-----END PUBLIC KEY-----"
);
And:
string aaa=rsa_decrypt(
enc,
"-----BEGIN RSA PRIVATE KEY-----\nMIICWwIB...7rXPvA==\n-----END RSA PRIVATE KEY-----",
false
);
Related, you should probably use RSAES_OAEP_SHA_Encryptor and RSAES_OAEP_SHA_Decryptor. An aaproachable treatment is available at A bad couple of years for the cryptographic token industry.