在尝试加密然后解密文件时,我遇到以下异常:
com.example.common.crypto.CipherException: org.bouncycastle.openpgp.PGPException: Exception starting decryption
at com.example.common.crypto.PGPFileCipher.decrypt(PGPFileCipher.java:151)
at com.example.common.crypto.PGPFileCipherTest.testEncryptDecryptFile(PGPFileCipherTest.java:41)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:43)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:76)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:62)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.executeTestSet(AbstractDirectoryTestSuite.java:140)
at org.apache.maven.surefire.suite.AbstractDirectoryTestSuite.execute(AbstractDirectoryTestSuite.java:127)
at org.apache.maven.surefire.Surefire.run(Surefire.java:177)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.maven.surefire.booter.SurefireBooter.runSuitesInProcess(SurefireBooter.java:345)
at org.apache.maven.surefire.booter.SurefireBooter.main(SurefireBooter.java:1009)
Caused by: org.bouncycastle.openpgp.PGPException: Exception starting decryption
at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
at org.bouncycastle.openpgp.PGPPublicKeyEncryptedData.getDataStream(Unknown Source)
at com.example.common.crypto.PGPFileCipher.decrypt(PGPFileCipher.java:128)
... 28 more
Caused by: java.io.EOFException: premature end of stream in PartialInputStream
at org.bouncycastle.bcpg.BCPGInputStream$PartialInputStream.read(Unknown Source)
at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source)
at java.io.InputStream.read(InputStream.java:82)
at javax.crypto.CipherInputStream.a(DashoA13*..)
at javax.crypto.CipherInputStream.read(DashoA13*..)
at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source)
... 31 more
我的 PGPFileCipher 类是:
package com.example.common.crypto;
public class PGPFileCipher implements FileCipher {
private static final Logger logger = LoggerFactory.getLogger(PGPFileCipher.class);
public File encryptFile(File inputFile, String encryptedFilePath, String publicKeyPath) throws CipherException {
FileInputStream publicKeyStream = null;
FileOutputStream encryptedFileOutputStream = null;
try {
publicKeyStream = new FileInputStream(new File(publicKeyPath));
File encryptedFile = new File(encryptedFilePath);
encryptedFileOutputStream = new FileOutputStream(encryptedFile);
encryptedFileOutputStream.write(encrypt(getBytesFromFile(inputFile), publicKeyStream, ""));
encryptedFileOutputStream.flush();
return encryptedFile;
} catch (Exception e) {
throw new CipherException(e);
} finally {
IOUtils.closeQuietly(encryptedFileOutputStream);
IOUtils.closeQuietly(publicKeyStream);
}
}
private PGPPrivateKey findSecretKey(PGPSecretKeyRingCollection pgpSec, long keyID, char[] pass) throws CipherException {
try {
PGPSecretKey pgpSecKey = pgpSec.getSecretKey(keyID);
if (pgpSecKey == null) {
return null;
}
return pgpSecKey.extractPrivateKey(pass, new BouncyCastleProvider());
} catch (Exception e) {
throw new CipherException(e);
}
}
/**
* decrypt the passed in message stream
*
* @param encrypted
* The message to be decrypted.
* @param keyIn
* InputStream of the key
*
* @return Clear text as a byte array. I18N considerations are not handled
* by this routine
* @exception CipherException
*/
public byte[] decrypt(byte[] encrypted, InputStream keyIn, char[] password) throws CipherException {
ByteArrayOutputStream out = null;
InputStream clear = null;
InputStream in = null;
InputStream unc = null;
try {
in = new ByteArrayInputStream(encrypted);
in = PGPUtil.getDecoderStream(in);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc = null;
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));
while (sKey == null && it.hasNext()) {
pbe = (PGPPublicKeyEncryptedData) it.next();
sKey = findSecretKey(pgpSec, pbe.getKeyID(), password);
}
if (sKey == null) {
throw new IllegalArgumentException(
"secret key for message not found.");
}
clear = pbe.getDataStream(sKey, new BouncyCastleProvider());
PGPObjectFactory pgpFact = new PGPObjectFactory(clear);
PGPCompressedData cData = (PGPCompressedData) pgpFact.nextObject();
pgpFact = new PGPObjectFactory(cData.getDataStream());
PGPLiteralData ld = (PGPLiteralData) pgpFact.nextObject();
unc = ld.getInputStream();
out = new ByteArrayOutputStream();
int ch;
while ((ch = unc.read()) >= 0) {
out.write(ch);
}
out.flush();
byte[] returnBytes = out.toByteArray();
return returnBytes;
} catch (Exception e) {
e.printStackTrace();
throw new CipherException(e);
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(clear);
IOUtils.closeQuietly(unc);
}
}
/**
* Simple PGP encryptor between byte[].
*
* @param clearData
* The test to be encrypted
* @param keyInputStream
* Input stream of the key
* @param fileName
* File name. This is used in the Literal Data Packet (tag 11)
* which is really inly important if the data is to be related to
* a file to be recovered later. Because this routine does not
* know the source of the information, the caller can set
* something here for file name use that will be carried. If this
* routine is being used to encrypt SOAP MIME bodies, for
* example, use the file name from the MIME type, if applicable.
* Or anything else appropriate.
*
* @return encrypted data.
* @exception CipherException
*/
public byte[] encrypt(byte[] clearData, InputStream keyInputStream, String fileName)
throws CipherException {
OutputStream out = null;
OutputStream cOut = null;
PGPCompressedDataGenerator comData = null;
PGPLiteralDataGenerator lData = null;
ByteArrayOutputStream encOut = null;
ByteArrayOutputStream bOut = null;
OutputStream cos = null;
OutputStream pOut = null;
try {
PGPPublicKey encKey = readPublicKey(keyInputStream);
if (fileName == null || fileName.trim().length() < 1) {
fileName = PGPLiteralData.CONSOLE;
}
encOut = new ByteArrayOutputStream();
out = encOut;
bOut = new ByteArrayOutputStream();
comData = new PGPCompressedDataGenerator(
PGPCompressedDataGenerator.ZIP);
cos = comData.open(bOut); // open it with the final
// destination
lData = new PGPLiteralDataGenerator();
// we want to generate compressed data. This might be a user option
// later,
// in which case we would pass in bOut.
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);
pOut.flush();
PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(
PGPEncryptedData.CAST5, false, new SecureRandom(),
new BouncyCastleProvider());
cPk.addMethod(encKey);
byte[] bytes = bOut.toByteArray();
cOut = cPk.open(out, bytes.length);
cOut.write(bytes); // obtain the actual bytes from the compressed stream
cOut.flush();
encOut.flush();
return encOut.toByteArray();
} catch (Exception e) {
throw new CipherException(e);
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(cOut);
IOUtils.closeQuietly(encOut);
IOUtils.closeQuietly(bOut);
IOUtils.closeQuietly(cos);
IOUtils.closeQuietly(pOut);
try {
if (lData != null) {
lData.close();
}
if (comData != null) {
comData.close();
}
} catch (IOException ignored) {}
}
}
private PGPPublicKey readPublicKey(InputStream in)
throws CipherException {
try {
in = 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.
//
//
// iterate through the key rings.
//
Iterator rIt = pgpPub.getKeyRings();
while (rIt.hasNext()) {
PGPPublicKeyRing kRing = (PGPPublicKeyRing) rIt.next();
Iterator kIt = kRing.getPublicKeys();
while (kIt.hasNext()) {
PGPPublicKey k = (PGPPublicKey) kIt.next();
if (k.isEncryptionKey()) {
return k;
}
}
}
throw new IllegalArgumentException("Can't find encryption key in key ring.");
} catch (Exception e) {
throw new CipherException(e);
}
}
private byte[] getBytesFromFile(File file) throws CipherException {
InputStream is = null;
try {
is = new FileInputStream(file);
// Get the size of the file
long length = file.length();
if (length > Integer.MAX_VALUE) {
throw new CipherException("File is too large: " + file.getName());
}
return IOUtils.toByteArray(is);
} catch (IOException e) {
throw new CipherException(e);
} finally {
IOUtils.closeQuietly(is);
}
}
}
我试图运行的测试是:
package com.example.common.crypto;
public class PGPFileCipherTest {
private static final Logger logger = LoggerFactory.getLogger(PGPFileCipherTest.class);
@Rule
public TemporaryFolder folder = new TemporaryFolder();
@Test
public void testEncryptDecryptFile() throws IOException, CipherException {
FileOutputStream decryptedFileOutputStream = null;
InputStream privateKeyStream = null;
try {
PGPFileCipher fileCipher = new PGPFileCipher();
Resource inputFile = new ClassPathResource("testInputFile.txt");
Resource publicKey = new ClassPathResource("PUBLIC.asc");
Resource privateKey = new ClassPathResource("PRIVATE.asc");
// Encrypt file
File encryptedFile = fileCipher.encryptFile(inputFile.getFile(), folder.newFile("testInputFile_enc.txt").getPath(), publicKey.getFile().getPath());
// Now decrypt the file
File decryptedFile = folder.newFile("testInputFile_dec.txt");
decryptedFileOutputStream = new FileOutputStream(decryptedFile);
privateKeyStream = new FileInputStream(privateKey.getFile());
decryptedFileOutputStream.write(fileCipher.decrypt(FileUtils.readFileToByteArray(encryptedFile), privateKeyStream, "".toCharArray()));
decryptedFileOutputStream.flush();
} finally {
IOUtils.closeQuietly(decryptedFileOutputStream);
IOUtils.closeQuietly(privateKeyStream);
}
}
}
不幸的是,我对 BouncyCastle 并不是很熟悉,而且似乎没有正确关闭/刷新流,但我似乎无法追踪它。这是 BouncyCastle 示例之一的修改版本,仅供参考。在此先感谢您的帮助。