Reputation: 349
I need to code some cryptographic logic in C (.NET / C# is not an option). I've gotten as far as BCryptSetProperty in generating a key pair. The API consistently fails with STATUS_NOT_SUPPORTED / 0xC00000BB. I've tried variations on the parameters. I have not been able to find a C example for using BCryptSetProperty with DSA to generate keys. Any assistance is appreciated.
NTSTATUS nts,nts2;
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
DWORD cbCipherText = 0,
cbPlainText = 0,
cbData = 0,
cbKeyObject = 0,
cbBlockLen = 0,
cbBlob = 0;
// Get access to the algorithm
try {
nts = BCryptOpenAlgorithmProvider(&hAlg,
BCRYPT_DSA_ALGORITHM, // The algorithm we will be using
MS_PRIMITIVE_PROVIDER, // The default; this parameter could be NULL
0 // | BCRYPT_PROV_DISPATCH // This flag may only be used in driver
);
}
catch(...) {}
if (STATUS_SUCCESS != nts)
{
cout << "BCryptOpenAlgorithmProvider failed with status 0X" << std::hex << nts << endl;
return nts;
}
#define KEYSIZE 3072
// Make an empty holder for our key
nts = BCryptGenerateKeyPair(hAlg,
&hKey, // Empty new key
KEYSIZE, // Key length - 3072 max length for BCRYPT_DSA_ALGORITHM
0); // No flags defined
if (STATUS_SUCCESS != nts) { cout << "BCryptGenerateKeyPair failed with status 0X" << hex << nts << endl; goto cleanup1; }
// Set parameters to be used in generating the key into our empty holder
// Build a header for the key
#define CBSEEDLENGTH 43
#define CBGROUPSIZE 32
#define CBKEYLENGTH 384
struct _DSA_PARAMETER_BLOB
{
BCRYPT_DSA_PARAMETER_HEADER_V2 H;
UCHAR Seed[CBSEEDLENGTH]; // Big-endian.
UCHAR q[CBGROUPSIZE]; // Big-endian.
UCHAR Modulus[CBKEYLENGTH]; // Big-endian.
UCHAR Generator[CBKEYLENGTH]; // Big-endian.
} dpb;
dpb.H.cbLength = sizeof(dpb);
dpb.H.dwMagic = BCRYPT_DSA_PARAMETERS_MAGIC_V2;
dpb.H.cbKeyLength = CBKEYLENGTH; // 3072 / 8
dpb.H.hashAlgorithm = DSA_HASH_ALGORITHM_SHA512; // only the left 256 bits are used
dpb.H.standardVersion = DSA_FIPS186_3;
dpb.H.cbSeedLength = CBSEEDLENGTH; // For L == 3072, len(q) = 256, cbSeedLength (measured in bytes) must be >= len(q), so >=32
dpb.H.cbGroupSize = CBGROUPSIZE; // Because L > 1024 (L == 3072) (groupsize ::= len(q))
dpb.H.Count[0] = dpb.H.Count[1] = dpb.H.Count[2] = 0;
dpb.H.Count[3] = 64; // # of iterations stored big endian
nts = BCryptSetProperty(hAlg, // Note: all of these calls reference the top level object - the algorithm
BCRYPT_DSA_PARAMETERS, // Setting parameters for key generation
(PUCHAR)&dpb, // Parameters struct
sizeof(dpb),
0); // No flags defined
if (STATUS_SUCCESS != nts) { cout << "BCryptSetProperty failed with status 0X" << hex << nts << endl; goto cleanup2; }
Upvotes: 0
Views: 148
Reputation: 349
The shortest answer, now that I know more about how DSA and other cryptography works, is that there is no free lunch. CNG will not automatically provide the required huge random numbers for DSA. It is up to the designer to choose appropriate random numbers.
One of the best sources I found in my research for specific information and examples is Svetlin Nakov. See for instance: https://cryptobook.nakov.com/asymmetric-key-ciphers/the-rsa-cryptosystem-concepts and https://cryptobook.nakov.com/digital-signatures
You can find some useful prime numbers there to test your coding, but you should not use those prime numbers in your final code as they have been published in examples.
For actually generating keys, you are best off using a tool such as OpenSSL to generate the keys into a PEM file, then import them with CNG as described here: How to convert a public key in PEM format to a CNG key blob?
Upvotes: 0