2013-10-04 35 views
6

Próbuję odszyfrować i zweryfikować wiadomość PGP przy użyciu bibliotek Java BouncyCastle, ale mam problemy, narzekając na przedwczesne zakończenia PartialInputStream.Bouncycastle PGP odszyfrowuj i zweryfikuj

Wiem, że szyfrowanie działa dobrze, ponieważ mogę odszyfrować i zweryfikować wiadomości utworzone za pomocą funkcji szyfrowania za pomocą polecenia gpg w wierszu poleceń.

Oto kod:

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(); 
     } 
    } 

Po kilku przebiegów message = plainFact.nextObject(); jest wyjątek:

-----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) 

Jakieś pomysły?

Nota boczna, kod odszyfrowujący pochodzi z How to decrypt a signed pgp encrypted file?, nieznacznie zmodyfikowany, aby odpowiadał: Wiadomości będą pochodziły tylko z tej metody szyfrowania i obsługi kluczy bezpośrednio zamiast kluczowych strumieni.

Cheers

Ramo

Odpowiedz

8

Byłem niedawno stara się zrobić tego samego rodzaju rzeczy i połączyć tę metodę opartą na kodzie znalazłem w przykładach BouncyCastle i samouczków znalazłem w internecie. Dla moich celów mój kod ma pojedynczy obiekt kryptograficzny, który ma parę kluczy publiczny/prywatny. W przykładowym kodzie możesz zamienić w swoich tajnych kluczach. Przetestowałem tę metodę z długowiecznym procesem, który wykonał kilka dziesiątek szyfrowania & szyfrując/odszyfrowując & weryfikując akcje i nie dostał wyjątku, który widzisz.

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

Zabawa Fakt: Przenoszenie ten kod C# faktycznie pracował lepiej niż którykolwiek z rzeczywistych C# przykłady dla prowadzenia to. –

1

Telefonujesz:

encryptedDataGenerator.open(out, 4096) 

gdzie prawdopodobnie oznaczało:

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

Pierwsza wersja daje rozmiar, aby otworzyć (co jest źle), druga wersja jest dając bufor bajtowy.

(wiem, że to jest stary, ale przyszedł tutaj, bo miałem ten sam problem z jakimś przykładowym kodem a więc może inne)

3

Pierwsze nadmuchiwany zamek grać razem nie zawsze jest łatwe. Fragmenty kodu z Stackoverflow sprawiają, że działa tak samo, jak, ale są to przeważnie tajemne fragmenty kodu, których żaden z użytkowników naprawdę nie rozumie.

Problem z tym jest: w systemie produkcji copy'n'paste fragmentów szybko stają się „No Go” i obszary „Nie dotykać”.

ostrzał wykonywalne mogą mieć poważne konsekwencje dla bezpieczeństwa, najmniej jest obsługa parametrów wiersza poleceń (mówimy o nazwach plików ze spacjami ... skąd mam wiedzieć? Nie pytajcie ...)

I Miałem wszystkie te (i więcej ..) problemy, a po jakimś goleniu napisałem bibliotekę do obsługi PGP z Bouncycastle.

odszyfrowywania działa tak:

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

Bibliotekę można znaleźć na 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

Witam Jens, używam twojej biblioteki i jest naprawdę dobra. Mam problem z tym, że zaszyfrowałem plik i podpisałem go na stronie [email protected], a następnie, gdy próbowałem odszyfrować za pomocą Open PGP mac, stwierdziłem, że weryfikacja nie powiodła się dla [email protected], mimo że plik został odszyfrowany. Czy możesz pomóc plz –

+0

Proszę utworzyć problem w bugtracker Github i połączyć go tutaj (abyśmy mogli rozwiązać problem w trackerze i zaktualizować odpowiedź tutaj później) – Jens

+0

Po walce z Bouncy Castle przez ostatnie kilka dni, mogę sprawdź, czy ta biblioteka jest dobra. –