Extraction de données Python à partir d'un PDF crypté

12

Je suis un récent diplômé en mathématiques pures qui n'a suivi que quelques cours de programmation de base. Je fais un stage et j'ai un projet d'analyse de données interne. Je dois analyser les PDF internes des dernières années. Les PDF sont "sécurisés". En d'autres termes, ils sont cryptés. Nous n'avons pas de mots de passe PDF, encore plus, nous ne savons pas si des mots de passe existent. Mais, nous avons tous ces documents et nous pouvons les lire manuellement. Nous pouvons également les imprimer. Le but est de les lire avec Python car c'est le langage que nous avons une idée.

Tout d'abord, j'ai essayé de lire les fichiers PDF avec certaines bibliothèques Python. Cependant, les bibliothèques Python que j'ai trouvées ne lisent pas les fichiers PDF chiffrés. À ce moment-là, je ne pouvais pas non plus exporter les informations à l'aide d'Adobe Reader.

Deuxièmement, j'ai décidé de décrypter les PDF. J'ai réussi à utiliser la bibliothèque Python pykepdf. Pykepdf fonctionne très bien! Cependant, les fichiers PDF déchiffrés ne peuvent pas être lus aussi bien avec les bibliothèques Python du point précédent ( PyPDF2 et Tabula ). Pour le moment, nous avons fait quelques améliorations car en utilisant Adobe Reader, je peux exporter les informations des PDF décryptés, mais le but est de tout faire avec Python.

Le code que je montre fonctionne parfaitement avec les PDF non cryptés, mais pas avec les PDF cryptés. Il ne fonctionne pas avec les fichiers PDF déchiffrés qui ont également été obtenus avec pykepdf.

Je n'ai pas écrit le code. Je l'ai trouvé dans la documentation des bibliothèques Python Pykepdf et Tabula . La solution PyPDF2 a été écrite par Al Sweigart dans son livre, " Automatisez les trucs ennuyeux avec Python ", que je recommande fortement. J'ai également vérifié que le code fonctionne correctement, avec les limitations que j'ai expliquées auparavant.

Première question, pourquoi je ne peux pas lire les fichiers décryptés, si les programmes fonctionnent avec des fichiers qui n'ont jamais été cryptés?

Deuxième question, pouvons-nous lire avec Python les fichiers décryptés d'une manière ou d'une autre? Quelle bibliothèque peut le faire ou est impossible? Tous les PDF déchiffrés sont-ils extractibles?

Merci pour votre temps et votre aide!!!

J'ai trouvé ces résultats en utilisant Python 3.7, Windows 10, les ordinateurs portables Jupiter et Anaconda 2019.07.

Python

import pikepdf
with pikepdf.open("encrypted.pdf") as pdf:
  num_pages = len(pdf.pages)
  del pdf.pages[-1]
  pdf.save("decrypted.pdf")

import tabula
tabula.read_pdf("decrypted.pdf", stream=True)

import PyPDF2
pdfFileObj=open("decrypted.pdf", "rb")
pdfReader=PyPDF2.PdfFileReader(pdfFileObj)
pdfReader.numPages
pageObj=pdfReader.getPage(0)
pageObj.extractText()

Avec Tabula, je reçois le message "le fichier de sortie est vide".

Avec PyPDF2, je reçois seulement '/ n'

MISE À JOUR 10/3/2019 Pdfminer.six (Version novembre 2018)

J'ai obtenu de meilleurs résultats en utilisant la solution publiée par DuckPuncher . Pour le fichier décrypté, j'ai obtenu les étiquettes, mais pas les données. La même chose se produit avec le fichier crypté. Pour le fichier qui n'a jamais été chiffré fonctionne parfaitement. Comme j'ai besoin des données et des étiquettes des fichiers cryptés ou décryptés, ce code ne fonctionne pas pour moi. Pour cette analyse, j'ai utilisé pdfminer.six qui est la bibliothèque Python qui a été publiée en novembre 2018. Pdfminer.six comprend une bibliothèque pycryptodome. Selon leur documentation, " PyCryptodome est un package Python autonome de primitives cryptographiques de bas niveau .."

Le code est dans la question d'échange de pile: Extraire du texte d'un fichier PDF en utilisant PDFMiner en python?

J'aimerais si vous voulez répéter mon expérience. Voici la description:

1) Exécutez les codes mentionnés dans cette question avec n'importe quel PDF qui n'a jamais été chiffré.

2) Faites de même avec un PDF "sécurisé" (c'est un terme qu'Adobe utilise), je l'appelle le PDF crypté. Utilisez un formulaire générique que vous pouvez trouver en utilisant Google. Après l'avoir téléchargé, vous devez remplir les champs. Sinon, vous vérifieriez les étiquettes, mais pas les champs. Les données sont dans les champs.

3) Déchiffrez le PDF chiffré à l'aide de Pykepdf. Ce sera le PDF décrypté.

4) Exécutez à nouveau les codes à l'aide du PDF déchiffré.

MISE À JOUR 10/4/2019 Camelot (Version juillet 2019)

J'ai trouvé la bibliothèque Python Camelot. Attention, vous avez besoin de camelot-py 0.7.3.

Il est très puissant et fonctionne avec Python 3.7. De plus, il est très simple à utiliser. Tout d'abord, vous devez également installer Ghostscript . Sinon, cela ne fonctionnera pas. Vous devez également installer Pandas . N'utilisez pas pip install camelot-py . Utilisez plutôt pip install camelot-py [cv]

L'auteur du programme est Vinayak Mehta. Frank Du partage ce code dans une vidéo YouTube "Extraire des données tabulaires du PDF avec Camelot en utilisant Python."

J'ai vérifié le code et il fonctionne avec des fichiers non cryptés. Cependant, cela ne fonctionne pas avec les fichiers cryptés et décryptés, et c'est mon objectif .

Camelot est orienté pour obtenir des tableaux à partir de fichiers PDF.

Voici le code:

Python

import camelot
import pandas
name_table = camelot.read_pdf("uncrypted.pdf")
type(name_table)

#This is a Pandas dataframe
name_table[0]

first_table = name_table[0]   

#Translate camelot table object to a pandas dataframe
first_table.df

first_table.to_excel("unencrypted.xlsx")
#This creates an excel file.
#Same can be done with csv, json, html, or sqlite.

#To get all the tables of the pdf you need to use this code.
for table in name_table:
   print(table.df)

MISE À JOUR 10/7/2019 J'ai trouvé une astuce. Si j'ouvre le fichier PDF sécurisé avec Adobe Reader, que je l'imprime à l'aide de Microsoft to PDF et que je l'enregistre au format PDF, je peux extraire les données à l'aide de cette copie. Je peux également convertir le fichier PDF en JSON, Excel, SQLite, CSV, HTML et d'autres formats. C'est une solution possible à ma question. Cependant, je cherche toujours une option pour le faire sans cette astuce car l'objectif est de le faire à 100% avec Python. Je crains également que si une meilleure méthode de cryptage est utilisée, l'astuce ne fonctionnera peut-être pas. Parfois, vous devez utiliser Adobe Reader plusieurs fois pour obtenir une copie extractible.

MISE À JOUR 10/8/2019. Troisième question. J'ai maintenant une troisième question. Tous les pdf sécurisés / cryptés sont-ils protégés par mot de passe? Pourquoi pikepdf ne fonctionne pas? Je suppose que la version actuelle de pikepdf peut briser certains types de cryptages, mais pas tous. @constt a mentionné que PyPDF2 peut briser un certain type de protection. Cependant, je lui ai répondu que j'avais trouvé un article selon lequel PyPDF2 peut briser les cryptages effectués avec Adobe Acrobat Pro 6.0, mais pas avec les versions postérieures.

Débutant
la source
2
Je n'ai pas pu reproduire ces problèmes avec PyPDF2, tout fonctionne très bien. J'ai utilisé pdftkainsi que des services en ligne pour crypter des fichiers. Pouvez-vous publier des liens vers des fichiers pdf "gênants"?
constt
1
OK merci! Avez-vous essayé d'utiliser qpdfpour décrypter vos fichiers? Dans le cas où il fera l'affaire, vous pouvez l'appeler depuis votre script en utilisant le subprocessmodule pour décrypter les fichiers avant de les analyser.
constt
1
Premièrement, PyPDF2 ne peut pas déchiffrer les fichiers PDF Acrobat => 6.0. Deuxièmement, pikepdf n'a actuellement pas d'implémentation d'extraction de texte.
vie est complexe
1
@Beginner Je suppose que cela a à voir avec la mise en forme sous-jacente utilisée par pykepdf pour écrire le PDF non chiffré.
vie est complexe
2
"Tous les fichiers PDF sécurisés / cryptés sont-ils protégés par mot de passe?" - non. Il existe également des fichiers PDF chiffrés à l'aide de la cryptographie à clé privée / publique basée sur les certificats X509.
mkl

Réponses:

8

DERNIÈRE MISE À JOUR 10-11-2019

Je ne sais pas si je comprends parfaitement votre question. Le code ci-dessous peut être affiné, mais il lit dans un PDF chiffré ou non chiffré et extrait le texte. Veuillez me faire savoir si j'ai mal compris vos besoins.

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO

def extract_encrypted_pdf_text(path, encryption_true, decryption_password):

  output = StringIO()

  resource_manager = PDFResourceManager()
  laparams = LAParams()

  device = TextConverter(resource_manager, output, codec='utf-8', laparams=laparams)

  pdf_infile = open(path, 'rb')
  interpreter = PDFPageInterpreter(resource_manager, device)

  page_numbers = set()

  if encryption_true == False:
    for page in PDFPage.get_pages(pdf_infile, page_numbers, maxpages=0, caching=True, check_extractable=True):
      interpreter.process_page(page)

  elif encryption_true == True:
    for page in PDFPage.get_pages(pdf_infile, page_numbers, maxpages=0, password=decryption_password, caching=True, check_extractable=True):
      interpreter.process_page(page)

 text = output.getvalue()
 pdf_infile.close()
 device.close()
 output.close()
return text

results = extract_encrypted_pdf_text('encrypted.pdf', True, 'password')
print (results)

J'ai noté que votre code pikepdf utilisé pour ouvrir un PDF chiffré manquait un mot de passe, qui aurait dû lancer ce message d'erreur:

pikepdf._qpdf.PasswordError: encrypted.pdf: mot de passe invalide

import pikepdf

with pikepdf.open("encrypted.pdf", password='password') as pdf:
num_pages = len(pdf.pages)
del pdf.pages[-1]
pdf.save("decrypted.pdf")

Vous pouvez utiliser tika pour extraire le texte du fichier decrypted.pdf créé par pikepdf .

from tika import parser

parsedPDF = parser.from_file("decrypted.pdf")
pdf = parsedPDF["content"]
pdf = pdf.replace('\n\n', '\n')

De plus, pikepdf n'implémente pas actuellement l'extraction de texte, ce qui inclut la dernière version v1.6.4.


J'ai décidé d'exécuter quelques tests en utilisant divers fichiers PDF cryptés.

J'ai nommé tous les fichiers cryptés «encrypted.pdf» et ils ont tous utilisé le même mot de passe de cryptage et de décryptage.

  1. Adobe Acrobat 9.0 et versions ultérieures - niveau de cryptage AES 256 bits

    • pikepdf a réussi à décrypter ce fichier
    • PyPDF2 n'a pas pu extraire le texte correctement
    • tika pourrait extraire le texte correctement
  2. Adobe Acrobat 6.0 et versions ultérieures - niveau de cryptage RC4 128 bits

    • pikepdf a réussi à décrypter ce fichier
    • PyPDF2 n'a pas pu extraire le texte correctement
    • tika pourrait extraire le texte correctement
  3. Adobe Acrobat 3.0 et versions ultérieures - niveau de cryptage RC4 40 bits

    • pikepdf a réussi à décrypter ce fichier
    • PyPDF2 n'a pas pu extraire le texte correctement
    • tika pourrait extraire le texte correctement
  4. Adobe Acrobat 5.0 et versions ultérieures - niveau de cryptage RC4 128 bits

    • créé avec Microsoft Word
    • pikepdf a réussi à décrypter ce fichier
    • PyPDF2 pourrait extraire le texte correctement
    • tika pourrait extraire le texte correctement
  5. Adobe Acrobat 9.0 et versions ultérieures - niveau de cryptage AES 256 bits

    • créé en utilisant pdfprotectfree
    • pikepdf a réussi à décrypter ce fichier
    • PyPDF2 pourrait extraire le texte correctement
    • tika pourrait extraire le texte correctement

PyPDF2 a pu extraire du texte à partir de fichiers PDF déchiffrés non créés avec Adobe Acrobat.

Je suppose que les échecs ont quelque chose à voir avec la mise en forme intégrée dans les PDF créés par Adobe Acrobat. Des tests supplémentaires sont nécessaires pour confirmer cette conjecture sur le formatage.

tika a pu extraire du texte de tous les documents décryptés avec pikepdf.


 import pikepdf
 with pikepdf.open("encrypted.pdf", password='password') as pdf:
    num_pages = len(pdf.pages)
    del pdf.pages[-1]
    pdf.save("decrypted.pdf")


 from PyPDF2 import PdfFileReader

 def text_extractor(path):
   with open(path, 'rb') as f:
     pdf = PdfFileReader(f)
     page = pdf.getPage(1)
     print('Page type: {}'.format(str(type(page))))
     text = page.extractText()
     print(text)

    text_extractor('decrypted.pdf')

PyPDF2 ne peut pas déchiffrer les fichiers PDF Acrobat => 6.0

Ce problème est ouvert aux propriétaires de modules depuis le 15 septembre 2015 . Il n'est pas clair dans les commentaires liés à ce problème quand ce problème sera résolu par les propriétaires du projet. Le dernier commit était le 25 juin 2018.

Problèmes de décryptage PyPDF4

PyPDF4 est le remplacement de PyPDF2. Ce module a également des problèmes de décryptage avec certains algorithmes utilisés pour crypter les fichiers PDF.

fichier de test: Adobe Acrobat 9.0 et versions ultérieures - niveau de cryptage AES 256 bits

Message d'erreur PyPDF2: seuls les codes d'algorithme 1 et 2 sont pris en charge

Message d'erreur PyPDF4: seuls les codes d'algorithme 1 et 2 sont pris en charge. Ce PDF utilise le code 5


MISE À JOUR DE LA SECTION 10-11-2019

Cette section fait suite à vos mises à jour les 10-07-2019 et 10-08-2019.

Dans votre mise à jour, vous avez indiqué que vous pouviez ouvrir un «pdf sécurisé avec Adobe Reader» et imprimer le document dans un autre PDF, ce qui supprimait le drapeau «SECURED». Après avoir fait quelques tests, je pense que j'ai compris ce qui se passe dans ce scénario.

Niveau de sécurité Adobe PDF

Les PDF Adobe ont plusieurs types de contrôles de sécurité qui peuvent être activés par le propriétaire du document. Les contrôles peuvent être appliqués avec un mot de passe ou un certificat.

  1. Cryptage des documents (appliqué avec un mot de passe d'ouverture de document)

    • Chiffrer tout le contenu du document (le plus courant)
    • Chiffrer tout le contenu du document sauf les métadonnées => Acrobat 6.0
    • Crypter uniquement les pièces jointes => Acrobat 7.0
  2. Édition et impression restrictives (appliquées avec un mot de passe d'autorisation)

    • Impression autorisée
    • Modifications autorisées

L'image ci-dessous montre un fichier Adobe PDF crypté avec un cryptage AES 256 bits. Pour ouvrir ou imprimer ce PDF, un mot de passe est requis. Lorsque vous ouvrez ce document dans Adobe Reader avec le mot de passe, le titre indique SECURED

password_level_encryption

Ce document nécessite un mot de passe pour s'ouvrir avec les modules Python mentionnés dans cette réponse. Si vous essayez d'ouvrir un PDF crypté avec Adobe Reader. Vous devriez voir ceci:

mot_de_passe

Si vous n'obtenez pas cet avertissement, le document n'a pas d'activation des contrôles de sécurité ou n'a activé que les restrictions d'édition et d'impression.

L'image ci-dessous montre que l'édition restrictive est activée avec un mot de passe dans un document PDF. Remarque l'impression est activée . Pour ouvrir ou imprimer ce PDF, aucun mot de passe n'est requis . Lorsque vous ouvrez ce document dans Adobe Reader sans mot de passe, le titre indiquera SECURED. Il s'agit du même avertissement que le PDF crypté qui a été ouvert avec un mot de passe.

Lorsque vous imprimez ce document dans un nouveau PDF, l' avertissement SECURED est supprimé, car la modification restrictive a été supprimée.

password_level_restrictive_editing

Tous les produits Adobe appliquent les restrictions définies par le mot de passe des autorisations. Toutefois, si les produits tiers ne prennent pas en charge ces paramètres, les destinataires du document peuvent contourner tout ou partie des restrictions définies.

Je suppose donc que le document que vous imprimez au format PDF a une édition restrictive activée et n'a pas de mot de passe requis pour ouvrir activé.

Concernant la rupture du cryptage PDF

Ni PyPDF2 ni PyPDF4 ne sont conçus pour rompre la fonction de mot de passe d'ouverture de document d'un document PDF. Les deux modules lèveront l'erreur suivante s'ils tentent d'ouvrir un fichier PDF protégé par mot de passe crypté.

PyPDF2.utils.PdfReadError: le fichier n'a pas été déchiffré

La fonction d'ouverture de mot de passe d'un fichier PDF crypté peut être contournée à l'aide de diverses méthodes, mais une seule technique peut ne pas fonctionner et certaines ne seront pas acceptables en raison de plusieurs facteurs, notamment la complexité du mot de passe.

Le cryptage PDF fonctionne en interne avec des clés de cryptage de 40, 128 ou 256 bits selon la version PDF. La clé de chiffrement binaire est dérivée d'un mot de passe fourni par l'utilisateur. Le mot de passe est soumis à des contraintes de longueur et d'encodage.

Par exemple, PDF 1.7 Adobe Extension Level 3 (Acrobat 9 - AES-256) a introduit des caractères Unicode (65 536 caractères possibles) et a augmenté la longueur maximale à 127 octets dans la représentation UTF-8 du mot de passe.


Le code ci-dessous ouvrira un PDF avec l'édition restrictive activée. Il enregistrera ce fichier dans un nouveau PDF sans que l'avertissement SECURED ne soit ajouté. Le code tika analysera le contenu du nouveau fichier.

from tika import parser
import pikepdf

# opens a PDF with restrictive editing enabled, but that still 
# allows printing.
with pikepdf.open("restrictive_editing_enabled.pdf") as pdf:
  pdf.save("restrictive_editing_removed.pdf")

  # plain text output
  parsedPDF = parser.from_file("restrictive_editing_removed.pdf")

  # XHTML output
  # parsedPDF = parser.from_file("restrictive_editing_removed.pdf", xmlContent=True)

  pdf = parsedPDF["content"]
  pdf = pdf.replace('\n\n', '\n')
  print (pdf)

Ce code vérifie si un mot de passe est requis pour ouvrir le fichier. Ce code peut être affiné et d'autres fonctions peuvent être ajoutées. Il existe plusieurs autres fonctionnalités qui peuvent être ajoutées, mais la documentation de pikepdf ne correspond pas aux commentaires dans la base de code, donc des recherches supplémentaires sont nécessaires pour améliorer cela.

# this would be removed once logging is used
############################################
import sys
sys.tracebacklimit = 0
############################################

import pikepdf
from tika import parser

def create_pdf_copy(pdf_file_name):
  with pikepdf.open(pdf_file_name) as pdf:
    new_filename = f'copy_{pdf_file_name}'
    pdf.save(new_filename)
    return  new_filename

def extract_pdf_content(pdf_file_name):
  # plain text output
  # parsedPDF = parser.from_file("restrictive_editing_removed.pdf")

  # XHTML output
  parsedPDF = parser.from_file(pdf_file_name, xmlContent=True)

  pdf = parsedPDF["content"]
  pdf = pdf.replace('\n\n', '\n')
  return pdf

def password_required(pdf_file_name):
  try:
    pikepdf.open(pdf_file_name)

  except pikepdf.PasswordError as error:
    return ('password required')

  except pikepdf.PdfError as results:
    return ('cannot open file')


filename = 'decrypted.pdf'
password = password_required(filename)
if password != None:
  print (password)
elif password == None:
  pdf_file = create_pdf_copy(filename)
  results = extract_pdf_content(pdf_file)
  print (results)
La vie est complexe
la source
2
Comment ouvrez-vous un fichier PDF sécurisé sans fournir de mot de passe?
vie est complexe
1
Faites-vous référence uniquement à une protection d'édition restrictive?
vie est complexe
1
Réponse mise à jour avec un code qui fonctionnait avec un PDF dont la protection restrictive contre les modifications était activée, mais permettait l'impression.
vie est complexe
1
Pouvez-vous utiliser XHTML?
vie est complexe
1
J'ai modifié la réponse pour afficher XHTML. JSON est possible, mais il nécessite de creuser dans le code de projet github lié à l'analyseur tika.
vie est complexe
1

Vous pouvez essayer de gérer l'erreur que ces fichiers produisent lorsque vous ouvrez ces fichiers sans mot de passe.

import pikepdf

def open_pdf(pdf_file_path, pdf_password=''):
    try:
        pdf_obj = pikepdf.Pdf.open(pdf_file_path)

    except pikepdf._qpdf.PasswordError:
        pdf_obj = pikepdf.Pdf.open(pdf_file_path, password=pdf_password)

    finally:
        return pdf_obj

Vous pouvez utiliser le pdf_obj retourné pour votre travail d'analyse. De plus, vous pouvez fournir le mot de passe au cas où vous auriez un PDF crypté.

Mahendra Singh
la source
1
Merci pour votre réponse! Nous essayons de le lire sans mot de passe. À ce moment, nous avons pu le faire avec la méthode qui a été expliquée dans ma MISE À JOUR 10/7/2019
Débutant
C'est loin de répondre à la question. On dirait que vous n'avez pas lu la question complète.
shoonya ek
1
Cela gère les PDF sécurisés où normalement pikepdf échoue lorsque la valeur par défaut du mot de passe est Aucun. En passant une chaîne vide, il est capable d'ouvrir et d'analyser correctement un document PDF sécurisé (dans les cas de test que j'ai exécutés).
Mahendra Singh
1
@Beginner vous n'avez pas besoin de convertir les PDF ici dans ce cas. C'est juste à partir de mon expérience antérieure que les PDF sécurisés fonctionnent en fournissant un mot de passe vide.
Mahendra Singh
1
@Beginner c'est mon code entier. Cela ne renvoie que le pdf_object de pikepdf. Dans le cas où vous souhaitez enregistrer ce pdf, enregistrez simplement l'objet retourné en utilisant pdf_obj.save ('your_file_path'). Après cela, vous pouvez utiliser ce PDF pour analyser du texte et d'autres objets. J'utilise une bibliothèque appelée PdfPlumber pour l'extraction de texte.
Mahendra Singh du
1

Pour tabula-py, vous pouvez essayer l'option de mot de passe avec read_pdf. Cela dépend de la fonction de tabula-java, donc je ne sais pas quel cryptage est pris en charge.

chezou
la source