I was given a task to build a java method to encrypt data (String or InputStream) using PGP. I have generated a test public key, private key with passphrase following the guide here. I then exported the public key using the command below and copy and pasted it to my java code.
gpg --armor --output pubkey.txt --export 'Your Name'
I am able to encrypt a file using the command below
gpg --encrypt --armor -r 'Your Name' plaintext.txt
And I get the below content which is what I'm trying to achieve in my java program. I might need to sign it later as part of the requirement but all I want to do for now is to be able to successfully encrypt it like this.
-----BEGIN PGP MESSAGE-----
Version: GnuPG v2.0.22 (GNU/Linux)
hQEMA0PxXau0Q30VAQf/RuWsN3f4L2HW2GJWOZUjetJsw0odXYbDc7Sug1gZULP8
I0KRrvxnHgiiJgSlBZsws8E8iB1/LDCYJ8oJGj6olicz83iUT8VLdzdJZlc0+96/
BHAvtSTtEv2PWZlh307nU+Zn9cuGAccaijyekCosS5/0JpDyXSFefsLTexMgphAL
veXsxtsISyUU6S0xUux6Ac9HgUWTpCrlNaSdqBN1bk7y8YuvbZgbQ5akwY5FEbq1
f9rxmgXgEgz3N+7f8n5yN2OvWiEyXb+qngVgDLzysD8NTtKDqtw5nViscvVF1h3v
AebdxYxOKGYnWk6XAWhpIgIZdY0ZXG0yu9NJH5VfLtJSAc3c6d2/Nhb7g+k+f2Mn
srZW6XzHCeyGQQqSfr5YJfyUVdsW12udmhnc+ErbRkz84oDkMvFaxes6+2AAKrP/
jdWXsp4fTPl454m+tG5ec/Kn0Q==
=cZH2
-----END PGP MESSAGE-----
The java method I need to build accepts a Public Key as a String parameter where I then use the following method to convert it to a PGPPublicKey.
private static PGPPublicKey getPublicKey(String keyAscii) throws IOException, PGPException, Exception {
InputStream encodedKey = new ByteArrayInputStream(keyAscii.getBytes());
InputStream decodedKey = PGPUtil.getDecoderStream(encodedKey);
JcaPGPPublicKeyRingCollection pgpPub = new JcaPGPPublicKeyRingCollection(decodedKey);
decodedKey.close();
PGPPublicKey key = null;
Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext()) {
PGPPublicKeyRing kRing = rIt.next();
Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext()) {
PGPPublicKey k = kIt.next();
if (k.isEncryptionKey()) {
key = k;
}
}
}
if (key == null) {
throw new Exception("Can't find key");
}
return key;
}
However, when I run the java program, I get an error "exception constructing public key" on the following line of code.
OutputStream cOut = encGen.open(encOut, new byte[4096]);
Complete error below
org.bouncycastle.openpgp.PGPException: exception constructing public key
at org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter.getPublicKey(Unknown Source)
at org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator.encryptSessionInfo(Unknown Source)
at org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator.generate(Unknown Source)
at org.bouncycastle.openpgp.PGPEncryptedDataGenerator.open(Unknown Source)
at org.bouncycastle.openpgp.PGPEncryptedDataGenerator.open(Unknown Source)
at pgp.PgpImpl.encrypt(PgpImpl.java:84)
at pgp.PgpImpl.main(PgpImpl.java:216)
Caused by: java.security.NoSuchProviderException: no such provider: BC
at sun.security.jca.GetInstance.getService(GetInstance.java:83)
at sun.security.jca.GetInstance.getInstance(GetInstance.java:206)
at java.security.KeyFactory.getInstance(KeyFactory.java:211)
at org.bouncycastle.jcajce.util.NamedJcaJceHelper.createKeyFactory(Unknown Source)
at org.bouncycastle.openpgp.operator.jcajce.OperatorHelper.createKeyFactory(Unknown Source)
at org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter.implGeneratePublic(Unknown Source)
... 7 more
I followed the sample here but it didn't provide any information on how to construct the publickey coming from a String.
Here's my code so far.
package pgp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Iterator;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.jcajce.JcaPGPPublicKeyRingCollection;
public class PgpImpl {
public PgpImpl() {
}
private static String encrypt(byte[] data, PGPPublicKey encryptionKey) throws Exception {
String step = "Step-0";
try {
step = "Step-1";
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom())
.setProvider("BC"));
step = "Step-2";
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey)
.setSecureRandom(new SecureRandom()).setProvider("BC"));
step = "Step-3";
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
step = "Step-4";
// create an indefinite length encrypted stream
OutputStream cOut = encGen.open(encOut, new byte[4096]);
step = "Step-5";
// write out the literal data
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(cOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, data.length,
new Date());
pOut.write(data);
pOut.close();
// finish the encryption
cOut.close();
step = "Step-6";
return new String(encOut.toByteArray());
} catch (Exception e) {
//throw new Exception(String.format("%s: %s", e.getMessage(), step));
e.printStackTrace();
}
return new String(step);
}
private static PGPPublicKey getPublicKey(String keyAscii) throws IOException, PGPException, Exception {
InputStream encodedKey = new ByteArrayInputStream(keyAscii.getBytes());
InputStream decodedKey = PGPUtil.getDecoderStream(encodedKey);
JcaPGPPublicKeyRingCollection pgpPub = new JcaPGPPublicKeyRingCollection(decodedKey);
decodedKey.close();
PGPPublicKey key = null;
Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext()) {
PGPPublicKeyRing kRing = rIt.next();
Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext()) {
PGPPublicKey k = kIt.next();
if (k.isEncryptionKey()) {
key = k;
}
}
}
if (key == null) {
throw new Exception("Can't find key");
}
return key;
}
public static void main(String[] args) {
String publicKey = "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n" + "Version: GnuPG v2.0.22 (GNU/Linux)\r\n"
+ "\r\n" + "mQENBGA1A70BCADK8BnH6GgMbnS1TJSpJvgH+D9VIw0sN8XZWQsUmWWV9WSqhqXt\r\n"
+ "5wNC4XJDcaWtMCapaekQXV5S52T7QCxAz/E5oZzIDe+IUCHQz0WUs37S4Wnw+SZ6\r\n"
+ "QNPXOFaC4nNByRq6gvg0+wtD2Bo/3OJur3f0O0aRSHNiwfd0PdFgG0NU5vGV9PwE\r\n"
+ "xbTMpGssWexIC0MwJaYfJkxzov33CkwLaITvBTCn/J3oeX6JarMkgpurp1FAW0Jk\r\n"
+ "YzgGMOOxwuEVedwP4NtEPce+UtLv2NHHfqsW6xSxjWqsJkMdJ9afzu1jvn9M6e0j\r\n"
+ "MOTmPUCYVCioXK59It8ngN8NLtwaPgfnBwcbABEBAAG0BWFsbGVuiQE5BBMBAgAj\r\n"
+ "BQJgNQO9AhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQxzGJxIrjAlxq\r\n"
+ "aggAgoiO82MZZMHyhZ3uLD4qTQ2vsT6+onhCr83kw0eFNM5AH0r3xlVARXHaViWC\r\n"
+ "SFutpb/34lrCTpJfLfKwdFU2bJP2SI3hAujtTg45UFklswu6GZaqQno6JKkZM4hw\r\n"
+ "ltFIXU1dMpIud7nsJ2QU46TI97n+HeD7DvOSGY/CFPnNot0YFHxXCKtHdPHk8JO3\r\n"
+ "JdOG0X90Yi9XSI1USv8HL/WjOTvhSqo7Qps2MpcUZrfNsa0H9Adk9xVYiz0nKNPY\r\n"
+ "qLQxFAiHb34vdav4e28anJ8th93SfiRn5OFK2G6R3DlhLlvn3h1dSAT6vSOrzx80\r\n"
+ "EylyMg2BIbRfp+JEgwCMf2V8X7kBDQRgNQO9AQgA3qV0wYvdH5M4XBzVwDtuGuIs\r\n"
+ "+GRcSRQqmvnt94e8ZE4Kv2w2Pf/JxPMwnPC92lVRypdOjmTZrT3R0z7g+D8mU5A9\r\n"
+ "o/CPvvSShA8Jh3z69S+hLP0nSaajsVsQlBGrI8ehI1EVJDsNh15PZrl27OK0aBb4\r\n"
+ "Fp0BYm0D2HaLnQPD4/jhTR13i1mt5E5hmBwiZiiWr/Wa1i1g1o/XaT4CApu91zgg\r\n"
+ "cmJBz9DL/C2hYC5lkp/cz5IJYp5BsvfA2lwamca33aHxFj8+Bz3+REWa8zvEqQ9U\r\n"
+ "a26RbPVjkeGChwNWLxNTuj1rNDdqB/KZO6iM02orqW86L45SKTBWYqPcpD7GeQAR\r\n"
+ "AQABiQEfBBgBAgAJBQJgNQO9AhsMAAoJEMcxicSK4wJcOLEIAMevvOk9iZ13T3yA\r\n"
+ "+ZW8mWKKE5aXy93VPKAvplP/WlW2VVGeb+6rEkFFsdN4doYIJPEIr+U7K0GDR6XX\r\n"
+ "TKLyI7BtUZPegOdjgcFWVGFnFogDnkrO+IPY+JUy1VMg8fGStThfa2dYEgd7yqpq\r\n"
+ "fZ97q5RQun1B+wyRdPDgC39roSGEwtXbRCZnuSMVNT7J9a2qnXkenvQRSoPjY7wQ\r\n"
+ "tn1wUfnHyjyS9OzfXTSHDi2A5JDRCh5L/V7Q93/P5Isv/U4QzIWudGM6AjuaoZ6i\r\n"
+ "chksRI9EchNKnSut9ebTyTkIJ80sB7Eyfp8TtORAnz8/Xf8A8aYD73r9rD4poSmo\r\n" + "FV15pP8=\r\n" + "=Yc74\r\n"
+ "-----END PGP PUBLIC KEY BLOCK-----";
try {
PGPPublicKey key = getPublicKey(publicKey);
System.out.println(encrypt("Test".getBytes(), key));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (PGPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Appreciate any help.
UPDATE #1: After getting the stacktrace, I found out that the error is due to an invalid provider which I then change using
BouncyCastleProvider provider = new BouncyCastleProvider();
So I had to change all reference to the provider.
private static String encrypt(byte[] data, PGPPublicKey encryptionKey) {
BouncyCastleProvider provider = new BouncyCastleProvider();
String step = "Step-0";
try {
step = "Step-1";
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom())
.setProvider(provider));
step = "Step-2";
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey)
.setSecureRandom(new SecureRandom()).setProvider(provider));
step = "Step-3";
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
step = "Step-4";
// create an indefinite length encrypted stream
OutputStream cOut = encGen.open(encOut, new byte[1 << 16]);
step = "Step-5";
// write out the literal data
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(cOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, data.length,
new Date());
pOut.write(data);
pOut.close();
// finish the encryption
cOut.close();
step = "Step-6";
return new String(encOut.toByteArray());
} catch (Exception e) {
//throw new Exception(String.format("%s: %s", e.getMessage(), step));
e.printStackTrace();
}
return new String("");
}
But now I'm getting a different error.
org.bouncycastle.openpgp.PGPException: Exception creating cipher
at org.bouncycastle.openpgp.PGPEncryptedDataGenerator.open(Unknown Source)
at org.bouncycastle.openpgp.PGPEncryptedDataGenerator.open(Unknown Source)
at pgp.PgpImpl.encrypt(PgpImpl.java:85)
at pgp.PgpImpl.main(PgpImpl.java:217)
Caused by: org.bouncycastle.openpgp.PGPException: invalid key: Illegal key size
at org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder$MyPGPDataEncryptor.<init>(Unknown Source)
at org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder.build(Unknown Source)
... 4 more
Caused by: java.security.InvalidKeyException: Illegal key size
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1034)
at javax.crypto.Cipher.init(Cipher.java:1367)
at javax.crypto.Cipher.init(Cipher.java:1301)
... 6 more
UPDATE#2: Did a bit of research and came across this. Followed the instructions there and got everything working.
Your modified code works for me: (I have taken out the exception handling, but that should make no difference)
package pgp;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Iterator;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator;
import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.jcajce.JcaPGPPublicKeyRingCollection;
public class PgpImpl {
public PgpImpl() {
}
private static String encrypt(byte[] data, PGPPublicKey encryptionKey) throws PGPException, IOException {
BouncyCastleProvider provider = new BouncyCastleProvider();
String step = "Step-0";
step = "Step-1";
PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256)
.setWithIntegrityPacket(true)
.setSecureRandom(new SecureRandom())
.setProvider(provider));
step = "Step-2";
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encryptionKey)
.setSecureRandom(new SecureRandom()).setProvider(provider));
step = "Step-3";
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
step = "Step-4";
// create an indefinite length encrypted stream
OutputStream cOut = encGen.open(encOut, new byte[1 << 16]);
step = "Step-5";
// write out the literal data
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(cOut, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, data.length,
new Date());
pOut.write(data);
pOut.close();
// finish the encryption
cOut.close();
step = "Step-6";
return new String(encOut.toByteArray());
}
private static PGPPublicKey getPublicKey(String keyAscii) throws IOException, PGPException, Exception {
InputStream encodedKey = new ByteArrayInputStream(keyAscii.getBytes());
InputStream decodedKey = PGPUtil.getDecoderStream(encodedKey);
JcaPGPPublicKeyRingCollection pgpPub = new JcaPGPPublicKeyRingCollection(decodedKey);
decodedKey.close();
PGPPublicKey key = null;
Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext()) {
PGPPublicKeyRing kRing = rIt.next();
Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext()) {
PGPPublicKey k = kIt.next();
if (k.isEncryptionKey()) {
key = k;
}
}
}
if (key == null) {
throw new Exception("Can't find key");
}
return key;
}
public static void main(String[] args) throws Exception {
String publicKey = "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n" + "Version: GnuPG v2.0.22 (GNU/Linux)\r\n"
+ "\r\n" + "mQENBGA1A70BCADK8BnH6GgMbnS1TJSpJvgH+D9VIw0sN8XZWQsUmWWV9WSqhqXt\r\n"
+ "5wNC4XJDcaWtMCapaekQXV5S52T7QCxAz/E5oZzIDe+IUCHQz0WUs37S4Wnw+SZ6\r\n"
+ "QNPXOFaC4nNByRq6gvg0+wtD2Bo/3OJur3f0O0aRSHNiwfd0PdFgG0NU5vGV9PwE\r\n"
+ "xbTMpGssWexIC0MwJaYfJkxzov33CkwLaITvBTCn/J3oeX6JarMkgpurp1FAW0Jk\r\n"
+ "YzgGMOOxwuEVedwP4NtEPce+UtLv2NHHfqsW6xSxjWqsJkMdJ9afzu1jvn9M6e0j\r\n"
+ "MOTmPUCYVCioXK59It8ngN8NLtwaPgfnBwcbABEBAAG0BWFsbGVuiQE5BBMBAgAj\r\n"
+ "BQJgNQO9AhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQxzGJxIrjAlxq\r\n"
+ "aggAgoiO82MZZMHyhZ3uLD4qTQ2vsT6+onhCr83kw0eFNM5AH0r3xlVARXHaViWC\r\n"
+ "SFutpb/34lrCTpJfLfKwdFU2bJP2SI3hAujtTg45UFklswu6GZaqQno6JKkZM4hw\r\n"
+ "ltFIXU1dMpIud7nsJ2QU46TI97n+HeD7DvOSGY/CFPnNot0YFHxXCKtHdPHk8JO3\r\n"
+ "JdOG0X90Yi9XSI1USv8HL/WjOTvhSqo7Qps2MpcUZrfNsa0H9Adk9xVYiz0nKNPY\r\n"
+ "qLQxFAiHb34vdav4e28anJ8th93SfiRn5OFK2G6R3DlhLlvn3h1dSAT6vSOrzx80\r\n"
+ "EylyMg2BIbRfp+JEgwCMf2V8X7kBDQRgNQO9AQgA3qV0wYvdH5M4XBzVwDtuGuIs\r\n"
+ "+GRcSRQqmvnt94e8ZE4Kv2w2Pf/JxPMwnPC92lVRypdOjmTZrT3R0z7g+D8mU5A9\r\n"
+ "o/CPvvSShA8Jh3z69S+hLP0nSaajsVsQlBGrI8ehI1EVJDsNh15PZrl27OK0aBb4\r\n"
+ "Fp0BYm0D2HaLnQPD4/jhTR13i1mt5E5hmBwiZiiWr/Wa1i1g1o/XaT4CApu91zgg\r\n"
+ "cmJBz9DL/C2hYC5lkp/cz5IJYp5BsvfA2lwamca33aHxFj8+Bz3+REWa8zvEqQ9U\r\n"
+ "a26RbPVjkeGChwNWLxNTuj1rNDdqB/KZO6iM02orqW86L45SKTBWYqPcpD7GeQAR\r\n"
+ "AQABiQEfBBgBAgAJBQJgNQO9AhsMAAoJEMcxicSK4wJcOLEIAMevvOk9iZ13T3yA\r\n"
+ "+ZW8mWKKE5aXy93VPKAvplP/WlW2VVGeb+6rEkFFsdN4doYIJPEIr+U7K0GDR6XX\r\n"
+ "TKLyI7BtUZPegOdjgcFWVGFnFogDnkrO+IPY+JUy1VMg8fGStThfa2dYEgd7yqpq\r\n"
+ "fZ97q5RQun1B+wyRdPDgC39roSGEwtXbRCZnuSMVNT7J9a2qnXkenvQRSoPjY7wQ\r\n"
+ "tn1wUfnHyjyS9OzfXTSHDi2A5JDRCh5L/V7Q93/P5Isv/U4QzIWudGM6AjuaoZ6i\r\n"
+ "chksRI9EchNKnSut9ebTyTkIJ80sB7Eyfp8TtORAnz8/Xf8A8aYD73r9rD4poSmo\r\n" + "FV15pP8=\r\n" +
"=Yc74\r\n"
+ "-----END PGP PUBLIC KEY BLOCK-----";
PGPPublicKey key = getPublicKey(publicKey);
System.out.println(encrypt("Test".getBytes(), key));
}
}
My pom.xml has:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpg-jdk15on</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
</dependency>
Can you run that code and see what happens locally?
Related
I have an existing PGP v 1.46 code in maintenance project. now I need to update to 1.56 and it's not working. a lot of classes and methods are changed. I have no idea about PGP.
Here is a sample demo of the exact code.
plugins {
id 'java'
}
group 'com.encryptor.pgp'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'src/main/resources/libs', include: '*.jar')
compile group: 'org.bouncycastle', name: 'bcpg-jdk15on', version: '1.46'
compile group: 'org.bouncycastle', name: 'bcprov-ext-jdk15on', version: '1.46'
compile group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.46'
}
when i update from 1.46 to 1.56, it gives error.
processor
package com.encryptor.pgp;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class PGPFileProcessor {
private String encryptFile;
private String decryptFile;
private String passphrase;
private String publicKeyFile;
private String privateKeyFile;
private String plainTextFile;
private boolean asciiArmored = false;
private boolean integrityCheck = true;
public PGPFileProcessor() {
}
public void setPublicKeyFile(String publicKeyFile) {
this.publicKeyFile = publicKeyFile;
}
public void setPrivateKeyFile(String privateKeyFile) {
this.privateKeyFile = privateKeyFile;
}
public void setEncryptFile(String encryptFile) {
this.encryptFile = encryptFile;
}
public void setDecryptFile(String decryptFile) {
this.decryptFile = decryptFile;
}
public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}
public void setPlainTextFile(String plainTextFile) {
this.plainTextFile = plainTextFile;
}
public void setAsciiArmored(boolean asciiArmored) {
this.asciiArmored = asciiArmored;
}
public void setIntegrityCheck(boolean integrityCheck) {
this.integrityCheck = integrityCheck;
}
public boolean encrypt() throws Exception {
FileInputStream keyIn = new FileInputStream(publicKeyFile);
FileOutputStream out = new FileOutputStream(encryptFile, true);
PGPUtil.encryptFile(out, plainTextFile, PGPUtil.readPublicKey(keyIn), asciiArmored, integrityCheck);
out.close();
keyIn.close();
return true;
}
public boolean decrypt() throws Exception {
FileInputStream in = new FileInputStream(encryptFile);
FileInputStream keyIn = new FileInputStream(privateKeyFile);
FileOutputStream out = new FileOutputStream(decryptFile);
PGPUtil.decryptFile(in, out, keyIn, passphrase.toCharArray());
in.close();
out.close();
keyIn.close();
return true;
}
}
Main Class
package com.encryptor.pgp;
public class PGPMain {
public static void main(String[] args) throws Exception {
PGPFileProcessor pgpFileProcessor = new PGPFileProcessor();
pgpFileProcessor.setEncryptFile("enc.txt"); pgpFileProcessor.setDecryptFile("dec.txt");
pgpFileProcessor.setPassphrase("pgpencr");
pgpFileProcessor.setInputFile("plain.txt");
private boolean asciiArmored = false;
pgpFileProcessor.setPublicKeyFile("publickey.key");
pgpFileProcessor.setPrivateKeyFile("pivatekey.key");
pgpFileProcessor.encrypt();*/
pgpFileProcessor.encrypt();
pgpFileProcessor.decrypt();
}
}
Util
package com.encryptor.pgp;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.*;
import java.io.*;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Iterator;
public class PGPUtil {
#SuppressWarnings("unchecked")
public static PGPPublicKey readPublicKey(InputStream in) throws IOException, PGPException {
in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);
PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(in);/* we just loop through the collection till we find a key suitable for encryption, in the real world you would probably want to be a bit smarter about this. */
PGPPublicKey key = null;/* iterate through the key rings. */
Iterator<PGPPublicKeyRing> rIt = pgpPub.getKeyRings();
while (key == null && rIt.hasNext()) {
PGPPublicKeyRing kRing = rIt.next();
Iterator<PGPPublicKey> kIt = kRing.getPublicKeys();
while (key == null && kIt.hasNext()) {
PGPPublicKey k = kIt.next();
if (k.isEncryptionKey()) key = k;
}
}
if (key == null) throw new IllegalArgumentException("Can't find encryption key in key ring.");
return key;
}
private static PGPPrivateKey findSecretKey(InputStream keyIn, long keyID, char[] pass) throws IOException, PGPException, NoSuchProviderException {
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(org.bouncycastle.openpgp.PGPUtil.getDecoderStream(keyIn));
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) return null;
return pgpSecKey.extractPrivateKey(pass, "BC");
}
#SuppressWarnings("unchecked")
public static void decryptFile(InputStream in, OutputStream out, InputStream keyIn, char[] passwd) throws Exception {
Security.addProvider(new BouncyCastleProvider());
in = org.bouncycastle.openpgp.PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();/* the first object might be a PGP marker packet. */
if (o instanceof PGPEncryptedDataList) enc = (PGPEncryptedDataList) o;
else enc = (PGPEncryptedDataList) pgpF.nextObject();/* find the secret key */
Iterator<PGPPublicKeyEncryptedData> it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
while (sKey == null && it.hasNext()) {
pbe = it.next();
sKey = findSecretKey(keyIn, pbe.getKeyID(), passwd);
}
if (sKey == null) throw new IllegalArgumentException("Secret key for message not found.");
InputStream clear = pbe.getDataStream(sKey, "BC");
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object message = plainFact.nextObject();
if (message instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) message;
PGPObjectFactory pgpFact = new PGPObjectFactory(cData.getDataStream());
message = pgpFact.nextObject();
}
if (message instanceof PGPLiteralData) {
PGPLiteralData ld = (PGPLiteralData) message;
InputStream unc = ld.getInputStream();
int ch;
while ((ch = unc.read()) >= 0) out.write(ch);
} else if (message instanceof PGPOnePassSignatureList)
throw new PGPException("Encrypted message contains a signed message - not literal data.");
else throw new PGPException("Message is not a simple encrypted file - type unknown.");
if (pbe.isIntegrityProtected() && !pbe.verify()) throw new PGPException("Message failed integrity check");
}
public static void encryptFile(OutputStream out, String fileName, PGPPublicKey encKey, boolean armor, boolean withIntegrityCheck) throws IOException, NoSuchProviderException, PGPException {
Security.addProvider(new BouncyCastleProvider());
if (armor) out = new ArmoredOutputStream(out);
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedData.ZIP);
org.bouncycastle.openpgp.PGPUtil.writeFileToLiteralData(comData.open(bOut), PGPLiteralData.BINARY, new File(fileName));
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, withIntegrityCheck, new SecureRandom(), "BC");
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
out.close();
}
}
It looks like as that your "old" PGP encryption methods were taken from a Bouncy Castle example, so I used the renewed Bouncy Castle examples in https://github.com/bcgit/bc-java/tree/master/pg/src/main/java/org/bouncycastle/openpgp/examples for a simple test.
You wrote that the problem shows up when updating Bouncy Castle to version 1.56 - this is outdated as well and the actual version is 1.65 and my example works with this version (and OpenJDK 11.0.6). Take my files as a working basis for your maintenance.
First you need a PGP keypair - for my example I generated one with 'RSAKeyPairGenerator.java -a myidentity mypassphrase' to get the files 'secret.asc' (private key) and 'pub.asc' (public key).
For PGP file encryption you need two more files - 'KeyBasedLargeFileProcessor.java' and 'PGPExampleUtil.java.' In class 'KeyBasedLargeFileProcessor.java' I changed the constructor of the methods decryptFile and encryptFile from 'private' to 'public' to get access from the PGPMain.java.
It's just one line of code for pgp file encryption and another line for pgp file decryption. As the original filename can get stored within the encrypted file
I added a "rename-method" to change the original filename 'plain.txt' to 'plain_org.txt'. Be aware that the decryptFile-method will overwrite an existing file without warning or notice and there is no propper exception handling.
The complete set of files including Bouncy Castle library is available here: https://github.com/java-crypto/Stackoverflow/tree/master/PGP_Encryption_after_Update_Not_Working. You need the bcprov-jdk15to18-165.jar and bcpg-jdk15on-165.jar!
PGPMain.java:
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchProviderException;
import java.security.Security;
public class PGPMain {
public static void main(String[] args) throws NoSuchProviderException, IOException, PGPException {
System.out.println("https://stackoverflow.com/questions/61927913/bouncycastle-update-from-1-46-to-1-56-not-working");
Security.addProvider(new BouncyCastleProvider()); // get bouncy castle: https://www.bouncycastle.org/latest_releases.html
System.out.println("\nJava version: " + Runtime.version() + " BouncyCastle Version: " + Security.getProvider("BC"));
// create a keypair with RSAKeyPairGenerator.java
// encryption
KeyBasedLargeFileProcessor.encryptFile("enc.txt", "plain.txt", "pub.asc", false, true);
// rename plaintextfile as it will be overwritten by decryptFile (filename is stored within encrypted file)
File file = new File("plain.txt");
file.renameTo(new File("plain_org.txt"));
// decryption will generate the decrypted file with original filename !
KeyBasedLargeFileProcessor.decryptFile("enc.txt", "secret.asc", "mypassphrase".toCharArray(), "defaultfilename.txt");
// return the original filename, to change this behavior change the code in class KeyBasedLargeFileProcessor lines 142-146
}
}
KeyBasedLargeFileProcessor.java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Iterator;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPOnePassSignatureList;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.bouncycastle.util.io.Streams;
/**
* A simple utility class that encrypts/decrypts public key based
* encryption large files.
* <p>
* To encrypt a file: KeyBasedLargeFileProcessor -e [-a|-ai] fileName publicKeyFile.<br>
* If -a is specified the output file will be "ascii-armored".
* If -i is specified the output file will be have integrity checking added.
* <p>
* To decrypt: KeyBasedLargeFileProcessor -d fileName secretKeyFile passPhrase.
* <p>
* Note 1: this example will silently overwrite files, nor does it pay any attention to
* the specification of "_CONSOLE" in the filename. It also expects that a single pass phrase
* will have been used.
* <p>
* Note 2: this example generates partial packets to encode the file, the output it generates
* will not be readable by older PGP products or products that don't support partial packet
* encoding.
* <p>
* Note 3: if an empty file name has been specified in the literal data object contained in the
* encrypted packet a file with the name filename.out will be generated in the current working directory.
*/
public class KeyBasedLargeFileProcessor
{ // source: https://github.com/bcgit/bc-java/blob/master/pg/src/main/java/org/bouncycastle/openpgp/examples/KeyBasedLargeFileProcessor.java
// changed from private to public
public static void decryptFile(
String inputFileName,
String keyFileName,
char[] passwd,
String defaultFileName)
throws IOException, NoSuchProviderException
{
InputStream in = new BufferedInputStream(new FileInputStream(inputFileName));
InputStream keyIn = new BufferedInputStream(new FileInputStream(keyFileName));
decryptFile(in, keyIn, passwd, defaultFileName);
keyIn.close();
in.close();
}
/**
* decrypt the passed in message stream
*/
private static void decryptFile(
InputStream in,
InputStream keyIn,
char[] passwd,
String defaultFileName)
throws IOException, NoSuchProviderException
{
in = PGPUtil.getDecoderStream(in);
try
{
JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
//
// the first object might be a PGP marker packet.
//
if (o instanceof PGPEncryptedDataList)
{
enc = (PGPEncryptedDataList)o;
}
else
{
enc = (PGPEncryptedDataList)pgpF.nextObject();
}
//
// find the secret key
//
Iterator it = enc.getEncryptedDataObjects();
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
PGPUtil.getDecoderStream(keyIn), new JcaKeyFingerprintCalculator());
while (sKey == null && it.hasNext())
{
pbe = (PGPPublicKeyEncryptedData)it.next();
sKey = PGPExampleUtil.findSecretKey(pgpSec, pbe.getKeyID(), passwd);
}
if (sKey == null)
{
throw new IllegalArgumentException("secret key for message not found.");
}
InputStream clear = pbe.getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(sKey));
JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
PGPCompressedData cData = (PGPCompressedData)plainFact.nextObject();
InputStream compressedStream = new BufferedInputStream(cData.getDataStream());
JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(compressedStream);
Object message = pgpFact.nextObject();
if (message instanceof PGPLiteralData)
{
PGPLiteralData ld = (PGPLiteralData)message;
String outFileName = ld.getFileName();
if (outFileName.length() == 0)
{
outFileName = defaultFileName;
}
InputStream unc = ld.getInputStream();
OutputStream fOut = new BufferedOutputStream(new FileOutputStream(outFileName));
Streams.pipeAll(unc, fOut);
fOut.close();
}
else if (message instanceof PGPOnePassSignatureList)
{
throw new PGPException("encrypted message contains a signed message - not literal data.");
}
else
{
throw new PGPException("message is not a simple encrypted file - type unknown.");
}
if (pbe.isIntegrityProtected())
{
if (!pbe.verify())
{
System.err.println("message failed integrity check");
}
else
{
System.err.println("message integrity check passed");
}
}
else
{
System.err.println("no message integrity check");
}
}
catch (PGPException e)
{
System.err.println(e);
if (e.getUnderlyingException() != null)
{
e.getUnderlyingException().printStackTrace();
}
}
}
// changed from private to public
public static void encryptFile(
String outputFileName,
String inputFileName,
String encKeyFileName,
boolean armor,
boolean withIntegrityCheck)
throws IOException, NoSuchProviderException, PGPException
{
OutputStream out = new BufferedOutputStream(new FileOutputStream(outputFileName));
PGPPublicKey encKey = PGPExampleUtil.readPublicKey(encKeyFileName);
encryptFile(out, inputFileName, encKey, armor, withIntegrityCheck);
out.close();
}
private static void encryptFile(
OutputStream out,
String fileName,
PGPPublicKey encKey,
boolean armor,
boolean withIntegrityCheck)
throws IOException, NoSuchProviderException
{
if (armor)
{
out = new ArmoredOutputStream(out);
}
try
{
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new JcePGPDataEncryptorBuilder(PGPEncryptedData.CAST5).setWithIntegrityPacket(withIntegrityCheck).setSecureRandom(new SecureRandom()).setProvider("BC"));
cPk.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider("BC"));
OutputStream cOut = cPk.open(out, new byte[1 << 16]);
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedData.ZIP);
PGPUtil.writeFileToLiteralData(comData.open(cOut), PGPLiteralData.BINARY, new File(fileName), new byte[1 << 16]);
comData.close();
cOut.close();
if (armor)
{
out.close();
}
}
catch (PGPException e)
{
System.err.println(e);
if (e.getUnderlyingException() != null)
{
e.getUnderlyingException().printStackTrace();
}
}
}
public static void main(
String[] args)
throws Exception
{
Security.addProvider(new BouncyCastleProvider());
if (args.length == 0)
{
System.err.println("usage: KeyBasedLargeFileProcessor -e|-d [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
return;
}
if (args[0].equals("-e"))
{
if (args[1].equals("-a") || args[1].equals("-ai") || args[1].equals("-ia"))
{
encryptFile(args[2] + ".asc", args[2], args[3], true, (args[1].indexOf('i') > 0));
}
else if (args[1].equals("-i"))
{
encryptFile(args[2] + ".bpg", args[2], args[3], false, true);
}
else
{
encryptFile(args[1] + ".bpg", args[1], args[2], false, false);
}
}
else if (args[0].equals("-d"))
{
decryptFile(args[1], args[2], args[3].toCharArray(), new File(args[1]).getName() + ".out");
}
else
{
System.err.println("usage: KeyBasedLargeFileProcessor -d|-e [-a|ai] file [secretKeyFile passPhrase|pubKeyFile]");
}
}
}
Error : javax.crypto.IllegalBlockSizeException: Input length must be
multiple of 16 when decrypting with padded cipher
Tried Solutions: I've tried to to change the padding to "AES/ECB/NoPadding", "AES/ECB/PKCS5", "AES/CBC/NoPadding", "AES/CBC/PKCS5Padding" and still received the same error or an error stating only AES or Rijndael required. Then I tried making the key use "AES" parameter and ALGO set to "AES/CBC/PKCS5Padding", but I recieved a missing parameter error which I tried to fix my adding new IvParameterSpec(new byte[16]) to cipher.init. It still resulted into the 16 bit issue. So I'm stuck now.
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.FileReader;
import java.security.Key;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.*;
import java.io.PrintWriter;
import java.io.FileWriter;
import java.util.*;
import java.io.*;
// Don't forget to import any supporting classes you plan to use.
public class Crypto
{
private Scanner fileText;
private PrintWriter fileEncrypt;
private Scanner inputFile;
private PrintWriter outputFile;
private static final String ALGO = "AES/CBC/PKCS5Padding";
private byte[] keyValue;
public Crypto(String key)
{
keyValue = key.getBytes();
}
public String encrypt(String Data) throws Exception
{
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = new BASE64Encoder().encode(encVal);
return encryptedValue;
}
public String decrypt(String encryptedData) throws Exception
{
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decodedValue = new BASE64Decoder().decodeBuffer(encryptedData);
byte[] decValue = c.doFinal(decodedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
public Key generateKey() throws Exception
{
Key key = new SecretKeySpec(keyValue, "AES");
return key;
}
// encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt", "CryptoCiphertext.txt" )
// encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt", "CryptoDeciphered.txt")
public void encrypt_decrypt(String function_type , String source_file , String
target_file)
{
String lineValue = "";
String convertedValue = "";
try
{
inputFile = new Scanner(new File(source_file));
}
catch(Exception e)
{
System.out.println("( " + source_file + ") - File Opening Error");
}
try
{
outputFile = new PrintWriter(new FileWriter(target_file));
}
catch(Exception e)
{
System.out.println("( " + target_file + ") - File Opening Error");
}
while(inputFile.hasNext())
{
lineValue = inputFile.nextLine();
System.out.println("Source Line: " + lineValue);
try
{
if (function_type == "ENCRYPT")
{
convertedValue = encrypt(lineValue);
}
else if (function_type == "DECRYPT")
{
convertedValue = decrypt(lineValue);
}
}
catch(Exception e)
{
System.out.println(e);
}
System.out.println("Converted Line : " + convertedValue);
outputFile.write(convertedValue);
}
inputFile.close();
outputFile.close();
}
public static void main( String args[] ) throws IOException
{
// Write your code here...
// You will read from CryptoPlaintext.txt and write to
CryptoCiphertext.txt.
Crypto c = new Crypto("dk201anckse29sns");
c.encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt", "CryptoCiphertext.txt"
);
c.encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt",
"CryptoDeciphered.txt");
//
// And then read from CryptoCiphertext.txt and write to
CryptoDeciphered.txt.
//
// DON'T forget your comments!
// =============================== DO NOT MODIFY ANY CODE BELOW HERE
==============================
// Compare the files
System.out.println(compareFiles() ? "The files are identical!" : "The
files are NOT identical.");
}
/**
* Compares the Plaintext file with the Deciphered file.
*
* #return true if files match, false if they do not
*/
public static boolean compareFiles() throws IOException
{
Scanner pt = new Scanner(new File("CryptoPlaintext.txt")); // Open the
plaintext file
Scanner dc = new Scanner(new File("CryptoDeciphered.txt")); // Open the
deciphered file
// Read through the files and compare them record by record.
// If any of the records do not match, the files are not identical.
while(pt.hasNextLine() && dc.hasNextLine())
if(!pt.nextLine().equals(dc.nextLine())) return false;
// If we have any records left over, then the files are not identical.
if(pt.hasNextLine() || dc.hasNextLine()) return false;
// The files are identical.
return true;
}
}
There are two errors in your code:
You forgot to generate a random IV and prefix it to your ciphertext (before encoding it to base 64). You'd need to find the IV from your ciphertext and then retrieve it back again during decryption.
Note that CBC code requires an IV indistinguishable from random. You can create it using new SecureRandom (only during encryption, of course) and IvParameterSpec. The code will probably run without this as the default implementation in Java defaults on an all-zero IV. Possibly that is enough for this assignment.
But that's not what generates the error; that's much more of a banality:
You're calling outputFile.write instead of outputFile.println which means that newlines aren't inserted, and all base 64 encodings are put in a single line.
Note that you should not use any classes from sun.misc. Those are private to the Java implementation and are not part of the Java API. The new Java versions have java.util.Base64 for your convenience. Actually, the sun.misc version may insert line endings within the base 64 encoding that will break your code for longer lines.
For example:
package nl.owlstead.stackoverflow;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.Key;
import java.util.Base64;
import java.util.Scanner;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
private Scanner inputFile;
private PrintWriter outputFile;
private static final String ALGO = "AES/CBC/PKCS5Padding";
private byte[] keyValue;
public Crypto(String key) {
keyValue = key.getBytes();
}
public String encrypt(String Data) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key,
new IvParameterSpec(new byte[c.getBlockSize()]));
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = Base64.getEncoder().encodeToString(encVal);
return encryptedValue;
}
public String decrypt(String encryptedData) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key,
new IvParameterSpec(new byte[c.getBlockSize()]));
byte[] decodedValue = Base64.getDecoder().decode(encryptedData);
byte[] decValue = c.doFinal(decodedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
public Key generateKey() throws Exception {
Key key = new SecretKeySpec(keyValue, "AES");
return key;
}
public void encrypt_decrypt(String function_type, String source_file,
String target_file) {
String lineValue = "";
String convertedValue = "";
try {
inputFile = new Scanner(new File(source_file));
} catch (Exception e) {
System.out.println("( " + source_file + ") - File Opening Error");
}
try {
outputFile = new PrintWriter(new FileWriter(target_file));
} catch (Exception e) {
System.out.println("( " + target_file + ") - File Opening Error");
}
while (inputFile.hasNext()) {
lineValue = inputFile.nextLine();
System.out.println("Source Line: " + lineValue);
try {
if (function_type == "ENCRYPT") {
convertedValue = encrypt(lineValue);
} else if (function_type == "DECRYPT") {
convertedValue = decrypt(lineValue);
}
} catch (Exception e) {
System.out.println(e);
}
System.out.println("Converted Line : " + convertedValue);
outputFile.println(convertedValue);
}
inputFile.close();
outputFile.close();
}
public static void main(String args[]) throws IOException {
Crypto c = new Crypto("dk201anckse29sns");
c.encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt",
"CryptoCiphertext.txt");
c.encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt",
"CryptoDeciphered.txt");
System.out.println(compareFiles() ? "The files are identical!"
: "The files are NOT identical.");
}
/**
* Compares the Plaintext file with the Deciphered file.
*
* #return true if files match, false if they do not
*/
public static boolean compareFiles() throws IOException {
Scanner pt = new Scanner(new File("CryptoPlaintext.txt")); // Open the
Scanner dc = new Scanner(new File("CryptoDeciphered.txt")); // Open the
// Read through the files and compare them record by record.
// If any of the records do not match, the files are not identical.
while (pt.hasNextLine() && dc.hasNextLine()) {
String ptl = pt.nextLine();
String dcl = dc.nextLine();
if (!ptl.equals(dcl))
{
System.out.println(ptl);
System.out.println(dcl);
continue;
// return false;
}
}
// If we have any records left over, then the files are not identical.
if (pt.hasNextLine() || dc.hasNextLine())
return false;
// The files are identical.
return true;
}
}
A working solution for you:
Just added a random IV value while initiating your cipher during encrypting and decrypting.
c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[16]));
package com.samples;
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.PrintWriter;
import java.io.FileWriter;
// Don't forget to import any supporting classes you plan to use.
public class Crypto
{
private Scanner fileText;
private PrintWriter fileEncrypt;
private Scanner inputFile;
private PrintWriter outputFile;
private static final String ALGO = "AES/CBC/PKCS5Padding";
private byte[] keyValue;
public Crypto(String key)
{
keyValue = key.getBytes();
}
public String encrypt(String Data) throws Exception
{
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[16]));
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = new BASE64Encoder().encode(encVal);
return encryptedValue;
}
public String decrypt(String encryptedData) throws Exception
{
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(new byte[16]));
byte[] decodedValue = new BASE64Decoder().decodeBuffer(encryptedData);
byte[] decValue = c.doFinal(decodedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
public Key generateKey() throws Exception
{
Key key = new SecretKeySpec(keyValue, "AES");
return key;
}
// encrypt_decrypt("ENCRYPT", "CryptoPlaintext.txt", "CryptoCiphertext.txt" )
// encrypt_decrypt("DECRYPT", "CryptoCiphertext.txt", "CryptoDeciphered.txt")
public void encrypt_decrypt(String function_type, String source_file, String target_file)
{
String lineValue = "";
String convertedValue = "";
try
{
inputFile = new Scanner(new File(source_file));
}
catch(Exception e)
{
System.out.println("( " + source_file + ") - File Opening Error");
}
try
{
outputFile = new PrintWriter(new FileWriter(target_file));
}
catch(Exception e)
{
System.out.println("( " + target_file + ") - File Opening Error");
}
while(inputFile.hasNext())
{
lineValue = inputFile.nextLine();
System.out.println("Source Line: " + lineValue);
try
{
if (function_type == "ENCRYPT")
{
convertedValue = encrypt(lineValue);
}
else if (function_type == "DECRYPT")
{
convertedValue = decrypt(lineValue);
}
}
catch(Exception e)
{
System.out.println(e);
}
System.out.println("Converted Line : " + convertedValue);
outputFile.write(convertedValue);
}
inputFile.close();
outputFile.close();
}
public static void main( String args[] ) throws IOException
{
// Write your code here...
// You will read from CryptoPlaintext.txt and write to CryptoCiphertext.txt.
Crypto c = new Crypto("dk201anckse29sns");
c.encrypt_decrypt("ENCRYPT", "C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoPlaintext.txt", "C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoCiphertext.txt"
);
c.encrypt_decrypt("DECRYPT", "C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoCiphertext.txt",
"C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoDeciphered.txt");
//
// And then read from CryptoCiphertext.txt and write to CryptoDeciphered.txt.
//
// DON'T forget your comments!
// =============================== DO NOT MODIFY ANY CODE BELOW HE ==============================
// Compare the files
System.out.println(compareFiles() ? "The files are identical!" : "The files are NOT identical.");
}
/**
* Compares the Plaintext file with the Deciphered file.
*
* #return true if files match, false if they do not
*/
public static boolean compareFiles() throws IOException
{
Scanner pt = new Scanner(new File("C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoPlaintext.txt")); // Open the plaintext file
Scanner dc = new Scanner(new File("C:\\Users\\mundrap\\Eclipse_Workspace\\Java-8\\src\\com\\samples\\CryptoDeciphered.txt")); // Open the deciphered file
// Read through the files and compare them record by record.
// If any of the records do not match, the files are not identical.
while(pt.hasNextLine() && dc.hasNextLine())
if(!pt.nextLine().equals(dc.nextLine())) return false;
// If we have any records left over, then the files are not identical.
if(pt.hasNextLine() || dc.hasNextLine()) return false;
// The files are identical.
return true;
}
}
I need to decrypt in JAVA a file encrypted in UNIX with the following command:
openssl aes-256-cbc -a -salt -in password.txt -out password.txt.enc
mypass
mypass
I have to decrypt in java as I do here I do in UNIX
openssl aes-256-cbc -d -a -in password.txt.enc -out password.txt.new
mypass
Someone can give me a java code to do this?
OpenSSL generally uses its own password based key derivation method, specified in EVP_BytesToKey, please see the code below. Furthermore, it implicitly encodes the ciphertext as base 64 over multiple lines, which would be required to send it within the body of a mail message.
So the result is, in pseudocode:
salt = random(8)
keyAndIV = BytesToKey(password, salt, 48)
key = keyAndIV[0..31]
iv = keyAndIV[32..47]
ct = AES-256-CBC-encrypt(key, iv, plaintext)
res = base64MimeEncode("Salted__" | salt | ct))
and the decryption therefore is:
(salt, ct) = base64MimeDecode(res)
key = keyAndIV[0..31]
iv = keyAndIV[32..47]
pt = AES-256-CBC-decrypt(key, iv, plaintext)
which can be implemented in Java like this:
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.util.encoders.Base64;
public class OpenSSLDecryptor {
private static final Charset ASCII = Charset.forName("ASCII");
private static final int INDEX_KEY = 0;
private static final int INDEX_IV = 1;
private static final int ITERATIONS = 1;
private static final int ARG_INDEX_FILENAME = 0;
private static final int ARG_INDEX_PASSWORD = 1;
private static final int SALT_OFFSET = 8;
private static final int SALT_SIZE = 8;
private static final int CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE;
private static final int KEY_SIZE_BITS = 256;
/**
* Thanks go to Ola Bini for releasing this source on his blog.
* The source was obtained from here .
*/
public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md,
byte[] salt, byte[] data, int count) {
byte[][] both = new byte[2][];
byte[] key = new byte[key_len];
int key_ix = 0;
byte[] iv = new byte[iv_len];
int iv_ix = 0;
both[0] = key;
both[1] = iv;
byte[] md_buf = null;
int nkey = key_len;
int niv = iv_len;
int i = 0;
if (data == null) {
return both;
}
int addmd = 0;
for (;;) {
md.reset();
if (addmd++ > 0) {
md.update(md_buf);
}
md.update(data);
if (null != salt) {
md.update(salt, 0, 8);
}
md_buf = md.digest();
for (i = 1; i < count; i++) {
md.reset();
md.update(md_buf);
md_buf = md.digest();
}
i = 0;
if (nkey > 0) {
for (;;) {
if (nkey == 0)
break;
if (i == md_buf.length)
break;
key[key_ix++] = md_buf[i];
nkey--;
i++;
}
}
if (niv > 0 && i != md_buf.length) {
for (;;) {
if (niv == 0)
break;
if (i == md_buf.length)
break;
iv[iv_ix++] = md_buf[i];
niv--;
i++;
}
}
if (nkey == 0 && niv == 0) {
break;
}
}
for (i = 0; i < md_buf.length; i++) {
md_buf[i] = 0;
}
return both;
}
public static void main(String[] args) {
try {
// --- read base 64 encoded file ---
File f = new File(args[ARG_INDEX_FILENAME]);
List<String> lines = Files.readAllLines(f.toPath(), ASCII);
StringBuilder sb = new StringBuilder();
for (String line : lines) {
sb.append(line.trim());
}
String dataBase64 = sb.toString();
byte[] headerSaltAndCipherText = Base64.decode(dataBase64);
// --- extract salt & encrypted ---
// header is "Salted__", ASCII encoded, if salt is being used (the default)
byte[] salt = Arrays.copyOfRange(
headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE);
byte[] encrypted = Arrays.copyOfRange(
headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.length);
// --- specify cipher and digest for EVP_BytesToKey method ---
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
MessageDigest md5 = MessageDigest.getInstance("MD5");
// --- create key and IV ---
// the IV is useless, OpenSSL might as well have use zero's
final byte[][] keyAndIV = EVP_BytesToKey(
KEY_SIZE_BITS / Byte.SIZE,
aesCBC.getBlockSize(),
md5,
salt,
args[ARG_INDEX_PASSWORD].getBytes(ASCII),
ITERATIONS);
SecretKeySpec key = new SecretKeySpec(keyAndIV[INDEX_KEY], "AES");
IvParameterSpec iv = new IvParameterSpec(keyAndIV[INDEX_IV]);
// --- initialize cipher instance and decrypt ---
aesCBC.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decrypted = aesCBC.doFinal(encrypted);
String answer = new String(decrypted, ASCII);
System.out.println(answer);
} catch (BadPaddingException e) {
// AKA "something went wrong"
throw new IllegalStateException(
"Bad password, algorithm, mode or padding;" +
" no salt, wrong number of iterations or corrupted ciphertext.");
} catch (IllegalBlockSizeException e) {
throw new IllegalStateException(
"Bad algorithm, mode or corrupted (resized) ciphertext.");
} catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
Beware that the code specifies ASCII as character set. The character set used may differ for your application / terminal / OS.
In general you should force OpenSSL to use the NIST approved PBKDF2 algorithm, as using the OpenSSL key derivation method - with an iteration count of 1 - is insecure. This may force you to use a different solution than OpenSSL. Note that password based encryption is inherently rather insecure - passwords are much less secure than randomly generated symmetric keys.
OpenSSL 1.1.0c changed the digest algorithm used in some internal components. Formerly, MD5 was used, and 1.1.0 switched to SHA256. Be careful the change is not affecting you in both EVP_BytesToKey and commands like openssl enc.
It's probably best to explicitly specify the digest in the command line interface (e.g. -md md5 for backwards compatibility or sha-256 for forwards compatibility) for the and make sure that the Java code uses the same digest algorithm ("MD5" or "SHA-256" including the dash). Also see the information in this answer.
Below are OpenSSLPBEInputStream and OpenSSLPBEOutputStream which can be used to encrypt/decrypt arbitrary streams of bytes in a way that is compatible with OpenSSL.
Example usage:
// The original clear text bytes
byte[] originalBytes = ...
// Encrypt these bytes
char[] pwd = "thePassword".toCharArray();
ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
OpenSSLPBEOutputStream encOS = new OpenSSLPBEOutputStream(byteOS, ALGORITHM, 1, pwd);
encOS.write(originalBytes);
encOS.flush();
byte[] encryptedBytes = byteOS.toByteArray();
// Decrypt the bytes
ByteArrayInputStream byteIS = new ByteArrayInputStream(encryptedBytes);
OpenSSLPBEInputStream encIS = new OpenSSLPBEInputStream(byteIS, ALGORITHM, 1, pwd);
Where ALGORITHM (using just JDK classes) can be: "PBEWithMD5AndDES", "PBEWithMD5AndTripleDES", "PBEWithSHA1AndDESede", "PBEWithSHA1AndRC2_40".
To handle "openssl aes-256-cbc -a -salt -in password.txt -out password.txt.enc" of the original poster, add bouncey castle to the classpath, and use algorthm= "PBEWITHMD5AND256BITAES-CBC-OPENSSL".
/* Add BC provider, and fail fast if BC provider is not in classpath for some reason */
Security.addProvider(new BouncyCastleProvider());
The dependency:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.44</version>
</dependency>
The input stream:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
public class OpenSSLPBEInputStream extends InputStream {
private final static int READ_BLOCK_SIZE = 64 * 1024;
private final Cipher cipher;
private final InputStream inStream;
private final byte[] bufferCipher = new byte[READ_BLOCK_SIZE];
private byte[] bufferClear = null;
private int index = Integer.MAX_VALUE;
private int maxIndex = 0;
public OpenSSLPBEInputStream(final InputStream streamIn, String algIn, int iterationCount, char[] password)
throws IOException {
this.inStream = streamIn;
try {
byte[] salt = readSalt();
cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.DECRYPT_MODE, algIn, iterationCount);
} catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IOException(e);
}
}
#Override
public int available() throws IOException {
return inStream.available();
}
#Override
public int read() throws IOException {
if (index > maxIndex) {
index = 0;
int read = inStream.read(bufferCipher);
if (read != -1) {
bufferClear = cipher.update(bufferCipher, 0, read);
}
if (read == -1 || bufferClear == null || bufferClear.length == 0) {
try {
bufferClear = cipher.doFinal();
} catch (IllegalBlockSizeException | BadPaddingException e) {
bufferClear = null;
}
}
if (bufferClear == null || bufferClear.length == 0) {
return -1;
}
maxIndex = bufferClear.length - 1;
}
return bufferClear[index++] & 0xff;
}
private byte[] readSalt() throws IOException {
byte[] headerBytes = new byte[OpenSSLPBECommon.OPENSSL_HEADER_STRING.length()];
inStream.read(headerBytes);
String headerString = new String(headerBytes, OpenSSLPBECommon.OPENSSL_HEADER_ENCODE);
if (!OpenSSLPBECommon.OPENSSL_HEADER_STRING.equals(headerString)) {
throw new IOException("unexpected file header " + headerString);
}
byte[] salt = new byte[OpenSSLPBECommon.SALT_SIZE_BYTES];
inStream.read(salt);
return salt;
}
}
The output stream:
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
public class OpenSSLPBEOutputStream extends OutputStream {
private static final int BUFFER_SIZE = 5 * 1024 * 1024;
private final Cipher cipher;
private final OutputStream outStream;
private final byte[] buffer = new byte[BUFFER_SIZE];
private int bufferIndex = 0;
public OpenSSLPBEOutputStream(final OutputStream outputStream, String algIn, int iterationCount,
char[] password) throws IOException {
outStream = outputStream;
try {
/* Create and use a random SALT for each instance of this output stream. */
byte[] salt = new byte[PBECommon.SALT_SIZE_BYTES];
new SecureRandom().nextBytes(salt);
cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.ENCRYPT_MODE, algIn, iterationCount);
/* Write header */
writeHeader(salt);
} catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | InvalidAlgorithmParameterException e) {
throw new IOException(e);
}
}
#Override
public void write(int b) throws IOException {
buffer[bufferIndex] = (byte) b;
bufferIndex++;
if (bufferIndex == BUFFER_SIZE) {
byte[] result = cipher.update(buffer, 0, bufferIndex);
outStream.write(result);
bufferIndex = 0;
}
}
#Override
public void flush() throws IOException {
if (bufferIndex > 0) {
byte[] result;
try {
result = cipher.doFinal(buffer, 0, bufferIndex);
outStream.write(result);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IOException(e);
}
bufferIndex = 0;
}
}
#Override
public void close() throws IOException {
flush();
outStream.close();
}
private void writeHeader(byte[] salt) throws IOException {
outStream.write(OpenSSLPBECommon.OPENSSL_HEADER_STRING.getBytes(OpenSSLPBECommon.OPENSSL_HEADER_ENCODE));
outStream.write(salt);
}
}
Small common class:
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
class OpenSSLPBECommon {
protected static final int SALT_SIZE_BYTES = 8;
protected static final String OPENSSL_HEADER_STRING = "Salted__";
protected static final String OPENSSL_HEADER_ENCODE = "ASCII";
protected static Cipher initializeCipher(char[] password, byte[] salt, int cipherMode,
final String algorithm, int iterationCount) throws NoSuchAlgorithmException, InvalidKeySpecException,
InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException {
PBEKeySpec keySpec = new PBEKeySpec(password);
SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm);
SecretKey key = factory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(cipherMode, key, new PBEParameterSpec(salt, iterationCount));
return cipher;
}
}
In Kotlin:
package io.matthewnelson.java_crypto
import java.util.*
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
class OpenSSL {
/**
* Will decrypt a string value encrypted by OpenSSL v 1.1.1+ using the following cmds from terminal:
*
* echo "Hello World!" | openssl aes-256-cbc -e -a -p -salt -pbkdf2 -iter 15739 -k qk4aX-EfMUa-g4HdF-fjfkU-bbLNx-15739
*
* Terminal output:
* salt=CC73B7D29FE59CE1
* key=31706F84185EA4B5E8E040F2C813F79722F22996B48B82FF98174F887A9B9993
* iv =1420310D41FD7F48E5D8722B9AC1C8DD
* U2FsdGVkX1/Mc7fSn+Wc4XLwDsmLdR8O7K3bFPpCglA=
* */
fun decrypt_AES256CBC_PBKDF2_HMAC_SHA256(
password: String,
hashIterations: Int,
encryptedString: String
): String {
val encryptedBytes = Base64.getDecoder().decode(encryptedString)
// Salt is bytes 8 - 15
val salt = encryptedBytes.copyOfRange(8, 16)
// println("Salt: ${salt.joinToString("") { "%02X".format(it) }}")
// Derive 48 byte key
val keySpec = PBEKeySpec(password.toCharArray(), salt, hashIterations, 48 * 8)
val keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val secretKey = keyFactory.generateSecret(keySpec)
// Decryption Key is bytes 0 - 31 of the derived key
val key = secretKey.encoded.copyOfRange(0, 32)
// println("Key: ${key.joinToString("") { "%02X".format(it) }}")
// Input Vector is bytes 32 - 47 of the derived key
val iv = secretKey.encoded.copyOfRange(32, 48)
// println("IV: ${iv.joinToString("") { "%02X".format(it) }}")
// Cipher Text is bytes 16 - end of the encrypted bytes
val cipherText = encryptedBytes.copyOfRange(16, encryptedBytes.lastIndex + 1)
// Decrypt the Cipher Text and manually remove padding after
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
val decrypted = cipher.doFinal(cipherText)
// println("Decrypted: ${decrypted.joinToString("") { "%02X".format(it) }}")
// Last byte of the decrypted text is the number of padding bytes needed to remove
val plaintext = decrypted.copyOfRange(0, decrypted.lastIndex + 1 - decrypted.last().toInt())
return plaintext.toString(Charsets.UTF_8)
}
}
Don't use ase-128-cbc, use ase-128-ecb.
only take first 16 bytes as key because key is 128 bits
hash output is printed in hex, which every 2 chars presents a byte value
hashpwd=echo -n $password| openssl sha1 | sed 's#.*=\\s*##g' | cut -c 1-32
openssl enc -aes-128-ecb -salt -in -out -K $hashpwd
Java Code is here:
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
//openssl enc -nosalt -aes-128-ecb
// -in <input file>
// -out <output file>
// -K <16 bytes in hex, for example : "abc" can be hashed in SHA-1, the first 16 bytes in hex is a9993e364706816aba3e25717850c26c>
private final static String TRANSFORMATION = "AES"; // use aes-128-ecb in openssl
public static byte[] encrypt(String passcode, byte[] data) throws CryptographicException {
try {
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, genKeySpec(passcode));
return cipher.doFinal(data);
} catch (Exception ex) {
throw new CryptographicException("Error encrypting", ex);
}
}
public static String encryptWithBase64(String passcode, byte[] data) throws CryptographicException {
return new BASE64Encoder().encode(encrypt(passcode, data));
}
public static byte[] decrypt(String passcode, byte[] data) throws CryptographicException {
try {
Cipher dcipher = Cipher.getInstance(TRANSFORMATION);
dcipher.init(Cipher.DECRYPT_MODE, genKeySpec(passcode));
return dcipher.doFinal(data);
} catch (Exception e) {
throw new CryptographicException("Error decrypting", e);
}
}
public static byte[] decryptWithBase64(String passcode, String encrptedStr) throws CryptographicException {
try {
return decrypt(passcode, new BASE64Decoder().decodeBuffer(encrptedStr));
} catch (Exception e) {
throw new CryptographicException("Error decrypting", e);
}
}
public static SecretKeySpec genKeySpec(String passcode) throws UnsupportedEncodingException, NoSuchAlgorithmException {
byte[] key = passcode.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
return new SecretKeySpec(key, TRANSFORMATION);
}
Tested and passed in jdk6 and jdk8.
I know how to sign data using a certificate present on system(machine) but the requirement is to sign the data using certi present on browser. Below is the code. If anyone find my code wrong then please let me know because i am not sure regarding its correctness.
PS - This code works.
import java.io.FileInputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
public class class123 {
public static void main(String[] args) {
String input = "shubham";
byte[] signature = createSignature(input.getBytes());
System.out.println(createSignature(input.getBytes()));
verifySignature(input.getBytes(), signature);
}
private static byte[] createSignature(byte[] file) {
byte[] signature = null;
String Password="abc";
try {
java.security.KeyStore keyStoreFile = java.security.KeyStore.getInstance("PKCS12");
keyStoreFile.load(new FileInputStream("D:\\1.p12"), Password.toCharArray()); //address of certificate (pfx file) and corresponding password.
Enumeration<String> aliases = keyStoreFile.aliases();
String alias = aliases.nextElement();
PrivateKey privateKey = (PrivateKey) keyStoreFile.getKey(alias, Password.toCharArray());
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initSign(privateKey);
dsa.update(file, 0, file.length);
signature = dsa.sign();
} catch (Exception e) {
e.printStackTrace();
}
return signature;
}
private static void verifySignature(byte[] file, byte[] sign) {
String Password="abc";
try {
java.security.KeyStore keyStoreFile = java.security.KeyStore.getInstance("PKCS12");
keyStoreFile.load(new FileInputStream("D:\\1.p12"), Password.toCharArray());
Enumeration<String> aliases = keyStoreFile.aliases();
String alias = aliases.nextElement();
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initVerify(((X509Certificate) keyStoreFile.getCertificate(alias)).getPublicKey());
dsa.update(file);
boolean ret = dsa.verify(sign);
System.out.println(ret);
} catch (Exception e) {
e.printStackTrace();
}
}
}
it's fine, Kindly check this blog post as I wrote it before while accessing the Microsoft Certificate store to sign and verify, it May help you.
import java.io.FileOutputStream;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.cert.CertificateExpiredException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HttpsURLConnection;
public class CertificateFromBrowser {
public static void main(String[] args) throws Exception {
testConnectionTo(""); // pass the url (eg: https://www.example.com)
}
public static void testConnectionTo(String aURL) throws Exception {
URL destinationURL = new URL(aURL);
HttpsURLConnection conn = (HttpsURLConnection) destinationURL.openConnection();
conn.connect();
Certificate[] certs = conn.getServerCertificates();
System.out.println("nb = " + certs.length);
for (Certificate cert : certs) {
System.out.println("");
System.out.println("");
System.out.println("");
System.out.println("################################################################");
System.out.println("");
System.out.println("");
System.out.println("");
System.out.println("Certificate is: " + cert);
if (cert instanceof X509Certificate) {
try {
((X509Certificate) cert).checkValidity();
System.out.println("Certificate is active for current date");
} catch (CertificateExpiredException cee) {
System.out.println("Certificate is expired");
}
} else {
System.err.println("Unknown certificate type: " + cert);
}
}
}
}
I'm pretty familiar with PGP, and the way it works. I'd like to include some functionality in a project, but so far my research has left my head spinning. What I'd like to do is create a public key with a defined private pass phrase. From there, I'd share the public key with someone where they can then encrypt a message using the key and return it to me where I can decrypt. I envision the code looking something like this.
To generate my private pgp key:
PGPKey key = new PGPKey();
key.setPassPhrase("MySecretPassword!!!1");
key.generateRandomSharedKey();
key.build();
To encrypt I'd give my shared key to a friend:
String encryptedText = PGPTools.Encrypt("Text to encrypt", getSharedKey());
To decrypt the encrypted string after being sent back to me:
String decryptedText = PGPTools.Decrypt(encryptedText, key, "MySecretPassword!!!1")
Obviously I know I'm skipping out on a ton of details. Through my research I've seen references to libraries like Bouncy Castle and Spongy Castle. Any help would be hugely appreciated!
I want to post my solution because A. it was extremely difficult to get this working, and B. If any crypto pro's want to audit my code I'd be eternally grateful.
I included the 4 following libraries:
compile 'com.madgag.spongycastle:core:1.50.0.0'
compile 'com.madgag.spongycastle:pg:1.50.0.0'
compile 'com.madgag.spongycastle:pkix:1.50.0.0'
compile 'com.madgag.spongycastle:prov:1.50.0.0'
Bouncy Castle needs to be added as a security provider. I included this code in a class that initializes some other objects when the app loads.
static {
Security.addProvider(new BouncyCastleProvider());
}
Here's the utils class I created that really contains the nuts and bolts. Edited slightly:
import com.example.Device;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.Features;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.crypto.generators.RSAKeyPairGenerator;
import org.spongycastle.crypto.params.RSAKeyGenerationParameters;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPEncryptedDataList;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyPair;
import org.spongycastle.openpgp.PGPKeyRingGenerator;
import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPLiteralDataGenerator;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.bc.BcPBESecretKeyEncryptorBuilder;
import org.spongycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.bc.BcPGPKeyPair;
import org.spongycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Iterator;
public class PgpUtils {
private static final String PROVIDER = "SC";
private static final String KEY_RING_ID = "asdf#asdf.com";
public static String decrypt(String encryptedText, String password) throws Exception {
byte[] encrypted = encryptedText.getBytes();
InputStream in = new ByteArrayInputStream(encrypted);
in = PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc;
Object o = pgpF.nextObject();
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
} else {
enc = (PGPEncryptedDataList) pgpF.nextObject();
}
PGPPrivateKey sKey = null;
PGPPublicKeyEncryptedData pbe = null;
while (sKey == null && enc.getEncryptedDataObjects().hasNext()) {
pbe = (PGPPublicKeyEncryptedData)enc.getEncryptedDataObjects().next();
sKey = getPrivateKey(getPGPSecretKeyRing(), pbe.getKeyID(), password.toCharArray());
}
if (pbe != null) {
InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey));
PGPObjectFactory pgpFact = new PGPObjectFactory(clear);
PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject();
pgpFact = new PGPObjectFactory(cData.getDataStream());
PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
InputStream unc = ld.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
int ch;
while ((ch = unc.read()) >= 0) {
out.write(ch);
}
byte[] returnBytes = out.toByteArray();
out.close();
return new String(returnBytes);
}
return null;
}
private static PGPPublicKey getPublicKey(PGPPublicKeyRing publicKeyRing) {
Iterator<?> kIt = publicKeyRing.getPublicKeys();
while (kIt.hasNext()) {
PGPPublicKey k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
return k;
}
}
return null;
}
private static PGPPrivateKey getPrivateKey(PGPSecretKeyRing keyRing, long keyID, char[] pass) throws PGPException {
PGPSecretKey secretKey = keyRing.getSecretKey(keyID);
PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(pass);
return secretKey.extractPrivateKey(decryptor);
}
public static String encrypt(String msgText) throws IOException, PGPException {
byte[] clearData = msgText.getBytes();
PGPPublicKey encKey = getPublicKey(getPGPPublicKeyRing());
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
OutputStream out = new ArmoredOutputStream(encOut);
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(PGPCompressedDataGenerator.ZIP);
OutputStream cos = comData.open(bOut);
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
OutputStream pOut = lData.open(cos, PGPLiteralData.BINARY, PGPLiteralData.CONSOLE, clearData.length, new Date());
pOut.write(clearData);
lData.close();
comData.close();
PGPEncryptedDataGenerator encGen =
new PGPEncryptedDataGenerator(
new JcePGPDataEncryptorBuilder(PGPEncryptedData.AES_256).setWithIntegrityPacket(true).setSecureRandom(
new SecureRandom()).setProvider(PROVIDER));
if (encKey != null) {
encGen.addMethod(new JcePublicKeyKeyEncryptionMethodGenerator(encKey).setProvider(PROVIDER));
byte[] bytes = bOut.toByteArray();
OutputStream cOut = encGen.open(out, bytes.length);
cOut.write(bytes);
cOut.close();
}
out.close();
return new String(encOut.toByteArray());
}
public final static PGPKeyRingGenerator generateKeyRingGenerator (char[] pass) throws PGPException{
RSAKeyPairGenerator kpg = new RSAKeyPairGenerator();
kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), new SecureRandom(), 2048, 12));
PGPKeyPair rsakp_sign = new BcPGPKeyPair(PGPPublicKey.RSA_SIGN, kpg.generateKeyPair(), new Date());
PGPKeyPair rsakp_enc = new BcPGPKeyPair(PGPPublicKey.RSA_ENCRYPT, kpg.generateKeyPair(), new Date());
PGPSignatureSubpacketGenerator signhashgen = new PGPSignatureSubpacketGenerator();
signhashgen.setKeyFlags(false, KeyFlags.SIGN_DATA|KeyFlags.CERTIFY_OTHER|KeyFlags.SHARED);
signhashgen.setPreferredSymmetricAlgorithms(false, new int[]{SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, SymmetricKeyAlgorithmTags.AES_128});
signhashgen.setPreferredHashAlgorithms(false, new int[]{HashAlgorithmTags.SHA256, HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512, HashAlgorithmTags.SHA224});
signhashgen.setFeature(false, Features.FEATURE_MODIFICATION_DETECTION);
PGPSignatureSubpacketGenerator enchashgen = new PGPSignatureSubpacketGenerator();
enchashgen.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
PGPDigestCalculator sha1Calc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA1);
PGPDigestCalculator sha256Calc = new BcPGPDigestCalculatorProvider().get(HashAlgorithmTags.SHA256);
PBESecretKeyEncryptor pske = (new BcPBESecretKeyEncryptorBuilder(PGPEncryptedData.AES_256, sha256Calc, 0xc0)).build(pass);
PGPKeyRingGenerator keyRingGen = new PGPKeyRingGenerator (PGPSignature.POSITIVE_CERTIFICATION, rsakp_sign,
KEY_RING_ID, sha1Calc, signhashgen.generate(), null, new BcPGPContentSignerBuilder(rsakp_sign.getPublicKey().getAlgorithm(),
HashAlgorithmTags.SHA1), pske);
keyRingGen.addSubKey(rsakp_enc, enchashgen.generate(), null);
return keyRingGen;
}
private static PGPPublicKeyRing getPGPPublicKeyRing() throws IOException {
ArmoredInputStream ais = new ArmoredInputStream(new ByteArrayInputStream(Device.getDevice().getPgpPublicKey().getBytes()));
return (PGPPublicKeyRing) new PGPObjectFactory(ais).nextObject();
}
private static PGPSecretKeyRing getPGPSecretKeyRing() throws IOException {
ArmoredInputStream ais = new ArmoredInputStream(new ByteArrayInputStream(Device.getDevice().getPgpSecretKey().getBytes()));
return (PGPSecretKeyRing) new PGPObjectFactory(ais).nextObject();
}
public final static String genPGPPublicKey (PGPKeyRingGenerator krgen) throws IOException {
ByteArrayOutputStream baosPkr = new ByteArrayOutputStream();
PGPPublicKeyRing pkr = krgen.generatePublicKeyRing();
ArmoredOutputStream armoredStreamPkr = new ArmoredOutputStream(baosPkr);
pkr.encode(armoredStreamPkr);
armoredStreamPkr.close();
return new String(baosPkr.toByteArray(), Charset.defaultCharset());
}
public final static String genPGPPrivKey (PGPKeyRingGenerator krgen) throws IOException {
ByteArrayOutputStream baosPriv = new ByteArrayOutputStream ();
PGPSecretKeyRing skr = krgen.generateSecretKeyRing();
ArmoredOutputStream armoredStreamPriv = new ArmoredOutputStream(baosPriv);
skr.encode(armoredStreamPriv);
armoredStreamPriv.close();
return new String(baosPriv.toByteArray(), Charset.defaultCharset());
}
}
Here is how I create the private and public keys:
final PGPKeyRingGenerator krgen = PgpUtils.generateKeyRingGenerator("password".toCharArray());
String pgpPublicKey = PgpUtils.genPGPPublicKey(krgen);
String pgpSecretKey = PgpUtils.genPGPPrivKey(krgen);
And finally encrypting and decrypting using your own public key:
String encrypted = PgpUtils.encrypt("message text");
String decrypted = PgpUtils.decrypt(encrypted, "Password");
I don't have enough rep to comment on joey_g216 excellent answer above. Initially it worked for me, but then failed on decrypting various files. This is because the structure of a PGPObject can change.
To get decryption to work I had to adjust:
public static String decrypt(String encryptedText, String password) throws Exception {
to include:
// Could be
// signature + compressed -> data
// signature + data
// data
// compressed -> data
Object z = pgpFact.nextObject();
while (!(z instanceof PGPLiteralData))
{
if (z instanceof PGPCompressedData) {
PGPCompressedData cData = (PGPCompressedData) z;
pgpFact = new PGPObjectFactory(cData.getDataStream(), bcKeyFingerprintCalculator);
}
if (z instanceof PGPOnePassSignatureList) {
// ignore for now!
}
z = pgpFact.nextObject();
}
PGPLiteralData ld = (PGPLiteralData) z;
Looks like you want to generate a public key pair using BouncyCastle?
This one asks how to do it without a password, but you can take a look how to do it with a password too:
How to generate OpenPGP KeyPair without passphrase using BouncyCastle?