I am trying to encrypt a file using a private key via RSA and then decrypt it using the public key in Java. I know very well that this is the reverse use case of how RSA is normally used with private/public key pairs.
My objective is to take a file, encrypt it on one system using a private key, and then decrypt it on a different system using the public key. I plan to distribute the public key so that anyone can read the file. What I am trying to prevent is from anyone being able to create the file.
I have found these C header functions and them implemented in PHP so i know what I am trying to do is possible
int RSA_public_encrypt(int flen, unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
int RSA_private_decrypt(int flen, unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
int RSA_private_encrypt(int flen, unsigned char *from,
unsigned char *to, RSA *rsa,int padding);
int RSA_public_decrypt(int flen, unsigned char *from,
unsigned char *to, RSA *rsa,int padding);
How can I achieve the same behavior in Java? I keep googling and searching for things but maybe I am just not using the right words. Everything keeps coming up showing me how to encrypt with public and decrypt with private when I am trying to do the opposite.
The file i want to encrypt ranges from 5-10MB in size.
Thanks!
If you want identify the person who owns the private key, and therefore owns the file, I guess the process you are looking for is "digital signature" (and not encryption), as comments #EJP
Digest the file with a hashing algorithm like SHA-256. Creates a summary of a few bytes "hash"
Sign the hash using the RSA private key. This is called the "signature"
Send the file and the signature to a third party. They can verify the signature using the public key. If signature match then you can ensure the identity of the sender of the message and that has not been altered
In this case case file is not encrypted, is hashed and signed.
If you need to encrypt to hide the content, then you can't use RSA, because message size is limited by the length of the key. So 10mb is too big. In this case I suggest to use en encryption algorithm like AES to encrypt the content.
The third party will need the AES decryption key. Generate it before this or encrypt the AES key with a RSA public key of the third party using RSASA-OAEP padding (according link provided by #Maarten Bodewes)
Generating a digital signature
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(privateKey);
sig.update (data)
byte[] signature = sig.sign();
Verifiying the signature
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(publicKey);
sig.update(data);
boolean verifies = sig.verify(signature);
Related
I'm trying to encrypt BMP image withe RSA in java,it's supposed to create the encrypted and decrypted images.
Ok so after reading the comments and learned that it is not safe to use RSA alone;I edit my Question. and tryd Java Cryptography but cipher.doFinal() don't accepted a data longer than 245 bytes
File bmpFile = new File("C:\\Users\\acer\\Desktop\\py\\6.bmp");
BufferedImage image = ImageIO.read(bmpFile);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ImageIO.write(image, "bmp", baos );
byte[] b = baos.toByteArray();
byte[] b1=new byte[b.length];
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair pair = keyPairGen.generateKeyPair();
PublicKey publicKey = pair.getPublic();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipher.update(b);
b1 = cipher.doFinal();
bmpFile=new File("C:\\Users\\acer\\Desktop\\py\\66.bmp");
FileOutputStream fos = new FileOutputStream(bmpFile);
fos.write(b1);
fos.flush();
fos.close();
and it give :
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 245 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:344)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2048)
most of my image are 198x135
i found here in stack onverflow that
The RSA algorithm can only encrypt data that has a maximum byte length of the RSA key length in bits divided with eight minus eleven padding bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.
and said that you have to encrypt the data with symmetric key and encrypt the symmetric key with rsa.
but i whent to encrypt the data with RSA.
And i went to ask,i have to send the encrypting image to Other pc but the problem is that p,q are random
Asymmetric encryption means encrypting for a specific target (public key).
So the steps would be:
the receiver creates its private key (p, q, e) and public key (N, d) private key (p, q, d) and public key (N, e)
the receiver sends its public key to the sender
sender uses the public key to encrypt the message
receiver can use its private key to decrypt the data
So if you want to use RSA to encrypt any data, the parameters are random for the target receiver, but given for the sender.
do i have to encrypt theme with Symmetric algorithm like RC4 and send theme with the image
As you already may find out, RSA operations are pretty slow. So common way to use RSA is hybrid encryption - encrypting data with a random symmetric encryption key and use RSA to encrypt only the random key.
image.setRGB(i, j,pixels[i][j].intValue());
This won't work. Encryption of any data will have length of the key length. Effectively you need 1024 bit for each image pixel in your case. Trimming the bigint to the intValue you are loosing information.
That's why the (already mentioned) hybrid encryption is used
it's part of my School Project
If you will use RSA for real life projects:
Textbook RSA has several weaknesses, to make the solution secure, you need to use a padding, common standards are pkcs#1 v1.5 or OAEP padding
In reality you should use the default crypto library which is much faster and resilient against side-channel attacks
Here is my Code for generating RSA key
public static void generateRsaKeyPair() {
try {
KeyPairGenerator keyPairGene = KeyPairGenerator.getInstance("RSA");
keyPairGene.initialize(512);
KeyPair keyPair = keyPairGene.genKeyPair();
serverPublicKey = (RSAPublicKey) keyPair.getPublic();
serverPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
} catch (Exception e) {
e.printStackTrace();
} finally{
}
}
Now I want to convert DER/ASN.1 Encoded bytes, Also know what are the default encoded format use when generating RSA key using Java JCE API.
The RSA key pair that is created using the SunRsaSign provider consists of an internal representation mainly consisting of BigInteger values. RSA is an algorithm that is using integer algorithmic after all. Internally it is not likely to be encoded in ASN.1 as the algorithm cannot be performed using binary. You can use your debugger to browse through the internal fields, but note that the internal structure is an implementation detail and should not be relied upon.
To convert the public key to ASN.1 you just need to call serverPublicKey.getEncoded() and you'll get a SubjectPublicKeyInfo structure, which is an ASN.1 data structure defined for X.509 certificates, encoded using binary BER encoding scheme (DER is a subset of BER, usually the encoding is DER compatible). It consists of a sequence that contains an OID - indicating the RSA key type - and the PKCS#1 encoded public key. You can find the details in here, here and of course structure RSAPublicKey in here.
You can also call serverPrivateKey.getEncoded() to get an unprotected, inner PKCS#8 structure. However, it is highly questionable if you should do that. You should not distribute unprotected private keys. If you need to store it, use a well protected PKCS#12 key store instead.
Innocently, I thought "SHA1withRSA algorithm" was simply operating the plainText with "SHA1", and use RSA/pkcs1padding to encrypt the result of "SHA1"。However, I found I was wrong until I wrote some java code to test what I thought.
I use RSA publickey to decrypt the signature which I use the corresponding privatekey to sign with "SHA1withRSA algorithm" . But I found the result is not equal to "SHA1(plainText)", below is my java code:
String plaintext= "123456";
Signature signature=Signature.getInstance("SHA1withRSA",new BouncyCastleProvider());
signature.initSign(pemPrivatekey);
signature.update(plaintext.getBytes());
byte[] sign = signature.sign();
//RSA decode
byte[] bytes = RsaCipher.decryptByRsa(sign, pemPublickey);
String rsaDecodeHex=Hex.toHexString(bytes);
System.out.println(rsaDecodeHex.toLowerCase());
String sha1Hex = Hash.getSha1(plaintext.getBytes());
System.out.println(sha1Hex);
//rsaDecodeHex!=sha1Hex
Easy to find that rsaDecodeHex!=sha1Hex, where
rsaDecodeHex=3021300906052b0e03021a050004147c4a8d09ca3762af61e59520943dc26494f8941b
and
sha1Hex=7c4a8d09ca3762af61e59520943dc26494f8941b 。
So, What's the detail in "SHA1withRSA" ?
The digital signature algorithm defined in PCKS#1 v15 makes a RSA encryption on digest algorithm identifier and the digest of the message encoded in ASN.1
signature =
RSA_Encryption(
ASN.1(DigestAlgorithmIdentifier + SHA1(message) ))
See (RFC2313)
10.1 Signature process
The signature process consists of four steps: message digesting, data
encoding, RSA encryption, and octet-string-to-bit-string conversion.
The input to the signature process shall be an octet string M, the
message; and a signer's private key. The output from the signature
process shall be a bit string S, the signature.
So your rsaDecodeHex contains the algorithm identifier and the SHA1 digest of plainText
I have a public key which is used to encrypt some binary data. How can I write the code that can decrypt this data with the same public key ?
You can't - the whole point about public/private-key-cryptography like RSA is that when you encrypt some data using a public key you can only decrypt it using the matching (!) private key. So without the proper private key you won't be able to recover your AES-key and you won't get back to your cleartext.
Assume I have the following Java code to generate a Public-private keypair:
KeyPairGenerator generator = KeyPairGenerator.getInstance ("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
generator.initialize (1024, random);
KeyPair pair = generator.generateKeyPair();
RSAPrivateKey priv = (RSAPrivateKey)pair.getPrivate();
RSAPublicKey pub = (RSAPublicKey)pair.getPublic();
// Sign a message
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initSign (priv);
dsa.update ("Hello, World".getBytes(), 0, "Hello, World".length());
byte[] out = dsa.sign();
/* save the signature in a file */
FileOutputStream sigfos = new FileOutputStream("sig");
sigfos.write(out);
sigfos.close();
How would one go about and decrypt the file "sig" in PHP? I've read the post: https://stackoverflow.com/a/1662887/414414 which supplies a function to convert a DER file to PEM (Assume I also save the public key from Java).
I have tried something like:
$key = openssl_pkey_get_public ("file://pub_key.pem");
$data = null;
openssl_public_decrypt ( file_get_contents ("sig"), $data, $key);
echo $data, "\n";
It successfully decrypts the message, but it is many weird characters.
Our scenario is a Java client that is sending messages to a PHP server, but encrypts the data with a private key. PHP knows about the public key, which it should use to decrypt and validate the message.
I've read a lot of posts regarding this issue here on SO, but I've come to realize that this is a bit specific issue, especially if there's different algorithms in use, etc. So sorry if this may be a duplicate.
Any feedbacks are greatly appreciated!
an "RSA signature" is usually more than just "encrypt with private key, decrypt with public key", since Public key protocols like PKCS#1 also specify padding schemes, and all signature schemes will encrypt a digest of the message, instead of the full message. I cannot find any documentation if java's signature scheme uses the signature padding scheme specified in PKCS#1, but my suspicion is that it is.
If it is, you will instead want to use the openssl_verify method in PHP, documented here. This will return a 0 or 1 if the signature is invalid or valid, respectively.
In the event that Java does not use a padding scheme, your issue is that the data encrypted in the signature is a hash of the message, instead of the message itself (you can see in the Java code that it uses the SHA-1 hash algorithm). So on the PHP side, you will need to take the sha1 hash of your message using the sha1 method with $raw_output set to true, and compare those strings to ensure your message is valid.
From the snippet
$key = openssl_pkey_get_public ("file://pub_key.pem");
It looks like you're referencing the public key, which would be the wrong one to decrypt. Double check ?