Is it possible to encrypt a message outside of AWS KMS using a "key material" and decrypt using KMS? - cryptography

I have created a custom managed symmetric key (for encryption / decryption purposes) in AWS KMS and imported my own key material from an external source.
For symmetric keys, AWS KMS uses the AES-256-GCM algorithm spec.
What I am trying to understand is whether it would be possible to encrypt a message outside of KMS using the same key material with AES-256-GCM spec, and decrypt the ciphertext using AWS KMS?
From my understanding after a number of tries on this matter, it seems like when KMS itself performs an encryption, it adds some additional metadata to the ciphertext which are in return checked/validated at the time of decryption.
And even if we are to use the same "key material" that's already imported to KMS and perform an encryption externally (without using KMS) with AES-256-GCM spec, the cipher decryption from the KMS side fails due to various exceptions such as: version mismatches / invalid algo ID etc.
Note: Maybe the answer is obvious that it might not be possible to achieve what we are trying to do here due to AWS KMS's internals on custom managed symmetric keys... but in that case it'd be much appreciated if some alternatives can be discussed.

Related

How can one encrypt content using RSA for Chilkat in C# and decrypt it in Java?

UPDATE
I see that lots of people find my question too long (because there is lots to explain), read the first sentence and then just think that I'm going on the worst tangent possible without seeing the entire question. If the question isn't clear enough please let me know. I'm trying to condense it in the simplest way and not to cause any confusion.
The reason for the public key decryption is to achieve a form of digital signing where the recipient decrypts the encrypted content to reveal a hash value. I didn't see the need to mention this in the question as I wanted to find out how to perform this operation in its basic form. However to avoid any further concerns and warnings around what RSA is all about and that public key decryption is bad, I updated my question with that disclaimer.
BACKGROUND
I have written a C# application that uses the Chilkat's RSA library to take content and encrypt it using a personal Private Key.
Then I would like to use a public website to allow someone to decrypt that very content (that's encrypted) by using an associated public key.
Now, I found a 3rd party website (and there are not a lot of them, BTW) that allows you to decrypt content using a RSA public key (https://www.devglan.com/online-tools/rsa-encryption-decryption).
Unfortunately when I try to use it, I get a "Decrypt error".
Here is a sample setup. I have generated my own personal Public & Private Key pairs. In my C# application, I'm taking a string and encrypting it with a private key and encoding it using Base64.
const string originalContent = "This !s original c0nt3nt";
var rsa = new Chilkat.Rsa();
rsa.GenerateKey(2048);
var encryptedBytes = rsa.EncryptBytes(Encoding.UTF8.GetBytes(originalContent), true);
var encryptedEncodedString = Convert.ToBase64String(encryptedBytes);
Console.WriteLine($"Encrypted:{Environment.NewLine}{encryptedEncodedString}");
Console.WriteLine();
var privateKeyBytes = rsa.ExportPrivateKeyObj().GetPkcs8();
var privateKeyEncodedString = Convert.ToBase64String(privateKeyBytes);
Console.WriteLine($"Private Key:{Environment.NewLine}{privateKeyEncodedString}");
Console.WriteLine();
var publicKeyBytes = rsa.ExportPublicKeyObj().GetDer(false);
var publicKeyEncodedString = Convert.ToBase64String(publicKeyBytes);
Console.WriteLine($"Public Key:{Environment.NewLine}{publicKeyEncodedString}");
Console.WriteLine();
var decyptedContentBytes = rsa.DecryptBytes(encryptedBytes, false);
var decryptedContentString = Encoding.UTF8.GetString(decyptedContentBytes);
Console.WriteLine($"Decrypted:{Environment.NewLine}{decryptedContentString}");
Console.WriteLine();
Console.WriteLine("Press ENTER to quit");
Console.ReadLine();
This sample console app will write out all the necessary information needed to use for the next part of the process and to demonstrate that in principle it works as expected.
Example:
Here are the sample values from the console window:
ENCRYPTED CONTENT
H5JTsGhune1n3WWSPjwVJuUwp70Hsh1Ojaa0NFCVyq0qMjVPMxnknexOG/+HZDrIYsZM7EnPulpmihJk4QyLM8T2KNQIhbWuMHvzgHYlcPJdXpGZhAxwfklL4HP0iRUUXJBsJcS/2XoUDZ6elUoMIFY9cDB4O+WFxKS/5vzLEukTLbQ3aEBNg3xaf9fg12F8LcMxZ3GDsk0W9b6oJci09NTxXd6KKes0RM1hnOhw6bu0U33ZLF3sa0nH9Kdf8w23PoKc/tl12Jsa8N1A4OjaT5910UF8FRH6OkAbNKnxqXcL7+V4HVuHchi3ghuFivAW57boLeHr7OG7wOEC/gfPOw==
PRIVATE KEY
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8xYcNXckXf1X4Kd6qE5c7pddfWdKo71mcwZWskuaq+wq3FTcCTAedo/Vcx8Vxn+RMn5XE7QCDzcAAN0K/BzQsoU81myRzZ+bKP+TJ5HH0jClCUMj+ideEm0fay873jnbG0hKEOJPVxPWwKq3jvDLLmWrdgvd/UiDStDm286SFKfMlLWkSw8YIc5nXsthAgP0hv8Nj7UDKvTEG5o3boTuhG1JQARCEXP0fTdIiv0cEFlSN3KkgF4KDf32Vt2x57N/+PJXpQvcECkLwPpBAq/aM0qbtgeiILxavfBJRwQ5zXDUmZHepvSjK6KIYQsTavQQLDXnFKuXa2fxOJHIlys6pAgMBAAECggEBAI0ZMBtDkL2phj7aPP7vaclB6rvwzc9MKLVM1W2K2DPRNW8nwlhLMB4aoZnaELEfjGvhlPb/F7VtIyiGJbPX1J3PbP9qmVJRxWZDX+WwhaT+5xAUhkgMDDWoQ4s9b9QGfq2Z9BE0oPvWHraxEAz7bRRV9lTgQdK/Np2H7OPdNYn6SW8qVgAukgTBqVno4VDbC34bJwal0e63oBFFfensWlhPtDUQB/uQX7UiRfEkxL+CNuqVLDoAeXWmSVWOPlDTKVu1y1bzfA+WMOKHm1ndq21I07TUPf9FcgYdKf4yKpWvMfVeDev6Oo/2mlac+vrJO571S+h4a5m79jUhCeJwX4kCgYEA7q5hrNtMbErA6dgEOG+KpFTaeqbknwtcykVApEvHt4LKULedAvwkORu65acKFYkxbMt19Fx7ligGxg0yOQRWX1BXK1XOCo9eYOjvOVlbRqBywLIbegehoZQ0LoSsdRcOvFq7EbMV3BaxCmxgpnrCZ75VaCYUMzylIduPWKeT9xcCgYEAyngNIIgsXfpCI+HHILNpprFfS2JBBGPx8N/d9cXahKCJhxrMe8K64CSMyxTwum5DXjJnbE4QBsoowRZTCEF6JUBagRM/pQrVX/CK//oyUUaa5+1S/0OxlUevXR7TD6gcpGNEdPjruc+gZzhfKFuWh+V9mJQUviqm3RjAcEdHAD8CgYBL0kOfGM8vO5QK9R9qGiztxTLecbQAvihM7TD6wEQCjN7eQ2Xyc8zCA4gcujKe4sU7rWqcJODxs2drdPe2WyVhA/GdB5X7js3JdVXBXxx61C9//VRzMIds/9qPyH/MdnWs6hmxJrXUA7Vb/U+6sxacxD73ZdlW6XX/ynLAFAQSIwKBgQCk4i12j87p3ZMdW5HprJJeoNYFMwfVxnrSec1tiGoTVhWJxCZAp22+eaV7ARumB4OvY4bcKZpdnSahUEfgUkphqc3Kjd1nz7HCxsa7/YoarFAcjiXoIb2t30oNoLurZXGl4f1u8QQvNsnfJYZA/I1TMG4e4oEd+OgY6D5XcYR9ywKBgGdaZmoBieiw0NkbijjgQZ0WILDmrIYdsSp4HMp6XDeVvdMb/qYg2jTnvVyqMSb8NdfCOB0GT19r1isQX9RnUgxPikJbVLj8WjAQjHT28mtmRn+Ju/3KT75RJ/LHY3SySNMOgTW75X2u8v0ELdEiiOmc/vTkCYoS/oqp92ELjT1Y
PUBLIC KEY
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvMWHDV3JF39V+CneqhOXO6XXX1nSqO9ZnMGVrJLmqvsKtxU3AkwHnaP1XMfFcZ/kTJ+VxO0Ag83AADdCvwc0LKFPNZskc2fmyj/kyeRx9IwpQlDI/onXhJtH2svO9452xtIShDiT1cT1sCqt47wyy5lq3YL3f1Ig0rQ5tvOkhSnzJS1pEsPGCHOZ17LYQID9Ib/DY+1Ayr0xBuaN26E7oRtSUAEQhFz9H03SIr9HBBZUjdypIBeCg399lbdseezf/jyV6UL3BApC8D6QQKv2jNKm7YHoiC8Wr3wSUcEOc1w1JmR3qb0oyuiiGELE2r0ECw15xSrl2tn8TiRyJcrOqQIDAQAB
WEBSITE PUBLIC KEY DECRYPTION ATTEMPT
Now, when I go to the website (mentioned above) I paste in my encrypted content in the encrypted content text block and I paste my public key that I generated in the text block underneath it and set the RSA Key Type to Public Key. But it fails.
SECOND ATTEMPT
But...
I have done some troubleshooting by taking my personal Private & Public keys that I generated and I use the website to perform the encryption & decryption with my keys and I'm able to encrypt my string and decrypt that encrypted content successfully which leads me to believe that somehow my Chilkat encryption setup is not fully aligned with the one that the website uses.
WHAT I NOTICED
So I started reading what the website had to offer and the author of the page posted an explanation on how to accomplish this (https://www.devglan.com/java8/rsa-encryption-decryption-java) which uses the Java RSA libraries under the hood. Apparently, there are two Java RSA ciphers that can be used "RSA" and "RSA/ECB/PKCS1Padding".
I am not so familiar with the Java libs and I know enough of cryptography to know how to get things done but there are lots of technical aspects that are still unclear to me as to help me figure out where to go next.
QUESTION
My question is, is there anything in Chilkat that I need to setup so that it can encrypt content that would allow a Java application (like the website link posted above) to be able to decrypt? (of course Chilkat needs to be able to decrypt it as well)
A message that has been encrypted with the private key using the Chilkat-library cannot be decrypted with the public key using Java (at least not with the standard SunJCE-provider) or the java-based web-site, since different padding variants are used on both sides.
The prerequisite for a successful decryption is that both encryption and decryption use the same padding variant. The same applies to signing and verification.
There are two variants of the PKCS1-v1.5-padding described in RFC8017: One is RSAES-PKCS1-v1_5, which is used in the context of encryption and decryption, and the other is RSASSA-PKCS1-v1_5, which is used in the context of signing and verifying. RSAES-PKCS1-v1_5 is non-deterministic, i.e. repeated encryption of the same plaintext with the same key always generates different ciphertexts. RSASSA-PKCS1-v1_5 is deterministic, that is, it always generates the same ciphertext under the mentioned conditions.
Since the padding variant depends on the respective platform/library, a general statement is not possible. However, for the Chilkat-library and Java (standard SunJCE provider) the following applies (PKCS1-v1.5-padding assumed):
The methods that Chilkat provides in the context of encryption/decryption use RSAES-PKCS1-v1_5 regardless of whether the public or private key is used for encryption. Analog methods also exist in the context of signing/verifying. These use RSASSA-PKCS1-v1_5.
To check this, the padding variant can be determined by setting the Chilkat.Rsa#NoUnpad flag to true, so that the padding is not removed during decryption. Another option for a test is to repeatedly encrypt the same plaintext with the same key. Since RSAES-PKCS1-v1_5 is probabilistic, different ciphertexts are generated each time.
In Java, the Cipher-class determines which padding variant is used based on the mode (encryption or decryption) and the key type used (private or public). For encryption with the public key and decryption with the private key, RSAES-PKCS1-v1_5 is used. For encryption with the private key and decryption with the public key, RSASSA-PKCS1-v1_5 is used. For signing/verifying, Java provides the Signature-class which uses RSASSA-PKCS1-v1_5.
To check this, proceed as described above. In Java, you can prevent the padding from being removed with RSA/ECB/NoPadding during decryption.
Since in the context of encryption/decryption the public key is used for encryption and the private key is used for decryption, and dedicated classes or methods are used in the context of signing/verifying, there are no or few use cases for direct encryption with the private key and decryption with the public key. Furthermore or maybe because of that these processes are not uniformly implemented in the libraries as you can see in the example of the Chilkat-library and Java.
Altogether three cases can be distinguished for the Chilkat-library and Java:
Within the same library/language, encryption can be performed with the public or private key and decryption with the respective counterpart. For this reason the encryption and decryption on the web site (using Java) works in the posted example Second Attempt: Both the encryption with the private key and the decryption with the public key use RSASSA-PKCS1-v1_5.
If in the Chilkat-code the public key is used for encryption and in Java the private key is used for decryption, RSAES-PKCS1-v1_5 is used for both encryption and decryption, which is why decryption works.
However, if in the Chilkat-code the private key is used for encryption and in Java the public key is used for decryption, RSAES-PKCS1-v1_5 is used for encryption and RSASSA-PKCS1-v1_5 is used for decryption. Both padding variants therefore differ and decryption fails. This corresponds to the scenario described in the question.
After this explanation now to your question: My question is, is there anything in Chilkat that I need to setup so that it can encrypt content that would allow a Java application (like the website link posted above) to be able to decrypt? Since the Java-code uses RSASSA-PKCS1-v1_5 for decryption with a public key, it would be necessary for compatibility to change the padding variant in the Chilkat-code from RSAES-PKCS1-v1_5 to RSASSA-PKCS1-v1_5 in the context of encryption/decryption. If you look at Chilkat's RSA-methods, it seems that this is not intended, but that the logic for determining the padding variant is hard coded (as probably with most libraries). You can only choose between PKCS1-v1.5-padding and OAEP for padding. This means that a message encrypted with the private key using the Chilkat-code cannot be decrypted with the public key in Java or on the website.
What are the alternatives? According to the question, the goal is: The reason for the public key decryption is to achieve a form of digital signing where the recipient decrypts the encrypted content to reveal a hash value.
Here it would be a good idea to create a standard signature on the Chilkat-side, e.g. with signBytes. The hash of the data is created automatically and RSASSA-PKCS1-v1_5 is used as padding variant (if the data are already hashed, the method signHash can be used). On the Java-side, this signature can be verified. Alternatively, the signature can be decrypted with the public key, which allows the hash value to be determined, since Java uses the padding variant RSASSA-PKCS1-v1_5 in both cases. Decryption is also possible on the web site, but the decrypted data are not displayed properly because they are only given as a string (which does not produce any meaningful output because of the arbitrary byte-sequences in a hash) and the encoding cannot be changed to hexadecimal or Base64.
Another possibility might be to use Chilkat on the Java-side as well. Probably Chilkat uses a uniform logic across platforms (which I didn't verify however).
I'm going to (hopefully) answer this question after only reading the 1st part of it. I got to the point where you wrote "... I'm taking a string and encrypting it with a private key ...", and this raised the red flag.
Public key encryption should be where you encrypt using the recipient's public key. The private key is used to decrypt. The point of encryption is that only the intended recipient can decrypt and view the message. With public/private key pairs, you can provide your public key to anybody, but you are in sole possession of your private key. Therefore, anybody can use your public key to encrypt a message intended for you, but you are the only one who can decrypt. This makes sense.
Signing is the opposite: You use your private key to sign, and anybody can verify using your public key. A signature can optionally contain the signed data, so that the act of verifying the signature also extracts the original data. Thus, you verify that (1) the data could only have been signed by the holder of the private key, (2) the data was not modified, and (3) you recover the original data.
Chilkat's API provides the ability to use the public/private keys in the opposite way, which doesn't make any sense, but was needed because there are systems "out there" that do things that make no sense, and Chilkat was needed to perform the opposite. (It makes no sense to encrypt something that anybody can decrypt.)
I think the code behind the devglan website is not capable of doing the RSA encrypt/decrypt in the opposite way. You would need to encrypt using your public key, and then give your private key to the other person.
Or.. you could instead create an "opaque signature" using Chilkat, which is a signature containing the data, and then find the devglan online tool to verify/extract the data from the PKCS7 signature (if the devglan tool exists). This way you can keep your private key and give the public key to the recipient.
Finally.. it seems to me that you're really treating the public/private key as a shared secret -- i.e. a secret only shared between sender and receiver. In that case, why bother using RSA at all? (Remember, RSA is only for encrypting/decrypting small amounts of data. The max number of bytes you can encrypt is equal to the key size minus some overhead. So if you have a 2048-bit key, then you can maximally encrypt 2048/8 bytes minus the overhead used in padding, which is on the order of 20 bytes or so.) If semantically you just have a shared secret, then you might simplify and use symmetric encryption (AES) where the secret key is just a random bunch of bytes and you have no data size limit.

Send RSA public key over socket them import it for encryption [VB .NET]

I'm writing that generates public and private key pair, then send the public key over the socket to another programs to be used to encrypt string data.
I'm using RSA in VB.NET and I was able to generate the required keys:
My public key v+u4Lt4nyLXincU+wbReOTU3nwiTZ7MlFkA7cytLOjuviHrAdnaVAV8+WoFhy9nADGtk1K0OLAE1ZwGzt/kgUw==
My question is: is the public key enough for encryption by other users? and if so, how can I import it to RSA parameters to encrypt data?
Edit:
I used the following code in an attempt to import the public key but it failed:
Dim Parameters As New RSAParameters
Parameters.Modulus = encoder.GetBytes(publicKey)
RSA.ImportParameters(Parameters)
My question is: is the public key enough for encryption by other
users?
The answer is as so often: It depends.
The way you describe your proposed protocol this would be widely open to man-in-the-middle-attacks. Eve could just intercept the message on the wire and replace the key by her own.
From a theoretical point of view, it is sufficient to encrypt data. However, to be useful in real world scenarios, you have to use symmetric encryption as well.
So here is my advice:
Don't do your own protocols if you have to ask such questions. Never. Not once. Use Well known, established technology like TLS!
I had a similar project in VB.net, you will not be able to send messages of a large length because if you are using 2048bit RSA, the maximum size of data you would be able to send would be 245 bytes. https://security.stackexchange.com/questions/33434/rsa-maximum-bytes-to-encrypt-comparison-to-aes-in-terms-of-security
I used the RSA public key so each client would use AES encryption and pass their own keys which are encrypted with RSA, the server would then decrypt the key and use that to resolve data. This uses both asymmetric(RSA) and symmetric encryption(AES). Here is a link with a useful video that explains this when I was doing this project. https://www.youtube.com/watch?v=6H_9l9N3IXU&t=271s
The way I imported the keys was using the .toXMLString and .fromXMLString in the RSA CryptoServiceProvider. Here is a great link explaining it, probably doing a better job that I could. https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsa.toxmlstring(v=vs.110).aspx
I found this page that was very useful for me here
Also I found that I was making the mistake of converting the encrypted byte array to string after encryption then back to byte array just before decryption. The last byte array didn't have the same size as the original.

AWS S3 SSE GetObject requires secret key

The idea was to generate a random key for every file being uploaded, pass this key to S3 in order to encrypt it and store the key in the database. Once the user wants to access the file, the key is read from the database and passed to S3 once again.
The first part works. My objects are uploaded and encrypted successfully, but I have issues with retrieving them.
Retrieving files with request headers set:
When setting the request headers such as x-amz-server-side-encryption-customer-algorithm etc. when performing the GET request to the resource, works, and I am able to access it. But since I want to these resources as src to an <img>-Tag, I cannot perform GET requests which require headers to be set.
Thus, I thought about:
Pre signing urls:
To create a pre signed url, I built the HMAC SHA1 of the required string and used it as a signature. The calculated signature is accepted by S3 but I get the following error when requesting the pre signed URL:
Requests specifying Server Side Encryption with Customer provided keys must provide an appropriate secret key.
The URL has the form:
https://s3-eu-west-1.amazonaws.com/bucket-id/resource-id?x-amz-server-side-encryption-customer-algorithm=AES256&AWSAccessKeyId=MyAccessKey&Expires=1429939889&Signature=GeneratedSignature
The reason why the error is shown seems to be pretty clear to me. At no point in the signing process was the encryption key used. Thus, the request cannot work. As a result, I added the encryption key as Base64, and Md5 representation as parameters to the URL. The URL now has the following format:
https://s3-eu-west-1.amazonaws.com/bucket-id/resource-id?x-amz-server-side-encryption-customer-algorithm=AES256&AWSAccessKeyId=MyAccessKey&Expires=1429939889&Signature=GeneratedSignature&x-amz-server-side-encryption-customer-key=Base64_Key&x-amz-server-side-encryption-customer-key-MD5=Md5_Key
Although the key is now present (imho), I do get the same error message.
Question
Does anyone know, how I can access my encrypted files with a GET request which does not provide any headers such as x-amz-server-side-encryption-customer-algorithm?
It seems intuitive enough to me that what you are trying should have worked.
Apparently, though, when they say "headers"...
you must provide all the encryption headers in your client application.
— http://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html#sse-c-how-to-programmatically-intro
... they do indeed actually mean headers and S3 doesn't accept these particular values when delivered as part of the query string, as you would expect, since S3 sometimes is somewhat flexible in that regard.
I've tested this, and that's the conclusion I've come to: doing this isn't supported.
A GET request with x-amz-server-side-encryption-customer-algorithm=AES256 included in the query string (and signature), along with the X-Amz-Server-Side-Encryption-Customer-Key and X-Amz-Server-Side-Encryption-Customer-Key-MD5 headers does work as expected... as I believe you've discovered... but putting the key and key-md5 in the query string, with or without including it in the signature seems like a dead end.
It seemed somewhat strange, at first, that they wouldn't allow this in the query string, since so many other things are allowed there... but then again, if you're going to the trouble of encrypting something, there seems little point in revealing the encryption key in a link... not to mention that the key would then be captured in the S3 access logs, leaving the encryption seeming fairly well pointless all around -- and perhaps that was their motivation for requiring it to actually be sent in the headers and not the query string.
Based on what I've found in testing, though, I don't see a way to use encrypted objects with customer-provided keys in hyperlinks, directly.
Indirectly, of course, a reverse proxy in front of the S3 bucket could do the translation for you, taking the appropriate values from the query string and placing them into the headers, instead... but it's really not clear to me what's to be gained by using customer-provided encryption keys for downloadable objects, compared to letting S3 handle the at-rest encryption with AWS-managed keys. At-rest encryption is all you're getting either way.

Confused how to use AES and HMAC

I'm a little bit confused how to use AES and HMAC (based on SHA-256) in a proper way when transmitting the encrypted message from server to client or vice versa
Correct me please if the following method is wrong:
Take the secure key from the key file (for example we suggest to use the AES-256 and we already have a valid 256 bit length key)
Generate random IV for AES-256 to be sure that the same plain text will have different encryption output.
Calculate HMAC-SHA-256 using the plain text and secure key. (The first question: should I use IV for HMAC calculation? For example append to secure key?)
Encrypt the plain text using AES-256.
Compose message for client as following: HMAC + IV+ ENCRYPTED_MESSAGE
The biggest question is: is it possible somehow not to send IV to client but instead generate it the same way and still be crypto safe? Or is it ok to send the IV too?
I've end up with random IV generation, because it is needed that the same plain text will end up in different encryption results.
Don't send hmac(message),aes(message). If you send the same message twice, anyone can tell. Moreover, you need to mac the cipher-text to prevent attacks such as the padding oracle attack.
IV: In general, the easy way to deal with the iv is to prepend it, since it's fixed length, to the cipher text.You can then just read off those bytes before decrypting. IV's can be public, but they must be random.
HMAC: Easiest way, if your library supports it, is to use AES in GCM mode (or, less preferably EAX). This is not actually an HMAC, but it provides authenticated encryption which is just as good.If these are not supported :
You do need to compute the hmac over both the iv and the cipher text. if || is concatenation ( which you could do with say array copy if dealing with byte arrays) then you want
CipherText = iv|| aes(key1,iv,message)
tag = hmac(key2,ciphertext)
And then send (tag,CipherText). On the other end, you run the same hmac call to regenerate the tag and the compare the received tag with the computed one. Compare the sha1 or sha256 hashes of the tags instead of directly so you don't leak where the comparison fails.
You should use a different key for the hmac. In practice tacking the sha1 sum of your encryption key is good enough.

Can one encrypt with a private key/decrypt with a public key?

[Disclaimer: I know, if you know anything about crypto you're probably about to tell me why I'm doing it wrong - I've done enough Googling to know this seems to be the typical response.]
Suppose the following: you have a central authority that wants to issue login cookies for a given domain. On this domain, you don't necessarily trust everyone, but you have a few key end-points who should be able to read the cookie. I say a few, but in practice this number of "trusted" partners may be large. The cookie doesn't contain much information - a username, a timestamp, an expiry, a random number. It should remain small of course, for performance reasons, even after encryption (within reason). Now, there are two security issues:
1) We don't trust every webserver on this domain with user data. For this reason, the ability to read the cookie should be restricted to these trusted partners.
2) While we trust these partners to protect our user's data, we'd still like the central point of authority to be unforgeable (again, within reason).
Now, if we generate a private RSA key for the authority and keep it secret, and distribute the public key only to the "trusted partners", we should be able to encrypt with the private key and have it readable by anyone with the public key. What I'm unclear on is, would it still be necessary to sign the message, or would the act of decrypting be evidence that it was generated with the private key? Is this any way in which this scheme would be better or worse than disseminating a symmetric key to all parties involved and using that to encrypt, while using the private key merely to sign? And of course feel free to tell me all the ways this is a stupid idea, but bear in mind that practical arguments will probably be more convincing than rehashing Alice and Bob.
Oh, and implementation pointers would be welcome, though one can find the basics on Google, if there are any "gotchas" involved that would be useful!
Nate Lawson explains here and here why you can't securely use the public key as a closely-held secret decryption key (it's a subtle point, and a mistake plenty of others have made before you, so don't feel bad!).
Just use your public key to sign for authenticity, and a separate symmetric key for the secrecy.
I've read enough on interesting attacks against public key systems, and RSA in particular, that I agree absolutely with this conclusion:
Public key cryptosystems and RSA in
particular are extremely fragile. Do
not use them differently than they
were designed.
(That means: Encrypt with the public key, sign with the private key, and anything else is playing with fire.)
Addendum:
If you're interesting in reducing the size of the resulting cookies, you should consider using ECDSA rather than RSA to produce the signatures - ECDSA signatures are considerably smaller than RSA signatures of an equivalent security factor.
In cryptography you are what you know. In your scenario, you have a central authority which is able to issue your cookies, and you want no other entity to be able to do the same. So the central authority must "know" some private data. Also, you want the "trusted web servers" to be able to access the contents of the cookies, and you do not want just anybody do read the cookies. Thus, the "trusted web servers" must also have their own private data.
The normal way would be that the authority applies a digital signature on the cookies, and that the cookies are encrypted with a key known to the trusted web servers. What your are thinking about looks like this:
There is a RSA modulus n and the two usual RSA exponents d and e (such that ed = 1 modulo p-1 and q-1 where n=pq). The central authority knows d, the trusted web servers know e, the modulus n is public.
The cookie is processed by the central authority by padding it into an integer c modulo n, and computing s = c^d mod n.
The trusted web servers access the cookie data by computing c = s^e mod n.
Although such a scheme may work, I see the following problems in it:
For basic security, e must be large. In usual RSA descriptions, e is the public exponent and is small (like e = 3). A small exponent is no problem when it is public, but since you do not want cookie contents to be accessible by third parties, you must make e big enough to resist exhaustive search. At the same time, trusted web servers must not know p and q, only n. This means that trusted web servers will need to compute things with a big modulus and a big exponent, and without knowing the modulus factors. This seems a minor point but it disqualifies many RSA implementation libraries. You will be "on your own", with big integers (and all the implementation issues known as "side-channel leaks").
Resistance of RSA signatures, and resistance of RSA encryption, have been well studied, but not together. It so happens that the padding is essential, and you do not use the same padding scheme for encryption and for signatures. Here, you want a padding scheme which will be good for both signature and encryption. Cryptographers usually consider that good security is achieved when hundreds of trained cryptographers have looked at the scheme for a few years, and found no blatant weakness (or fixed whatever weaknesses were found). Home-cooked schemes almost always fail to achieve security.
If many people know a secret, then it is not a secret anymore. Here, if all web servers know e, then you cannot revoke a trusted web server privileges without choosing a new e and communicating the new value to all remaining trusted web servers. You would have that problem with a shared symmetric key too.
The problem of ensuring confidentiality and verifiable integrity in the same type is currently being studied. You may look up signcryption. There is no established standard yet.
Basically I think you will be happier with a more classical design, with digital signatures used only for signing, and (symmetric or asymmetric) encryption for the confidentiality part. This will allow you to use existing libraries, with the least possible homemade code.
For the signature part, you may want to use DSA or ECDSA: they yield much shorter signatures (typically 320 bits for a DSA signature of security equivalent to a 1024-bit RSA signature). From the central authority point of view, ECDSA also allows better performance: on my PC, using a single core, OpenSSL crunches out more than 6500 ECDSA signatures per second (in the P-192 NIST curve) and "only" 1145 RSA signatures per second (with a 1024-bit key). The ECDSA signatures consist in two 192-bit integers, i.e. 384 bits to encode, while the RSA signatures are 1024-bit long. ECDSA in P-192 is considered at least as strong, and probably stronger, than RSA-1024.
You should use a digital sigantures scheme of some sort, or some other mechanism that is aimed to solve the integrity problem of your scenario.
The encryption itself isn't enough.
How would you know that the decrypted messege is what it should be?
Decrypting a cookie encrypted with the right key will surely provide a "valid" cookie, but what happens when you decrypt a cookie encrypted with the wrong key? or just some meaningless data? well, you might just get a cookie that looks valid! (the timestamps are in the range you consider valid, the username is legal, the random number is... uh... a number, etc.).
In most asymmetric encryption algorithms I know off, there is no built-in validation. That means that decrypting a message with the wrong key will not "fail" - it will only give you a wrong plaintext, which you must distinguish from a valid plaintext. This is where integrity comes to play, most commonly - using digital signatures.
BTW, RSA is long studied and has several "gotchas", so if you plan to implement it from scratch you better read ahead on how to avoid creating "relatively easy to break" keys.
Public keys are by definition, public. If you're encrypting with a private key and decrypting with a public key, that's not safe from prying eyes. All it says: "this data is coming from person X who holds private key X" and anyone can verify that, because the other half of the key is public.
What's to stop someone you don't trust putting public key X on a server you don't trust?
If you want a secure line of communication between two servers, you need to have all of those trusted servers have their own public/private key pairs, we'll say key pair Y for one such server.
Server X can then encrypt a message with private key X and public key Y. This says "server X sent a message that only Y could read, and Y could verify it was from X."
(And that message should contain a short-lived symmetric key, because public key crypto is very time-consuming.)
This is what SSL does. It uses public key crypto to set up a session key.
That being said, use a library. This stuff is easy to screw up.
The answer to your question "would the act of decrypting be evidence that it was generated with the private key", the answer is yes if the recipient can do simple validation of the data. Say you have "User name : John, timestamp : <number>, expiry : dd/mm/yyyy". Now if a wrong public key is used to decrypt, the probability that you will get "User name : <some letters>, timestamp : <only numbers>, expiry : ??/??/????" is zero. You can validate using a regular expression (regex) like "User name : [a-zA-Z]+, timestamp : [0-9]+, expiry : .... " and drop it validation fails. You can even check if the day is between 1 and 31, month is between 1 and 12 but you won't get to use it as regex will typically fail at "User name : " if wrong public key is used. If validation succeeds you still have to check the timestamp and ensure that you don't have a replay attack.
However, consider the following:
RSA public key crypto is not designed for bulk encryption of structured data as it can be exploited by attacker. public key crypto is typically used in 2 ways: 1) for securely transporting the symmetric key (which by definition has no structure) which will be used in bulk encryption; this is done by encrypting the symmetric key using the public key and 2) digitally signing a document by encrypting not the document but the hash of the document (again something which has no structure) using the private key. In your case you are encrypting the cookie which has a definite structure.
You are depending on the public key not getting into the hands of the wrong person or entity.
public key encryption is about 1000 times slower that symmetric key encryption. This may not be a factor in you case.
If you still want to use this approach, and are able to distribute the public key only to the "trusted partners", you should generate a random session key (i.e. symmetric key), encrypt it using the private key and send to all recipients who have the public key. You then encrypt the cookie using the session key. You can change the session key periodically. More infrequently you can change the public key also: you can generate a new public/private key pair and encrypt public key with the session key and send it to all recipients.
I presume you trust the 'trusted partners' to decrypt and verify the cookie, but don't want them to be able to generate their own cookies? If that's not a problem, you can use a much simpler system: Distribute a secret key to all parties, and use that to both encrypt the cookie and generate an HMAC for it. No need for public key crypto, no need for multiple keys.
As an alternative to your key distribution approach, which may or may not be suitable in your application, consider using Kerberos, which uses symmetric key encryption, a single highly protected bastion server that controls all the keying material, and a clever set of protocols (See the Needham-Schroder protocol)