2013-10-04 18 views
6

Sto tentando di decrittografare e verificare un messaggio PGP utilizzando le librerie java BouncyCastle, ma sto riscontrando problemi, lamentando la fine prematura di PartialInputStream.Bouncycastle PGP decifrare e verificare

So che la crittografia funziona correttamente, perché posso decodificare e verificare i messaggi creati con la funzione di crittografia utilizzando gpg sulla riga di comando.

Ecco il codice:

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

Dopo un paio di basi di message = plainFact.nextObject(); viene generata l'eccezione:

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

Tutte le idee?

Nota a margine, questo codice di decrittografia è venuto da How to decrypt a signed pgp encrypted file?, leggermente modificato per soddisfare: i messaggi arriveranno solo da tale metodo di crittografia e la gestione delle chiavi direttamente anziché i flussi di chiavi.

Acclamazioni

Ramo

risposta

8

Recentemente sono stato cercando di fare lo stesso tipo di cose e mettere insieme questo metodo basato sul codice che ho trovato negli esempi BouncyCastle e tutorial che ho trovato sul web. Per i miei scopi, il mio codice ha un oggetto crypto singleton che ha una coppia di chiavi pubblica/privata. Nel codice di esempio, è possibile sostituire

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

con la chiave segreta. Ho provato questo metodo con un processo di lunga durata che ha fatto diverse decine di crittografie & firmare/decifrare le azioni di verifica & e non ha ottenuto l'eccezione che stai vedendo.

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

Curiosità: Porting questo codice in C# in realtà funzionato meglio di qualsiasi delle attuali C# esempi per fare Questo. –

1

Si sta chiamando:

encryptedDataGenerator.open(out, 4096) 

dove probabilmente volevi dire:

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

La prima versione è dare una dimensione per aprire (che è sbagliato), la seconda versione è dare un buffer di byte.

(so che questo è vecchio, ma è venuto qui perché ho avuto lo stesso problema con qualche esempio di codice e così potrebbe altri)

3

Ottenere Castello gonfiabile per giocare insieme non è sempre facile. Code-snipets di Stackoverflow lo rendono funziona solo così, ma sono per lo più pezzi di codice arcano che nessuno degli utenti realmente capisce.

Il problema è: in un sistema di produzione frammenti Copy'n'Paste rapidamente diventato "no go" e aree "non toccare".

Sborsare eseguibili possono avere alcune implicazioni di sicurezza gravi, meno di tutti è la gestione dei parametri della riga di comando (si parla di nomi di file con spazi ... come faccio a sapere? Non chiedere ...)

I ho avuto tutti questi (e più ..) problemi, e dopo qualche rasatura yak ho scritto una libreria per gestire PGP con Bouncycastle.

Decriptare funziona così:

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

La biblioteca si possono trovare presso 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

Ciao Jens, sto usando la tua libreria ed è davvero buona. Sto riscontrando questo problema anche se ho crittografato il file e firmato con un utente [email protected] e poi quando ho provato a decrittografarlo usando Open PGP di Mac, dice che la verifica non è riuscita per [email protected] anche se il file è stato decodificato. Potete aiutare plza –

+0

Si prega di creare un problema nel bugtracker di Github e collegarlo qui (in modo che possiamo risolvere il problema nel tracker e aggiornare la risposta qui in seguito) – Jens

+0

Dopo aver combattuto con Castello Bouncy negli ultimi giorni, posso verifica che questa libreria sia drogata. –

Problemi correlati