TheHorse
TheHorse

Reputation: 2797

WinAPI -> CryptoAPI -> RSA, encrypt with private, decrypt with public

Good day.

I need to teach Windows CryptoAPI to encrypt the message with private (not public) part of the key, and decrypt with public. This is necessary to give users information, that they can read, but can't change.

How it works now:

I get the context

CryptAcquireContext(@Prov, PAnsiChar(containerName), nil, PROV_RSA_FULL, 0)

generate a key pair

CryptGenKey(Prov, CALG_RSA_KEYX, CRYPT_EXPORTABLE, @key)

Encrypt (and the problem is here. "key" - a keypair, and the function uses its public part);

CryptEncrypt(key, 0, true, 0, @res[1], @strLen, buffSize)

Decrypt (the same problem here, it uses the private part of the key)

CryptDecrypt(key, 0, true, 0, @res[1], @buffSize)

Thank you for your attention / help.

Update

Yes, I could use a digital signature and other metods...

The problem is that I need to encrypt one database field and make sure that no one but me can change it. It will be possible to read this field only with the help of my program (till someone decompiles it and get public key). This could be done with symmetrical key and digital signatures, but then i will need to create another field and store another key and so on...

I do hope that we can somehow teach WIN API to do as I want. I know that i can do so with RSA, and I hope that somehow WinAPI supports this feature.

Upvotes: 4

Views: 7836

Answers (2)

Ian Boyd
Ian Boyd

Reputation: 256571

Strictly speaking, when "signing" a message:

  • the person with the private key decrypts the hash with their private key.
  • they then send that "decrypted" hash along with the message.
  • the receiver then encrypts the signature with the public key

If the "encrypted" hash matches the hash of the original message, you know the message has not been altered, and was sent by the person with the private key. The following pseudo-code represents the signing algorithm:

//Person with private key generating message and signature
originalHash = GenerateHashOfMessage(message);
signature = RsaDecrypt(originalHash, privateKey);

//Receiver validating signed message
hash = GenerateHashOfMessage(message);
originalHash = RsaEncrypt(signature, publicKey);
messageValid = (hash == originalHash);

This same mechanism can be used to accomplish what you want. Except you don't care about hashes, you just want to encrypt some (small) amount of data:

//Person with private key
cipherText = RsaDecrypt(plainText, privateKey);

//Person with public key
plainText = RsaEncrypt(cipherText, publicKey);

i'll leave the CryptoAPI calls as an excercise - since i'm still trying to figure out Microsoft's Crypto API.

Upvotes: 3

Marcus Adams
Marcus Adams

Reputation: 53830

Encrypting data with the private key and decrypting it with the public key isn't supported because anyone with the "published" public key could decrypt it. What's the value in encrypting it then?

If you want to verify that data hasn't been changed, you will want to sign the data instead. Signing encrypts a hash of the data with the private key. Look at the signing functions.

You may be able to trick out the signing functions to do what you want. I've done this with other implementations, but I haven't tried with the Microsoft CryptoAPI.

Also, note that with RSA encryption, the plain text message cannot be longer than the key. So, if you are using a 2048 bit key, you can only encrypt a message body of up to 256 bytes (minus a few for overhead).

Consider using asymmetric encryption just to pass a symmetric key, and use the symmetric key to encrypt and decrypt any size data.

Update

You may be able to use the CryptSignHash() function for this. Normally, this is used to "sign" a hash, but you can put any data you want into the hash:

Set the hash value in the hash object by using the HP_HASHVAL value of the dwParam parameter in CryptSetHashParam.

You might be limited to so many bytes if the input is expected to be a SHA1 hash value.

Alternatively, you may wish to consider using OpenSSL. If I recall correctly, it's pretty straight forward to use its RSA signing functions to encrypt with the private key.

Also, I accomplished the same thing using the old (freeware) version of SecureBlackbox. You may be able to find the old free version, but it's not Unicode friendly, so you'll have some conversion to do if you're using a new Delphi. I've done this in the past also, so it's not too difficult.

You may also consider trying out the current SecureBlackbox and purchase it if it works for you.

Otherwise, as you stated, sign it to detect tampering, and encrypt it with a symmetric key that only the program knows in order to obfuscate it.

If they crack your code, anything's fair game anyway.

Upvotes: 2

Related Questions