I got an application that in C#, using RSACryptoServiceProvider, I load a public key from an XML and then I'm encrypting a hash value and saving it.
In another application, I load the private key and then I'm able to decrypt the information correctly.
The problem is that I want to make quick tests to see if everything's ok.
From MSDN,
The security of RSA derives from the fact that, given the public key { e, n }, it is computationally infeasible to calculate d, either directly or by factoring n into p and q. Therefore, any part of the key related to d, p, or q must be kept secret.
so the most important parameters of the private key are the exponent and the primes (D, P, Q).
My test was to modify the private key before loading it. First, I changed one character in the first prime and I got a bad key. Same when just modifying one character in the second prime.
But when I modified one character in the exponent, it was still able to correctly decode it. So my question is, which things can be changed from the private key and still be able to decode, and which are essential?
While Modulus, Exponent, and D are all that are required to do RSA technically, very few implementations actually use D, because it's much more efficient to do RSA via the Chinese Remainder Theorem.
While the Windows CAPI (used by RSACryptoServiceProvider) will roundtrip any value of D you import, it doesn't actually use it. Windows CNG (used by RSACng) requires that D be provided on import of a private key, then promptly throws it away, recomputing it on export (or import, I suppose).
You can see that last quirk by generating an RSA key with OpenSSL (which generates d via phi (which is the form Wikipedia mentions) and importing it into CNG, which when exporting has replaced d with the calculation based on lambda (LCM(p-1, q-1)).
Anyways, what all this amounts to is that while "changing D is bad" it can, in practice, just be ignored by the library doing the actual math; and that seems to be the case here. But you shouldn't do it, because it might not interoperate well after going via a PFX or other private key transfer mechanism.
Related
I'm creating an IAP plugin for the local Android market that I think has similar API as google play.
I build an Android side and it will return all response from the market to the unity c# side.
All part working fine but I can't figure out how to verify the signature of the response.
I'm new in cryptography and searching day to day about this.
Please correct me if I'm wrong.
They use a hash algorithm to sign data and encrypt that hash with private key.
I must decrypt signature with public key and compare hashes.
This is my public key (Base64):
MIHNMA0GCSqGSIb3DQEBAQUAA4G7ADCBtwKBrwDltnT/AaF3oMt+F3lza5JEvce0GLS1L9v9Z53lQ3LNluGk0eI2ukgWm7tAiIOLQgn11Sb9mW2VWkYTWGnZ1AZtY0GwdUQJUr7u3CWNznE6XH4UCVOVhGDCLnFrG8BcfDelhcfReGZQ3izOefhc4Oq6vZf5PfLwximK+FH27fR6XL8vg3yyK4LSwT764Dfd6H3IGes6EdTx/C3C690jdyMvhi2Q3qBiqfepHzW/jV8CAwEAAQ==
This key is in ASN.1 DER format.
I break it out and find this data:
SEQUENCE
SEQUENCE
OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption(PKCS #1)
NULL
BIT STRING
SEQUENCE
INTEGER 969837669069837851043825067021609343597253227631794160042862620526559…
INTEGER 65537
As I read on net first INTEGER is Modules and second INTEGER is Exponent
So in c# write a code like this =>
var parameter = new RSAParameters
{
Modulus = HexToByteArray(/* "first_INTEGET" */),
Exponent = BitConverter.GetBytes(/* "second_INTEGER" */)
};
Market send me a Json like this:
{"orderId": "0j8oJgE0Bett-neB", "purchaseToken": "0j8oJgE0Bett-neB", "developerPayload": "payload", "packageName": "com.some.market", "purchaseState": 0, "purchaseTime": 1520676644872, "productId": "card-1"}
The signature is like this:
hTFeQd25PZJ2DhGmXd0eO+C+oBeWsg983I4e5ztXtKAUrOIaNBaqAxHU3vW8acBs1I9fE5cxx/DI/sQGY4QSvpDnSm9aYz3do3joHPOXIVvXjSJfejxwzp9DKMUPd6LrgtxkaGevG+94NuKHFxpCdZlovEPXRJZyEznbASuYLqeW0KjP3jnvvw2O5iNlQRdh98h4Q18bSsaxq9zaRKExFLHkhNf/yO5m84kRB1G8
I'm searched for a method to do this but I don't know which method is true for me.
My verify code is this:
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(parameter);
var hash = new SHA1Managed();
bool dataOK =
rsa.VerifyData(hash.ComputeHash(Encoding.UTF8.GetBytes(json)), CryptoConfig.MapNameToOID("SHA1"), Encoding.UTF8.GetBytes(signature));
}
How truly convert signature to byte[] to verify? (with encoding or what???)
I'm searching a lot but more search more confuse.
Am I going the wrong way or using wrong method or ...?
Why should workflow be complicated?
Can anyone help me, please?
Thanks.
OK, I'll answer in order:
They use a hash algorithm to sign data and encrypt that hash with private key. I must decrypt signature with public key and compare hashes.
No, that's not correct. You should use a signature verification method, as you're currently doing. Seeing signature as encryption of a hash is incorrect; even the latest RSA standards go out of their way to explain this. For RSA the internal padding method is different. For ECDSA there is no direct encryption/decryption possible using the same scheme.
As I read on net first INTEGER is Modules and second INTEGER is Exponent
Yes, although it is spelled modulus, not modules. It is the public exponent, there is also a private exponent for the private key. Also without the caps.
How truly convert signature to byte[] to verify? (with encoding or what???)
Standard Base 64 is already mentioned in the comment section. Note that the key and signature size are not common (but that's OK in itself).
Why should workflow be complicated?
Well, somebody has to code it in the end and crypto is hard. But to make it easier for you: the entire ASN.1 structure is called a SubjectPublicKeyInfo structure; if you look on the internet you will find pre-made code to import from such a structure.
To finally verify the structure: make sure that you use the correct signature format (RSA-PKCS#1 v1.5 or RSA-PSS) and that you know exactly which binary data is fed to the signature generation function. For instance, the signature over the JSON could be in ASCII or UTF-8 or it could be in UTF-16 LE or BE.
Better ask the creator of the signature.
I'm using an RSA library that crashes at certain decrypt operations that contain invalid ciphertext. For security reasons, I don't have access to the RSA key or the ciphertext that are involved in crashes.
In my lab, I tried to submit multiple RSA keys and tweaked ciphertext values to force the crash. I tried with keys from 512 to 4096 bits, and I tweaked a valid ciphertext by changing each bit, prepending garbage, appending garbage, truncating at the tail or truncating at the head. In all cases, the library behaves correctly and reports "invalid ciphertext supplied" instead of crashing as it randomly happens on production.
My last resort is to try unusual RSA keys, for instance with unusual exponents. Do you know any library or tool that allows me to fully customize RSA key generation?
I can call it from C#, Java or C++ (Visual Studio), or it can also be a Windows command-line tool. Sample code or link to reference documentation is highly appreciated.
The CoreFX RSA tests have a couple of unusual keys defined.
DiminishedDPParameters has a DP value which can be expressed with one less byte than usual (when P and Q are both half the width of N/D DP is usually the same size as P or Q). (Generation technique: keep hitting up-enter with openssl genrsa [keysize] | openssl rsa -text -noout until one of the parameters was deficient)
UnusualExponentParameters uses E=0x01B1 instead of E=0x010001 (Generation technique: OpenSSL's RSA_generate_key takes a value for E. Just made one up, but stuck with a prime number for ease of the generator.)
One other thing that makes for an unusual E value is to be larger than 4 bytes (a limitiation of Windows CAPI / .NET RSACryptoServiceProvider). There doesn't seem to be a test with one of those, so here's an inline version. (Crypto disclaimer: This key was generated as an example, and has clearly been exposed to the Internet, don't use it for anything you want to be secret).
$ openssl rsa -in bigexponent.key -text -noout
Private-Key: (2048 bit)
modulus:
00:af:81:c1:cb:d8:20:3f:62:4a:53:9e:d6:60:81:
75:37:23:93:a2:83:7d:48:90:e4:8a:19:de:d3:69:
73:11:56:20:96:8d:6b:e0:d3:da:a3:8a:a7:77:be:
02:ee:0b:6b:93:b7:24:e8:dc:c1:2b:63:2b:4f:a8:
0b:bc:92:5b:ce:62:4f:4c:a7:cc:60:63:06:b3:94:
03:e2:8c:93:2d:24:dd:54:6f:fe:4e:f6:a3:7f:10:
77:0b:22:15:ea:8c:bb:5b:f4:27:e8:c4:d8:9b:79:
eb:33:83:75:10:0c:5f:83:e5:5d:e9:b4:46:6d:df:
be:ee:42:53:9a:ef:33:ef:18:7b:77:60:c3:b1:a1:
b2:10:3c:2d:81:44:56:4a:0c:10:39:a0:9c:85:cf:
6b:59:74:eb:51:6f:c8:d6:62:3c:94:ae:3a:5a:0b:
b3:b4:c7:92:95:7d:43:23:91:56:6c:f3:e2:a5:2a:
fb:0c:14:2b:9e:06:81:b8:97:26:71:af:2b:82:dd:
39:0a:39:b9:39:cf:71:95:68:68:7e:49:90:a6:30:
50:ca:77:68:dc:d6:b3:78:84:2f:18:fd:b1:f6:d9:
ff:09:6b:af:7b:eb:98:dc:f9:30:d6:6f:cf:d5:03:
f5:8d:41:bf:f4:62:12:e2:4e:3a:fc:45:ea:42:bd:
88:47
publicExponent: 8589935681 (0x200000441)
privateExponent:
64:af:9b:a5:26:24:83:da:92:b5:3f:13:43:9f:d0:
ef:13:01:2f:87:9a:bc:03:cb:7c:06:f1:20:99:04:
f3:52:c1:f2:23:51:9d:c4:8b:fa:ee:bb:51:1b:0d:
95:5f:61:67:b5:0e:03:4f:ea:2a:bc:59:0b:4e:a9:
fb:f0:c5:1f:9f:fe:a1:6f:79:27:ae:68:1c:bf:73:
58:45:2b:ca:29:d5:87:05:e0:ca:a1:06:01:3b:09:
a6:f5:f5:91:14:98:d2:c4:fd:69:15:58:54:88:e5:
f3:ad:89:83:6c:93:c8:77:5a:fa:b4:d1:3c:20:14:
26:6b:e8:ee:6b:8a:a6:6c:9e:94:2d:49:34:66:c8:
e3:a3:70:f8:e6:37:8c:e9:5d:63:7e:03:67:36:70:
be:4b:ca:ce:5f:cd:ad:d2:38:d9:f3:2c:a3:5d:e8:
45:77:6a:c4:bf:36:11:88:12:32:8c:49:3f:91:c2:
5a:9b:d4:26:72:d0:af:af:de:0a:f7:e6:19:07:8d:
48:b4:85:ef:91:93:3d:dc:ff:b5:45:87:b8:f5:12:
d2:23:c8:18:94:e9:17:84:98:2f:3c:5c:65:87:13:
51:f4:65:5a:b0:23:c4:ad:99:b6:b0:3a:96:f9:04:
6c:e1:24:a4:71:e8:28:f0:5f:8d:b3:bc:7c:cc:f2:
d1
prime1:
00:e4:3a:38:26:a9:72:04:ae:3c:d8:64:9a:84:db:
4b:bf:07:25:c4:b0:8f:8c:43:84:05:57:a0:cd:04:
e3:13:af:6d:04:60:dd:e6:9c:dc:50:8a:d0:43:d7:
25:14:da:7a:66:bc:91:8c:d9:62:4f:48:56:44:b9:
de:ea:b2:be:0e:11:29:56:d4:72:cf:0f:d5:1f:80:
fd:33:87:2d:2d:cc:56:2a:05:88:b0:12:e8:c9:0c:
e7:d2:54:b9:47:92:c6:e7:a0:2b:3c:ca:a1:50:e6:
7a:64:37:7a:cc:49:47:9a:d5:eb:55:54:93:b2:10:
0c:b0:41:09:56:f7:d7:3b:f5
prime2:
00:c4:dd:2d:7a:dd:6c:a5:07:40:d3:97:3f:40:c4:
de:bd:ba:b5:1f:7f:51:81:ab:ae:72:6c:32:59:6a:
3e:dd:0a:ee:44:da:ad:dd:8a:9b:7a:86:4c:4f:fd:
ae:00:c4:cb:1f:10:17:7b:a0:1c:04:66:f8:12:d5:
22:61:0f:8c:45:43:f1:c3:ef:57:9f:a9:e1:3a:e8:
da:1a:4a:8d:ae:30:78:61:d2:ce:ac:03:56:02:79:
b6:1b:65:14:98:98:83:fe:86:c5:c7:42:0d:31:28:
38:fc:2f:70:be:d5:9b:52:29:65:42:01:88:26:64:
ce:fa:38:b4:8a:37:23:e9:cb
exponent1:
09:ec:f1:51:f5:cd:d2:c9:e6:e5:26:82:36:4f:a5:
b4:ed:09:4f:62:2e:40:31:bf:46:b8:51:35:8a:58:
4d:cc:b5:32:8b:0b:d9:b6:35:89:18:3f:49:15:93:
d2:a3:ac:ad:14:e0:aa:cd:a1:f1:81:b5:c7:d9:3c:
57:ed:26:e6:2c:9f:c2:6a:f3:7e:4a:06:44:ec:e8:
2a:7b:a8:ae:d8:8f:f1:d8:e9:c5:6c:c6:63:85:cd:
b2:44:eb:3d:57:d1:7e:6a:d4:20:b1:9c:9e:2b:ee:
18:19:2b:81:62:65:b7:4d:a5:5f:a3:82:5f:92:2d:
9d:8e:83:5b:76:bf:30:71
exponent2:
00:89:b3:3b:69:57:89:17:4b:88:36:8c:49:46:39:
d4:d3:26:72:24:57:2a:40:b2:fe:61:91:03:84:22:
8e:3d:bd:11:ee:d9:04:0c:d0:39:77:e9:e0:d7:fc:
8b:fc:4b:f4:a9:32:83:52:9f:f1:d9:65:90:b1:8f:
4e:ab:ef:03:03:79:4f:29:3e:88:dc:76:1b:3e:23:
af:ec:b1:9f:29:f8:a4:d2:a9:05:8b:71:4c:f3:f4:
d1:07:33:f1:3e:a7:2b:bf:1f:be:c8:d7:1e:10:6d:
0c:e2:11:5f:3a:d2:de:02:03:25:c3:87:9a:09:1c:
41:3c:d6:39:7f:83:b3:cb:89
coefficient:
7c:57:ed:74:c9:17:6f:ba:76:c2:31:83:20:25:15:
06:2c:66:4d:4d:49:ff:3e:03:70:47:a3:09:da:10:
f1:59:0c:e0:1b:7a:1c:d1:a4:32:6d:c7:58:83:df:
f9:31:10:ab:06:5a:ae:d1:40:c9:b9:81:76:a8:81:
08:09:ad:ec:75:e8:67:64:a0:95:15:97:ef:46:7f:
a8:fd:50:91:81:cd:2e:49:1e:43:be:41:08:4e:5b:
e1:b5:62:ee:76:e9:f9:2c:9a:b1:e5:ae:ad:9d:29:
1a:63:37:e4:de:85:bd:e6:7a:0d:72:b4:e5:5a:dc:
f2:07:f7:a5:a5:22:5e:15
I spent 100s of hours researching this subject, and other senior programmer who coded the original project also could not make it work. I have an xml with a parameter of SignatureValue, Certificate (X509Certificate2) and Digest Value. The created and given Signature value stated in the same xml was made by converting concatinated fields (equal to Digest Value) into a hash (SHA1), then encrypted via private key. Private key is taken out of the certificate for privacy and I only have the Public key within. Now, no matter how I code around it, I always get a false value back (as in VerifyHash/verifyHashResult is false). Here is the code I am using:
// Need your help please.
static void VerifyHash(string sigVal , string digestVal, System.Security.Cryptography.X509Certificates.X509Certificate2 cert)
{
sigValInBytes = Convert.FromBase64String(sigVal);
try
{
using (RSACryptoServiceProvider rsaProviderDecrypt = (RSACryptoServiceProvider)cert.PublicKey.Key)
{
// Line below always return value of FALSE no matter how I code it. Here I want to verify the hashed freshly calculated digest value that is now hashed with the signature value
rsaProviderDecrypt.Decrypt(sigValInBytes, false);
rsaProviderDecrypt.Dispose();
}
}
}
// At the main program I get the certificate from the xml given and call the method above:
main
{
// Code below gets the certificate details from a given xml, details of each variable confirmed to be accurate.
char[] Base64_x509ByteArray;
Base64_x509ByteArray = t.DigitalSignatures.First().X509Data.ToCharArray();
byte[] x509ByteArray;
x509ByteArray = Convert.FromBase64CharArray(Base64_x509ByteArray, 0, Base64_x509ByteArray.Length);
// Here am creating the certificate from the gathered data/certificate:
System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new System.Security.Cryptography.X509Certificates.X509Certificate2(x509ByteArray);
VerifyHash(t.DigitalSignatures.FirstOrDefault().SignatureValue.Trim(), concatenatedFieldValues, cert);
}
Some shots in the dark:
Find the piece that is broken: Try doing the entire "encrypt / hash check" process in code without transfering anything over XML. If you can hash a string locally, and the hashes match, then the problem is in XML. Otherwise, the problem is in the cert or decryptor.
If the problem is on the cert / encryptor side, try hash matching with a local .NET cryptography class. If that fails, the problem is an encryption setting. Otherwise, it is the cert.
BIG shot in the dark: The call to Dispose right after the hash check. It shouldn't matter, but that caused an issue while I was decrypting using the Rijndael algorithm. Best guess was the optimizer was closing the stream early or something weird like that. Moving the constructor out of the using statement and manually calling Dispose after accessing the result fixed that "optimization".
Might try a reversable encryption algorithm. Rinjdael is native to .NET, and is reversable. Good for debug and proof of concept work. (Note: it uses Time as part of the salt, so RJ doesn't match hashes, it decrypts. So not good for passwords in Production environments.)
If the XML is the cause, check the encodings. Encryption is very sensitive to encodings, and XML serializers are finnicky beasts to begin with. The strings may look the same, but represented differently, or extra control characters added. Sql Server nvarchars are UCS-2, varchars are iso-8859-1, C# strings are utf-8, etc. Easy for encodings to mis-match, and an encoding change would easily cause this. Try converting the original value to utf-16 before inserting into the Xml, and set the Xml Declaration Encoding to utf-16. Just to be safe.
Note about NotePad: if you have opened the Xml in Notepad to take a quick look or edit, and saved it, there are probably extra "end of line" characters on your strings now. If you did the same in Word... oh my... Might want to try an original copy.
Failing that, try generating new encrypted values and see if they match.
I want to use an asymmetric cryptography algorithm, but I need it have short Key Size(not like RSA which is at least 384).
I need it to be about around 20.
Is it possible?
That's a .NET restriction on the key size; RSA can be used with any key size. It just doesn't make sense to do so.
Think about it, with a 20-bit key you can brute force the it in 2^20 attempts and that's just too easy with today's computers.
There are several ways to have a short key size.
1. With RSA
A RSA public key consists in a big number n (the "modulus") and a (usually small) number e (the public exponent). e can be as small as 3, and in a closed setup (where you control key generation) you can force the use of a conventional e, the same for everybody. A typical size for n is 1024 bits (i.e. 128 bytes).
n is the product of two prime numbers (n = p*q). Knowledge of p and q is sufficient to rebuild the private key (nominally a value d which is a multiplicative inverse of e modulo p-1 and q-1). Assuming that n is known, knowledge of p alone is sufficient (if you know n and p, you can compute q with a simple division). For proper security, p and q should have similar sizes, so even by taking the smaller of the two, you still need to store about 512 bits or so -- that's 64 bytes).
It has also been suggested to select a small d (the "private exponent"). But this makes e essentially random, hence large; you can no longer use a conventional small value for e. This basically doubles the public key size. Also, forcing a small d can make the key weak (it has been shown to be the case when the size of d is no more than 29% of the size of n, but that does not prove in any way that a d of 30% the size of n is safe). This is generally considered to be a bad idea.
2. With DSA / Diffie-Hellman
DSA is a digital signature algorithm. Diffie-Hellman is a key exchange algorithm. Both are "asymmetric cryptographic algorithms" and you would use one or the other, or both, depending on your needs. In both cases, there is a public mathematical group (numbers modulo a big prime number p for the basic DSA and DH; elliptic curve variants use an elliptic curve as group); the public key is a group element, and the private key is the discrete logarithm of that element relatively to a conventional generator. In other words, a prime p and a number g modulo p are given (they can be shared by all key holders, even); a private key is a number x corresponding to the public key y = gx mod p. The private key is chosen modulo a small prime q. q is known and must be large enough so as to defeat the generic discrete logarithm algorithms; in practice, we want a 160-bit or more q.
This means that a private key fits in about 20 bytes. That's not 20 decimal digits, but closer.
3. With ANY cryptographic algorithm
When you generate a key pair, you do so with:
a deterministic procedure;
a source of random bits.
For instance, with RSA, you generate p and q by creating random odd numbers of the right size and looping until a prime number is found. For a given random source, the whole process is deterministic: given the same random bits, this will find the same primes p and q.
Hence you can develop a PRNG seeded by a secret key K and use it as random source for the key generation process. Whenever you need the private key, you run the key generation process again, using K as input. And voilà! Your private key, the one you need to store, is now K.
With RSA, this makes private key usage quite expensive (RSA key generation is not easy). However, with DSA / Diffie-Hellman, this would be very inexpensive: the private key is only a random number modulo q (the group order) which can be generated with much less cost than using the private key for a digital signature or an asymmetric key exchange.
This leads to the following procedure:
The "private key", as stored, is K.
The group parameters for DSA / Diffie-Hellman are hardcoded in the application; everybody uses the same group and that is not a problem. The group order is q, a known prime of at least 160 bits. If you use an elliptic curve variant, then q is a property of the curve, hence a given.
When you need to sign or to perform a key exchange (key exchange is used to emulate asymmetric encryption), you compute SHA-512(K), which yields a 512-bit sequence. You take the first half (256 bits), interpret it as a number (with big-endian or little-endian convention, as you wish, provided that you always use the same convention), and reduce it modulo q to get the private DSA key. Similarly, you use the second half of the SHA-512 output to get the private DH key.
The key generation is very slightly biased but this does not imply much security trouble. Note that if you need a DSA key and a DH key, then you can use the same group but you should not use the same private key (hence the use of both halves of the SHA-512 output).
How big should be K ? With a hash function such as SHA-512, K can be any arbitrary sequence of bits. However, K should be wide enough to defeat exhaustive search. A 1024-bit RSA key, or a 1024-bit DSA modulus (the p modulus for DSA), provide a security level which is very roughly equivalent to an 80-bit symmetric key. Similarly, a 160-bit group order for DSA/DH provides the same level. 80 bits are not that much; you cannot go lower than that if you want to be taken seriously. This means that K should be chosen among a space of at least 280 possible keys; in other words, if K is selected as uniformly random bytes, then it must be at least 10 bytes long. With decimal digits, you need at least 24 digits. Anything below that is intrinsically weak, and that's unavoidable.
Standard warning: if anything of the above is not obvious or crystal clear to you, then do not even think about implementing it. Implementation of cryptographic algorithm is tricky, especially since the deadliest errors cannot be tested (it is not because the program runs and appears to work properly that it does not contain security weaknesses).
You may want to consider using Elliptic Curve Cryptography, if you can find a standard implementation of it. It provides the same level of protection against brute force as RSA, with substantially shorter key lengths.
The standard disclaimer about cooking up your own cryptosystems applies here, of course.
Is there any way to perform private key encryption in C#?
I know about the standard RSACryptoServiceProvider in System.Security.Cryptography, but these classes provide only public key encryption and private key decryption. Also, they provide digital signature functionality, which uses internally private key encryption, but there are not any publicly accessible functions to perform private key encryption and public key decryption.
I've found this article on codeproject, which is a very good start point for performing this kind of encryption, however, I was looking for some ready-to-use code, as the code in the article can hardly encrypt arbitrary-long byte arrays containing random values (that means any values, including zeroes).
Do you know some good components (preferably free) to perform private key encryption?
I use .NET 3.5.
Note: I know this is generally considered as bad way of using asymmetric encryption (encrypting using private key and decrypting using public key), but I just need to use it that way.
Additional Explanation
Consider you have
var bytes = new byte[30] { /* ... */ };
and you want to use 2048bit RSA to ensure no one have changed anything in this array.
Normally, you would use digital signature (ie. RIPEMD160), which you then attach to the original bytes and send over to the receiver.
So, you have 30 bytes of original data, and additional 256 bytes of digital signature (because it is a 2048bit RSA), which is overall of 286 bytes. Hovewer, only 160 bits of that 256 bytes are actually hash, so there is exactly 1888 bits (236 bytes) unused.
So, my idea was this:
Take the 30 bytes of original data, attach to it the hash (20 bytes), and now encrypt these 50 bytes. You get 256 bytes long message, which is much shorter than 286 bytes, because "you were able to push the actual data inside the digital signature".
ECDSA Resources
MSDN
Eggheadcafe.com
c-plusplus.de
MSDN Blog
Wiki
DSA Resources
CodeProject
MSDN 1
MSDN 2
MSDN 3
Final Solution
If anyone is interested how I've solved this problem, I'm going to use 1024bit DSA and SHA1, which is widely supported on many different versions of Windows (Windows 2000 and newer), security is good enough (I'm not signing orders, I just need to ensure that some child can't crack the signature on his iPhone (:-D)), and the signature size is only 40 bytes long.
What you are trying to design is known as a "Signature scheme with message recovery".
Designing a new signature scheme is hard. Designing a new signature scheme with message recovery is harder. I don't know all the details about your design, but there is a good chance that it is susceptible to a chosen message attack.
One proposal for signature schemes with message recovery is RSA PSS-R. But unfortunately, this proposal is covered with a patent.
The IEEE P1363 standarization group, once discussed the addition of signature schemes with message recovery. However, I'm not sure about the current state of this effort, but it might be worth checking out.
Your Public key is a sub-set of your private key. You can use your private key as a public key as it will only use the components of the full key it requires.
In .NET both your private & public keys are stored in the RSAParameters struct. The struct contains fields for:
D
DP
DQ
Exponent
InverseQ
Modulus
P
Q
If you're at the point where the data is so small that the digital signature is huge in comparison, then you have excess signature. The solution isn't to roll your own algorithm, but to cut down what's there. You definitely don't want to try to combine a key with the hash in an amateurish way: this has been broken already, which is why we have HMAC's.
So here's the basic idea:
Create a session key using a cryptographically strong RNG.
Transmit it via PKE.
Use the session key to generate an HMAC-SHA1 (or HMAC-RIPEMD160, or whatever).
If the size of the hash is absurdly large for the given data, cut it in half by XORing the top with the bottom. Repeat as needed.
Send the data and the (possibly cut-down) hash.
The receiver uses the data and the session key to regenerate the hash and then compares it with the one transmitted (possibly after first cutting it down.)
Change session keys often.
This is a compromise between the insanity of rolling your own system and using an ill-fitting one.
I'm wide open to constructive criticism...
I get it now, after reading the comments.
The answer is: don't do it.
Cryptographic signature algorithms are not algorithms from which you can pick and choose - or modify - steps. In particular, supposing a signature sig looks something like encrypt(hash), orig + sig is not the same as encrypt(orig + hash). Further, even outdated signature algorithms like PKCS v1.5 are not as simple as encrypt(hash) in the first place.
A technique like the one you describe sacrifices security for the sake of cleverness. If you don't have the bandwidth for a 256 byte signature, then you need one of:
a different algorithm,
more bandwidth, or
a smaller key.
And if you go with (1), please be sure it's not an algorithm you made up! The simple fact is that crypto is hard.