/// <summary> /// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed. /// </summary> /// <remarks> /// Decrypts the specified encryptedData and extracts the digital signers if the content was also signed. /// </remarks> /// <returns>The decrypted stream.</returns> /// <param name="encryptedData">The encrypted data.</param> /// <param name="signatures">A list of digital signatures if the data was both signed and encrypted.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="encryptedData"/> is <c>null</c>. /// </exception> /// <exception cref="PrivateKeyNotFoundException"> /// The private key could not be found to decrypt the stream. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The user chose to cancel the password prompt. /// </exception> /// <exception cref="System.UnauthorizedAccessException"> /// 3 bad attempts were made to unlock the secret key. /// </exception> /// <exception cref="Org.BouncyCastle.Bcpg.OpenPgp.PgpException"> /// An OpenPGP error occurred. /// </exception> public Stream GetDecryptedStream (Stream encryptedData, out DigitalSignatureCollection signatures) { if (encryptedData == null) throw new ArgumentNullException ("encryptedData"); using (var armored = new ArmoredInputStream (encryptedData)) { var factory = new PgpObjectFactory (armored); var obj = factory.NextPgpObject (); var list = obj as PgpEncryptedDataList; if (list == null) { // probably a PgpMarker... obj = factory.NextPgpObject (); list = obj as PgpEncryptedDataList; if (list == null) throw new PgpException ("Unexpected OpenPGP packet."); } PgpPublicKeyEncryptedData encrypted = null; PrivateKeyNotFoundException pkex = null; bool hasEncryptedPackets = false; PgpSecretKey secret = null; foreach (PgpEncryptedData data in list.GetEncryptedDataObjects ()) { if ((encrypted = data as PgpPublicKeyEncryptedData) == null) continue; hasEncryptedPackets = true; try { secret = GetSecretKey (encrypted.KeyId); break; } catch (PrivateKeyNotFoundException ex) { pkex = ex; } } if (!hasEncryptedPackets) throw new PgpException ("No encrypted packets found."); if (secret == null) throw pkex; factory = new PgpObjectFactory (encrypted.GetDataStream (GetPrivateKey (secret))); List<IDigitalSignature> onepassList = null; PgpSignatureList signatureList = null; PgpCompressedData compressed = null; var memory = new MemoryBlockStream (); obj = factory.NextPgpObject (); while (obj != null) { if (obj is PgpCompressedData) { if (compressed != null) throw new PgpException ("Recursive compression packets are not supported."); compressed = (PgpCompressedData) obj; factory = new PgpObjectFactory (compressed.GetDataStream ()); } else if (obj is PgpOnePassSignatureList) { if (memory.Length == 0) { var onepasses = (PgpOnePassSignatureList) obj; onepassList = new List<IDigitalSignature> (); for (int i = 0; i < onepasses.Count; i++) { var onepass = onepasses[i]; var pubkey = PublicKeyRingBundle.GetPublicKey (onepass.KeyId); if (pubkey == null) { // too messy, pretend we never found a one-pass signature list onepassList = null; break; } onepass.InitVerify (pubkey); var signature = new OpenPgpDigitalSignature (pubkey, onepass) { PublicKeyAlgorithm = GetPublicKeyAlgorithm (onepass.KeyAlgorithm), DigestAlgorithm = GetDigestAlgorithm (onepass.HashAlgorithm), }; onepassList.Add (signature); } } } else if (obj is PgpSignatureList) { signatureList = (PgpSignatureList) obj; } else if (obj is PgpLiteralData) { var literal = (PgpLiteralData) obj; using (var stream = literal.GetDataStream ()) { var buffer = new byte[4096]; int nread; while ((nread = stream.Read (buffer, 0, buffer.Length)) > 0) { if (onepassList != null) { // update our one-pass signatures... for (int index = 0; index < nread; index++) { byte c = buffer[index]; for (int i = 0; i < onepassList.Count; i++) { var pgp = (OpenPgpDigitalSignature) onepassList[i]; pgp.OnePassSignature.Update (c); } } } memory.Write (buffer, 0, nread); } } } obj = factory.NextPgpObject (); } memory.Position = 0; if (signatureList != null) { if (onepassList != null && signatureList.Count == onepassList.Count) { for (int i = 0; i < onepassList.Count; i++) { var pgp = (OpenPgpDigitalSignature) onepassList[i]; pgp.CreationDate = signatureList[i].CreationTime; pgp.Signature = signatureList[i]; } signatures = new DigitalSignatureCollection (onepassList); } else { signatures = GetDigitalSignatures (signatureList, memory); memory.Position = 0; } } else { signatures = null; } return memory; } }
DigitalSignatureCollection GetDigitalSignatures (PgpSignatureList signatureList, Stream content) { var signatures = new List<IDigitalSignature> (); var buf = new byte[4096]; int nread; for (int i = 0; i < signatureList.Count; i++) { var pubkey = PublicKeyRingBundle.GetPublicKey (signatureList[i].KeyId); var signature = new OpenPgpDigitalSignature (pubkey, signatureList[i]) { PublicKeyAlgorithm = GetPublicKeyAlgorithm (signatureList[i].KeyAlgorithm), DigestAlgorithm = GetDigestAlgorithm (signatureList[i].HashAlgorithm), CreationDate = signatureList[i].CreationTime, }; if (pubkey != null) signatureList[i].InitVerify (pubkey); signatures.Add (signature); } var memory = content as MemoryStream; if (memory != null) { // We can optimize things a bit if we've got a memory stream... var buffer = memory.GetBuffer (); for (int index = (int) memory.Position; index < (int) memory.Length; index++) { byte c = buffer[index]; for (int i = 0; i < signatures.Count; i++) { if (signatures[i].SignerCertificate != null) { var pgp = (OpenPgpDigitalSignature) signatures[i]; pgp.Signature.Update (c); } } } } else { while ((nread = content.Read (buf, 0, buf.Length)) > 0) { for (int i = 0; i < signatures.Count; i++) { if (signatures[i].SignerCertificate != null) { var pgp = (OpenPgpDigitalSignature) signatures[i]; pgp.Signature.Update (buf, 0, nread); } } } } return new DigitalSignatureCollection (signatures); }
DigitalSignatureCollection GetDigitalSignatures (PgpSignatureList signatureList, Stream content) { var signatures = new List<IDigitalSignature> (); var buf = new byte[4096]; int nread; for (int i = 0; i < signatureList.Count; i++) { var pubkey = PublicKeyRingBundle.GetPublicKey (signatureList[i].KeyId); var signature = new OpenPgpDigitalSignature (pubkey, signatureList[i]) { PublicKeyAlgorithm = GetPublicKeyAlgorithm (signatureList[i].KeyAlgorithm), DigestAlgorithm = GetDigestAlgorithm (signatureList[i].HashAlgorithm), CreationDate = signatureList[i].CreationTime, }; if (pubkey != null) signatureList[i].InitVerify (pubkey); signatures.Add (signature); } while ((nread = content.Read (buf, 0, buf.Length)) > 0) { for (int i = 0; i < signatures.Count; i++) { if (signatures[i].SignerCertificate != null) { var pgp = (OpenPgpDigitalSignature) signatures[i]; pgp.Signature.Update (buf, 0, nread); } } } return new DigitalSignatureCollection (signatures); }