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
Related
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.
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.
I have been using jCryption for a secure login. On the client i am using the JavaScript package and on the Java decryption i am using BouncyCastle jar to decrypt.
The problem is that it works OK in Tomcat but when i take the same webapp and deploy on Jboss i am having problems loading the BouncyCastle jar.
My question is: is there a way to encrypt using jCryption that will produce a more standardized RSA output which will allow me to use other security providers?
jcryption isn't as secure as you might think:
http://www.securityfocus.com/archive/1/520683
My recommendation... do something similar to this:
http://www.frostjedi.com/terra/dev/rsa/index.php
The following URL elaborates:
http://area51.phpbb.com/phpBB/viewtopic.php?f=84&t=33024&start=0
Generally speaking, if you want security, avoid JavaScript cryptography, use SSL/TLS instead.
The main problems are:
insufficient quality of implementation of cryptographic routines (e.g. random numbers)
the client has no idea whether the script may have been tampered with by a MITM attacker, even if the JavaScript library is of sufficient quality.
You're not actually adding much security by using JavaScript cryptography on your website unfortunately.
Here is the snippet for RSA decoding compatible with jCryption. We assume that encExternalKey is what jCryption send in key parameter on handshake call. modulus and secretExponent are taken from 100_1024_keys.inc.php file that comes with jCryption.
RSAPrivateKeySpec privateKeySpec =
new RSAPrivateKeySpec(new BigInteger(modulus, 10), new BigInteger(secretExponent, 10));
RSAPrivateKey privateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(privateKeySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
StringBuilder externalKeyBuf =
new StringBuilder(new String(cipher.doFinal(new BigInteger(encExternalKey, 16).toByteArray())));
String externalKey = externalKeyBuf.reverse().toString().trim();
Authenticated encryption requires that we use some accepted standard for encrypting and authenticating a message. So we both encrypt the message and compute a MAC on the message to verify it has not been tampered with.
This question outlines a way to perform password based key strengthening and encryption:
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
But as far as I can tell, this does not compute any MAC on the ciphertext and so would be insecure. What is the accepted standard for performing authenticated encryption in Java?
I would recommend using GCM mode encryption. It is included in the latest JDK (1.7) by default. It uses a counter mode encryption (a stream cipher, no padding required) and adds an authentication tag. One big advantage is that it requires only a single key, whereas HMAC adds another key to the mix. Bouncy Castle has an implementation as well, which is moslty compatible with one provided by Oracle.
GCM mode encryption is also features in a TLS RFC, and in XML encrypt 1.1 (both not final). GCM mode provides all three security features: confidentiality, integrity and authenticity of the data send. The String would be "AES/GCM/NoPadding" instead of the CBC one you are now deploying. As said, make sure you have the latest JDK from Oracle, or have Bouncy Castle provider installed.
Also check out my answer here, which is mostly about String encoding, but I've succesfully tried GCM mode too - see the comment.
When transferring files from one server to another through secure ftp, I use private/public key pairs with the private key residing on the "from" server and the public key residing on the "to" server.
Using private/public key pairs is a secure standard when transferring files.
I believe it would also be a secure means in the context of a Java application.
Check out Generating and Verifying Signatures and Generate Public and Private Keys
for more details on using a private/public key pair setup for digital signatures in Java.
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