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
Related
I would like to sign a certificate request with a smartcard's private key with PKCS#11. I do mean signing the CSR itself and not writing a certificate in response.
So I am using OpenSSL to create the X509 certificate request, and I want to use PKCS#11 and C_Sign to create the signature. How can I do that?
This is what I have currently, but when I try to generate a certificate from this request with OpenSSL CLI, it says the signature doesn't match so I must be doing something wrong. I'm not sure what to pass C_Sign —right now I've tried the output of i2d_X509_REQ()— and how to set the signature back in X509_REQ once it's been created (I've tried building an ASN1_BIT_STRING object).
Note: this is not a duplicate of this question because this one is for a certificate and works for an old version of OpenSSL's API. Although, I have tried to use the answer by manually exposing the internals of the X509_REQ structure (see last code block).
X509_REQ makeCSR() {
/* Create OpenSSL EVP_PKEY from exported public key components */
RSA* openssl_rsa = RSA_new();
BIGNUM* bn_modulus = BN_bin2bn(modulus.data(), (int) modulus.size(), nullptr);
BIGNUM* bn_public_exponent = BN_bin2bn(public_exponent.data(), (int) public_exponent.size(), nullptr);
int success = RSA_set0_key(openssl_rsa, bn_modulus, bn_public_exponent, nullptr);
EVP_PKEY* evp_pkey = EVP_PKEY_new();
/* Add public key to certificate request */
EVP_PKEY_assign(evp_pkey, EVP_PKEY_RSA, openssl_rsa);
X509_REQ* request = X509_REQ_new();
X509_REQ_set_pubkey(request, evp_pkey);
/* Set certificate request attributes */
// ...
/* Sign certificate request with smart card */
unsigned char* buffer { nullptr };
int size = i2d_X509_REQ(request, &buffer);
std::vector<unsigned char> der_encoded_request(buffer, buffer + size);
std::vector<unsigned char> signature = smartcard->signCertificateRequest(der_encoded_request);
/* Build signature object */
ASN1_BIT_STRING* asn1_signature = ASN1_BIT_STRING_new();
ASN1_BIT_STRING_set(asn1_signature, signature.data(), (int) signature.size());
asn1_signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
asn1_signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
X509_ALGOR* x509_algor = X509_ALGOR_new();
ASN1_OBJECT* a = OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS));
X509_ALGOR_set0(x509_algor, a, V_ASN1_NULL, nullptr);
/* Add signature to X509_REQ */
X509_REQ_set1_signature_algo(request, x509_algor);
X509_REQ_set0_signature(request, asn1_signature);
return request;
}
std::vector<unsigned char> signCertificateRequest(std::vector<unsigned char>& certificate_request)
{
CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, nullptr, 0 };
auto result = s_pkcs11->fn->C_SignInit(m_session_handle, &mechanism, private_key_handle);
unsigned long signature_length { 0 };
result = pkcs11->C_Sign(m_session_handle,
certificate_request.data(),
(unsigned long) certificate_request.size(),
nullptr,
&signature_length);
std::vector<unsigned char> signature(signature_length);
result = pkcs11->C_Sign(m_session_handle,
certificate_request.data(),
(unsigned long) certificate_request.size(),
signature.data(),
&signature_length);
return signature;
}
I've also tried exposing the internals of X509_REQ and passing the output of i2d_X509_REQ_INFO(&request->req_info, &buffer) to C_Sign; or using ASN1_item_i2d(); and also copying the signature output directly to request->signature->data.
request->req_info.enc.modified = 1;
X509_ALGOR_set0(&request->sig_alg,
OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS)),
V_ASN1_NULL,
nullptr);
unsigned char* certDerBuf = NULL;
const auto certDerLen = ASN1_item_i2d(ASN1_VALUE*) &request->req_info,
&certDerBuf,
ASN1_ITEM_rptr(X509_REQ_INFO));
std::vector<unsigned char> certDerVec(certDerBuf, certDerBuf + certDerLen);
std::vector<unsigned char> signature = smartcard->signCertificateRequest(certDerVec);
request->signature->data = (unsigned char*) OPENSSL_malloc(signature.size());
request->signature->length = (int) signature.size();
request->signature->data = signature.data();
I was able to resolve my problem in the end; this is my complete solution. Error handling and PKCS#11 session handling have been omitted. I use a unique_ptr for managing OpenSSL resources. I had to expose OpenSSL's X509_REQ and X509_REQ_INFO structures because I couldn't find accessors for req_info in OpenSSL 1.1.1h.
// OpenSSL internal definitions
using CRYPTO_REF_COUNT = int;
struct X509_req_info_st {
ASN1_ENCODING enc; /* cached encoding of signed part */
ASN1_INTEGER* version; /* version, defaults to v1(0) so can be NULL */
X509_NAME* subject; /* certificate request DN */
X509_PUBKEY* pubkey; /* public key of request */
/*
* Zero or more attributes.
* NB: although attributes is a mandatory field some broken
* encodings omit it so this may be NULL in that case.
*/
STACK_OF(X509_ATTRIBUTE) * attributes;
};
struct X509_req_st {
X509_REQ_INFO req_info; /* signed certificate request data */
X509_ALGOR sig_alg; /* signature algorithm */
ASN1_BIT_STRING* signature; /* signature */
CRYPTO_REF_COUNT references;
CRYPTO_RWLOCK* lock;
};
template<typename T>
using AutoDeletedPtr = std::unique_ptr<T, std::function<void(T*)>>;
void makeCSR(const std::vector<unsigned char>& modulus,
const std::vector<unsigned char>& public_exponent)
{
/* Create OpenSSL EVP_PKEY from exported public key components */
auto* openssl_rsa = RSA_new();
auto bn_modulus = BN_bin2bn(modulus.data(), static_cast<int>(modulus.size()), nullptr);
auto bn_public_exponent = BN_bin2bn(public_exponent.data(),
static_cast<int>(public_exponent.size()),
nullptr);
auto success = RSA_set0_key(openssl_rsa, bn_modulus, bn_public_exponent, nullptr);
auto key_file = AutoDeletedPtr<BIO>(BIO_new_file("key.pem", "wb"), BIO_free);
PEM_write_bio_RSA_PUBKEY(key_file.get(), openssl_rsa);
auto evp_pkey = AutoDeletedPtr<EVP_PKEY>(EVP_PKEY_new(), EVP_PKEY_free);
/* Add public key to certificate request */
EVP_PKEY_assign(evp_pkey.get(), EVP_PKEY_RSA, openssl_rsa);
auto request = AutoDeletedPtr<X509_REQ>(X509_REQ_new(), X509_REQ_free);
X509_REQ_set_pubkey(request.get(), evp_pkey.get());
/* Set certificate request attributes */
// ...
/* Sign certificate request with smart card */
unsigned char* buffer { nullptr };
auto size = i2d_X509_REQ_INFO(&request->req_info, &buffer);
std::vector<unsigned char> der_encoded_request(buffer, buffer + size);
auto signature = signCertificateRequest(der_encoded_request);
auto asn1_signature = ASN1_BIT_STRING_new();
ASN1_BIT_STRING_set(asn1_signature, signature.data(), static_cast<int>(signature.size()));
auto x509_algor = AutoDeletedPtr<X509_ALGOR>(X509_ALGOR_new(), X509_ALGOR_free);
auto* a = OBJ_nid2obj(pkcs11SignatureAlgorithmToNid(CKM_SHA1_RSA_PKCS));
auto algor_set_result = X509_ALGOR_set0(x509_algor.get(), a, V_ASN1_NULL, nullptr);
X509_REQ_set1_signature_algo(request.get(), x509_algor.get());
X509_REQ_set0_signature(request.get(), asn1_signature);
request->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
request->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
// Do what you want with the request
}
std::vector<unsigned char> signCertificateRequest(std::vector<unsigned char>& certificate_request)
{
// You'll need an open session and be logged in
CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY;
CK_KEY_TYPE private_key_type = CKK_RSA;
std::array<unsigned char, 4> id = "myid";
std::vector<CK_ATTRIBUTE> private_key_template = {
{ CKA_CLASS, &private_key_class, sizeof(private_key_class) },
{ CKA_KEY_TYPE, &private_key_type, sizeof(private_key_type) },
{ CKA_ID, id.data(), static_cast<long>(id.size()) },
};
auto result = s_pkcs11->fn->C_FindObjectsInit(m_session_handle,
private_key_template.data(),
static_cast<unsigned long>(private_key_template.size()));
CK_OBJECT_HANDLE private_key_handle { 0 };
unsigned long object_count { 0 };
result = s_pkcs11->fn->C_FindObjects(m_session_handle, &private_key_handle, 1, &object_count);
result = s_pkcs11->fn->C_FindObjectsFinal(m_session_handle);
CK_MECHANISM mechanism = { CKM_SHA1_RSA_PKCS, nullptr, 0 };
auto result = s_pkcs11->fn->C_SignInit(m_session_handle, &mechanism, private_key_handle);
unsigned long signature_length { 0 };
result = s_pkcs11->fn->C_Sign(m_session_handle,
certificate_request.data(),
static_cast<unsigned long>(certificate_request.size()),
nullptr,
&signature_length);
std::vector<unsigned char> signature(signature_length);
result = s_pkcs11->fn->C_Sign(m_session_handle,
certificate_request.data(),
static_cast<unsigned long>(certificate_request.size()),
signature.data(),
&signature_length);
return signature;
}
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)
}
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.
I have a sample signature generator in C which will create a hash message <count:mac-addr> and generate a signature.
When I use char *message = "120:08:00:27:7c:b6:18";
and sign, the signature is verified successfully.
But when I use
char * generate_hash()
{
xmlDoc *document;
xmlNode *root, *first_child, *node;
char *filename;
char *ap_count;
char *ap_mac_address;
char *message;
filename = "/license.xml";
document = xmlReadFile(filename, NULL, 0);
root = xmlDocGetRootElement(document);
first_child = root->children;
for (node = first_child; node; node = node->next) {
if ( strcmp((char*)node->name, "ap_count") == 0 ) {
ap_count = (char*)xmlNodeGetContent(node);
}
if ( strcmp((char*)node->name, "ap_mac_address") == 0 ){
ap_mac_address = (char*)xmlNodeGetContent(node);
}
}
message = (char *) malloc(strlen(ap_count)+ strlen(ap_mac_address) +1 );
memset(message,0x0,(1 + strlen(ap_count)+ strlen(ap_mac_address)));
strcpy(message,ap_count);
strcat(message,":");
strcat(message,ap_mac_address);
printf(" %d \n", (1 + strlen(ap_count)+ strlen(ap_mac_address)));
return message;
}
--- while verifying,
char* message;
message = generate_hash();
I am using the below function call to generate the signature in both the cases.
if(RSA_sign(NID_sha256, (unsigned char*) message, strlen(message),
signature, &slen, private_key) != 1) {
ERR_print_errors_fp(stdout);
return 1;
}
The signature verification fails with this above procedure. Not sure what I am doing wrong here.
Below is the call I am using to verify the same.
verified = RSA_verify(NID_sha256, (unsigned char*) message,
strlen(message), sign, file_len, public_key);
verified = RSA_verify(NID_sha256, (unsigned char*) message,
strlen(message), sign, file_len, public_key);
The signature could have an embedded NULL. Do not treat it like string data, and don't use strlen on it.
You have to manage a pointer and an explicit length.
The description of RSA_verify tells following:
RSA_verify() verifies that the signature sigbuf of size siglen matches
a given message digest m of size m_len. type denotes the message
digest algorithm that was used to generate the signature. rsa is the
signer's public key.
So, using RSA_verify with original message is not correct: message digest should be used instead.
Looking for a bit of help using OpenSSL's HMAC function. Currently this function is failing on the HMAC call. ONLY for OSX. Both linux and windows os's are working okay.
QString tradingDialog::HMAC_SHA512_SIGNER(QString UrlToSign, QString Secret){
QString retval = "";
QByteArray byteArray = UrlToSign.toUtf8();
const char* URL = byteArray.constData();
QByteArray byteArrayB = Secret.toUtf8();
const char* Secretkey = byteArrayB.constData();
const EVP_MD *md = EVP_sha512();
unsigned char* digest = NULL;
// Be careful of the length of string with the choosen hash engine. SHA1 produces a 20-byte hash value which rendered as 40 characters.
// Change the length accordingly with your choosen hash engine
char mdString[129] = { 0 };
// Using sha512 hash engine here.
digest = HMAC(md, Secretkey, strlen( Secretkey), (unsigned char*) URL, strlen( URL), NULL, NULL);
for(int i = 0; i < 64; i++){
sprintf(&mdString[i*2], "%02x", (unsigned int)digest[i]);
}
retval = mdString;
return retval;
}
You don't say what the problem is on osx, but it looks like you're not nul terminating mdString, so try changing it to
char mdString[129] = { 0 };
The crashlog you linked to shows that your app is aborting because the stack has been corrupted (I assume this happens on exit).
I would say the final sprintf is causing this, as it is adding a nul byte after the end of your mdString array. Try the above modification and see if that helps.
This ought to crash on all platforms, but I guess you got "lucky".