Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 4 years ago.
Improve this question
There is RSA signature check which has following properties:
RSA modulus N which is 2048-bit
RSA exponent equal 41 (0x29)
Signature uses EMSA-PKCS1-v1_5 padding of SHA1 hash, however the decoder only compares 20 least significant bytes which are the SHA1 hash
This means that all but the 20 least significant bytes, after rising forged signature to 41st power modulo N, can be garbage.
I have checked following:
Bleichenbacher's RSA signature forgery based on implementation error is not applicable here as it deals with most significant bytes (least significant bytes are garbage) and as I tested it cannot forge signature that would contain 20 bytes of arbitrary chosen prefix when e=41
Yutaka Oiwa, Kazukuni Kobara, Hajime Watanabe: A New Variant for an Attack Against RSA Signature Verification Using Parameter Field - RSA modulus is too small for this method to be effective here. This is the attack that Intel used in BERserk
Is there any computationally feasible algorithm that could be used to forge signature that verifier, which only checks the 20 least significant bytes, would accept?
Edit:
Is the Poncho comment from another RSA attack question feasible here? If so, how to implement:
x=MD5(Message) find a 128 bit number y with (which will exist if x is odd, and can be found in 128 steps);
The changes in this case would be:
41 instead of 3
SHA1 instead of MD5
180 instead of 128
Edit2:
I think the Poncho comments mentions exactly the same what is implemented in forge_suffix_odd presented in BERserk Vulnerability part 1.
This function however, as noted previously, gives too big values. Is the result of forge_suffix_odd the lowest possible number fullfilling the goal?
Edit3: forge_suffix_odd returns the lowest possible number fullfilling the goal.
Edit4: Greg Martin answered my question at Mathematics and provided very useful solution for SHA1 checksums whose least significant bits equal 0.
Related
This question is related to Opening and checking a Pem file in SWI-Prolog
Once I have downloaded and opened the certificates how do I verify the signature chain?
I have:
:-use_module(library(http/http_client)).
url('https://s3.amazonaws.com/echo.api/echo-api-cert-4.pem').
url_data1(Url,Certs):-
http_open(Url,Stream,[]),
all_certs(Stream,Certs),
forall(member(C,Certs),my_validate(C)),
close(Stream).
all_certs(Stream,[C1|Certs]):-
catch(load_certificate(Stream,C1),_,fail),
all_certs(Stream,Certs),!.
all_certs(_Stream,[]).
my_validate(C):-
memberchk(to_be_signed(Signed),C),
memberchk(key(Key),C),
memberchk(signature(Signature),C),
memberchk(signature_algorithm(A),C),
algo_code(A,Code),
rsa_verify(Key,Signed,Signature,[type(Code)]).
algo_code('RSA-SHA256',sha256).
algo_code('RSA-SHA1',sha1).
This currently fails.
Preliminaries
Verifying digital signatures and entire certificate chains is extremely easy with Prolog.
However, you need to have a basic understanding of how certificates are signed. A certificate chain is a sequence of certificates C0, C1, ..., CN. I am using CN to denote the root certificate. Depending on the used convention, you may mutatis mutandis reverse the order of course.
Importantly, certificate Ck is signed using the private key corresponding to the public key of Ck+1.
Thus, one of the issues with your code is that you are mistakenly using the public key of C to verify the signature of C even though the certificate was signed with the private key corresponding to a different certificate.
A different issue stems from some confusion about what is being signed. We are signing the hash of the to-be-signed part of a certificate, not the data itself. Thus, we must verify the signature against that hash.
Concrete example
To make this answer self-contained, I post here the relevant data from your use case, i.e., the relevant attributes of the certificates that the file contains at the time of this writing.
Data
First certificate
From the first certificate in the chain, we need the signature and the to-be-signed portion, which are:
signature("7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20").
to_be_signed("30820466A00302010202103F25CAAEE5AA838FA91C051A75FC719D300D06092A864886F70D01010B0500307E310B3009060355040613025553311D301B060355040A131453796D616E74656320436F72706F726174696F6E311F301D060355040B131653796D616E746563205472757374204E6574776F726B312F302D0603550403132653796D616E74656320436C61737320332053656375726520536572766572204341202D204734301E170D3136313030373030303030305A170D3137313033303233353935395A306D310B30090603550406130255533113301106035504080C0A57617368696E67746F6E3110300E06035504070C0753656174746C6531193017060355040A0C10416D617A6F6E2E636F6D2C20496E632E311C301A06035504030C136563686F2D6170692E616D617A6F6E2E636F6D30820122300D06092A864886F70D01010105000382010F003082010A02820101009CAFB306BB910354E76E0406C44F9BE178934D9C83906C09EB4EBC006B1742682DF655610BC0934C2E30751E4D5E8B1BA15EBFEB7C28AD008DA38D7672C0558D4CB71F5FD512CFB92AFF80880394B8AA017C453CCC0BC709CEC698E29480D89D703034312A71DD94CC48619B91F68B8A44739DEEA7159EA334E9E4A93B460FA4AB0886202CD02B49C6283F321C5C4CA91C5AE8827CEB47811ED1871E7C66724BCD58A9EBFF9658B4D5D02046FA6702DCBAF2B6139B190D8735121BA2086C51C8C5724A0044C090688A25C819A5F1B6D4E9390E4DF21AB11263F203E4E9F1BCCC625D29D7C21B7C243E9B775E6E8B4B4F0DAF390748E964B968A9065EDBAA11A30203010001A382020730820203301E0603551D110417301582136563686F2D6170692E616D617A6F6E2E636F6D30090603551D1304023000300E0603551D0F0101FF0404030205A0301D0603551D250416301406082B0601050507030106082B0601050507030230610603551D20045A30583056060667810C010202304C302306082B06010505070201161768747470733A2F2F642E73796D63622E636F6D2F637073302506082B0601050507020230190C1768747470733A2F2F642E73796D63622E636F6D2F727061301F0603551D230418301680145F60CF619055DF8443148A602AB2F57AF44318EF302B0603551D1F042430223020A01EA01C861A687474703A2F2F73732E73796D63622E636F6D2F73732E63726C305706082B06010505070101044B3049301F06082B060105050730018613687474703A2F2F73732E73796D63642E636F6D302606082B06010505073002861A687474703A2F2F73732E73796D63622E636F6D2F73732E637274300F06032B654D0408300602010102010130818B060A2B06010401D679020402047D047B0079007700A7CE4A4E6207E0ADDEE5FDAA4B1F86768767B5D002A55D47310E7E670A95EAB2000001579FAA404A0000040300483046022100CA6B7C069C140B774E8D64ED95D4A59E92349E04AA3C71B304F32C74BC857F81022100FA152CF8558BA0A87B515AB7522D1BE1725740E128042309DD274FEEA5EB3B62").
Second certificate
From the second certificate, we only need the public key to verify the signature that was issued for the previous certificate:
key(public_key(rsa("B2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7", "010001", -, -, -, -, -, -))).
Verification
Computing the hash
As I said, a hash is what was actually signed. Critically, I don't mean the hash of the whole certificate, but the hash of the to-be-signed portion. This difference is important, because the hash of the whole certificate also comprises the signature, and that is of course not yet available when the certificate is being signed.
In SWI-Prolog, we can obtain the hash of the to-be-signed portion using library(crypto):
?- to_be_signed(TBS),
hex_bytes(TBS, Bytes),
crypto_data_hash(Bytes, Hash, [algorithm(sha256), encoding(octet)]).
TBS = "3082...EB3B62",
Bytes = [48, 130, 4, 102, 160, 3, 2, 1, 2|...],
Hash = '651bdcdd90251f71a47a5d1bbc6f28486c94d2dc3739dcd58ecb09b3f224ee05'.
I am using sha256 because the first certificate indicates (RSA and) SHA256 in its signature_algorithm/1 field.
Verifying the signature using CLP(FD) constraints
One of the easiest ways to verify an RSA signature is to use CLP(FD) constraints. We only need to compute SigExp mod p. We plug in our concrete numbers, using (#=)/2 to evaluate the arithmetic expression over integers:
?- X #= 0x7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20^0x010001
mod 0xB2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7.
It yields:
X = 986236757547332986472011617696226561292849812918563355472727826767720188564083584387121625107510786855734801053524719833194566624465665316622563244215340671405971599343902468620306327831715457360719532421388780770165778156818229863337344187575566725786793391480600129482653072861971002459947277805295727097226389568776499707662505334062639449916265137796823793276300221537201727072401742985542559596685092673521228140822200236743113743661549252453726123450722876929538747702356573783116197523966334991563351853851212597377279504828784716104866621888265058037501385433453379649364782998949981722124880992983641605.
Excursion: On efficiency and use of CLP(FD).
You may now say: "Well, I don't really need (#=)/2, I can always use (is)/2 which I learned decades ago." But, if you are using (is)/2 in such examples, you easily end up with code that is thousands of times less efficient. As a simple benchmark, consider the predicate:
signature_pow(Sig, Exp, P, Pow) :-
Pow #= Sig^Exp mod P.
Now we have, for the query:
?- time(signature_pow(0x7B86A6E7A86B192579380108B7EADA1C25E288AB46120117DC6A80635324C89713C70206EF3FE13CCA5EDFF43601972CB96658826ADCD68B0FB3AE7F5607D036D6AE8AF824CD96D7F1B4DB58E714343031F292B17E6EEF83872A0FD586CC0D1DA85A677E4AB4C6540E9132B5BC5644533E0388F830B1B6757B7DB88AB82846F08B3B6DCEC7F24319AB7EA56F86592DDDEC4522CAF331C8B81A4E543FBDBF4D661B534BAE546465DB88A525BC82A7B4127F0AFF4A55525927A66A09055743F109E30D90CA074D258166F0E472CB7CCDB0747ADE74F7040CFEEB9A78C3483864C5106D542556C874AF768005A6EC83ADEB2EE32F8E6F7182A362775C2BF40AFA20, 0x010001, 0xB2D805CA1C742DB5175639C54A520996E84BD80CF1689F9A422862C3A530537E5511825B037A0D2FE17904C9B496771981019459F9BCF77A9927822DB783DD5A277FB2037A9C5325E9481F464FC89D29F8BE7956F6F7FDD93A68DA8B4B82334112C3C83CCCD6967A84211A22040327178B1C6861930F0E5180331DB4B5CEEB7ED062ACEEB37B0174EF6935EBCAD53DA9EE9798CA8DAA440E25994A1596A4CE6D02541F2A6A26E2063A6348ACB44CD1759350FF132FD6DAE1C618F59FC9255DF3003ADE264DB42909CD0F3D236F164A8116FBF28310C3B8D6D855323DF1BD0FBD8C52954A16977A522163752F16F9C466BEF5B509D8FF2700CD447C6F4B3FB0F7, Pow)).
the timing:
% 16 inferences, 0.000 CPU in 0.000 seconds (99% CPU, 130624 Lips)
In contrast, if we regress in Prolog language development and replace (#=)/2 by (is)/2, we get:
% 3 inferences, 1.847 CPU in 1.852 seconds (100% CPU, 2 Lips)
Reason: In SWI-Prolog, certain goals involving (#=)/2 automatically use specialized arithmetic predicates. You do not need to learn these predicates to use them. CLP(FD) does it for you.
Recommendation: Use CLP(FD) constraints for reasoning over integers in Prolog. They typically make your predicates more general, and sometimes vastly more efficient. clpfd
Now, what about X? To see what it is, consider its hexadecimal encoding:
?- format("~16r", [$X]).
1fffffff...fff003031300d060960864801650304020105000420651bdcdd90251f71a47a5d1bbc6f28486c94d2dc3739dcd58ecb09b3f224ee05
This sounds familiar: At the end, you see that the hash of the to-be-signed portion of the certificate appears. This means that the signature checks out!
Verifying the signature with rsa_verify/4
Alternatively, we can use rsa_verify/4 from library(crypto) to verify the signature.
Here is the full query:
?- to_be_signed(TBS),
hex_bytes(TBS, Bytes),
crypto_data_hash(Bytes, Hash, [algorithm(sha256), encoding(octet)]),
signature(Sig),
key(Key),
rsa_verify(Key, Hash, Sig, [type(sha256)]).
Since this succeeds, we know that the private key corresponding to Key was used to produce the signature.
Closing remarks
I have one important remark: Normally, this is all of course completely unnecessary!
The SWI-Prolog SSL infrastructure automatically verifies the certificate chain and thus all signatures every single time you use http_open/3 and related predicates to make a connection via TLS. But it is interesting to make these calculations yourself. Sometimes it is even necessary, if, as in this example, you are reasoning over certificates you have stored somewhere.
One small additional remark: Please use setup_call_cleanup/3 in your code. Otherwise, you risk leaking file descriptors if anything goes wrong before close/1, which is in fact even the case in your example.
is it true that RSA encryption only can handle limited payload of data ? ... im confused with the theory ... theoretically there is no note regarding this ...
RSA encrypts a single message which has a length which is somewhat smaller than the modulus. Specifically, the message is first "padded", resulting in a sequence of bytes which is then interpreted as a big integer between 0 and n-1, where n is the modulus (a part of the public key) -- so the padded message cannot be longer than the modulus, which implies a strict maximum length on the raw message.
Specifically, with the most common padding scheme (PKCS#1 "old-style", aka "v1.5"), the padding adds at least 11 bytes to the message, and the total padded message length must be equal to the modulus length, e.g. 128 bytes for a 1024-bit RSA key. Thus, the maximum message length is 117 bytes. Note that the resulting encrypted message length has the same size than the modulus, so the encryption necessarily expands the message size by at least 11 bytes.
The normal way of using RSA for encrypted a big message (say, an e-mail) is to use an hybrid scheme:
A random symmetric key K is chosen (a raw sequence of, e.g., 128 to 256 random bits).
The big message is symmetrically encrypted with K, using a proper and efficient symmetric encryption scheme such as AES.
K is asymmetrically encrypted with RSA.
"Splitting" a big message into so many 117-byte blocks, each to be encrypted with RSA, is not normally done, for a variety of reasons: it is difficult to do it right without adding extra weaknesses; each block would be expanded by 11 bytes, implying a non-negligible total message size increase (network bandwidth can be a scarce resource); symmetric encryption is much faster.
In the basic RSA algorithm (without padding) which is not very secure the size of the message is limited to be smaller than the modulus.
To enhance the security of RSA you should use padding schemes as defined in PKCS1. Depending on the scheme you choose the size of the message can be significantly smaller than the modulus.
http://en.wikipedia.org/wiki/PKCS1
is it true that RSA encryption only can handle limited payload of data ? ... im confused with the theory ... theoretically there is no note regarding this ...
RSA encrypts a single message which has a length which is somewhat smaller than the modulus. Specifically, the message is first "padded", resulting in a sequence of bytes which is then interpreted as a big integer between 0 and n-1, where n is the modulus (a part of the public key) -- so the padded message cannot be longer than the modulus, which implies a strict maximum length on the raw message.
Specifically, with the most common padding scheme (PKCS#1 "old-style", aka "v1.5"), the padding adds at least 11 bytes to the message, and the total padded message length must be equal to the modulus length, e.g. 128 bytes for a 1024-bit RSA key. Thus, the maximum message length is 117 bytes. Note that the resulting encrypted message length has the same size than the modulus, so the encryption necessarily expands the message size by at least 11 bytes.
The normal way of using RSA for encrypted a big message (say, an e-mail) is to use an hybrid scheme:
A random symmetric key K is chosen (a raw sequence of, e.g., 128 to 256 random bits).
The big message is symmetrically encrypted with K, using a proper and efficient symmetric encryption scheme such as AES.
K is asymmetrically encrypted with RSA.
"Splitting" a big message into so many 117-byte blocks, each to be encrypted with RSA, is not normally done, for a variety of reasons: it is difficult to do it right without adding extra weaknesses; each block would be expanded by 11 bytes, implying a non-negligible total message size increase (network bandwidth can be a scarce resource); symmetric encryption is much faster.
In the basic RSA algorithm (without padding) which is not very secure the size of the message is limited to be smaller than the modulus.
To enhance the security of RSA you should use padding schemes as defined in PKCS1. Depending on the scheme you choose the size of the message can be significantly smaller than the modulus.
http://en.wikipedia.org/wiki/PKCS1
I want to know if RSA signatures are unique for a data.
Suppose I have a "hello" string. The method of computing the RSA signature is firstly to get the sha1 digest(these are , I know, unqiue for data), then add a header with OID and padding scheme mentioned and do some mathematical jiggle to give the signature.
Now assuming padding is same, will the signature generating by openSSL or Bouncy Castle be same?
If yes, my only fear is, won't it be easy to get back the "text"/data??
I actaully tried to do an RSA signature of some data and the signatures from OpenSSL and BC was different. I repeated it but got same signature again and again for each of them. I realized that the two signatures of the methods were different because of the difference in padding. However I am still not sure why the signatures of each of the libs are same all the time I repeat them. Can somebody please give an easy explanation?
The "usual" padding scheme, described in PKCS#1 as the "old-style, v1.5" padding, is deterministic. It works like this:
The data to sign is hashed (e.g. with SHA-1).
A fixed header is added; that header is actually an ASN.1 structure which identifies the hash function which was just used to process the data.
Padding bytes are added (on the left): 0x00, then 0x01, then some 0xFF bytes, then 0x00. The number of 0xFF bytes is adjusted so that the resulting total length is exactly the byte length of the modulus (i.e. 128 bytes for a 1024-bit RSA key).
The padded value is converted to an integer (which is less than the modulus), which goes through the modular exponentiation which is at the core of RSA. The result is converted back to a sequence of bytes, and that's the signature.
All these operations are deterministic, there is no random, hence it is normal and expected that signing the same data with the same key and the same hash function will yield the same signature ever and ever.
However there is a slight underspecification in the ASN.1-based fixed header. This is a structure which identifies the hash function, along with "parameters" for that hash function. Usual hash functions take no parameters, hence the parameters shall be represented with either a special "NULL" value (which takes a few bytes), or be omitted altogether: both representations are acceptable (although the former is supposedly preferred). So, the raw effect is that there are two versions of the "fixed header", for a given hash function. OpenSSL and Bouncycastle do not use the same header. However, signature verifiers are supposed to accept both.
PKCS#1 also describes a newer padding scheme, called PSS, which is more complex but with a stronger security proof. PSS includes a bunch of random bytes, so you will get a distinct signature every time.
Signatures are not a privacy mechanism; it's not considered a problem if you can get the plaintext back out. If your message must be kept secret, then encrypt as well as sign.
Nevertheless, remember that RSA signatures are created using a signer's private key. Given such a signature, you can use the signer's public key to "undo" the RSA transform (raise the message's signature to e, mod n) and get out the SHA1 or other hash value that was provided as its input. You still can't undo the hash function to get the input plaintext corresponding to a signature that has become detached from its message.
RSA for encryption is a different matter. Padding methods for encryption here do include random data in order to defeat traffic analysis.
This is why you add a salt/initialisation vector on top of your key. That way it shouldn't be possible to tell which records came from the same plaintext.
I'm trying to encrypt some date using a public key derived form the exchange key pair made with the CALG_RSA_KEYX key type. I determined the block size was 512 bits using cryptgetkeyparam KP_BLOCKLEN. It seems the maximum number of bytes I can feed cryptencrypt in 53 (424 bits) for which I get an encrypted length of 64 back. How can I determine how many bytes I can feed into cryptencrypt? If I feed in more than 53 bytes, the call fails.
RSA using the usual PKCS#1 v.1.5 mode can encrypt a message that is at most k-11 bytes, where k is the length of the modulus in bytes. So a 512 bit key can encrypt up to 53 bytes and a 1024 bit key can encrypt up to 117 bytes.
RSA using OAEP can encrypt a message up to k-2*hLen-2, where k is the modulus byte-length and hLen is the length of the output of the underlying hash-function. So using SHA-1, a 512 bit key can encrypt up to 22 bytes and a 1024 bit key can encrypt up to 86 bytes.
You should not normally use a RSA key to encrypt your message directly. Instead you should generate a random symmetric key (f.x. an AES key), encrypt your message with the symmetric key, encrypt the key with the RSA key and transmit both encryptions to the recipient. This is usually called hybrid encryption.
EDIT: Although this response is marked as accepted by the OP, please see Rasmus Faber response instead, as this is a much better response. Posted 24 hours later, Rasmus's response corrects factual errors,in particular a mis-characterization of OAEP as a block cipher; OAEP is in fact a scheme used atop PKCS-1's Encoding Primitive for the purpose of key-encryption. OAEP is more secure and puts an even bigger limit on the maximum message length, this limit is also bound to a hash algorithm and its key length.
Another shortcoming of the following reply is its failure to stress that CALG_RSA_KEYX should be used exclusively for the key exchange, after which transmission of messages of any length can take place with whatever symmetric key encryption algorithm desired. The OP was aware of this, he was merely trying to "play" with the PK, and I did cover that much, albeit deep in the the long remarks thread.
Fore the time being, I'm leaving this response here, for the record, and also as Mike D may want to refer to it, but do remark-me-in, if you think that it would be better to remove it altogether; I don't mind doing so for sake of clarity!
-mjv- Sept 29, 2009
Original reply:
Have you check the error code from GetLastError(), following cryptencrypt()'s false return?
I suspect it might be NTE_BAD_LEN, unless there's be some other issue.
Maybe you can post the code that surrounds your calling criptencryt().
Bingo, upon seeing the CryptEncrypt() call.
You do not seem to be using the RSAES w/ OAEP scheme, since you do not have the CRYPT_OAEP flag on. This OAEP scheme is a block cipher based upon RSAES. This latter encryption algorihtm, however, can only encrypt messages slightly less than its key size (expressed in bytes). This is due to the minimum padding size defined in PKCS#1; such padding helps protect the algorithm from some key attacks, I think the ones based on known cleartext).
Therefore you have three options:
use the CRYPT_OAEP in the Flag parameter to CryptEncrypt()
extend the key size to say 1024 (if you have control over it, beware that longer keys will increase the time to encode/decode...)
Limit yourself to clear-text messages shorter than 54 bytes.
For documentation purposes, I'd like to make note of a few online resources.
- The [RSA Labs][1] web site which is very useful in all things crypto.
- Wikipedia articles on the subject are also quite informative, easier to read
and yet quite factual (I think).
When in doubt, however, do consult a real crypto specialist, not someone like me :-)