Sign and decrypt data using private keys located on a HSM - encryption

I have a task to sign and decrypt data, but the private keys are located at a HSM ( Luna SA, / Safenet ). I installed all the client software and connected the Luna SA CSP to the test servers.
Using the PKCS#11 functions provided, I am able to list and export the public keys on the HSM as PCCERT_CONTEXT (CertCreateCertificateContext). When I try to acquire the private key (using CryptoAPI function CryptAcquireCertificatePrivateKey), I receive an error code CRYPT_E_NO_KEY_PROPERTY.
I am probably missing the link between the certificate data and the CSP/HSM. Has anybody done something similar and can give any hints?
EDIT
I sucessfully created CER files from all the keys located on the HSM.
When i know use signtool.exe (the one that ships with Microsoft Plattform SDK) i am able to sign a dll with a key on the HSM (the tool wizard lets me choose key container, key spec, ...). I tried to use the information the tool shows me and set the private key
bool LinkPrivateKey(PCCERT_CONTEXT cert)
{
CRYPT_KEY_PROV_INFO cryptKeyProvInfo;
memset(&cryptKeyProvInfo, 0, sizeof(cryptKeyProvInfo));
cryptKeyProvInfo.pwszContainerName = L"MSS";
cryptKeyProvInfo.pwszProvName = L"Luna Cryptographic Services for Microsoft Windows";
cryptKeyProvInfo.dwProvType = PROV_RSA_FULL;
cryptKeyProvInfo.dwFlags = CRYPT_MACHINE_KEYSET; // CERT_SET_KEY_CONTEXT_PROP_ID | CERT_SET_KEY_PROV_HANDLE_PROP_ID;
cryptKeyProvInfo.cProvParam = 0;
cryptKeyProvInfo.dwKeySpec = AT_SIGNATURE;
return CertSetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, 0, &cryptKeyProvInfo) != FALSE;
}
but CryptAcquirePrivateKey still gives me the same error. I believe I am missing only a small bit here, since the signtool is able to access the private key
Edit2
The screnshot shows KEYEXCHANGE but I chose SIGNATURE
Edit3
I changed the LinkPrivateKeyfunction a little bit, now it works
bool LinkPrivateKey(PCCERT_CONTEXT cert)
{
CRYPT_KEY_PROV_INFO cryptKeyProvInfo;
memset(&cryptKeyProvInfo, 0, sizeof(cryptKeyProvInfo));
cryptKeyProvInfo.pwszContainerName = L"MSS";
cryptKeyProvInfo.pwszProvName = L"Luna Cryptographic Services for Microsoft Windows";
cryptKeyProvInfo.dwProvType = PROV_RSA_FULL;
cryptKeyProvInfo.dwFlags = 1; // CERT_SET_KEY_CONTEXT_PROP_ID | CERT_SET_KEY_PROV_HANDLE_PROP_ID;
cryptKeyProvInfo.dwKeySpec = AT_SIGNATURE;
return CertSetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, 0, &cryptKeyProvInfo) != FALSE;
}

I don't have any experience with Luna but this worked well for nCiper HSM:
certutil -repairstore -csp "nCipher Enhanced Cryptographic Provider" My <serial number of certificate>
where "nCipher Enhanced Cryptographic Provider" is the name of CSP that comes with HSM.
Serial number of certificate can be obtained using this command:
certutil -store My
It will print all certificates in Local_Machine\My store. The serial number will be between bunch of ====== like ================ Certificate 5 ================. It will also parse information about the certificates like serial number, subject etc. and it will run encrypt/decrypt test to verify usability of the certificate.
After you repair the binding you can use this (second) command to verify that it went well. Don't be fooled by output of the first command, I have never seen it put out anything other than success.
You can find more information about usage of certutil here.

As stated in my post, I can link the private key using
I changed the LinkPrivateKeyfunction a little bit, now it works
bool LinkPrivateKey(PCCERT_CONTEXT cert)
{
CRYPT_KEY_PROV_INFO cryptKeyProvInfo;
memset(&cryptKeyProvInfo, 0, sizeof(cryptKeyProvInfo));
cryptKeyProvInfo.pwszContainerName = L"MSS";
cryptKeyProvInfo.pwszProvName = L"Luna Cryptographic Services for Microsoft Windows";
cryptKeyProvInfo.dwProvType = PROV_RSA_FULL;
cryptKeyProvInfo.dwFlags = 1; // CERT_SET_KEY_CONTEXT_PROP_ID | CERT_SET_KEY_PROV_HANDLE_PROP_ID;
cryptKeyProvInfo.dwKeySpec = AT_SIGNATURE;
return CertSetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, 0, &cryptKeyProvInfo) != FALSE;
}
YOu have to replace L"MSS"with the key container defined on you server.
LunSA provides the tool keymap.exe that is insatlled along with the LunaCSP to get the container names.

Related

HSM decryption + encryption chaining

My application is acting as a switchboard to transfer very sensitive messages between two parties and I'm trying to figure out how I can do this without "looking" at the message they're trying to send.
I have a HSM, and I've generated a keypair for the sender - they're going to encrypt the message with their public key that I gave them, and I can decrypt the message using the private key I have in the HSM.
I'm going to then pass that message on by encrypting it with the public key of the ultimate receiver, which I have.
Is there any way to chain these two operations inside the HSM, without having the decrypted message anywhere near my application memory? I would like the plaintext content to never leave the boundaries of the HSM.
I know that some HSMs have a CodeSafe / SEE machine feature that lets me write embedded system code and run it inside the HSM, before I get into that I'd like to see if there's a way using the general PKCS / JCE / CNG APIs to do this safely.
If all you need is to re-encrypt the same secret under a different key, you can use C_Unwrap to create a temporal HSM object with value of the translated secret and then use C_Wrap to encrypt the value of this temporal HSM object for all the recipients.
This way the secret will never leave HSM.
Something like this (verified to be working on SafeNet Luna 7 with RSA-OAEP):
// Your private key for 'decrypting' secret. Must have key unwrapping allowed
CK_OBJECT_HANDLE hsmPrivateKey = ... ;
// Encrypted secret
byte[] wrappedKey = ... ; //
// Template for temporal generic secret key with value of the secret
CK_ATTRIBUTE[] tempTemplate = new CK_ATTRIBUTE[] {
new CK_ATTRIBUTE(CKA.CLASS, CKO.SECRET_KEY),
new CK_ATTRIBUTE(CKA.KEY_TYPE, CKK.GENERIC_SECRET),
new CK_ATTRIBUTE(CKA.TOKEN, false),
new CK_ATTRIBUTE(CKA.PRIVATE, true),
new CK_ATTRIBUTE(CKA.EXTRACTABLE, true),
new CK_ATTRIBUTE(CKA.SENSITIVE, true),
new CK_ATTRIBUTE(CKA.ENCRYPT, false),
new CK_ATTRIBUTE(CKA.DECRYPT, false),
new CK_ATTRIBUTE(CKA.WRAP, false),
new CK_ATTRIBUTE(CKA.UNWRAP, false),
new CK_ATTRIBUTE(CKA.SIGN, false),
new CK_ATTRIBUTE(CKA.VERIFY, false),
new CK_ATTRIBUTE(CKA.DERIVE, false)
};
// Unwrapping/decryption mechanism
CK_MECHANISM mechanism = ... ;
// Handle for temporal generic secret key with value of the secret
CK_OBJECT_HANDLE temporalValueHandle = new CK_OBJECT_HANDLE();
// Unwrap/decrypt the secret into temporal key
CryptokiEx.C_UnwrapKey(session, mechanism, hsmPrivateKey, wrappedKey, wrappedKey.length, tempTemplate, tempTemplate.length, temporalValueHandle);
// Wrap/encrypt the secret for recipients. Recipient public keys must have key wrapping allowed
for(CK_OBJECT_HANDLE recipientPublicKey : ... ) {
LongRef resSize = new LongRef(0);
CryptokiEx.C_WrapKey(session, mechanism, recipientPublicKey, temporalValueHandle, null, resSize);
byte[] rewrappedKey = new byte[CryptokiUtils.safeIntCast(resSize.value)];
CryptokiEx.C_WrapKey(session, mechanism, recipientPublicKey, temporalValueHandle, rewrappedKey, resSize);
System.out.println("Re-wrapped key: " + bytesToHexString(rewrappedKey));
}
// Delete temporal generic secret key
CryptokiEx.C_DestroyObject(session, temporalValueHandle);
Good luck!

How x509chain works in C#? What is purpose of using it?

I have implement client certificate based security in C#. Everything is clear and working good for me. I was finding perfect way to verify client certificate on server side, through which i came to x509Chain. But i am not clear about How it exactly works? How can we configure it? Is it secure way to verify certificate?
Any help will be Appreciated! Thanks!
Note: By theoretical point of view, i have read my documents on that but i am not sure for it's functionality. Please provide practical example and guide for that.
A bit of theory I wrote some time ago: http://social.technet.microsoft.com/wiki/contents/articles/3147.certificate-chaining-engine-cce.aspx
This article describes what is certificate chaining engine (CCE) and how it works in Windows in general. It is based on RFC5280 and Microsoft-specific implementation of certificate chaining engine. .NET uses native CryptoAPI functions, so X509Chain behaves in the same way as in native CryptoAPI.
The X509Chain does not work reliably for scenarios where you do not have the root certificate in the trusted CA store on the machine.
Others will advocate using bouncy castle. I wanted to avoid bringing in another library just for this task, so I wrote my own.
As see in RFC3280 Section 4.1 the certificate is a ASN1 encoded structure, and at it's base level is comprised of only 3 elements.
The "TBS" (to be signed) certificate
The signature algorithm
and the signature value
Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signatureValue BIT STRING
}
C# actually has a handy tool for parsing ASN1, the System.Formats.Asn1.AsnDecoder.
Using this, we can extract these 3 elements from the certificate to verify the chain.
The first step was extracting the certificate signature, since the X509Certificate2 class does not expose this information and it is necessary for the purpose of certificate validation.
Example code to extract the signature value part:
public static byte[] Signature(
this X509Certificate2 certificate,
AsnEncodingRules encodingRules = AsnEncodingRules.BER)
{
var signedData = certificate.RawDataMemory;
AsnDecoder.ReadSequence(
signedData.Span,
encodingRules,
out var offset,
out var length,
out _
);
var certificateSpan = signedData.Span[offset..(offset + length)];
AsnDecoder.ReadSequence(
certificateSpan,
encodingRules,
out var tbsOffset,
out var tbsLength,
out _
);
var offsetSpan = certificateSpan[(tbsOffset + tbsLength)..];
AsnDecoder.ReadSequence(
offsetSpan,
encodingRules,
out var algOffset,
out var algLength,
out _
);
return AsnDecoder.ReadBitString(
offsetSpan[(algOffset + algLength)..],
encodingRules,
out _,
out _
);
}
The next step is to extract the TBS certificate. This is the original data which was signed.
example code to extract the TBS certificate data:
public static ReadOnlySpan<byte> TbsCertificate(
this X509Certificate2 certificate,
AsnEncodingRules encodingRules = AsnEncodingRules.BER)
{
var signedData = certificate.RawDataMemory;
AsnDecoder.ReadSequence(
signedData.Span,
encodingRules,
out var offset,
out var length,
out _
);
var certificateSpan = signedData.Span[offset..(offset + length)];
AsnDecoder.ReadSequence(
certificateSpan,
encodingRules,
out var tbsOffset,
out var tbsLength,
out _
);
// include ASN1 4 byte header to get WHOLE TBS Cert
return certificateSpan.Slice(tbsOffset - 4, tbsLength + 4);
}
You may notice that when extracting the TBS certiifcate I needed to include the ASN1 header in the data, this is because the signature of the TBS Certificate INCLUDES this data (this annoyed me for a while).
For the first time in history, the Microsoft does not impede us with their API design, and we are able to obtain the Signature Algorithm directly from the X509Certificate2 object. Then we just need to decide to what extend we are going to implement different hash algorithms.
var signature = signed.Signature();
var tbs = signed.TbsCertificate();
var alg = signed.SignatureAlgorithm;
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnap/a48b02b2-2a10-4eb0-bed4-1807a6d2f5ad
switch (alg)
{
case { Value: var value } when value?.StartsWith("1.2.840.113549.1.1.") ?? false:
return signedBy.GetRSAPublicKey()?.VerifyData(
tbs,
signature,
value switch {
"1.2.840.113549.1.1.11" => HashAlgorithmName.SHA256,
"1.2.840.113549.1.1.12" => HashAlgorithmName.SHA384,
"1.2.840.113549.1.1.13" => HashAlgorithmName.SHA512,
_ => throw new UnsupportedSignatureAlgorithm(alg)
},
RSASignaturePadding.Pkcs1
) ?? false;
case { Value: var value } when value?.StartsWith("1.2.840.10045.4.3.") ?? false:
return signedBy.GetECDsaPublicKey()?.VerifyData(
tbs,
signature,
value switch
{
"1.2.840.10045.4.3.2" => HashAlgorithmName.SHA256,
"1.2.840.10045.4.3.3" => HashAlgorithmName.SHA384,
"1.2.840.10045.4.3.4" => HashAlgorithmName.SHA512,
_ => throw new UnsupportedSignatureAlgorithm(alg)
},
DSASignatureFormat.Rfc3279DerSequence
) ?? false;
default: throw new UnsupportedSignatureAlgorithm(alg);
}
As shown in the code above, https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gpnap/a48b02b2-2a10-4eb0-bed4-1807a6d2f5ad is a good resource to see the mapping of algorithms and OIDs.
Another thing you should be aware of is that there are some articles out there that claim that for elliptical curve algorithms, microsoft expects a R,S formatted key instead of a DER formatted key. I tried to convert the key to this format but it ultimately didn't work. What I discovered was that it was necessary to use the DSASignatureFormat.Rfc3279DerSequence parameter.
Additional certificate checks, like "not before" and "not after", or CRL and OCSP checks can be done in addition to the chain verification.

.Net Support of SHA256withRSA

I need to support the following signature:
Sign the UTF-8 representation of the input using SHA256withRSA (also known as RSASSA-PKCS1-V1_5-SIGN with the SHA-256 hash function) with the private key obtained from the Google Developers Console. The output will be a byte array.
The following code fails, with "Invalid algorithem specified". Is this a limitation of .NET? Here is a snippet of my code:
var rsa2 = new RSAPKCS1SignatureFormatter(rsa);
rsa2.SetHashAlgorithm("SHA256");
bytes = rsa2.CreateSignature(bytes);
The above requirement is from computing the signature for a Server to Server Applications for Google API.
https://developers.google.com/accounts/docs/OAuth2ServiceAccount#computingsignature
Thanks for any help.
Karl..
Here is the code that signs the JWT. I've removed the use of RSAPKC1SingatureFormatter class and using another HASHCompute method in an effort to get something to work (still not working)
I'm not sure this is correct and unfortunately my response from the Rest service is always the same "Invalid Grant" so hard to tell.
public string Generate(string HeadJWT, string ContentJWT, X509Certificate2 certificate)
{
var bytes = Utility.getBytes(HeadJWT);
var base64Head = Utility.Base64UrlEncode(bytes);
// base64 Url Encode Payload (Json Content)
bytes = Utility.getBytes(ContentJWT);
var base64Payload = Utility.Base64UrlEncode(bytes);
var secureInputValue = String.Format("{0}.{1}", base64Head, base64Payload);
bytes = Stub.Jwt.Utility.getBytes(secureInputValue);
bytes = Stub.Jwt.Utility.ComputeHMACSha265(bytes, certificate.PublicKey.EncodedKeyValue.RawData);
_signature = Stub.Jwt.Utility.Base64UrlEncode(bytes);
return String.Format("{0}.{1}.{2}", base64Head, base64Payload, _signature);
}
This cannot be a limitation of .NET in general, as the example Microsoft code seems to use "SHA256" itself. But it could be a limitation of your particular runtime.

Node.js Password Encryption Same As In Asp.Net

I have created a wesite in asp.net and use ms-sql database to save the records. Now want to convert it in node.js application. And want to use same sql database. In asp.net application I have encrypt the password for registered user. Below is code.
public static string CreateHash(string unHashed)
{
System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
byte[] data = System.Text.Encoding.ASCII.GetBytes(unHashed);
data = x.ComputeHash(data);
return System.Text.Encoding.ASCII.GetString(data);
}
public static bool MatchHash(string HashData, string HashUser)
{
HashUser = CreateHash(HashUser);
if (HashUser == HashData)
return true;
else
return false;
}
Now problem is that how I use same encryption in node.js. So when node application is ready old user can also make login. It only possible if node app also use same encryption that I have use in asp.net.
For node I have created all environment and use mssql module for database communication. Please help me fix that. Thanks!!
First of all MD5 shall no longer be used if you are serious about security.
Based on your comment and code, I fear there is a 'data loss' in the initial ASP.net code.
Let us have a look at CreateHash function again, I've added comments:
public static string CreateHash(string unHashed)
{
System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
// Convert unHashed string to bytes using ASCII coding
byte[] data = System.Text.Encoding.ASCII.GetBytes(unHashed);
// Compute MD5 hash from bytes
data = x.ComputeHash(data);
// Decode MD5 resulting bytes as ASCII
return System.Text.Encoding.ASCII.GetString(data);
}
The last line confuses me, it is decoding bytes received from MD5 function as if they were ASCII, but that is incorrect assumption. And the resulting encoded string as you gave in comment contains lots of "?'s".
Next node.js code will do similar except encode the string using hex rather than ascii:
var crypto = require('crypto')
function createHash(data) {
return crypto.createHash('md5').update(data, 'ascii').digest('hex')
}
To emulate "bytes to ascii" you could try .digest('binary') instead of hex. If it does not give what you expect, then you have to make a separate 'conversion' step from hex to ascii. (I am not experienced enough to give you elegant solution to the later one)

How to generate and use public key cryptography in WinCrypt

I'm currently experimenting with the Windows Cryptography API and running into some problems with Public-Key-Cryptography. I can find lots of examples of how to encrypt items, but nothing directly addressing a start-to-finish public key model.
Here's a rough outline of how my current code looks to generate an encryption key pair, I've removed the error checking code for readability
// MAKE AN RSA PUBLIC/PRIVATE KEY:
CryptGenKey(hProv, CALG_RSA_KEYX, CRYPT_EXPORTABLE, &hKey);
// NOW LET'S EXPORT THE PUBLIC KEY:
DWORD keylen;
CryptExportKey(hKey,0,PUBLICKEYBLOB,0,NULL,&keylen);
LPBYTE KeyBlob;
KeyBlob = (LPBYTE)malloc(keylen);
CryptExportKey(hKey,NULL,PUBLICKEYBLOB,0,KeyBlob,&keylen);
ofstream outputkey;
outputkey.open("TestPublicKey.txt", ios_base::out | ios_base::binary);
for(size_t i=0; i &lt keylen; ++i)
outputkey&lt&ltKeyBlob[i];
outputkey.close();
free(KeyBlob);
// NOW LET'S EXPORT THE PRIVATE KEY:
CryptExportKey(hKey, 0, PRIVATEKEYBLOB,0,NULL,&keylen);
KeyBlob = (LPBYTE)malloc(keylen);
CryptExportKey(hKey,NULL,PRIVATEKEYBLOB,0,KeyBlob,&keylen)
outputkey.open("TestPrivateKey.txt", ios_base::out | ios_base::binary);
for(size_t i=0;i&ltkeylen;++i)
outputkey&lt&ltKeyBlob[i];
outputkey.close();
free(KeyBlob);
// ENCRYPT A (SHORT) TEST MESSAGE [SHOULD JUST BE ANOTHER ALG'S KEY LATER]:
DWORD encryptBufferLen=0;
CryptEncrypt(hKey, 0, true, 0, NULL, &encryptBufferLen, 0); // how much space?
BYTE* encryptionBuffer = (BYTE*)malloc(encryptBufferLen);
memcpy(encryptionBuffer, TestMessage, TestMessageLen); // move for in-place-encrypt
CryptEncrypt(hKey,0,true,0, encryptionBuffer, &bufferlen, encryptBufferLen );
ofstream message;
message.open("Message.txt", ios_base::out | ios_base::binary);
for(size_t i=0;i&ltencryptBufferLen;++i)
message&lt&ltencryptionBuffer[i];
message.close();
My two exported keys are different, but both are able to decrypt message without the other key being loaded. Additionally, if I encrypt a new message in a new session that loads the exported public key, I can still decrypt it with either key.
Can anyone advise me on what I might be doing wrong or missing? Am I on completely the wrong path?
I do not fully understand your query. But generally
You do not encrypt data directly using a public key.
During Encryption: You use a session/symmetric/private key to encrypt data. This session key is then being encrypted by the AT_EXCHANGE public key.
During Decryption: The AT_EXCHANGE private key will decrypt the session key. In turn this session key will be used to decrypt the actual data.
I am not an expert with this encryption stuff just yet! but I am working with this at the moment so can feel your pain...
Something looks amiss with the encrypt bit
// ENCRYPT A (SHORT) TEST MESSAGE [SHOULD JUST BE ANOTHER ALG'S KEY LATER]:
DWORD encryptBufferLen=0;
CryptEncrypt(hKey, 0, true, 0, NULL, &encryptBufferLen, 0); // how much space?
BYTE* encryptionBuffer = (BYTE*)malloc(encryptBufferLen);
memcpy(encryptionBuffer, TestMessage, TestMessageLen); // move for in-place-encrypt
CryptEncrypt(hKey,0,true,0, encryptionBuffer, &bufferlen, encryptBufferLen );
It appears that you are asking the function to give you the size of the encrypted message so that you can assign its memory i.e. size... BUT you are not passing anything in apart from the key itself so I dont think it would be able to give that information surely..
You have CryptEncrypt(hKey, 0, true, 0, NULL, &encryptBufferLen, 0); // but why "NULL" when you actually should pass in the buffer containing the string to be encrypted, so that it can work out the size and return it for you! Then you can move on with the rest.. I know thats how the export bit works (because your working with the keys in that context), whereas here you are dealing with the actual message. Try passing in the parameter and see how that goes?
I think you'll find that your not actually able to decrypt with both keys, as you haven't actually encrypted anything yet???? Your encrypting a message into a zero sized buffer I think.
As I said not an expert but it does look wrong to me... Strangest thing is I found your post the other day and harvested the working bits to help me understand it, so I really hope that this info helps you!
Go through the links below provided by microsoft
http://msdn.microsoft.com/en-us/library/windows/desktop/aa382358(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa382044(v=vs.85).aspx
Are you using CryptImportKey on both keys? It looks like your encrypt is just using the handle to thekey you generated. To do Public/Private pairs correctly you should export just the public key with CryptExportKey and give it to whomever needs it. Although this isn't true "encryption" its a way for that person to know it is from you.

Resources