I'm trying to encrypt using the loaded des key from KeyStore and I get:
Exception in thread "main" java.security.InvalidKeyException: No installed provider supports this key: sun.security.pkcs11.P11Key$P11SecretKey
at javax.crypto.Cipher.chooseProvider(Cipher.java:878)
at javax.crypto.Cipher.init(Cipher.java:1213)
at javax.crypto.Cipher.init(Cipher.java:1153)
and this is my code:
public static void main(String[] args) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchPaddingException, IOException, CertificateException {
Provider provider = new sun.security.pkcs11.SunPKCS11(DesSaveLoad.class.getClassLoader().getResourceAsStream("pkcs11.cfg"));
Security.removeProvider(provider.getName());
Security.insertProviderAt(provider, 1);
KeyStore keyStore = KeyStore.getInstance("PKCS11", provider);
keyStore.load(null, null);
SecretKey desKey = desGenerateKey();
keyStore.setKeyEntry("t1", desKey, null, null);
SecretKey t1 = (SecretKey) keyStore.getKey("t1", null);
byte[] messageBytes = "message".getBytes();
desEncrypt(messageBytes, 0, messageBytes.length, desKey);
desEncrypt(messageBytes, 0, messageBytes.length, t1); //Exception is thrown here
}
public static SecretKey desGenerateKey() throws NoSuchAlgorithmException {
KeyGenerator keygenerator = null;
keygenerator = KeyGenerator.getInstance("DES");
return keygenerator.generateKey();
}
public static byte[] desEncrypt(byte[] plainText, int offset, int size, SecretKey key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher;
if (size % 8 != 0) {
cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
} else {
cipher = Cipher.getInstance("DES/ECB/NoPadding");
}
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainText, offset, size);
}
As you can see there is no exception thrown when encrypting using generated des key.
If you perform encryption using a HSM then the encryption procedure is performed within the HSM, not in the software. Cipher does not implement the encryption procedure itself. The underlying CipherSpi of the PKCS#11 provider for Cipher is chosen using delayed provider selection depending on the key given during the call to init(). So although the desEncrypt() function seems to perform the same operations, in reality the functionality depends on the provider, and in your case, on the PKCS#11 wrapper, library and of course HSM.
Now PKCS#11 is an interface specification; not all mechanisms in PKCS#11 will be implemented in every token. It is likely that some encryption algorithms are too obscure or too unsafe. The latter is probably the case for DES ECB as that algorithm is extremely insecure. That does not mean that DES keys cannot be used in general - they could still play a role in e.g. MAC calculations. So please check the documentation of your HSM if DES ECB is supported (in the current setting).
You can get more information about the PKCS#11 method calls by adding -Djava.security.debug=sunpkcs11 to your call to the Java interpreter (java or javaw). If DES does not work, try the much safer and more common "AES/CBC/PKCS5Padding" or triple DES mechanism.
See if this post helps
Either the key is incorrect (more likely) or the given key is not supported by the provider.
KeyStore.getInstance("PKCS11", provider);
PS: Are you using a custom provider?
Related
I have information security project about encrypting file using AES. and the using key in this algorithm is also encrypted using RSA algorithm and public key,
the problem is: after encrypting the random key it returns array byte[], how this array byte converted into key so I can encrypt the file?
NOTE [public_Key is generated from user using JPasswordField
and this is the challenge I faced from my course project]
public void AESEncryption(File file) throws FileNotFoundException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
String data;
SecretKey random_key;
int key_size=128;
Scanner myReader = new Scanner(file);
while (myReader.hasNextLine()) {
data = myReader.nextLine();
}
// create GenerateKey object to access public key
// GenerateKey is my personal class and contain public key
GenerateKey key = new GenerateKey();
// convert public key to string
String public_Key = key.PublicKey.getText();
// convert string public key to secret key
byte[] decodedKey = Base64.getDecoder().decode(public_Key);
SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
// generate random key
KeyGenerator g = KeyGenerator.getInstance("AES");
// give it size
g.init(key_size);
random_key = g.generateKey();
// encrypt the random key with RSA and public key
byte[] random_byteKey = random_key.getEncoded();
Cipher cipher_Key = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher_Key.init(Cipher.ENCRYPT_MODE, originalKey);
byte[] encrypted_key = cipher_Key.doFinal(random_byteKey); //RSA key
// after generating RSA key we will Encrypt file using RSA key
byte[] byte_message = data.getBytes();
Cipher cipherTxt = Cipher.getInstance("AES/GCM/NoPadding");
// the problem in here
cipherTxt.init(Cipher.ENCRYPT_MODE, encrypted_key);
byte[] encByte = cipherTxt.doFinal(byte_message);
}
You are not understanding what you need to do. First you generate a random AES key that is used solely for the data encryption. Then you encrypt that key with RSA using the trusted RSA public key which is part of the key pair of the receiver. So you never have to convert either the public key or the RSA ciphertext to a symmetric key.
As an aside, instead of using Cipher#doFinal() you should use Cipher#wrap() , which takes a symmetric key. That way you don't have to encode them to a byte array. It may also be more secure if a hardware module is used, for instance, depending on the Cipher implementation.
I'd strongly suggest you generate separate methods for these separate steps as well as for the file handling.
In the end, you'll need something more akin to this:
public static void hybridEncrypt(RSAPublicKey publicKey, File in, File out) throws IOException, InvalidKeyException {
int key_size=128;
try {
KeyGenerator g = KeyGenerator.getInstance("AES");
g.init(key_size);
SecretKey dataKey = g.generateKey();
// encrypt the random data key with the RSA public key
Cipher cipher_Key = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher_Key.init(Cipher.WRAP_MODE, publicKey);
byte[] encryptedKey = cipher_Key.wrap(dataKey);
Cipher cipherTxt = Cipher.getInstance("AES/GCM/NoPadding");
cipherTxt.init(Cipher.ENCRYPT_MODE, dataKey);
byte[] message = Files.readAllBytes(in.toPath());
byte[] encryptedMessage = cipherTxt.doFinal(message);
out.createNewFile();
Files.write(out.toPath(), encryptedKey);
Files.write(out.toPath(), encryptedMessage, StandardOpenOption.APPEND);
} catch(NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException e) {
throw new RuntimeException("RSA or AES/GCM not available", e);
} catch (BadPaddingException e) {
throw new RuntimeException("Padding failed for NoPadding", e);
}
}
public static void main(String[] args) throws Exception {
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
kpGen.initialize(3072);
KeyPair keyPairReceiver = kpGen.generateKeyPair();
RSAPublicKey publicKeyReceiver = (RSAPublicKey) keyPairReceiver.getPublic();
hybridEncrypt(publicKeyReceiver, new File("plain.txt"), new File("bla.bin"));
}
Beware that this is still not best practice code, for instance it uses the old PKCS#1 encryption instead of OAEP. Don't copy paste this guys - with encryption you need to understand what you are doing, and preferably use a well vetted high level library.
I am trying to set up RSA Encryption in my Java project. I generated an asymmetric key with the following command in the Terminal:
keytool -genkey -keyalg RSA -alias mykey -keystore mykey.jks -storepass mykeypass -keypass mykeypass
Now I load the keystore with the following method:
public void loadKeyStore() throws KeyStoreException, CertificateException, IOException,
NoSuchAlgorithmException {
keyStore = KeyStore.getInstance(KEY_TYPE);
char[] storePwdArray = STORE_PASS.toCharArray();
FileInputStream fis = new FileInputStream(KEY_STORE_PATH);
keyStore.load(fis, storePwdArray);
}
Now, I have two methods, one for encryption, one for decryption:
public String encrypt(String data) throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
if (keyStore == null) {
loadKeyStore();
}
Certificate cert = keyStore.getCertificate(ALIAS);
Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
rsa.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
byte[] encryptedBytes = rsa.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public String decrypt(String encryptedData) throws UnrecoverableKeyException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException {
if (keyStore == null) {
loadKeyStore();
}
Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
char[] keyPwdArray = KEY_PASS.toCharArray();
Key key = keyStore.getKey(ALIAS, keyPwdArray);
rsa.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedBytes = rsa.doFinal(encryptedData.getBytes());
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
In the main method I try to encrypt and decrypt a String and print it, like that:
public static void main(String[] args) throws UnrecoverableKeyException, NoSuchPaddingException, IllegalBlockSizeException, CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
RSAEncryptionService encryptionService = new RSAEncryptionService();
String secretMessage = "Hello World!";
String encryptedMessage = encryptionService.encrypt(secretMessage);
System.out.println(encryptedMessage);
String decryptedMessage = encryptionService.decrypt(encryptedMessage);
System.out.println(decryptedMessage);
}
The encryption works fine and returns something like that: B61g7zzXDNW9AO/Idc/OBZOCDOJpQTwgchD9uJisEBgxy8HV1XPYZZaLEnxkJHed2sBAQXEIyCDcIAHWk5rxn40tVd4NwlIUya1rB6WNvRFLrrN30G7VjMU6NNUdwJ55n7is2Ylfu0SkwNpy/o4e9LaZyzCyr4lJsTbFEXJQJKqLsOC+ysHYdhzx61Y8UJw6mUhleju7h11OcdDBdGEtAtBcKx9WDt2cgHrdtYUgUkwmEy3vTuuyUwVVpjA4QwUsjXnN+i19FQBZt67sMYIpUT4x4yJ8egqN4mJ2N8aNLwF7m/FS7EZphXdna4KN0srKBbPquB1ER5be6RnoyMFDsg==
But when it comes to the decryption, I get the following Exception:
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
at java.base/com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:349)
at java.base/com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:406)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2205)
What I tried:
I read somewhere on StackOverflow that I need to increase the size of the key. But, that also produces a longer encrypted String, and then the Exception just states "Data must not be longer than 512/1024/... bytes".
I tried using a KeyPair generated in Code, like below. That worked, but I have no idea how to get that KeyPair into my keystore.
public void initKeyStore() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, KeyStoreException, IOException, CertificateException {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
KeyPair pair = generator.generateKeyPair();
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, pair.getPublic());
byte[] secretMessageBytes = "secretMessage".getBytes(StandardCharsets.UTF_8);
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
String encodedMessage = Base64.getEncoder().encodeToString(encryptedMessageBytes);
System.out.println(encodedMessage);
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, pair.getPrivate());
byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
System.out.println(decryptedMessage);
}
The reason is simple. You are using a 2048 bit certificate. it cannot encrypt any data bigger than 256 bytes because it need to be smaller than RSA modulus in case no RSA padding is used. If RSA-OEAP padding is used, another 11 bytes will be used for padding.
In RSA encryption, you normally need to use key encapsulation to support encryption of any arbitrary data sizes. In nutshell, it means that you need to encrypt you data with a random symmetric key and symmetric algorithm(like AES), and then encrypt its key using RSA and send both to other side. Other side first decrypt symmetric key, and then use it to decrypt original data. So it will be something like this:
Encryption side
-----------------
1- Generate random AES key
2- Encrypt data with AES and generated key
3- Encrypt Key with RSA public key
4- Send 2 encrypted data to the other side
Decryption side
-----------------
1- Split received data to encrypted key and encrypted data
1- Decrypt AES Key with RSA private key
2- Decrypt data with AES and decrypted AES key
In addition, read comment on this answer for further reading about padding and in detail implementations.
BouncyCastle provides an implementation of Threefish, which can take a tweak as a parameter:
ThreeFishEngine engine = new ThreeFishEngine(256);
engine.init(true, new TweakableBlockCipherParams(...));
However, TweakableBlockCipherParams is not compatible with the AlgorithmParameter type that is used by instances of Java's default Cipher.
Is there a way to initialize this cipher with a tweak?
Cipher cipher = Cipher.getInstance("Threefish-256/CBC/NoPadding");
cipher.init(???);
You can only use Bouncy Castle's Threefish algorithm through Java's cryptography API if you don't want to use a tweak parameter during ciphering. Through Java's API you can only introduce a key and an initialization vector parameter, but this won't be used as a tweak parameter (I explained why after the code example, see below).
Also, for the below example to work you have to update your JRE/JDK with the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files which you can download from here. There are different versions for Java 7 and 8.
If you don't want to use a tweak parameter you can use the Threefish algorithm through the standard crypto API like this.
static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();
public static void main(String[] args) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("Threefish-1024", PROVIDER);
kg.init(1024);
SecretKey key = kg.generateKey();
byte[] plaintext = "Hi! I'm cat!".getBytes();
byte[] ciphertext = encrypt(key, plaintext);
System.out.println(new String(decrypt(key, ciphertext)));
// prints "Hi! I'm cat!"
}
static byte[] encrypt(SecretKey key, byte[] plaintext) throws Exception {
return encryptOrDecrypt(true, key, plaintext);
}
static byte[] decrypt(SecretKey key, byte[] ciphertext) throws Exception {
return encryptOrDecrypt(false, key, ciphertext);
}
static byte[] encryptOrDecrypt(boolean encrypt, SecretKey key, byte[] bytes) throws Exception {
Cipher cipher = Cipher.getInstance("Threefish-1024/CBC/PKCS5Padding", PROVIDER);
// note that we are creating a dummy iv parameter, in this case it
// should be 128 bytes long, because if it's not an exception is raised
cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[128]));
return cipher.doFinal(bytes);
}
I've downloaded a Bouncy Castle JAR with debug symbols from here and debugged the above code. The call to Cipher.init lands in Threefish.init and the variable params will be an instance of KeyParameter and not TweakableBlockCipherParameters in our case. So, tweakBytes will be null and won't be used during ciphering.
Knowing this, right now it's impossible to use the Java API to supply the tweak parameter to the underlying Threefish cipher engine.
Link to another very similar question
I need to encrypt some data with a password. It has to be a variation of AES with a 256bit key.
I searched a while on the web and came up with this two alogrithms. Now I do not know, which one to choose, cause I do not know, which one is 'saver'.
The first one is the PBEWithSHA256And256BitAES-CBC-BC:
public static byte[] encrypt(String plainText, char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 2048);
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKeyFactory keyFac = SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC", "BC");
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
Cipher encryptionCipher = Cipher.getInstance("PBEWithSHA256And256BitAES-CBC-BC", "BC");
encryptionCipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
return encryptionCipher.doFinal(plainText.getBytes());
}
The other one generates the Key with PBEWithSHA256And256BitAES-CBC-BC, but encrypts with AES:
public static byte[] encrypt(String plainText, char[] password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBEWithSHA256And256BitAES-CBC-BC", "BC");
KeySpec spec = new PBEKeySpec(password, salt, 2048, 256);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(plainText.getBytes());
}
So, which one - and why - is the more secure one? And what is the difference between them?
You should certainly specify a mode of operation for your block cipher. That's the "CBC" part of cipher in the BC implementation. Otherwise, you will default to ECB mode, which has simple codebook replay attack possible! So, long story short - don't use the bottom code snip, prefer the top one.
It could be fixed up to operate similar to how the BC implementation does by specifying mode of operation and other parameters - but honestly just use the BC if you don't know about this stuff - they've done the work and those providers are ready to use as you want "out of the box".
I am developing an application that needs to validate SHA256withECDSAsignatures with the help of secp256r1 (NIST P-256, P-256, prime256v1) public keys.
The public keys are generated by a different application at some earlier point in time and stored in my database in hex encoding. The format of the hex string here is equivalent to the hex string OpenSSL would generate when calling openssl ec -in x.pem -noout -text on a file x.pem that has previously been generated by openssl ecparam -genkey -name secp256r1 -out x.pem.
The message and signature are received from a different application.
Consider the following test data:
// Stored in Database
byte[] pubKey = DatatypeConverter.parseHexBinary("049a55ad1e210cd113457ccd3465b930c9e7ade5e760ef64b63142dad43a308ed08e2d85632e8ff0322d3c7fda14409eafdc4c5b8ee0882fe885c92e3789c36a7a");
// Received from Other Application
byte[] message = DatatypeConverter.parseHexBinary("54686973206973206a75737420736f6d6520706f696e746c6573732064756d6d7920737472696e672e205468616e6b7320616e7977617920666f722074616b696e67207468652074696d6520746f206465636f6465206974203b2d29");
byte[] signature = DatatypeConverter.parseHexBinary("304402205fef461a4714a18a5ca6dce6d5ab8604f09f3899313a28ab430eb9860f8be9d602203c8d36446be85383af3f2e8630f40c4172543322b5e8973e03fff2309755e654");
Now this should be a valid signature.
My objective is to validate the signature over the message using the Java and/or Bouncycastle crypto API. I have created a method isValidSignaturefor that:
private static boolean isValidSignature(byte[] pubKey, byte[] message,
byte[] signature) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, InvalidKeySpecException {
Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
ecdsaVerify.initVerify(getPublicKeyFromHex(pubKey));
ecdsaVerify.update(message);
return ecdsaVerify.verify(signature);
}
I have tried to extract the public key:
KeyFactory.generatePublic:
private static PublicKey getPublicKeyFromHex(byte[] pubKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
KeyFactory fact = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
return fact.generatePublic(new X509EncodedKeySpec(pubKey));
}
But this throws a java.security.spec.InvalidKeySpecException (DER length more than 4 bytes: 26).
What can I do to parse this?
The Bouncy Castle example code on elliptic curve key pair Generation and key factories got me pretty close.
Once I managed to create a ECDSA key factory and a curve specification for the secp256r1/NIST P-256/P-256/prime256v1 curve I was able to use ECPointUtil.decodePoint to obtain a curve point. I could then generate a public key specification that enabled me to generate a public key like this:
private PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1");
KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
ECNamedCurveSpec params = new ECNamedCurveSpec("prime256v1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = ECPointUtil.decodePoint(params.getCurve(), pubKey);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);
return pk;
}