I'm using IBM SDK Java Technology Edition and the code below:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = SecureRandom.getInstance("IBMSecureRandom", "IBMJCE");
random.setSeed(longToBytes(System.currentTimeMillis()));
keyGen.initialize(512, random);
KeyPair pairTytus = keyGen.generateKeyPair();
KeyPair pairRomek = keyGen.generateKeyPair();
KeyPair pairAtomek = keyGen.generateKeyPair();
// Making a wrap-key for private keys; based on password.
byte[] key = ("password").getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "MARS");
Cipher c1 = Cipher.getInstance("MARS/ECB/NoPadding");
c1.init(Cipher.WRAP_MODE, secretKeySpec);
c1.wrap(pairTytus.getPrivate());
While running the application I'm getting this exception:
Exception in thread "main" javax.crypto.IllegalBlockSizeException:
Input length not multiple of 16 bytes.
I read somewhere it has something to do with using "NoPadding", but MARS doesn't implement any padding in this library. Any thoughts how to avoid this exception?
I need to use both MARS and ECB in this place.
Related
I am trying to interface with external system which requires to send an RSA-OAEP SHA-256 public key with key size of 2048.
I am getting an error that the key has wrong bit length: '2350' .
I tried online encryption/decryption services using the generated KP and they seems to accept the key. What I am doing wrong?
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
SecureRandom random = new SecureRandom();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
generator.initialize(2048, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
// I am sending pubKey.getEncoded()
Example result of the public key:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuUKRnE07rwQ8Juegbhabv1kaHOrfou8+FNHYyhjn8jUPgwumALonsvztt4goivc1Bwm6sFwCIGnxf+2y1BI1saG3w5S1R24EUAN7efi7CS4LMEhtgJtavtWYEkEZj7OaU/BLnkB1rFAmBDU3vudSd3Gupgnbqtw7VjOH6Qrfnsh3phVr6DdHruq7SftOvCyhBucOax0hwt6enRs5UjBfbgDbbSMFaFdF4jZpE4Jnfl9gwRF51QP934Il1djPT6cezuEYlD8VAklLFPR+rOL73nvBCxLdZwdBlQHO8J8XGjWaNmAMHvyisFxkD8Ud9nC7m9MPb9J6+n3cWG7OL+C97QIDAQAB
Thank you
I have an issue with my java code. I'm trying to encrypt a file. However, when I run my java code I get "java.security.InvalidKeyException: Invalid AES key length: 162 bytes".
Here is the code:
byte[] rawFile;
File f = new File("./src/wonkybox.stl");
FileInputStream fileReader = new FileInputStream(f);
rawFile = new byte[(int)f.length()];
fileReader.read(rawFile);
/***** Encrypt the file (CAN DO THIS ONCE!) ***********/
//Generate the public/private keys
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
keyGen.initialize(1024, random);
KeyPair key = keyGen.generateKeyPair();
PrivateKey privKey = key.getPrivate();
PublicKey pubKey = key.getPublic();
//Store the keys
byte[] pkey = pubKey.getEncoded();
FileOutputStream keyfos = new FileOutputStream("./CloudStore/keys/pubkey");
keyfos.write(pkey);
keyfos.close();
pkey = privKey.getEncoded();
keyfos = new FileOutputStream("./CloudStore/keys/privkey");
keyfos.write(pkey);
keyfos.close();
//Read public/private keys
KeyFactory keyFactory = KeyFactory.getInstance("AES");
FileInputStream keyfis = new FileInputStream("./CloudStore/keys/pubkey");
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
PublicKey pub1Key = keyFactory.generatePublic(pubKeySpec);
keyfis = new FileInputStream("./CloudStore/keys/privkey");
encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
PrivateKey priv1key = keyFactory.generatePrivate(privKeySpec);
//Encrypt file using public key
Cipher cipher = Cipher.getInstance("AES");
System.out.println("provider= " + cipher.getProvider());
cipher.init(Cipher.ENCRYPT_MODE, pub1Key);
byte[] encryptedFile;
encryptedFile = cipher.doFinal(rawFile);
//Write encrypted file to 'CloudStore' folder
FileOutputStream fileEncryptOutput = new FileOutputStream(new File("./CloudStore/encrypted.txt"));
fileEncryptOutput.write(encryptedFile);
fileEncryptOutput.close();
The error occurs at the line "KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");".
AES is a symmetric algorithm, hence they use of KeyPairGenerator is not supported. To generate a key with AES you call KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); //set keysize, can be 128, 192, and 256
By looking at the rest of your code, it looks like you are trying to achive asymmetric encryption (since you call getPublic() and getPrivate() etc), so I advice you to switch to using RSA or any other asymmetric algorithm that java supports. You will most likley only need to replace AES with RSA in your getInstance(); calls, and pherhaps some fine-tuning. Good luck
As far as I know, AES is symmetric encryption algorithm i.e. it needs only one key for encryption/decryption.
From the JavaDoc of java.security.KeyPairGenerator:
The KeyPairGenerator class is used to generate pairs of public and private keys.
Meaning that it should be used for asymmetric encryption algorithms. For symmetric encryption algorithms one should use javax.crypto.KeyGenerator.
However, I advise simply mimicking some tutorial on how to encrypt / decrypt byte array in Java using AES like this one.
It uses sun.misc.Base64Encoder / Base64Decoder classes to encode / decode byte array to / from String, however you may skip this step.
Hope this helps
How can you use a keypair generator for AES? AES is a symmetric key algorithm. Refer this link. That means if you encrypt data using a key "k", then you will have to decrypt it also using the same key "k". But when you generate key pair, as the name suggests, two keys are generated and if you encrypt using one of the keys, you can decrypt only using the other key. This is the base for PKI.
If you want to use keypair generator use an algorithm like "rsa" or "dsa" in the getInstance() method like this :
KeyPairGenerator keygen=KeyPairGenerator.getInstance("rsa");
I think your code should now work fine after making the above change.
I'm getting the following exception when I try to encrypt a byte array with a EC public key:
java.security.InvalidKeyException: No installed provider supports this
key:
sun.security.ec.ECPublicKeyImpl
This exception is generated when I call Cipher.init(). The lines below show what I did in my program:
ECPublicKey publicKey ;
ECPrivateKey privateKey;
//Generating key paire (public and private keys)
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", "SunEC");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(571, random);
KeyPair pair = keyGen.generateKeyPair();
privateKey = (ECPrivateKey) pair.getPrivate();
publicKey = (ECPublicKey) pair.getPublic();
// get an AES cipher object with CTR encription mode
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
// encrypt the sharedSecret using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey);**
byte[] result = cipher.doFinal(data);
Must I add a provider to support this public key?
Finally, I found the source of this exception. The problem was initialization of cipher :
//This is the wrong initialization
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
//This is the right initialization
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding","SunJCE");
But now, i have another exception which is (it is less important than the previous one) :
java.security.InvalidKeyException: Invalid AES key length: 170 bytes
So what must I use as encrypting algorithm with ECDSA public key now ?
ECDSA is not used for encryption
rather you use RSA / Symetric cipher
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
key = keygen.generateKey();
String plain_input = "Hush-a-bye, baby, on the tree top,When the wind blows" ;
//encryption
cipher = Cipher.getInstance(""AES/EBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(plain_input.getBytes("UTF8"));
//decryption
cipher = Cipher.getInstance(""AES/EBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypted = cipher.doFinal(encrypted);
String plain_output = new String(decrypted, "UTF8");
I have a java program that encrypts file content with a random-generated key.
That key is encrpyted with RSA and saved into a text file.
Now, I have a java program that given the file and the keystore where the RSA key is stored, needs to first decrypt the encryped key and then with the key to decrypt the file.
Here's what I have so far:
// Fetch the other public key and decrypt the file encryption key
java.security.cert.Certificate cert2 = keystore.getCertificate("keyForSeckeyDecrypt");
Key secKeyPublicKey = cert2.getPublicKey();
Cipher cipher = Cipher.getInstance(secKeyPublicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, secKeyPublicKey);
keyFileFis = new FileInputStream(keyFile);
byte[] encryptedKey = new byte[128];
keyFileFis.read(encryptedKey);
byte[] realFileKey = cipher.doFinal(encryptedKey, 0, encryptedKey.length);
Key realKey = // THE PROBLEM!!!;
keyFileFis.close();
In short, I get the encrypted key from the key text file and decrypt it, now I have the decrypted key as a byte array, how would I make it a Key variable again?
I've generated the key this way:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
Key secKey = keyGen.generateKey();
cipher.init(Cipher.ENCRYPT_MODE, secKey);
And encrypted it this way:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
PrivateKey privateKey = kp.getPrivate();
Cipher keyCipher = Cipher.getInstance("RSA");
keyCipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedKey = keyCipher.doFinal(secKey.getEncoded());
FileOutputStream keyStream = new FileOutputStream("key.txt");
keyStream.write(encryptedKey);
keyStream.close();
I haven't tried it but from clicking through the API SecretKeySpec could be what you are looking for.
SecretKeySpec(byte[] key, String algorithm)
It can be used to construct a SecretKey from a byte array, without having to go through a (provider-based) SecretKeyFactory.
This class is only useful for raw secret keys that can be represented as a byte array and have no key parameters associated with them, e.g., DES or Triple DES keys.
If I get it right, this should work..
Key privateKey = keyStore.getKey("youralias", "password".toCharArray());
PublicKey publicKey = keyStore.getCertificate("youralias").getPublicKey();
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
Key secKey = keyGen.generateKey();
Cipher keyCipher = Cipher.getInstance("RSA");
keyCipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] encryptedKey = keyCipher.doFinal(secKey.getEncoded());
// Write & Read to/from file!
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] decryptedKey = decryptCipher.doFinal(encryptedKey);
boolean equals = Arrays.equals(secKey.getEncoded(), new SecretKeySpec(decryptedKey, "AES").getEncoded());
System.out.println(equals?"Successfull!":"Failed!");
In my program, I'm trying to encrypt some plaintext with RSA using the following code:
static String RSAEncrypt(String pubkey, String plain){
return encrypt(pubkey,plain,"RSA");
}
static String encrypt(String stringKey, String plain, String algo){
String enc="failed";
try{
byte[] byteKey = new BASE64Decoder().decodeBuffer(stringKey);
Key key = new SecretKeySpec(byteKey,algo);
byte[] data = plain.getBytes();
Cipher c = Cipher.getInstance(algo);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(data);
enc = new BASE64Encoder().encode(encVal);
}catch(Exception e){e.printStackTrace();}
return enc;
}
However, when it runs, it shows the following error:
java.security.InvalidKeyException: No installed provider supports this key: javax.crypto.spec.SecretKeySpec
at javax.crypto.Cipher.chooseProvider(Cipher.java:877)
at javax.crypto.Cipher.init(Cipher.java:1212)
at javax.crypto.Cipher.init(Cipher.java:1152)
at Crypto.encrypt(Crypto.java:37)
at Crypto.RSAEncrypt(Crypto.java:62)
I have tried changing it to RSA/None/PKCS1Padding and RSA/ECB/PKCS1Padding to no avail.. I know that installing BouncyCastle may help but I'd like to avoid it (I'd like to avoid more dependencies and I've been having some issues installing it anyway). Thanks in advance for any ideas.
As was said in the comments, SecretKeySpec is for symmetric algorithms only. You mentioned that you got your byte[] containing the key by calling getEncoded.
There are two possibilities and two resulting formats:
Encoding of an RSA PrivateKey
Calling PrivateKey#getEncoded on an instance of an RSA private key will result in a PKCS#8 encoding for private keys, and it can be restored with the help of PKCS8EncodedKeySpec.
Encoding of an RSA PublicKey
PublicKey#getEncoded on an RSA public key results in the generic X.509 public key encoding, and can be restored with X509EncodedKeySpec.
Example Usage
byte[] data = "test".getBytes("UTF8");
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair keyPair = kpg.genKeyPair();
byte[] pk = keyPair.getPublic().getEncoded();
X509EncodedKeySpec spec = new X509EncodedKeySpec(pk);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = cipher.doFinal(data);
byte[] priv = keyPair.getPrivate().getEncoded();
PKCS8EncodedKeySpec spec2 = new PKCS8EncodedKeySpec(priv);
PrivateKey privKey = keyFactory.generatePrivate(spec2);
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] plain = cipher.doFinal(encrypted);
System.out.println(new String(plain, "UTF8")); //=> "test"