Saltar al contenido

Extracción de datos de Python de un PDF cifrado

Solución:

ÚLTIMA ACTUALIZACIÓN 10-11-2019

No estoy seguro de haber entendido completamente su pregunta. El siguiente código se puede refinar, pero se lee en un PDF cifrado o no cifrado y extrae el texto. Por favor, avíseme si entendí mal sus requisitos.

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)

Noté que tu luciopdf al código utilizado para abrir un PDF cifrado le faltaba una contraseña, lo que debería haber arrojado este mensaje de error:

pikepdf._qpdf.PasswordError: encrypted.pdf: contraseña inválida

import pikepdf

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

Puedes usar tika para extraer el texto del archivo descifrado.pdf creado por luciopdf.

from tika import parser

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

Además, pikepdf no implementa actualmente la extracción de texto, esto incluye la última versión v1.6.4.


Decidí realizar un par de pruebas utilizando varios archivos PDF cifrados.

Llamé a todos los archivos cifrados ‘encrypted.pdf’ y todos usaron la misma contraseña de cifrado y descifrado.

  1. Adobe Acrobat 9.0 y posterior: nivel de cifrado AES de 256 bits

    • pikepdf pudo descifrar este archivo
    • PyPDF2 no pudo extraer el texto correctamente
    • tika pudo extraer el texto correctamente
  2. Adobe Acrobat 6.0 y posterior: nivel de cifrado RC4 de 128 bits

    • pikepdf pudo descifrar este archivo
    • PyPDF2 no pudo extraer el texto correctamente
    • tika pudo extraer el texto correctamente
  3. Adobe Acrobat 3.0 y posterior: nivel de cifrado RC4 de 40 bits

    • pikepdf pudo descifrar este archivo
    • PyPDF2 no pudo extraer el texto correctamente
    • tika pudo extraer el texto correctamente
  4. Adobe Acrobat 5.0 y posterior: nivel de cifrado RC4 de 128 bits

    • creado con Microsoft Word
    • pikepdf pudo descifrar este archivo
    • PyPDF2 podría extraer el texto correctamente
    • tika pudo extraer el texto correctamente
  5. Adobe Acrobat 9.0 y posterior: nivel de cifrado AES de 256 bits

    • creado usando pdfprotectfree
    • pikepdf pudo descifrar este archivo
    • PyPDF2 podría extraer el texto correctamente
    • tika pudo extraer el texto correctamente

PyPDF2 pudo extraer texto de archivos PDF descifrados que no se crearon con Adobe Acrobat.

Asumiría que las fallas tienen algo que ver con el formato incrustado en los PDF creados por Adobe Acrobat. Se requieren más pruebas para confirmar esta conjetura sobre el formato.

tika pudo extraer texto de todos los documentos descifrados con 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 no puede descifrar archivos PDF de Acrobat => 6.0

Este problema ha estado abierto con los propietarios del módulo desde el 15 de septiembre de 2015. No está claro en los comentarios relacionados con este problema cuándo los propietarios del proyecto solucionarán este problema. La última confirmación fue el 25 de junio de 2018.

Problemas de descifrado de PyPDF4

PyPDF4 es el reemplazo de PyPDF2. Este módulo también tiene problemas de descifrado con ciertos algoritmos utilizados para cifrar archivos PDF.

archivo de prueba: Adobe Acrobat 9.0 y posterior – nivel de cifrado AES de 256 bits

Mensaje de error de PyPDF2: solo se admiten los códigos de algoritmo 1 y 2

Mensaje de error de PyPDF4: solo se admiten los códigos de algoritmo 1 y 2. Este PDF usa el código 5


ACTUALIZAR SECCIÓN 10-11-2019

Esta sección es en respuesta a sus actualizaciones el 10-07-2019 y el 10-08-2019.

En su actualización, indicó que podía abrir un ‘pdf seguro con Adobe Reader’ e imprimir el documento en otro PDF, lo que elimina la marca ‘SEGURO’. Después de hacer algunas pruebas, creo que he descubierto lo que está ocurriendo en este escenario.

Nivel de seguridad de Adobe PDF

Los PDF de Adobe tienen varios tipos de controles de seguridad que puede habilitar el propietario del documento. Los controles se pueden hacer cumplir con una contraseña o un certificado.

  1. Cifrado de documentos (reforzado con una contraseña de apertura de documentos)

    • Cifrar todo el contenido del documento (más común)
    • Cifre todo el contenido del documento excepto los metadatos => Acrobat 6.0
    • Cifrar solo archivos adjuntos => Acrobat 7.0
  2. Edición e impresión restrictivas (aplicadas con una contraseña de permisos)

    • Impresión permitida
    • Cambios permitidos

La siguiente imagen muestra un PDF de Adobe cifrado con cifrado AES de 256 bits. Para abrir o imprimir este PDF se requiere una contraseña. Cuando abra este documento en Adobe Reader con la contraseña, el título indicará ASEGURADO

password_level_encryption

Este documento requiere una contraseña para abrirse con los módulos de Python que se mencionan en esta respuesta. Si intenta abrir un PDF cifrado con Adobe Reader. Deberías ver esto:

contraseña_prompt

Si no recibe esta advertencia, el documento no tiene habilitados los controles de seguridad o solo tiene habilitados los controles de edición e impresión restrictivos.

La siguiente imagen muestra la habilitación de la edición restrictiva con una contraseña en un documento PDF. La impresión de notas está habilitada. Para abrir o imprimir este PDF una contraseña no es requerido. Cuando abre este documento en Adobe Reader sin una contraseña, el título indicará ASEGURADO Esta es la misma advertencia que el PDF cifrado que se abrió con una contraseña.

Cuando imprime este documento en un nuevo PDF, ASEGURADO Se elimina la advertencia, porque se eliminó la edición restrictiva.

password_level_restrictive_editing

Todos los productos de Adobe aplican las restricciones establecidas por la contraseña de permisos. Sin embargo, si los productos de terceros no admiten esta configuración, documente los destinatarios pueden eludir algunas o todas las restricciones colocar.

Así que supongo que el documento que está imprimiendo en PDF tiene habilitada la edición restrictiva y no tener una contraseña requerida para abrir habilitado.

Respecto a romper el cifrado de PDF

Ninguno PyPDF2 o PyPDF4 están diseñados para romper la función de contraseña de apertura de documentos de un documento PDF. Ambos módulos arrojarán el siguiente error si intentan abrir un archivo PDF cifrado protegido con contraseña.

PyPDF2.utils.PdfReadError: el archivo no ha sido descifrado

La función de contraseña de apertura de un archivo PDF cifrado se puede omitir utilizando una variedad de métodos, pero una sola técnica podría no funcionar y algunas no serán aceptables debido a varios factores, incluida la complejidad de la contraseña.

El cifrado de PDF funciona internamente con cifrado keys de 40, 128 o 256 bits, según la versión del PDF. El cifrado binario key se deriva de una contraseña proporcionada por el usuario. La contraseña está sujeta a restricciones de longitud y codificación.

Por ejemplo, PDF 1.7 Adobe Extension Level 3 (Acrobat 9 – AES-256) introdujo caracteres Unicode (65.536 caracteres posibles) y aumentó la longitud máxima a 127 bytes en la representación UTF-8 de la contraseña.


El siguiente código abrirá un PDF con la edición restrictiva habilitada. Guardará este archivo en un nuevo PDF sin que se agregue la advertencia SECURED. los tika el código analizará el contenido del nuevo archivo.

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('nn', 'n')
  print (pdf)

Este código verifica si se requiere una contraseña para abrir el archivo. Este código se puede refinar y se pueden agregar otras funciones. Hay varias otras características que se pueden agregar, pero la documentación de pikepdf no coincide con los comentarios dentro del código base, por lo que se requiere más investigación para mejorar esto.

# 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('nn', '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)

Te invitamos a respaldar nuestra ocupación añadiendo un comentario y dejando una valoración te damos la bienvenida.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *