After upgrading Java to 1.8.0_171 I am always getting the following exception during RSA public key composing:
java.security.InvalidKeyException: exponent is larger than modulus
It was working before upgrade, does anyone know what to do with it?
RSAPublicKeySpec publicSpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey pub = factory.generatePublic(publicSpec); <----- EXCEPTION
I've found out that there was a change in Java from 1.8.0_161 referenced by JDK-8174756:
security-libs/javax.crypto
RSA public key validation
In 8u161, the RSA implementation in the SunRsaSign provider will reject any RSA
public key that has an exponent that is not in the valid range as defined by
PKCS#1 version 2.2. This change will affect JSSE connections as well as
applications built on JCE.
JDK-8174756 (not public)
Which basically means that modulus shouldn't be negative.
In that case, it is important to build BigInteger in the following manner to be positive:
BigInteger(1, modulus)
BigInteger(1, exponent)
After that change, it works again.
Related
I have one server and one client.
Client might be in different technologies like java , php.
server code is written in java.
What I am doing in server is, get exponent and modulus bytes of client public key and generate public key of client.
To generate client public key I am using the code below:
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulusBigInt,exponentBigInt);
keyFactory = KeyFactory.getInstance("RSA", "BC");
RSAPublicKey clientPublicKey = (RSAPublicKey) keyFactory.generatePublic(spec);
To encrypt data using client public key I am using below code:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, clientPublickey);
scrambled = cipher.doFinal(buffer);
Server Information
I have implemented RSA-1024 to encrypt AES key.
I am using RSA/ECB/PKCS1Padding algorithm. and I have also make sure that all clients have also consider 1 padding in their code.
Client - 1 (Java)
If client is also made in java than public key is successfully generated from exponent and modulus of client public key.
I am using the code below to generate key pair of client RSA key..
keyPairGene = KeyPairGenerator.getInstance("RSA");
keyPairGene.initialize(1024);
KeyPair keyPair = keyPairGene.genKeyPair();
RSAPublicKey clientPublickey = (RSAPublicKey) keyPair.getPublic();
Client -2 (php)
Now the problem is if client is in php .. than public key is successfully generated but when I try to encrypt using that public key at that time bad padding exception occurs when I have used default provider in server.
I am using bellow code to generate key pair of client RSA key..
$options = array('private_key_bits' => 1024,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'config' => realpath(__DIR__) . '/openssl.cnf');
#Generates New Private / Public Key Pair
$pkGenerate = openssl_pkey_new($options);
#Get Client Private Key
openssl_pkey_export($pkGenerate, $PrivateKey, NULL, $options);
#Get Client Public Key
$keyData = openssl_pkey_get_details($pkGenerate);
Than I have tried BC provider ... It gives me the exception below:
org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
I am not getting what is the problem occurs when I am trying to generate public key from exponent and modulus when client is in php...
if client is in java than its no issues.... and works perfectly..
Any type of help is welcome...
Note:
What I have observed from debugging code is ,
client's public key modulus byte's bit length at server side is varies between 1020 to 1023... it never reaches at 1024 though we have define size as 1024.
Still don't get what exactly the problem is...
but I have implemented work around for it...
I got stuck in generating public key of client using exponent and modulus.
So now I have used one standard format of public key certificate - DER & PEM.
What I did is, generated DER or PEM from PHP side using bellow code,
$options = array('private_key_bits' => 1024,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'config' => realpath(__DIR__) . '/openssl.cnf');
#Get Client Public Key
$keyData = openssl_pkey_get_details($pkGenerate);
$clientPublicKey = $keyData['key'];
$this->clientData['clientPublicKeyPEM'] = $keyData['key'];
And then send that generated PEM to Server (Java).
And at server side I have developed bellow code to regenerate Public key from POM string.
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
byte[] pubKeyBits = Base64.decodeBase64(clientPublickeyView.getModulusBytes());
PublicKey pubKey=keyFactory.generatePublic(new X509EncodedKeySpec(pubKeyBits));
I am trying to use a ECIES cipher to instantiate a SealedObject, but it fails with a NullPointerException. I am using Java JDK1.8.0_72 with Bouncy Castle bcprov-jdk15on v1.53 running on Windows 10. The code looks like this:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECIES");
kpg.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair keyPair = kpg.generateKeyPair();
Cipher cipher = Cipher.getInstance("ECIES");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
String toEncrypt = "Hello";
// Check that cipher works ok
cipher.doFinal(toEncrypt.getBytes());
// Using a SealedObject to encrypt the same string fails with a NullPointerException
SealedObject sealedObject = new SealedObject(toEncrypt, cipher);
The code successfully calls 'cipher.doFinal()' but fails when instantiating the SealedObject. The stack trace is:
java.lang.NullPointerException: string cannot be null
at org.bouncycastle.asn1.ASN1OctetString.<init>(Unknown Source)
at org.bouncycastle.asn1.DEROctetString.<init>(Unknown Source)
at org.bouncycastle.jcajce.provider.asymmetric.ies.AlgorithmParametersSpi.engineGetEncoded(Unknown Source)
at java.security.AlgorithmParameters.getEncoded(AlgorithmParameters.java:362)
at javax.crypto.SealedObject.<init>(SealedObject.java:179)
I'm trying to avoid specifying a particular provider (i.e. Bouncy Castle) and avoiding any provider-specific classes such as IESParameterSpec because the component uses external configuration to specify the algorithms to be used. The component is intended to be used as part of a messaging library in a fluid cluster of nodes where each node may use a different algorithm for encryption, so a SealedObject seems like a reasonable choice because it can be used to pass the algorithm used (any message that uses encryption uses the receiver's public key so the receiver must have the corresponding private key to decrypt the message).
Any thoughts or suggestions would be most welcome.
David Hook at Bouncy Castle had a look and identified an issue in org.bouncycastle.jcajce.provider.asymmetric.ies.AlgorithmParametersSpi.engineGetEncoded and provided a fix in 1.55b04. I tested this out and it has resolved this issue.
Thanks again for your help Maarten.
This class encrypt data values, to do this first I get the cipher as follows,
KeyStore primaryKeyStore = getKeyStore(keyStoreFile, password, keyType, provider);
java.security.cert.Certificate certs = primaryKeyStore.getCertificate(aliasName);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, certs);
But the last line of the above code gives the following error.
What does the wrong key usage means?
Can't I use this certificate for encryption purposes?
Is there separate certificate for encryption?
Caused by: java.security.InvalidKeyException: Wrong key usage
at javax.crypto.Cipher.init(Cipher.java:1640)
at javax.crypto.Cipher.init(Cipher.java:1549)
at my.test.ciphertool.CipherTool.initCipher(CipherTool.java:167)
... 1 more
It means exactly what it says it means in the Javadoc:
If the certificate is of type X.509 and has a key usage extension field marked as critical, and the value of the key usage extension field implies that the public key in the certificate and its corresponding private key are not supposed to be used for the operation represented by the value of opmode, an InvalidKeyException is thrown.
I'm trying to instantiate a java.security.PublicKey by using java.security.spec.RSAPublicKeySpec and java.security.KeyFactory.
But when running the following lines:
RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(modulus, publicExponent);
return KeyFactory.getInstance("RSA").generatePublic(publicKeySpec);
I always get an exception from org.bouncycastle package.
java.security.spec.InvalidKeySpecException: key spec not recognised
at org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePublic(Unknown Source)
at org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi.engineGeneratePublic(Unknown Source)
at java.security.KeyFactory.generatePublic(KeyFactory.java:315)
... (25 more)
This confuses me since the RSAPublicKeySpec should have any relation to Bouncy Castle crypto library? Can somebody please tell me whats wrong with my code?
In the JDK documentation for instantiating the KeyFactory
When you call the KeyFactory.getInstance(String algorithm). This method
traverses the list of registered security Providers, starting with the
most preferred Provider. A new KeyFactory object encapsulating the
KeyFactorySpi implementation from the first Provider that supports the
specified algorithm is returned.
It seems like that in your environment, the bouncy castle crypto is the most preferred provider.
I am not aware of any default Service Provider Interface for KeyFactory in JDK. Although there are many open source implementations available, few of them are RSAKeyFactory & DSAKeyFactory
Hey Just found this, there are many default implementations also available in JDK. look at this page for JCA
I have a JCE test that works fine with all Sun JDKs I have tried, but fails with various IBM J9 JDKs (e.g. 1.6.0 build pwi3260sr8-20100409_01(SR8)). The exception below happens when the cipher is initialized in encrypt mode. Why can the IBM JCE not use its own private key? Am I missing something in my code?
public void testBasicKeyGeneration() throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchProviderException, SignatureException {
KeyPairGenerator generator = KeyPairGenerator.getInstance( "RSA" );
generator.initialize( 2048 );
KeyPair pair = generator.generateKeyPair();
String data1 = "123456789012345678901234567890123456789012345678901234567890";
Cipher cipher = Cipher.getInstance( "RSA" );
cipher.init( Cipher.ENCRYPT_MODE, pair.getPrivate() );
byte[] encrypted = cipher.doFinal( data1.getBytes() );
cipher.init( Cipher.DECRYPT_MODE, pair.getPublic() );
byte[] decrypted = cipher.doFinal( encrypted );
String data2 = new String( decrypted );
assertEquals( "en/decryption failed", data1, data2 );
}
Here is the stack trace:
java.security.InvalidKeyException: Private key cannot be used to encrypt.
at com.ibm.crypto.provider.RSA.engineInit(Unknown Source)
at javax.crypto.Cipher.a(Unknown Source)
at javax.crypto.Cipher.a(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at test.Test.testBasicKeyGeneration(LicenseHelperTest.java:56)
There is a solution, see http://www-01.ibm.com/support/docview.wss?uid=swg1IV18625
with the property
-Dcom.ibm.crypto.provider.DoRSATypeChecking=false
you can use private keys to encrypt data.
I don't know this for sure but I believe that the JCE has an embedded policy limiting encryption to the public key and decryption to the private key.
In the example code the encryption was done with the private key. This would require the public key to decrypt, meaning that anyone with the public key could access the encoded data. Although this has it's uses it is not the accepted pattern and the IBM implementation may be "protecting" you from accidentally creating encrypted data that was publicly readable.
The fact that it tested properly when these were reversed tends to confirm my suspicions but I haven't yet found an official document stating as much.
IBM insists private keys cannot be used for encryption and public keys cannot be used for decryption, so they either see this artificial restriction as a feature, or someone is seriously confused here.
Here is how I worked around this problem:
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) ks.getKey(keyAlias, ksPassword.trim().toCharArray());
RSAPublicKeySpec spec = new RSAPublicKeySpec(privateKey.getModulus(),privateKey.getPrivateExponent());
Key fakePublicKey = KeyFactory.getInstance("RSA").generatePublic(spec);
encryptCipher.init(Cipher.ENCRYPT_MODE, fakePublicKey);
Essentially, I created a public key object with private key's crypto material. You will need to do the reverse, create a private key object with public key's crypto material, to decrypt with public key if you want to avoid the "Public key cannot be used to decrypt" exception.
I recently ran in to the same problem. This was eventually solved by using the bouncy castle implementation and adding this line to the java.security file
security.provider.1=org.bouncycastle.jce.provider.BouncyCastleProvider
#T.Rob commented that you may have made a mistake in encrypting with the private key. If "everyone" knows the public key, then anyone can decrypt your file. IBM's JCE behaviour is thus protecting people against this mistake.
I can see the logic of that.
However, there may be cases where you really do need to encrypt with the private key; e.g. as part of a protocol that needs to prove that you know the private key corresponding to a published public key.
If this is really what you want to do, you probably need to use a recent Sun JCE implementation (older Sun JCEs didn't implement RSA), or Bouncy Castle.
#Stephen C / #FelixM: IBM seems to be completely clueless about how RSA cryptography works and how it is intended to be used. Basically both operations (encrypt / decrypt) must be available for the public AND private key.
Encrypt with public key is needed to transmit the client-side part of the pre master secret in SSL/TLS handshakes. The server needs to decrypt with its private key. But if they negotiate something like ECDHE_RSA the server needs to SIGN parts of the handshake with the private key - thats encrypt with PrivateKey. Vice versa the client needs to decrypt with the public key from the certificate of the server to verify the hash value of the signature. (proving authenticity of the message)
So if I try to run ECDHE_RSA (server-side) on latest IBM JDK 7 the following happens:
java.security.InvalidKeyException: Private key cannot be used to encrypt.
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
at java.lang.Thread.run(Thread.java:777)
at com.ibm.crypto.provider.RSASSL.engineInit(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at java.security.Signature$CipherAdapter.engineInitSign(Signature.java:1239)
at java.security.Signature$Delegate.init(Signature.java:1116)
at java.security.Signature$Delegate.chooseProvider(Signature.java:1076)
at java.security.Signature$Delegate.engineInitSign(Signature.java:1140)
at java.security.Signature.initSign(Signature.java:522)
at net.vx4.lib.tls.core.TLSSignature.createSignature(TLSSignature.java:120)
As you can see we're using "Signature" and call "initSign", which requires indeed a PrivateKey. This proves IBM being clueless about this fact and obviously they don't even
have valid regression tests!
Use another crypto provider and don't believe IBM until they change their mind.
Best regards,
Christian