private void PrepareEncryptionDictRev2345(string ownerPassword, string userPassword, PdfEncryption encryptionDictionary, int permissionInt, Document document, int revision, int length) { var idArray = document.File.ID; //check if the document has an id yet. If it does not then generate one if (idArray == null || idArray.BaseDataObject.Count < 2) { DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); using (var md = MD5.Create()) { BigInteger time = new BigInteger((DateTime.UtcNow - Jan1st1970).TotalMilliseconds.ToString()); md.Update(time.ToByteArray()); md.Update(Charset.ISO88591.GetBytes(ownerPassword)); md.Update(Charset.ISO88591.GetBytes(userPassword)); md.Update(Charset.ISO88591.GetBytes(document.Information.ToString())); var finBlock = Charset.ISO88591.GetBytes(this.ToString()); PdfString idString = new PdfString(md.Digest(finBlock)); idArray = new FileIdentifier(); idArray.BaseID = idString; idArray.VersionID = idString; document.File.ID = idArray; } } PdfString id = idArray.BaseID; byte[] ownerBytes = ComputeOwnerPassword( Charset.ISO88591.GetBytes(ownerPassword), Charset.ISO88591.GetBytes(userPassword), revision, length); byte[] userBytes = ComputeUserPassword( Charset.ISO88591.GetBytes(userPassword), ownerBytes, permissionInt, id.GetBuffer(), revision, length, true); encryptionKey = ComputeEncryptedKey(Charset.ISO88591.GetBytes(userPassword), ownerBytes, null, null, null, permissionInt, id.GetBuffer(), revision, length, true, false); encryptionDictionary.OwnerKey = ownerBytes; encryptionDictionary.UserKey = userBytes; if (revision == 4) { PrepareEncryptionDictAES(encryptionDictionary, PdfName.AESV2); } }
/** * This will encrypt a string. * * @param string the string to encrypt. * @param objNum The object number. * @param genNum The object generation number. * * @throws IOException If an error occurs writing the new string. */ public void EncryptString(PdfString pdfString, long objNum, int genNum) { using (var data = new MemoryStream(pdfString.GetBuffer())) using (var buffer = new MemoryStream()) { if (EncryptData(objNum, genNum, data, buffer, false /* encrypt */)) { pdfString.SetBuffer(buffer.GetBuffer()); } } }
private byte[] GetDocumentIDBytes(PdfArray documentIDArray) { //some documents may not have document id, see //test\encryption\encrypted_doc_no_id.pdf byte[] documentIDBytes; if (documentIDArray != null && documentIDArray.Count >= 1) { PdfString id = (PdfString)documentIDArray.Resolve(0); documentIDBytes = id.GetBuffer(); } else { documentIDBytes = new byte[0]; } return(documentIDBytes); }
/** * This will decrypt a string. * * @param string the string to decrypt. * @param objNum The object number. * @param genNum The object generation number. * * @throws IOException If an error occurs writing the new string. */ private void DecryptString(PdfString pdfString, long objNum, long genNum) { // String encrypted with identity filter if (PdfName.Identity.Equals(stringFilterName)) { return; } using (var data = new MemoryStream(pdfString.GetBuffer())) using (var outputStream = new MemoryStream()) { try { if (EncryptData(objNum, genNum, data, outputStream, true /* decrypt */)) { pdfString.SetBuffer(outputStream.ToArray()); } } catch (Exception ex) { Debug.WriteLine($"error: Failed to decrypt PdfString of Length {pdfString.GetBuffer().Length} in object {objNum}: {ex.Message}", ex); } } }
/** * Prepares everything to decrypt the document. * * @param encryption encryption dictionary, can be retrieved via * {@link Document#getEncryption()} * @param documentIDArray document id which is returned via * {@link org.apache.pdfbox.cos.COSDocument#getDocumentID()} (not used by * this handler) * @param decryptionMaterial Information used to decrypt the document. * * @throws IOException If there is an error accessing data. If verbose mode * is enabled, the exception message will provide more details why the * match wasn't successful. */ public override void PrepareForDecryption(PdfEncryption encryption, PdfArray documentIDArray, DecryptionMaterial decryptionMaterial) { if (!(decryptionMaterial is PublicKeyDecryptionMaterial)) { throw new IOException( "Provided decryption material is not compatible with the document"); } SetDecryptMetadata(encryption.IsEncryptMetaData); if (encryption.Length != 0) { this.keyLength = encryption.Length; } PublicKeyDecryptionMaterial material = (PublicKeyDecryptionMaterial)decryptionMaterial; try { bool foundRecipient = false; //Org.BouncyCastle.X509.Extension. X509Certificate certificate = material.Certificate; X509CertificateEntry materialCert = null; if (certificate != null) { materialCert = new X509CertificateEntry(certificate); } // the decrypted content of the enveloped data that match // the certificate in the decryption material provided byte[] envelopedData = null; // the bytes of each recipient in the recipients array PdfArray array = (PdfArray)encryption.BaseDataObject.Resolve(PdfName.Recipients); if (array == null) { PdfCryptFilterDictionary defaultCryptFilterDictionary = encryption.DefaultCryptFilterDictionary; array = (PdfArray)defaultCryptFilterDictionary.BaseDataObject.Resolve(PdfName.Recipients); } byte[][] recipientFieldsBytes = new byte[array.Count][]; //TODO encryption.getRecipientsLength() and getRecipientStringAt() should be deprecated int recipientFieldsLength = 0; StringBuilder extraInfo = new StringBuilder(); for (int i = 0; i < array.Count; i++) { PdfString recipientFieldString = (PdfString)array.Resolve(i); byte[] recipientBytes = recipientFieldString.GetBuffer(); CmsEnvelopedData data = new CmsEnvelopedData(recipientBytes); var recipCertificatesIt = data.GetRecipientInfos().GetRecipients(); int j = 0; foreach (RecipientInformation ri in recipCertificatesIt) { // Impl: if a matching certificate was previously found it is an error, // here we just don't care about it RecipientID rid = ri.RecipientID; if (!foundRecipient && rid.Match(materialCert)) { foundRecipient = true; var privateKey = material.PrivateKey; // might need to call setContentProvider() if we use PKI token, see // http://bouncy-castle.1462172.n4.nabble.com/CMSException-exception-unwrapping-key-key-invalid-unknown-key-type-passed-to-RSA-td4658109.html //DotNetUtilities.GetKeyPair(ri.AlgorithmIdentifier) envelopedData = ri.GetContent(privateKey.Key); break; } j++; if (certificate != null) { extraInfo.Append('\n'); extraInfo.Append(j); extraInfo.Append(": "); if (ri is KeyTransRecipientInformation) { appendCertInfo(extraInfo, (KeyTransRecipientInformation)ri, certificate, materialCert); } } } recipientFieldsBytes[i] = recipientBytes; recipientFieldsLength += recipientBytes.Length; } if (!foundRecipient || envelopedData == null) { throw new IOException("The certificate matches none of " + array.Count + " recipient entries" + extraInfo.ToString()); } if (envelopedData.Length != 24) { throw new IOException("The enveloped data does not contain 24 bytes"); } // now envelopedData contains: // - the 20 bytes seed // - the 4 bytes of permission for the current user byte[] accessBytes = new byte[4]; Array.Copy(envelopedData, 20, accessBytes, 0, 4); AccessPermission currentAccessPermission = new AccessPermission(accessBytes); currentAccessPermission.IsReadOnly = true; CurrentAccessPermission = currentAccessPermission; // what we will put in the SHA1 = the seed + each byte contained in the recipients array byte[] sha1Input = new byte[recipientFieldsLength + 20]; // put the seed in the sha1 input Array.Copy(envelopedData, 0, sha1Input, 0, 20); // put each bytes of the recipients array in the sha1 input int sha1InputOffset = 20; foreach (byte[] recipientFieldsByte in recipientFieldsBytes) { Array.Copy(recipientFieldsByte, 0, sha1Input, sha1InputOffset, recipientFieldsByte.Length); sha1InputOffset += recipientFieldsByte.Length; } byte[] mdResult; if (encryption.Version == 4 || encryption.Version == 5) { mdResult = SHA256.Create().Digest(sha1Input); // detect whether AES encryption is used. This assumes that the encryption algo is // stored in the PDCryptFilterDictionary // However, crypt filters are used only when V is 4 or 5. PdfCryptFilterDictionary defaultCryptFilterDictionary = encryption.DefaultCryptFilterDictionary; if (defaultCryptFilterDictionary != null) { PdfName cryptFilterMethod = defaultCryptFilterDictionary.CryptFilterMethod; IsAES = PdfName.AESV2.Equals(cryptFilterMethod) || PdfName.AESV3.Equals(cryptFilterMethod); } } else { mdResult = SHA1.Create().Digest(sha1Input); } // we have the encryption key ... encryptionKey = new byte[this.keyLength / 8]; Array.Copy(mdResult, 0, encryptionKey, 0, this.keyLength / 8); } catch (Exception e) { throw new IOException("", e); } }