I'm writing module for server which will send e-mails. In client application user can add many receipients and each of them has its own public key. I want to encrypt attachments using multiple keys. For example if I add 3 receipients then attachments should be encrypted with 3 different public keys. I'm using bouncy castle to do that but it works only for the first public key in encryption process. I mean thath only the first person can decrypt using its own private key, for the rest it doesn't work.
My code for adding methods for each key looks like:
PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(dataEncryptor);
for (PGPPublicKey publicKey : publicKeys) {
encryptedDataGenerator.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(publicKey));
}
Whole method looks like:
public File encryptFile(String fileName,
boolean armor,
boolean withIntegrityCheck) throws IOException,
NoSuchProviderException,
PGPException {
Security.addProvider(new BouncyCastleProvider());
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData
= new PGPCompressedDataGenerator(PGPCompressedData.UNCOMPRESSED);
PGPUtil.writeFileToLiteralData(comData.open(bOut),
PGPLiteralData.BINARY,
new File(fileName));
comData.close();
BcPGPDataEncryptorBuilder dataEncryptor
= new BcPGPDataEncryptorBuilder(PGPEncryptedData.AES_256);
dataEncryptor.setWithIntegrityPacket(withIntegrityCheck);
dataEncryptor.setSecureRandom(new SecureRandom());
PGPEncryptedDataGenerator encryptedDataGenerator
= new PGPEncryptedDataGenerator(dataEncryptor);
for (PGPPublicKey publicKey : publicKeys) {
encryptedDataGenerator.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(publicKey));
}
byte[] bytes = bOut.toByteArray();
FileOutputStream localByteArrayOutputStream = new FileOutputStream(fileName);
Object localObject = localByteArrayOutputStream;
if (armor) {
localObject = new ArmoredOutputStream((OutputStream) localObject);
}
OutputStream localOutputStream = encryptedDataGenerator.open((OutputStream) localObject,
bytes.length);
localOutputStream.write(bytes);
localOutputStream.close();
return new File(fileName);
}
Can someone help me and tell me what I'm doing wrong?
Thank you for every help.
[EDIT]
This code works, I had problem in method loading multiple keys.
Well, I had the same problem a year later. I wish that you've solved yours. I'm writing my solution here just in case that someone else has similar issues.
Your encryption code doesn't have problem. The problem might be in the decryption. For an encrypted data object, the correct key should be found by using the key id stored with the object. My decryption process reads like the following:
private byte[] decryptWithKey(byte[] bytes, PGPSecretKey secKey, String pass)
throws PGPException, IOException {
PBESecretKeyDecryptor keyDec = new JcePBESecretKeyDecryptorBuilder(
new JcaPGPDigestCalculatorProviderBuilder().setProvider("BC").build())
.setProvider("BC").build(pass.toCharArray());
ByteArrayOutputStream bout = new ByteArrayOutputStream();
PGPPrivateKey privateKey = secKey.extractPrivateKey(keyDec);
PublicKeyDataDecryptorFactory dec1 =
new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(privateKey);
JcaPGPObjectFactory objFact = new JcaPGPObjectFactory(bytes);
PGPEncryptedDataList encList = (PGPEncryptedDataList) objFact.nextObject();
PGPPublicKeyEncryptedData encD = null;
for(Iterator<PGPPublicKeyEncryptedData> it = encList.iterator(); it.hasNext(); ) {
PGPPublicKeyEncryptedData end = it.next();
if (secKey.getKeyID() == end.getKeyID()) {
encD = end;
break;
}
}
assert encD != null: "Cannot find encrypted data with key: "
+ Long.toHexString(secKey.getKeyID());
InputStream in = encD.getDataStream(dec1);
byte[] buf = new byte[BufferSize];
for (int len; (len = in.read(buf)) >= 0; ) {
bout.write(buf, 0, len);
}
bout.close();
return bout.toByteArray();
}
The key is the for loop that finds the matching key for the encrypted object.
Related
I trying to encrypt a .tar using .pgp encryption technique , i have key value(which is again not a file)which i have to use to encrypt the .tar file. The key value is a string (i.e. keyVal="xxxxx").I am taking the reference from the below URL which is there in the github, and is referenced by many users.
https://github.com/matthewmccullough/encryption-jvm-bootcamp/blob/master/bc-pgp/src/main/java/com/ambientideas/cryptography/KeyBasedFileProcessorUtil.java
This this example i want to encrypt the .tar file which i have in the location
../myTarLocation in my code
public static void encryptFile(
OutputStream out,
String fileName,
PGPPublicKey encKey,
boolean armor,
boolean withIntegrityCheck)
throws IOException, NoSuchProviderException
{
if (armor)
{
out = new ArmoredOutputStream(out);
}
try
{
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedData.ZIP);
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();
}
catch (PGPException e)
{
System.err.println(e);
if (e.getUnderlyingException() != null)
{
e.getUnderlyingException().printStackTrace();
}
}
}
But i can't understand where i shall use the keyVal which i have , and how i will encrypt the .tar file into a .tar.pgp file. .Can anyone please help me.
I'm trying to write an encrypted file that will be decrypted using gpg and will be writing lines incrementally instead of in one chunk. I've generated the keys in GnuPG and am using the public key to encrypt. Here's the method I'm using to encrypt:
public static byte[] encrypt(byte[] clearData, PGPPublicKey encKey,
String fileName,boolean withIntegrityCheck, boolean armor)
throws IOException, PGPException, NoSuchProviderException {
if (fileName == null) {
fileName = PGPLiteralData.CONSOLE;
}
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
OutputStream out = encOut;
if (armor) {
out = new ArmoredOutputStream(out);
}
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
PGPCompressedDataGenerator comData = new PGPCompressedDataGenerator(
PGPCompressedDataGenerator.ZIP);
OutputStream cos = comData.open(bOut); // open it with the final
// destination
PGPLiteralDataGenerator lData = new PGPLiteralDataGenerator();
// we want to generate compressed data. This might be a user option
// later,
// in which case we would pass in bOut.
OutputStream pOut = lData.open(cos, // the compressed output stream
PGPLiteralData.BINARY, fileName, // "filename" to store
clearData.length, // length of clear data
new Date() // current time
);
pOut.write(clearData);
lData.close();
comData.close();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_192).setSecureRandom(new SecureRandom()));
cPk.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(encKey));
byte[] bytes = bOut.toByteArray();
OutputStream cOut = cPk.open(out, bytes.length);
cOut.write(bytes); // obtain the actual bytes from the compressed stream
cOut.close();
out.close();
return encOut.toByteArray();
}
And I have a small prototype test class to use that method like this:
PGPPublicKey pubKey = PGPEncryptionTools.readPublicKeyFromCol(new FileInputStream(appProp.getKeyFileName()));
byte[] encryptbytes = PGPEncryptionTools.encrypt("\nthis is some test text".getBytes(), pubKey, null, true, false);
byte[] encryptbytes2 = PGPEncryptionTools.encrypt("\nmore test text".getBytes(), pubKey, null, true, false);
FileOutputStream fos = new FileOutputStream("C:/Users/me/workspace/workspace/spring-batch-project/resources/encryptedfile.gpg");
fos.write(encryptbytes);
fos.write(encryptbytes2);
fos.flush();
fos.close();
So this creates encryptedfile.gpg, but when I go to GnuPG to decrypt the file, it works but it only outputs the first line "this is some test text".
How can I modify this code to be able to encrypt both lines and have GnuPG decrypt them?
You're producing multiple, independent OpenPGP messages each time calling your PGPEncryptionTools.encrypt(...) method. To only output a single OpenPGP message (which GnuPG also decrypt in a single run), write all plain text to a single stream (called pOut in your code) and do not close this before finally writing the last byte into the stream.
Hi guys I have to do this and I can encrypt file according to the des algorithm but I can not decyrpt again file ,I recieve error messaje like that :
javax.crypto.BadPaddingException Given final block not properly padded
I can not decrypt file I couldnt find why. Can u help me please
Thank you guys.
JAVA CODE :
public class Sifreleme {
public static void encrypt(){
try {
SecretKey key = KeyGenerator.getInstance("DES").generateKey();
FileOutputStream fosKey = new FileOutputStream("..\\KEY");
SecretKeyFactory keyfac = SecretKeyFactory.getInstance("DES");
DESKeySpec keyspec = (DESKeySpec) keyfac.getKeySpec(key, DESKeySpec.class);
fosKey.write(keyspec.getKey());
fosKey.close();
Cipher crypt = Cipher.getInstance("DES");
crypt.init(Cipher.ENCRYPT_MODE, key);
FileInputStream fis = new FileInputStream("C:\\Users\\akif\\Desktop\\zilsesi.mp3");
FileOutputStream fos = new FileOutputStream("C:\\Users\\akif\\Desktop\\sifrelenenzilsesi.mp3");
byte[] arrayBytes = new byte[8];
int bytesReads;
while ((bytesReads = fis.read(arrayBytes)) != -1) {
fos.write(crypt.doFinal(arrayBytes), 0, bytesReads);
}
fis.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void decrypt(){
try {
FileInputStream fisKey = new FileInputStream("..\\KEY");
byte[] arrayKey = new byte[fisKey.available()];
fisKey.read(arrayKey);
SecretKey key = new SecretKeySpec(arrayKey, "DES");
Cipher decrypt = Cipher.getInstance("DES");
decrypt.init(Cipher.DECRYPT_MODE, key);
FileInputStream fis = new FileInputStream("C:\\Users\\akif\\Desktop\\sifrelenenzilsesi.mp3");
byte[] encText = new byte[16];
int bytesReads;
while ((bytesReads = fis.read(encText)) != -1) {
fis.read(decrypt.doFinal(encText), 0, bytesReads);
}
fis.close();
System.out.println(new String(encText));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String []args) throws IOException{
encrypt();
decrypt();
}
Your code here:
while ((bytesReads = fis.read(encText)) != -1) {
fis.read(decrypt.doFinal(encText), 0, bytesReads);
}
Is rather obviously wrong: you need to write the plaintext generated by calling decrypt.doFinal just like you do during encryption. Currently you are overwriting the generated plaintext by the next block(s) of ciphertext because you call read twice in the loop.
Furthermore, depending on your DES Cipher implementation, you forgot about the IV.
A lot of other things are wrong as well, including:
the stream handling using getAvailable();
the use of the 56 bit DES cipher;
the use of ECB mode;
the repeated calls to doFinal (which results in a very large overhead and insecure code);
not using the CipherInputStream and CipherOutputStream (etcetera);
using a string as the key;
forgetting to close your streams when an exception occurs (use the try with resources);
the printStackTracke() exception handling;
the use of static fields as variables.
Using the platform encoding within new String(encText) is only likely wrong.
Note that using the wrong key / ciphertext combination will likely also result in this error.
I need to simply generate digital signature for a string value. I was trying to modificate this example, but has no success trying to make it work for string value. I ended up using this code snippet:
public static byte[] signData(byte[] data) throws Exception {
Provider bouncyCastle = new org.bouncycastle.jce.provider.BouncyCastleProvider();
PGPSecretKeyRingCollection secretKeyRings = new PGPSecretKeyRingCollection(new FileInputStream(new File("/path_to_binary.gpg")));
PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) secretKeyRings.getKeyRings().next();
PGPSecretKey secretKey = (PGPSecretKey) secretKeyRing.getSecretKeys().next();
PGPPrivateKey pgpPrivateKey = secretKey.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().setProvider(bouncyCastle).build("password".toCharArray()));
PGPSignatureGenerator sGen = new PGPSignatureGenerator(new JcaPGPContentSignerBuilder(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1).setProvider(bouncyCastle));
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
sGen.init(PGPSignature.STAND_ALONE, pgpPrivateKey);
Iterator userIDs = secretKey.getPublicKey().getUserIDs();
if (userIDs.hasNext()) {
spGen.setSignerUserID(false, (String)userIDs.next());
sGen.setHashedSubpackets(spGen.generate());
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
sGen.update(data);
baos.write(data);
sGen.generateOnePassVersion(false).encode(baos);
baos.flush();
baos.close();
return baos.toByteArray();
}
And got this result (string representation):
[B#50867e0f
What is obviously not a digital signature. What am I doing wrong?
In a larger application doing other things - I need to encrypt and decrypt a file. So I have been looking around and have implemented these two core functions that basically use RSA keys to wrap a random AES key that encrypts a file. The symmetric key and iv are written to the start of the file.
I'm getting an exception ("javax.crypto.BadPaddingException: Decryption error") in the decrypt function portion of below. On the unpackKeyandIV line -- the doFinal. Specifically this line is the Exception point:
Object[] keyIv = unpackKeyAndIV(xCipher.doFinal(keyBlock));
I've checked and remade the RSA key pairs. I've also checked the save/load of the keyBlock.
My gut is the problem has something to do with how I write/read the keyBlock --- or encoding perhaps?
One goal is to keep the RSA/AES instance as generic as possible so as not to need Bouncy Castle or extra Java security unlimited strength extensions.
Any thoughts on where I might be going wrong.
Thanks in advance.
[Final update: This code below is working. Error was passing in a corrupted privKey]
// RSA_INSTANCE = "RSA";
// ASSYM_CRYPTO_STR = 1024;
// SYM_CRYPTO_STR = 128;
// SYM_CRYPTO = "AES";
// AES_INSTANCE = "AES/CTR/NoPadding";
//
// File in = plain input file
// File out = encrypted output file
// Key pubKey = public Key (that wraps a random AES key)
public static void encryptFile(File in, File out, Key pubKey) throws Exception {
FileInputStream fin;
FileOutputStream fout;
int nread = 0;
byte[] inbuf = new byte[1024];
fout = new FileOutputStream(out);
fin = new FileInputStream(in);
SecureRandom random = new SecureRandom();
// symmetric wrapping
Key sKey = createKeyForAES(Config.SYM_CRYPTO_STR, random);
IvParameterSpec sIvSpec = createCtrIvForAES(0, random);
// encrypt symmetric key with RSA/pub key
Cipher xCipher = Cipher.getInstance(Config.RSA_INSTANCE);
xCipher.init(Cipher.ENCRYPT_MODE, pubKey, random);
byte[] keyBlock = xCipher.doFinal(packKeyAndIv(sKey, sIvSpec));
fout.write(keyBlock);
// encrypt data with symmetric key
Cipher sCipher = Cipher.getInstance(Config.AES_INSTANCE);
sCipher.init(Cipher.ENCRYPT_MODE, sKey, sIvSpec);
// Now read our file and encrypt it.
while((nread = fin.read(inbuf)) > 0) {
fout.write(sCipher.update(inbuf, 0, nread)); // cannot be null, by construction
}
// NB doFinal() cannot return null, but can return a zero-length array, which is benign below.
fout.write(sCipher.doFinal());
fout.flush();
fin.close();
fout.close();
}
// Decrypt File
public static void decryptFile(File in, File out, Key privKey) throws Exception {
FileInputStream fin;
FileOutputStream fout;
int nread = 0;
byte[] inbuf = new byte[1024];
fout = new FileOutputStream(out);
fin = new FileInputStream(in);
byte[] keyBlock = new byte[128];
nread = fin.read(keyBlock);
Cipher xCipher = Cipher.getInstance(Config.RSA_INSTANCE);
Cipher sCipher = Cipher.getInstance(Config.AES_INSTANCE);
// symmetric key/iv unwrapping step
xCipher.init(Cipher.DECRYPT_MODE, privKey);
Object[] keyIv = unpackKeyAndIV(xCipher.doFinal(keyBlock));
// decryption step
sCipher.init(Cipher.DECRYPT_MODE, (Key)keyIv[0], (IvParameterSpec)keyIv[1]);
while((nread = fin.read(inbuf)) >0) {
fout.write(sCipher.update(inbuf,0,nread));
}
fout.write(sCipher.doFinal());
fout.flush();
fin.close();
fout.close();
}
public static byte[] packKeyAndIv(Key key, IvParameterSpec ivSpec) throws IOException {
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
bOut.write(ivSpec.getIV());
bOut.write(key.getEncoded());
return bOut.toByteArray();
}
public static Object[] unpackKeyAndIV(byte[] data) {
byte[] keyD = new byte[16];
byte[] iv = new byte[data.length - 16];
return new Object[] {
new SecretKeySpec(data, 16, data.length - 16, "AES"),
new IvParameterSpec(data, 0, 16)
};
}
Per edits and comments. Error was a corrupted privKey being passed into the decrypt function. Above code works fine.
try adding the following under your constructor -
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());