2013-10-04 109 views
6

我試圖解密和驗證使用Java BouncyCastle庫的PGP消息,但我遇到了問題,抱怨PartialInputStream的過早結束。Bouncycastle PGP解密並驗證

我知道加密工作正常,因爲我可以在命令行上使用gpg解密和驗證使用加密函數創建的消息。

下面的代碼:

public static void signEncryptMessage(InputStream in, OutputStream out, PGPPublicKey publicKey, PGPPrivateKey secretKey, SecureRandom rand) throws Exception { 
     out = new ArmoredOutputStream(out); 

     PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(new BcPGPDataEncryptorBuilder(PGPEncryptedData.AES_256).setWithIntegrityPacket(true).setSecureRandom(rand)); 
     encryptedDataGenerator.addMethod(new BcPublicKeyKeyEncryptionMethodGenerator(publicKey)); 

     OutputStream compressedOut = new PGPCompressedDataGenerator(PGPCompressedData.ZIP).open(encryptedDataGenerator.open(out, 4096), new byte[4096]); 

     PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(new BcPGPContentSignerBuilder(publicKey.getAlgorithm(), HashAlgorithmTags.SHA512)); 
     signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, secretKey); 
     signatureGenerator.generateOnePassVersion(true).encode(compressedOut); 

     OutputStream finalOut = new PGPLiteralDataGenerator().open(compressedOut, PGPLiteralData.BINARY, "", new Date(), new byte[4096]); 

     byte[] buf = new byte[4096]; 
     int len; 
     while ((len = in.read(buf)) > 0) { 
      finalOut.write(buf, 0, len); 
      signatureGenerator.update(buf, 0, len); 
     } 

     finalOut.close(); 
     in.close(); 
     signatureGenerator.generate().encode(compressedOut); 
     compressedOut.close(); 
     encryptedDataGenerator.close(); 
     out.close(); 
    } 

    public static void decryptVerifyMessage(InputStream in, OutputStream out, PGPPrivateKey secretKey, PGPPublicKey publicKey) throws Exception { 
     in = new ArmoredInputStream(in); 

     PGPObjectFactory pgpF = new PGPObjectFactory(in); 
     PGPEncryptedDataList enc = (PGPEncryptedDataList) pgpF.nextObject(); 

     PGPObjectFactory plainFact = new PGPObjectFactory(((PGPPublicKeyEncryptedData) enc.getEncryptedDataObjects().next()).getDataStream(new JcePublicKeyDataDecryptorFactoryBuilder().setProvider("BC").build(secretKey))); 

     Object message = null; 

     PGPOnePassSignatureList onePassSignatureList = null; 
     PGPSignatureList signatureList = null; 
     PGPCompressedData compressedData = null; 

     message = plainFact.nextObject(); 
     ByteArrayOutputStream actualOutput = new ByteArrayOutputStream(); 

     while (message != null) { 
      System.out.println(message.toString()); 
      if (message instanceof PGPCompressedData) { 
       compressedData = (PGPCompressedData) message; 
       plainFact = new PGPObjectFactory(compressedData.getDataStream()); 
       message = plainFact.nextObject(); 
       System.out.println(message.toString()); 
      } 

      if (message instanceof PGPLiteralData) { 
       Streams.pipeAll(((PGPLiteralData) message).getInputStream(), actualOutput); 
      } else if (message instanceof PGPOnePassSignatureList) { 
       onePassSignatureList = (PGPOnePassSignatureList) message; 
      } else if (message instanceof PGPSignatureList) { 
       signatureList = (PGPSignatureList) message; 
      } else { 
       throw new PGPException("message unknown message type."); 
      } 
      message = plainFact.nextObject(); 
     } 
     actualOutput.close(); 
     byte[] output = actualOutput.toByteArray(); 
     if (onePassSignatureList == null || signatureList == null) { 
      throw new PGPException("Poor PGP. Signatures not found."); 
     } else { 

      for (int i = 0; i < onePassSignatureList.size(); i++) { 
       PGPOnePassSignature ops = onePassSignatureList.get(0); 
       System.out.println("verifier : " + ops.getKeyID()); 
       if (publicKey != null) { 
        ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), publicKey); 
        ops.update(output); 
        PGPSignature signature = signatureList.get(i); 
        if (ops.verify(signature)) { 
         Iterator<?> userIds = publicKey.getUserIDs(); 
         while (userIds.hasNext()) { 
          String userId = (String) userIds.next(); 
          System.out.println("Signed by " + userId); 
         } 
         System.out.println("Signature verified"); 
        } else { 
         throw new SignatureException("Signature verification failed"); 
        } 
       } 
      } 

     } 

     out.write(output); 
     out.flush(); 
     out.close(); 
    } 

    public static void main(String args[]) { 
     Security.insertProviderAt(new BouncyCastleProvider(), 0); 
     byte inBytes[] = "The quick brown fox jumps over the lazy dog.".getBytes(); 

     try { 
      SecureRandom rand = new SecureRandom(); 

      RSAKeyPairGenerator kpg = new RSAKeyPairGenerator(); 
      kpg.init(new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), rand, 1024, 90)); 

      BcPGPKeyPair sender = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL, kpg.generateKeyPair(), new Date()); 
      BcPGPKeyPair recip = new BcPGPKeyPair(PGPPublicKey.RSA_GENERAL, kpg.generateKeyPair(), new Date()); 

      ByteArrayOutputStream sendMessage = new ByteArrayOutputStream(); 
      ByteArrayOutputStream recvMessage = new ByteArrayOutputStream(); 
      signEncryptMessage(new ByteArrayInputStream(inBytes), sendMessage, recip.getPublicKey(), sender.getPrivateKey(), rand); 

      System.out.println(sendMessage.toString()); 

      decryptVerifyMessage(new ByteArrayInputStream(sendMessage.toByteArray()), recvMessage, recip.getPrivateKey(), sender.getPublicKey()); 

      System.out.println(recvMessage.toString()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

message = plainFact.nextObject();幾運行後拋出異常:

-----BEGIN PGP MESSAGE----- 
Version: BCPG v1.49 

hIwDbgERMnl/xpUBA/98O/by9Ib6/nzXiYWuwT2CYulTqzcY07iuHKB4KQc6m+H1 
ZBVAx+HozgxQXQdQcBTcp+YS7Xn3tsReiH28Z9805f65tmASoqrzdf35qiVgFhfA 
CbCfIq7cqC4rKut3Y8pNOs1mmhpeVNa+AqTZ1r46uyuloBTllI8OWzWoxjTcZdLP 
aQHe2BQnfYk+dFgXZ2LMBMtL9mcsEqRLWIdisJQ4gppyIbQNNE7q5gV1Es63yVoM 
3dpfYHxlnIZASuynSZyGorHpYMV6tWNwSRQ9Ziwaw4DwvQGyAHpb1O/tLqrfjLqN 
5dj5qNY6nElT1EM94Dd4FOBzI6x6JkfuCH3/Jp8lCA/p8K7jmYu9Xvdld8BgHmRF 
ymasPf1JC4xYFa9YQVnn4fK2l//2iVcVayv0On32kxD9XfkPUysYVH38glPaHb48 
qWk9i/x0Y3mmCy1RVAGWqimR5DEhZPubJ+Kjk3UsB1m90Pm/6a+/ZfpAEHcxshdX 
JeVBr7aQIX3PQIUl+ZPQsgAGEmo0abQVufuKfkfjX0Gh 
=ApMf 
-----END PGP MESSAGE----- 

[email protected] 
[email protected] 
[email protected] 
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:101) 
    at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:103) 
    at javax.crypto.CipherInputStream.read(CipherInputStream.java:177) 
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source) 
    at org.bouncycastle.openpgp.PGPEncryptedData$TruncatedStream.read(Unknown Source) 
    at java.io.InputStream.read(InputStream.java:170) 
    at org.bouncycastle.util.io.TeeInputStream.read(Unknown Source) 
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source) 
    at org.bouncycastle.bcpg.BCPGInputStream$PartialInputStream.read(Unknown Source) 
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source) 
    at org.bouncycastle.openpgp.PGPCompressedData$1.fill(Unknown Source) 
    at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158) 
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source) 
    at org.bouncycastle.bcpg.BCPGInputStream$PartialInputStream.read(Unknown Source) 
    at org.bouncycastle.bcpg.BCPGInputStream.read(Unknown Source) 
    at org.bouncycastle.util.io.Streams.readFully(Unknown Source) 
    at org.bouncycastle.bcpg.BCPGInputStream.readFully(Unknown Source) 
    at org.bouncycastle.bcpg.BCPGInputStream.readFully(Unknown Source) 
    at org.bouncycastle.bcpg.MPInteger.<init>(Unknown Source) 
    at org.bouncycastle.bcpg.SignaturePacket.<init>(Unknown Source) 
    at org.bouncycastle.bcpg.BCPGInputStream.readPacket(Unknown Source) 
    at org.bouncycastle.openpgp.PGPSignature.<init>(Unknown Source) 
    at org.bouncycastle.openpgp.PGPObjectFactory.nextObject(Unknown Source) 
    at main.decryptVerifyMessage(main.java:113) 
    at main.main(main.java:167) 

任何想法?

注意,這個解密代碼來自How to decrypt a signed pgp encrypted file?,稍作修改以適應:消息只會來自該加密方法,並且直接處理密鑰而不是密鑰流。

乾杯

拉莫

回答

8

最近,我試圖做同樣的事放在一起基於代碼這個方法我在BouncyCastle的例子和教程,我在網上找到找到。爲了我的目的,我的代碼有一個單身密碼對象,它具有公鑰/私鑰對。在示例代碼中,您可以用您的密鑰替換

INSTANCE._secretKeyRingCollection.getSecretKey(pbe.getKeyID()); 

。我已經用長壽命的過程測試了這種方法,該過程做了幾十次加密&簽名/解密&驗證操作,並沒有得到你所看到的異常。

public static void decryptAndVerify(InputStream in, OutputStream fOut, InputStream publicKeyIn) throws IOException, SignatureException, PGPException { 
    in = 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(); 
     PBESecretKeyDecryptor decryptor = new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()).build(INSTANCE._secretKeyPass.toCharArray()); 
     PGPSecretKey psKey = INSTANCE._secretKeyRingCollection.getSecretKey(pbe.getKeyID()); 
     if (psKey != null) { 
      sKey = psKey.extractPrivateKey(decryptor); 
     } 
    } 
    if (sKey == null) { 
     throw new IllegalArgumentException("Unable to find secret key to decrypt the message"); 
    } 

    InputStream clear = pbe.getDataStream(new BcPublicKeyDataDecryptorFactory(sKey)); 

    PGPObjectFactory plainFact = new PGPObjectFactory(clear); 

    Object message; 

    PGPOnePassSignatureList onePassSignatureList = null; 
    PGPSignatureList signatureList = null; 
    PGPCompressedData compressedData; 

    message = plainFact.nextObject(); 
    ByteArrayOutputStream actualOutput = new ByteArrayOutputStream(); 

    while (message != null) { 
     __l.trace(message.toString()); 
     if (message instanceof PGPCompressedData) { 
      compressedData = (PGPCompressedData) message; 
      plainFact = new PGPObjectFactory(compressedData.getDataStream()); 
      message = plainFact.nextObject(); 
     } 

     if (message instanceof PGPLiteralData) { 
      // have to read it and keep it somewhere. 
      Streams.pipeAll(((PGPLiteralData) message).getInputStream(), actualOutput); 
     } else if (message instanceof PGPOnePassSignatureList) { 
      onePassSignatureList = (PGPOnePassSignatureList) message; 
     } else if (message instanceof PGPSignatureList) { 
      signatureList = (PGPSignatureList) message; 
     } else { 
      throw new PGPException("message unknown message type."); 
     } 
     message = plainFact.nextObject(); 
    } 
    actualOutput.close(); 
    PGPPublicKey publicKey = null; 
    byte[] output = actualOutput.toByteArray(); 
    if (onePassSignatureList == null || signatureList == null) { 
     throw new PGPException("Poor PGP. Signatures not found."); 
    } else { 

     for (int i = 0; i < onePassSignatureList.size(); i++) { 
      PGPOnePassSignature ops = onePassSignatureList.get(0); 
      __l.trace("verifier : " + ops.getKeyID()); 
      PGPPublicKeyRingCollection pgpRing = new PGPPublicKeyRingCollection(
        PGPUtil.getDecoderStream(publicKeyIn)); 
      publicKey = pgpRing.getPublicKey(ops.getKeyID()); 
      if (publicKey != null) { 
       ops.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), publicKey); 
       ops.update(output); 
       PGPSignature signature = signatureList.get(i); 
       if (ops.verify(signature)) { 
        Iterator<?> userIds = publicKey.getUserIDs(); 
        while (userIds.hasNext()) { 
         String userId = (String) userIds.next(); 
         __l.trace(String.format("Signed by {%s}", userId)); 
        } 
        __l.trace("Signature verified"); 
       } else { 
        throw new SignatureException("Signature verification failed"); 
       } 
      } 
     } 

    } 

    if (pbe.isIntegrityProtected() && !pbe.verify()) { 
     throw new PGPException("Data is integrity protected but integrity is lost."); 
    } else if (publicKey == null) { 
     throw new SignatureException("Signature not found"); 
    } else { 
     fOut.write(output); 
     fOut.flush(); 
     fOut.close(); 
    } 
} 
+4

有趣的事實:移植的代碼到C#實際做比任何實際的C#示例更好地工作這個。 –

1

要調用:

encryptedDataGenerator.open(out, 4096) 

,你可能是指:

encryptedDataGenerator.open(out, new byte[4096]) 

第一個版本是給大小打開(這是錯誤的),第二個版本是給一個字節緩衝區。

(我知道這是舊的,但來到這裏是因爲我有一些示例代碼,同樣的問題,所以可能其他人)

3

獲取充氣城堡沿着發揮並不總是那麼容易。來自Stackoverflow的代碼嗅探使其的工作原理如此,但它們大部分都是神祕的代碼片段,沒有一個用戶真正理解。

與此問題是:在生產系統copy'n'paste snippets迅速成爲「不去」和「不接觸」領域。

脫殼而出的可執行文件可以有一些嚴重的安全隱患,至少所有的正在處理命令行參數(談論與空格的文件名...我怎麼知道?不要問...)

我出現了所有這些(以及更多)問題,經過一些犛牛剃毛後,我寫了一個圖書館來處理Bouncycastle的PGP。

解密是這樣工作的:

final InputStream plaintextStream = BouncyGPG 
       .decryptAndVerifyStream() 
       .withConfig(keyringConfig) 
       .andRequireSignatureFromAllKeys("[email protected]") 
       .fromEncryptedInputStream(cipherTextStream) 

庫可以在https://github.com/neuhalje/bouncy-gpg發現:

// in build.gradle add a dependency to bouncy castle and bouncy-gpg 
// ... 
dependencies { 
    compile 'org.bouncycastle:bcprov-jdk15on:1.56' 
    compile 'org.bouncycastle:bcpg-jdk15on:1.56' 
    // ... 
    compile 'name.neuhalfen.projects.crypto.bouncycastle.openpgp:bouncy-gpg:2.+' 
+0

你好Jens,我正在使用你的圖書館,它非常好。我遇到了這個問題,雖然我在哪裏加密文件並用用戶[email protected]簽名,然後當我試圖使用mac的Open PGP進行解密時,它表示驗證失敗,即使該文件被解密,但仍爲[email protected]。你能幫忙嗎PLZ –

+0

請在Github bugtracker中創建一個問題並將其鏈接到此處(以便我們可以解決跟蹤器中的問題並在此後更新答案) – Jens

+0

過去幾天與Bouncy Castle進行過戰鬥,我可以驗證這個庫是塗料。 –