I've been having trouble encrypting with an RSA public key. Here is a sample JUnit code that reproduces the problem:
public class CryptoTests {
private static KeyPair keys;
#BeforeClass
public static void init() throws NoSuchAlgorithmException{
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = CryptoUtils.getSecureRandom();
keyGen.initialize(2176, random);
keys = keyGen.generateKeyPair();
}
#Test
public void testRepeatabilityPlainRSAPublic() throws EdrmCryptoException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
byte[] plaintext = new byte [10];
Random r = new Random();
r.nextBytes(plaintext);
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted1 = rsa.doFinal(plaintext);
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted2 = rsa.doFinal(plaintext);
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, keys.getPublic());
byte[] encrypted3 = rsa.doFinal(plaintext);
assertArrayEquals(encrypted1, encrypted2);
assertArrayEquals(encrypted1, encrypted3);
}
}
The result? The assertion fails.
Why is this behaviour seen here? As far as I remember from my crypto classes, any key can be used for encryption. Yet this is not what happens here.
I've tested the same thing with the private key, and I get a repeatable output.
If, for some reason, RSA encryption with a public key is forbidden, then why am I not getting an exception?
What must I do to get repeatable results?
P.S. My JDK is 1.6.0_22 running on an Ubuntu 10.10 box.
My guess is that it's applying randomized padding, precisely to make it more secure. From the RSA wikipedia page:
Because RSA encryption is a deterministic encryption algorithm – i.e., has no random component – an attacker can successfully launch a chosen plaintext attack against the cryptosystem, by encrypting likely plaintexts under the public key and test if they are equal to the ciphertext. A cryptosystem is called semantically secure if an attacker cannot distinguish two encryptions from each other even if the attacker knows (or has chosen) the corresponding plaintexts. As described above, RSA without padding is not semantically secure.
...
To avoid these problems, practical RSA implementations typically embed some form of structured, randomized padding into the value m before encrypting it. This padding ensures that m does not fall into the range of insecure plaintexts, and that a given message, once padded, will encrypt to one of a large number of different possible ciphertexts.
You can confirm that what is happening is that random padding is being added by initialising your Cipher with the string "RSA/ECB/NoPadding". Now, you should see that the ciphertext is identical in each case (though for reasons stated by another answerer, you shouldn't really do this in practice).
To add extra detail to Jon's answer:
When you do Cipher.getInstance("...") you have a number of options, as you've probably gathered. The Standard Algorithm Names specify what these are.
The one you asked for, RSA is by default RSA under PKCS1, which, to quote the wikipedia article:
There are two schemes for encryption
and decryption:
RSAES-OAEP: improved encryption/decryption scheme; based on
the Optimal Asymmetric Encryption
Padding scheme proposed by Mihir
Bellare and Phillip Rogaway.
RSAES-PKCS1-v1_5: older encryption/decryption scheme as first
standardized in version 1.5 of PKCS#1.
See RSALab's PKCS1 documentation for the detail of said padding schemes.
Related
Use case:
I have a use case wherein client generates private and public key , sends the base 64 encoded public key to the server.
On server side I will encrypt a message using this public key and send the encrypted message to client , which the client decrypts using its private key.The algorithm agreed upon is 'RSA'.
The problem is on server side I am seeing that certain keys are working using X509EncodedKeySpec as key spec
byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
While some keys throw exception (Caused by: java.security.InvalidKeyException: IOException: algid parse error, not a sequence) using X509EncodedKeySpec but work using RSAPublicKeySpec:
byte[] publicBytes = Base64.decodeBase64(base64EncodedPubKey);
org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey pkcs1PublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.RSAPublicKey.getInstance(publicBytes);
BigInteger modulus = pkcs1PublicKey.getModulus();
BigInteger publicExponent = pkcs1PublicKey.getPublicExponent();
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);
So, what I came to understand is that client and server need to agree whether to use:
PKCS #1 or X.509 for encoding the key . My question is which one is better for my use case? Any guidelines when to use which format?
There's very little difference. The key format Java calls X.509, more exactly known as the ASN.1 structure SubjectPublicKeyInfo (or SPKI) defined in X.509 or equivalently and more conveniently in RFC5280 sec 4.1, is a quite simple way to handle a large and flexible set of algorithms: it consists of a substructure AlgorithmIdentifier which identifies the algorithm and its parameters if applicable, then an opaque BIT STRING which contains the actual key information (encoded) in a format depending on (the algorithm identified by) the AlgorithmIdentifier.
For RSA, the algorithm-dependent part is the ASN.1 structure RSAPublicKey defined in PKCS1 or more conveniently RFC8017 appendix A.1.1 and its earlier versions, and duplicated in RFC3279 sec 2.3.1. Thus for RSA the X.509 (SPKI) format contains the PKCS1 format, and since RSA doesn't have parameters (or at least key-related parameters), the only real difference is that the X.509 format explicitly specifies that the key is RSA -- which in your application you already know.
You have already discovered that vanilla (Oracle-was-Sun-now-OpenJDK) Java crypto, aka JCA Java Cryptographic Architecture, directly supports only the X.509 (SPKI) format, which is a minor advantage. However if you use BouncyCastle it is much easier to convert back and forth than the code in your Q; you simply use the org.bouncycastle.asn1.x509.SubjectPublicKeyInfo class to add or discard the AlgorithmIdentifier:
// test data source
KeyStore ks = KeyStore.getInstance("JKS"); ks.load (new FileInputStream (args[0]), args[1].toCharArray());
byte[] spkienc = ks.getCertificate(args[2]).getPublicKey().getEncoded();
System.out.println (DatatypeConverter.printHexBinary(spkienc));
// extract PKCS1 part of original SPKI
byte[] pkcs1enc = SubjectPublicKeyInfo.getInstance(spkienc).parsePublicKey().getEncoded();
System.out.println (DatatypeConverter.printHexBinary(pkcs1enc));
// rebuild SPKI from the PKCS1
AlgorithmIdentifier algid = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
byte[] spki2enc = new SubjectPublicKeyInfo (algid, pkcs1enc).getEncoded();
System.out.println (DatatypeConverter.printHexBinary(spki2enc));
See my answer to the similar golang x509.MarshalPKIXPublicKey vs x509.MarshalPKCS1PublicKey() and especially the links to:
Converting A public key in SubjectPublicKeyInfo format to RSAPublicKey format java
Generating RSA keys in PKCS#1 format in Java
Problem transmiting a RSA public key, javaME , bouncy castle
If you don't have BouncyCastle, it's a little harder; you need to write a partial ASN.1 parser or generator. Full ASN.1 processing is rather complicated, but for this case you need only a small subset that isn't too bad. (Yeah, that's faint praise.) I may add this later if I have more time.
A much bigger potential issue is that your key is not authenticated. The hard part of public key distribution, much harder than tiny format details, is making sure that only the legitimate key is distributed. If an attacker can substitute their publickey for the correct one, then the victim encrypts the supposedly secret data in a way the attacker can easily read, and all your fancy cryptography code is completely worthless.
This is why most actual systems don't distribute bare publickeys, but instead certificates that allow verifying the key is the correct key. There are a few certificate schemes, but the most widespread by far is X.509 and its Internet profile PKIX -- in fact the RFCs I referenced above, 5280 and 3279, are part of PKIX. SSL-now-TLS uses X.509. Code-signing uses X.509. S/MIME email uses X.509. (PGP/GPG uses a different kind of certificates, not X.509, but still certificates.) And (vanilla) Java directly supports X.509 certificates just as well or even better than it does "X.509" (SPKI) publickeys.
I am very new in the field of cryptography and have been stuck on this problem for two days.
I have a java code for AES/ECB encryption and I want my uwp app to use the same encryption technique but whatever I've tried so far gives different encryption results.
There are many answers on stackoverflow suggesting to use RijndaelManaged class, but this class is not available for UWP.
Here's java snippet
public string encrypt(String input, string key) {
SecretKeySpec skey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey);
crypted = cipher.doFinal(input.getBytes("UTF-8"));
return Base64.encodeToString(crypted,Base64.NO_WRAP);
}
You need to use this answer to see how to encrypt/decrypt. However, you need a different (ECB so insecure) algorithm. So instead of the given CBC cipher mode you need to use AesEcbPkcs7 from the SymmetricAlgorithmNames.
Notes:
PKCS#7 is the same as PKCS#5 as used in the Java code, more info here;
you of course don't need to use an IV for ECB mode, so strip that out;
the UTF-8 encoding and base 64 decoding I'll leave out, it should be easy to do these encodings in any language/environment (the Convert and UTF8Encoding classes seem to be available for UWP apps).
I have a modulus key and an exponent key, I create an RSA public key to encrypt a data, but I get this exception
java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block
Here are the details:
the Modulus value:
B390F7412F2554387597814A25BC11BFFD95DB2D1456F1B66CDF52BCC1D20C7FF24F3CCE7B2D66E143213F64247454782A377C79C74477A28AF6C317BE68BC6E8FF001D375F9363B5A7161C2DFBC2ED0850697A54421552C6288996AC61AF5A9F7DE218ABBC75A145F891266615EB81D11A22B7260F7608083B373BA4BC0756B
size: 256
the Exponent value:
010001
the Data to be encrypted:
1A0498EA0DF19B45043DA4688AE3A7B3D592D61CC0EBB82FB100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
size: 256
and here is the code:
public static String encryptData(String data, BigInteger modulus, BigInteger exponent) throws Exception {
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory factory = KeyFactory.getInstance("RSA", "BC");
PublicKey pub = factory.generatePublic(spec);
Cipher rsa = Cipher.getInstance("RSA", "BC");
rsa.init(Cipher.ENCRYPT_MODE, pub);
byte[] cipherText = rsa.doFinal(data.getBytes()); // ERROR HERE
return Hex.toString(cipherText);
}
Here is the public key generated
30819F300D06092A864886F70D010101050003818D0030818902818100B390F7412F2554387597814A25BC11BFFD95DB2D1456F1B66CDF52BCC1D20C7FF24F3CCE7B2D66E143213F64247454782A377C79C74477A28AF6C317BE68BC6E8FF001D375F9363B5A7161C2DFBC2ED0850697A54421552C6288996AC61AF5A9F7DE218ABBC75A145F891266615EB81D11A22B7260F7608083B373BA4BC0756B0203010001
size: 342
Thank you!
The usual way to use public key (asymmetric) encryption with large documents is to create a random, single-use passphrase. The passphrase is used with a password-based encryption (symmetric) algorithm (e.g., AES-256). Use AES to encrypt the document and then use the public key to encrypt the passphrase.
Generally speaking, symmetric encryption algorithms tend to be a couple of orders of magnitude faster than asymmetric encryption algorithms. So, not only does RSA not lend itself to directly encrypt large documents, but it also would take much more computation to do the encryption.
As an aside I will mention that encryption is hard to get right. I would strongly urge you to use a standard library in a standard way to ensure a secure implementation.
the error is just stupid:
byte[] cipherText = rsa.doFinal(data.getBytes()); // ERROR HERE
I encrypt data.getBytes(), but actually I first need to decode data from its hex representation and only then encrypt it.
I have to do a program in Java that compares 3 different asymmetric cipher algorithms. I want to choose the key size and the message size (that will be generated randomly), and I'd like to show the different time that every algorithm will be take for encrypt the same text with a key with the same dimension.
I want to compare RSA, DSA and ECIES. The first two don't pose any problems but for the last one I don't know what to do.
The main problems are :
Which elliptic curve is safe to use?
Can I use the same curve for different key sizes?
How can I create a Cipher in Java that uses "ECIES", it doesn't seem to exist?
ECIES is not present in the normal Java libraries, at least not up to the current date. You have to use a library like Bouncy Castle.
For quality of the curves you could take a look at http://safecurves.cr.yp.to (if you have the stomach for it). Each set of domain parameters is always directly tied to the key size. I like Brainpool curves myself; they are relatively standard and relatively safe if you use them with some care.
Note: never directly encrypt plaintext with RSA, DSA or ECIES, always try and use hybrid cryptography. So compare with input sizes of 128, 192 or 256 bits at most.
So, without further ado.
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECIES");
ECGenParameterSpec brainpoolP256R1 = new ECGenParameterSpec(
"brainpoolP256R1");
kpg.initialize(brainpoolP256R1);
KeyPair kp = kpg.generateKeyPair();
Cipher c = Cipher.getInstance("ECIES");
c.init(Cipher.ENCRYPT_MODE, kp.getPublic());
final byte[] aesKeyData = new byte[16];
SecureRandom rng = new SecureRandom();
rng.nextBytes(aesKeyData);
byte[] wrappedKey = c.doFinal(aesKeyData);
SecretKey aesKey = new SecretKeySpec(aesKeyData, "AES");
Arrays.fill(aesKeyData, (byte) 0);
}
I am creating a project to encrypt and decrypt a file. I have these two algorithms that work fine:
public static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
public static byte[] getRaw(String password_) throws Exception {
byte[] keyStart = password_.getBytes();
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
byte[] key = skey.getEncoded();
return key;
}
Now I need to explain how it works. Does it use a private key? Where is the key storage? Can anyone help me?
Note: see owlstead's answer for an excellent description of the flaws in your code example
Your encrypt() and decrypt() operations are performing AES encryption and decryption respectively, using Java's JCE libraries. A JCE provider will be selected to perform the actual cryptography - the provider chosen will be the first in the list of providers that offers an implementation of AES. You have defined the algorithm as only "AES", so the mode of operation and padding will be chosen by the provider. If you want to control this, use the form "AES/mode/padding" (see the docs for valid choices)
The getRaw method derives an AES key from a password. The raw bytes of the password provide the seed for a random number generator. The random number generator is then used to generate sufficient key material for a 128-bit AES key. A different password will produce a different seed, which should produce a different stream of random bytes and thus a different key. I suspect this approach is weakened by the lack of entropy present in most people's passwords, leading to a reduced key space and easier attacks.
There is no key storage in your example code. JCE keys are normally persisted using a KeyStore object and the storage mechanism is provider-dependent.
The above piece of code is a bunch of crap. Unfortunately it is frequently used as a code snippet for Android related code (Android code uses the same API as Java, so there is no need for an Android specific example, andt unfortunately it specifically fails on Android).
I'll explain the issues:
Using a SecureRandom as Password Based Key Derivation Function (PBKDF) is completely idiotic. The underlying implementation of the SecureRandom implementation may change. Furthermore, it is not specified by the SecureRandom that calling setSeed() as the first method will replace the seed; it may actually add the seed to the current state - and this is what certain newer android versions do.
Cipher.getInstance("AES") actually uses the provider defaults instead of specifying the mode of operation and padding mode for the given cipher. By default the Sun provider will use ECB mode which is not suitable for encrypting most data.
String.getBytes() - which is used for the password - returns the platform default encoding. Different platforms may have different default encodings. This means that different platforms will generate different keys.
Above code does not add a message authentication code (MAC or HMAC). This may lead to an attacker changing random ciphertext blocks, which leads to random plain text blocks. This may lead to loss of confidentiality as well if padding Oracle attacks apply.
It seems to me that you are a beginner in cryptography. Please use a higher level standard such as RNCryptor compatible code, or use a standard such as Cryptographic Message Syntax (CMS).