I am trying to learn how to sign messages using the RSA algorithm with SHA256 in Java. When I generated a 2048-bit KeyPair, I found that both the public and private key were 294 bytes. Here is my code for finding the size of the keys:
import java.security.*;
public class RSATesting
{
public static void main(String[] args) throws Exception
{
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
KeyPair pair = generator.generateKeyPair();
byte[] publicKeyBytes=pair.getPublic().getEncoded();
System.out.println(publicKeyBytes.length);
byte[] privateKeyBytes=pair.getPublic().getEncoded();
System.out.println(privateKeyBytes.length);
}
}
Why are the keys not 256 bytes?
Thanks
RSA keys aren't like AES keys.
AES keys are some random bytes. RSA keys are numbers. The RSA modulus (N) defines the lenght.
Your key is 294 bytes long, because of getEncoded();. It returns a formatted key and not the real lenght.
Related
Are there any way to set the signature size while making DER file using bouncycastle.
Now I am making cert file using org.bouncycastle.
The following is my code. It works fine, but I could not handle the signature length.
** for the SHA256WITHRSA signature algorithm, the length is static as 256 for 2048 RSA key, and 128 for 1024 RSA key.**
I want to make a cert with 2048 public key and 128 signature.
Security.addProvider(new BouncyCastleProvider());
KeyPair keyPair = createKeyPair();
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(new X500Name("CN=Issuer"), BigInteger.ONE, new GregorianCalendar().getTime(), new GregorianCalendar().getTime(), new X500Name("CN=Subject"), keyPair.getPublic());
JcaContentSignerBuilder signerBuiler = new JcaContentSignerBuilder("SHA256WITHRSA").setProvider(BouncyCastleProvider.PROVIDER_NAME);
ContentSigner contentSigner = signerBuiler.build(keyPair.getPrivate());
X509Certificate certificate = new JcaX509CertificateConverter().setProvider(BouncyCastleProvider.PROVIDER_NAME).getCertificate(certBuilder.build(contentSigner));
certificate.verify(keyPair.getPublic());
System.out.println( certificate);
As the following, it has 2048 public key, but the signature size is 128 byte. This is DER file what I wanna make.
The output of RSA operations is always the size of the RSA key used. 2048 bits is 256 bytes. It's impossible to get a 128-byte result using a 256-byte key.
I have a scenario where I put RSA/EC private key (created by openssl) in JSON payload. I have my customized parser where I look for banners and fetch the information between them.
Now for RSA key irrespective whether it is PKCS#1 (default openssl) or PKCS#8 following code is working:
KeyFactory factory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = factory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
where privateKeyBytes is decoded base64 byte of private key. Private key is in the PEM format I put in JSON payload.
However when I create private key for EC algorithm via openssl, same piece of code is giving following error:
java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: IOException : version mismatch:
(supported: 00, parsed: 01)
When I convert created pem to PKCS#8 format via (openssl pkcs8 command) then it works.
Note: My code uses "SUNRSA" provider for RSA algorithm and "SUNEC" provider for EC algorithm.
The RSA KEYS for example are generated in following manner:
Note: KeySize is the size of key you want to generate i.e 1024/2048 etc.
public static KeyPair generateRsaKeyPair(int keySizeInBits) throws NoSuchAlgorithmException {
KeyPairGenerator r = KeyPairGenerator.getInstance("RSA");
r.initialize(keySizeInBits, RandomUtil.getSecureRandom());
KeyPair keypair = r.generateKeyPair();
return keypair;
}
And EC key pair is generated as follows:
Note: CurveType is the type of curve used for EC algorithm key pair generation eg: prime256v1
public static KeyPair generateEcKeyPair(String curveType) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC","BC");
ECGenParameterSpec ecsp = new ECGenParameterSpec(curveType);
kpg.initialize(ecsp, new SecureRandom());
KeyPair kpU = kpg.generateKeyPair();
return kpU;
}
I have set up public and private key encryption in Java, and distributed the public keys of the two users (communication is between two users). I now want the users to exchange a symmetric key. What I am supposed to do:
User A generates a key.
User A encrypts the key with his private key and then encrypts it with B's public key.
User A sends the encrypted key.
User B receives the encrypted key.
User B decrypts the key with his private key and then A's public key.
My code for user A to generate the key:
1. KeyGenerator keyGenerator = KeyGenerator.getInstance(ENCMETHOD);
2. SecureRandom secureRandom = new SecureRandom();
3. int keyBitSize = 128;
4. keyGenerator.init(keyBitSize, secureRandom);
5. secretKey = keyGenerator.generateKey();
6. encodedKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());
// encrypt with public key of B and then my private key
7. String encryptedMessage = encodedKey;
8. encryptedMessage = ac.encryptText
(
ac.encryptText(encryptedMessage, otherUserPublickey),
privateKey
);
Line 8 throws the following error:
javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 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:2165)
at driver.AsymmetricCryptography.encryptText(AsymmetricCryptography.java:73) // please refer to the code section below for this method
at driver.ClientOne.main(ClientOne.java:158) // this is line 8 in the above code
The method AsymmetricCryptography.encryptText(String message, PrivateKey key):
public String encryptText(String msg, PrivateKey key)
throws
UnsupportedEncodingException, IllegalBlockSizeException,
BadPaddingException, InvalidKeyException {
this.cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64.encodeBase64String(cipher.doFinal(msg.getBytes("UTF-8")));
}
// this.cipher = Cipher.getInstance("RSA");
Any help is much appreciated. Thanks.
Looks like you are exceeding the amount of data you can encrypt with RSA (see here https://security.stackexchange.com/questions/44702/whats-the-limit-on-the-size-of-the-data-that-public-key-cryptos-can-handle) which essentially is the modulus size, possibly due to Base 64 encoding.
I'm trying to use an asymmetric private and public key combination to generate a symmetric key for encrypting and decrypting some text, but, I'm stuck unable to use the generated key as it is 128bytes in size and this is unacceptable for the AES encryption. I'd like to solve this problem using just the JRE (no external libraries). Do you have a solution?
I've included my example code below, there's a comment indicating the line I get the exception thrown.
(encryptCipher.init(Cipher.ENCRYPT_MODE, tomSecretKeySpec, iv);)
I read about KDF hashing, but Java doesn't seem to have an obvious way of invoking this on my 128byte key. Also, Im not sure this is the right answer since my understanding is that the longer the key, the more secure the encryption (for a given algorithm). Perhaps I need to switch from using AES/CBC/PKCS5Padding, but none of the other algorithms included with the JDK as standard seem to support the 128byte key either.
public void demoSymmetricEncryption() throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException {
String keyAlgorithm = "DiffieHellman";
String keyAgreementAlgorithm = "DiffieHellman";
String keySpecAlgorithm = "AES";
String cipherAlgorithm = "AES/CBC/PKCS5Padding";
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(keyAlgorithm);
keyGenerator.initialize(1024, new SecureRandom());
KeyPair tomKeyPair = keyGenerator.generateKeyPair();
PrivateKey tomPrivateKey = tomKeyPair.getPrivate();
PublicKey tomPublicKey = tomKeyPair.getPublic();
KeyPair steveKeyPair = keyGenerator.generateKeyPair();
PrivateKey stevePrivateKey = steveKeyPair.getPrivate();
PublicKey stevePublicKey = steveKeyPair.getPublic();
int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
System.out.println("Limited encryption policy files installed : " + (maxKeyLen == 128)); // returns false
KeyAgreement tomKeyAgreement = KeyAgreement.getInstance(keyAgreementAlgorithm);
keyGenerator.initialize(1024, new SecureRandom());
tomKeyAgreement.init(tomPrivateKey);
tomKeyAgreement.doPhase(stevePublicKey, true);
byte[] tomSecret = tomKeyAgreement.generateSecret();
SecretKeySpec tomSecretKeySpec = new SecretKeySpec(tomSecret, keySpecAlgorithm);
KeyAgreement steveKeyAgreement = KeyAgreement.getInstance(keyAgreementAlgorithm);
steveKeyAgreement.init(stevePrivateKey);
steveKeyAgreement.doPhase(tomPublicKey, true);
byte[] steveSecret = steveKeyAgreement.generateSecret();
SecretKeySpec steveSecretKeySpec = new SecretKeySpec(steveSecret, keySpecAlgorithm);
System.out.println("Secret Keys are identical : " + steveSecretKeySpec.equals(tomSecretKeySpec)); // returns true
String initVector = "RandomInitVector";
Cipher encryptCipher = Cipher.getInstance(cipherAlgorithm);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
// fails because AES key is 128 bytes not 128 bits in length - think I need to use KDF hash to shrink it appropriately.
encryptCipher.init(Cipher.ENCRYPT_MODE, tomSecretKeySpec, iv);
// Attempt to use the cipher
byte[] encryptedData = encryptCipher.doFinal("Hello".getBytes());
Cipher decryptCipher = Cipher.getInstance(cipherAlgorithm);
iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
decryptCipher.init(Cipher.DECRYPT_MODE, steveSecretKeySpec, iv);
byte[] decryptedData = decryptCipher.doFinal(encryptedData);
System.out.println("Decrypted Data : " + new String(decryptedData));
}
The output from the program is as follows:
Limited encryption policy files installed : false
Secret Keys are identical : true
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 128 bytes
at com.sun.crypto.provider.AESCrypt.init(AESCrypt.java:87)
at com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91)
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:582)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:339)
at javax.crypto.Cipher.implInit(Cipher.java:806)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at crypto.SymetricEncryptionTest.demoSymmetricEncryption(SymetricEncryptionTest.java:76)
at crypto.SymetricEncryptionTest.main(SymetricEncryptionTest.java:29)
The error is: * Invalid AES key length: 128 bytes*
Valid AES key sizes are 128-bits, 192-bits and 256-bits or in bytes: 16-bytes, 24-bytes and 32-bytes.
Use an AES key size that is valid.
The general method of generation a symmetric key is just to get the bytes from a cryptographic PRNG. For Java see Class SecureRandom.
For key derivation use PBKDF2, see Class SecretKeyFactory and Java Cryptography Architecture Standard Algorithm Name Documentation "PBKDF2WithHmacSHA1" (Constructs secret keys using the Password-Based Key Derivation Function function).
For an example see OWASP Hashing Java but use "PBKDF2WithHmacSHA1" as the algorithm.
The reason the code wasn't working was that I was using incompatible algorithms. The corrections are as follows:
Replace lines:
String keyAlgorithm = "DiffieHellman";
String keyAgreementAlgorithm = "DiffieHellman";
with
String keyAlgorithm = "EC";
String keyAgreementAlgorithm = "ECDH";
int keySize = 128;
and replace lines
keyGenerator.initialize(1024, new SecureRandom());
with
keyGenerator.initialize(keySize, new SecureRandom());
Program now produces output:
Limited encryption policy files installed : false
Secret Keys are identical : true
Decrypted Data : Hello
Technically, you probably also want to Base64 encode the encrypted output and then decode it again prior to the decode as below:
String encryptedData = Base64.encode(encryptCipher.doFinal("Hello".getBytes()));
byte[] decryptedData = decryptCipher.doFinal(Base64.decode(encryptedData));
Hi I was reading the following Wikipedia article on Public Key Cryptography
Public Key Cryptography
I saw this picture too which shows how to generate a symmetric key cipher using your Private Key and the OTHER persons Public Key.
Generate Symmetric Key/Shared Secret
I already know how to exchange the public keys between the parties however I was wondering If it were possible to implement the procedure in the picture using the Java Programming Language.
The Private and Public Keys used would be generated using RSA and the Key/Shared Secret to be generated would be a Symmetric Key for a Symmetric Cipher (I want to use AES-128)
I understand the theory behind this but am unsure how to implement it in Java properly, any ideas or help would be much appreciated :)
Try KeyAgreement and algorithm "DiffieHellman". Note that you need Java 8 for 2048 bit key sizes, Java 7 and below are stuck on 1024 (unless you install the Bouncy Castle provider).
Or you can go for the "ECDH" algorithm, but beware that takes a bit of a learning curve.
Thanks owlstead using that I was able to write the following code which generates RSA keys and then Tries to link the private keys together with the opposing public key using DiffieHellman
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class AESKeGenFromRSA
{
public static void main(String[] args)
{
try
{
// Generate RSA KeyPair for Alice
Cipher alice_rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Get RSA KeyPairGenerator Object Instance
KeyPairGenerator alice_gen = KeyPairGenerator.getInstance("RSA");
// Generate RSA Assymetric KeyPair
KeyPair alice_pair = alice_gen.generateKeyPair();
// Extract Public Key
PublicKey alice_pub = alice_pair.getPublic();
// Extract Private Key
PrivateKey alice_pvt = alice_pair.getPrivate();
System.out.println();
System.out.println("Alice Public: " + alice_pub);
System.out.println();
System.out.println("Alice Private: " + alice_pvt);
// Generate RSA KeyPair for Bob
Cipher bob_rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Get RSA KeyPairGenerator Object Instance
KeyPairGenerator bob_gen = KeyPairGenerator.getInstance("RSA");
// Generate RSA Assymetric KeyPair
KeyPair bob_pair = bob_gen.generateKeyPair();
// Extract Public Key
PublicKey bob_pub = bob_pair.getPublic();
// Extract Private Key
PrivateKey bob_pvt = bob_pair.getPrivate();
System.out.println();
System.out.println("Bob Public: " + bob_pub);
System.out.println();
System.out.println("Bob Private: " + bob_pvt);
// Create KeyAgreement for Alice
KeyAgreement alice_agreement = KeyAgreement.getInstance("DiffieHellman");
alice_agreement.init(alice_pvt);
alice_agreement.doPhase(bob_pub, true);
byte[] alice_secret = alice_agreement.generateSecret();
SecretKeySpec alice_aes = new SecretKeySpec(alice_secret, "AES");
// Create KeyAgreement for Bob
KeyAgreement bob_agreement = KeyAgreement.getInstance("DiffieHellman");
bob_agreement.init(bob_pvt);
bob_agreement.doPhase(alice_pub, true);
byte[] bob_secret = bob_agreement.generateSecret();
SecretKeySpec bob_aes = new SecretKeySpec(bob_secret, "AES");
System.out.println();
System.out.println(alice_aes.equals(bob_aes));
}
catch (NoSuchAlgorithmException e)
{e.printStackTrace();}
catch (NoSuchPaddingException e)
{e.printStackTrace();}
catch (InvalidKeyException e)
{e.printStackTrace();}
}
}
This is the exception that gets thrown when I try to run the program. I understand why this is happening but I'm a little unsure how to fix it.
java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPrivateCrtKeyImpl
at javax.crypto.KeyAgreement.chooseProvider(KeyAgreement.java:398)
at javax.crypto.KeyAgreement.init(KeyAgreement.java:464)
at javax.crypto.KeyAgreement.init(KeyAgreement.java:435)
at AESKeGenFromRSA.main(AESKeGenFromRSA.java:45)
It seems that PrivateKey objects are not a valid argument to the KeyAgreement.init(Key key) function, Any ideas would be appreciated ...