Skip to content

Cifrar y descifrar PDFs en Python, Rust, Go

Cobertura por binding. El descifrado (abrir con contraseña) está disponible en los cinco bindings. Guardar un PDF nuevo ya cifrado se soporta en Python, Rust, Go (DocumentEditor.SaveEncrypted) y WASM (saveEncryptedToBytes). La API pública de C# expone la apertura con contraseña mediante PdfDocument.OpenWithPassword, pero todavía no hay un wrapper SaveEncrypted público — si necesitas generar PDFs cifrados desde una pipeline de C#, usa la CLI de Rust o los bindings de Go/Python.

Cifra un PDF con contraseña:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("report.pdf")
doc.save_encrypted("protected.pdf", "userpass", "ownerpass")

WASM

import { WasmPdfDocument } from "pdf-oxide-wasm";

const doc = new WasmPdfDocument(bytes);
const encrypted = doc.saveEncryptedToBytes("userpass", "ownerpass", true, true, true, true);
// encrypted is a Uint8Array
doc.free();

Rust

use pdf_oxide::api::Pdf;

let mut doc = Pdf::open("report.pdf")?;
doc.save_encrypted("protected.pdf", "userpass", "ownerpass")?;

Go

package main

import (
    "log"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    editor, err := pdfoxide.OpenEditor("report.pdf")
    if err != nil { log.Fatal(err) }
    defer editor.Close()

    if err := editor.SaveEncrypted("protected.pdf", "userpass", "ownerpass"); err != nil {
        log.Fatal(err)
    }
}

C++

#include <pdf_oxide/pdf_oxide.hpp>

auto editor = pdf_oxide::DocumentEditor::open("report.pdf");
editor.save_encrypted("protected.pdf", "userpass", "ownerpass");

Swift

import PdfOxide

let editor = try DocumentEditor.open("report.pdf")
try editor.saveEncrypted("protected.pdf", userPassword: "userpass", ownerPassword: "ownerpass")

Dart

import 'package:pdf_oxide/pdf_oxide.dart';

final editor = DocumentEditor.open('report.pdf');
editor.saveEncrypted('protected.pdf', 'userpass', 'ownerpass');
editor.close();

R

library(pdfoxide)

editor <- pdf_editor_open("report.pdf")
pdf_editor_save_encrypted(editor, "protected.pdf", "userpass", "ownerpass")
pdf_editor_close(editor)

Julia

using PdfOxide

editor = open_editor("report.pdf")
save_encrypted(editor, "protected.pdf", "userpass", "ownerpass")

Zig

const pdf_oxide = @import("pdf_oxide");

var editor = try pdf_oxide.DocumentEditor.open("report.pdf");
defer editor.deinit();
try editor.saveEncrypted("protected.pdf", "userpass", "ownerpass");

Objective-C

#import "POXPdfOxide.h"
NSError *err = nil;

POXDocumentEditor *editor = [POXDocumentEditor openEditor:@"report.pdf" error:&err];
[editor saveEncryptedToPath:@"protected.pdf"
               userPassword:@"userpass"
              ownerPassword:@"ownerpass"
                      error:&err];

Elixir

{:ok, editor} = PdfOxide.open_editor("report.pdf")
:ok = PdfOxide.editor_save_encrypted(editor, "protected.pdf", "userpass", "ownerpass")

Abre un PDF cifrado:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("protected.pdf", password="userpass")
text = doc.extract_text(0)
print(text)

WASM

const doc = new WasmPdfDocument(encryptedBytes);
doc.authenticate("userpass");
const text = doc.extractText(0);
console.log(text);
doc.free();

Rust

let mut doc = Pdf::open_with_password("protected.pdf", "userpass")?;
let text = doc.extract_text(0)?;
println!("{}", text);

Go

doc, _ := pdfoxide.Open("protected.pdf")
defer doc.Close()

if _, err := doc.Authenticate("userpass"); err != nil { log.Fatal(err) }
text, _ := doc.ExtractText(0)
fmt.Println(text)

C#

using PdfOxide;

using var doc = PdfDocument.OpenWithPassword("protected.pdf", "userpass");
Console.WriteLine(doc.ExtractText(0));

C++

auto doc = pdf_oxide::Document::open_with_password("protected.pdf", "userpass");
auto text = doc.extract_text(0);
std::cout << text << "\n";

Swift

let doc = try Document.openWithPassword("protected.pdf", password: "userpass")
let text = try doc.extractText(0)
print(text)

Dart

final doc = PdfDocument.openWithPassword('protected.pdf', 'userpass');
final text = doc.extractText(0);
print(text);

R

doc <- pdf_open_with_password("protected.pdf", "userpass")
text <- pdf_extract_text(doc, 0)
cat(text)

Julia

doc = open_with_password("protected.pdf", "userpass")
text = extract_text(doc, 0)
println(text)

Zig

var doc = try pdf_oxide.Document.openWithPassword("protected.pdf", "userpass");
defer doc.deinit();
const text = try doc.extractText(a, 0);
defer a.free(text);

Objective-C

POXDocument *doc = [POXDocument openWithPassword:@"protected.pdf" password:@"userpass" error:&err];
NSString *text = [doc extractText:0 error:&err];
NSLog(@"%@", text);

Elixir

{:ok, doc} = PdfOxide.open_with_password("protected.pdf", "userpass")
{:ok, text} = PdfOxide.extract_text(doc, 0)
IO.puts(text)

PDF Oxide usa cifrado AES-256 de forma predeterminada. Licencia MIT, sin restricciones AGPL.

Instalación

pip install pdf_oxide

Cifrar PDFs

Cifrado básico

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("input.pdf")
doc.save_encrypted("output.pdf", "user123", "owner456")

WASM

const doc = new WasmPdfDocument(bytes);
const encrypted = doc.saveEncryptedToBytes("user123", "owner456", true, true, true, true);
doc.free();

Rust

let mut doc = Pdf::open("input.pdf")?;
doc.save_encrypted("output.pdf", "user123", "owner456")?;

C++

auto editor = pdf_oxide::DocumentEditor::open("input.pdf");
editor.save_encrypted("output.pdf", "user123", "owner456");

Swift

let editor = try DocumentEditor.open("input.pdf")
try editor.saveEncrypted("output.pdf", userPassword: "user123", ownerPassword: "owner456")

Dart

final editor = DocumentEditor.open('input.pdf');
editor.saveEncrypted('output.pdf', 'user123', 'owner456');
editor.close();

R

editor <- pdf_editor_open("input.pdf")
pdf_editor_save_encrypted(editor, "output.pdf", "user123", "owner456")
pdf_editor_close(editor)

Julia

editor = open_editor("input.pdf")
save_encrypted(editor, "output.pdf", "user123", "owner456")

Zig

var editor = try pdf_oxide.DocumentEditor.open("input.pdf");
defer editor.deinit();
try editor.saveEncrypted("output.pdf", "user123", "owner456");

Objective-C

POXDocumentEditor *editor = [POXDocumentEditor openEditor:@"input.pdf" error:&err];
[editor saveEncryptedToPath:@"output.pdf"
               userPassword:@"user123"
              ownerPassword:@"owner456"
                      error:&err];

Elixir

{:ok, editor} = PdfOxide.open_editor("input.pdf")
:ok = PdfOxide.editor_save_encrypted(editor, "output.pdf", "user123", "owner456")
  • Contraseña de usuario — requerida para abrir y ver el documento
  • Contraseña de propietario — requerida para acceso completo (imprimir, copiar, modificar)

Solo contraseña de apertura

Define una contraseña de usuario sin restricciones adicionales:

doc = PdfDocument("input.pdf")
doc.save_encrypted("locked.pdf", "viewpass", "adminpass")

Sin contraseña de apertura, restringe acciones

Deja que cualquiera abra el documento, pero restringe la impresión y la copia:

Python

doc = PdfDocument("input.pdf")
doc.save_encrypted(
    "restricted.pdf",
    "",            # No password to open
    "adminpass",   # Owner password for full access
    allow_print=False,
    allow_copy=False,
    allow_modify=False,
    allow_annotate=False,
)

WASM

const doc = new WasmPdfDocument(bytes);
const restricted = doc.saveEncryptedToBytes(
    "",           // No password to open
    "adminpass",  // Owner password for full access
    false,        // allowPrint
    false,        // allowCopy
    false,        // allowModify
    false         // allowAnnotate
);
doc.free();

Rust

let config = EncryptionConfig::new("", "adminpass")
    .with_permissions(Permissions::none());
doc.save_with_encryption("restricted.pdf", config)?;

Solo impresión

Permite ver e imprimir, pero no copiar ni modificar:

Python

doc = PdfDocument("input.pdf")
doc.save_encrypted(
    "print-only.pdf",
    "",
    "adminpass",
    allow_print=True,
    allow_copy=False,
    allow_modify=False,
)

WASM

const doc = new WasmPdfDocument(bytes);
const printOnly = doc.saveEncryptedToBytes(
    "",           // No password to open
    "adminpass",
    true,         // allowPrint
    false,        // allowCopy
    false,        // allowModify
    true          // allowAnnotate
);
doc.free();

Rust

let config = EncryptionConfig::new("", "adminpass")
    .with_permissions(Permissions::print_only());
doc.save_with_encryption("print-only.pdf", config)?;

Opciones de permisos

Parámetro Predeterminado Descripción
allow_print True Permite imprimir el documento
allow_copy True Permite copiar texto y gráficos
allow_modify True Permite modificar el contenido
allow_annotate True Permite agregar anotaciones

Descifrar PDFs

Abrir con contraseña

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("encrypted.pdf", password="secret")
text = doc.extract_text(0)
print(f"Pages: {doc.page_count()}")

WASM

const doc = new WasmPdfDocument(encryptedBytes);
doc.authenticate("secret");
const text = doc.extractText(0);
console.log(`Pages: ${doc.pageCount()}`);
doc.free();

Rust

let mut doc = Pdf::open_with_password("encrypted.pdf", "secret")?;
let text = doc.extract_text(0)?;
println!("Pages: {}", doc.page_count()?);

C++

auto doc = pdf_oxide::Document::open_with_password("encrypted.pdf", "secret");
auto text = doc.extract_text(0);
std::cout << "Pages: " << doc.page_count() << "\n";

Swift

let doc = try Document.openWithPassword("encrypted.pdf", password: "secret")
let text = try doc.extractText(0)
print("Pages: \(try doc.pageCount())")

Dart

final doc = PdfDocument.openWithPassword('encrypted.pdf', 'secret');
final text = doc.extractText(0);
print('Pages: ${doc.pageCount}');

R

doc <- pdf_open_with_password("encrypted.pdf", "secret")
text <- pdf_extract_text(doc, 0)
cat("Pages:", pdf_page_count(doc), "\n")

Julia

doc = open_with_password("encrypted.pdf", "secret")
text = extract_text(doc, 0)
println("Pages: ", page_count(doc))

Zig

var doc = try pdf_oxide.Document.openWithPassword("encrypted.pdf", "secret");
defer doc.deinit();
const text = try doc.extractText(a, 0);
defer a.free(text);
std.debug.print("Pages: {d}\n", .{try doc.pageCount()});

Objective-C

POXDocument *doc = [POXDocument openWithPassword:@"encrypted.pdf" password:@"secret" error:&err];
NSString *text = [doc extractText:0 error:&err];
NSLog(@"Pages: %ld", (long)[doc pageCountError:&err]);

Elixir

{:ok, doc} = PdfOxide.open_with_password("encrypted.pdf", "secret")
{:ok, text} = PdfOxide.extract_text(doc, 0)
{:ok, pages} = PdfOxide.page_count(doc)
IO.puts("Pages: #{pages}")

Guardar sin cifrado

Abre un PDF cifrado y guarda una copia sin cifrado:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("encrypted.pdf", password="secret")
doc.save("decrypted.pdf")

WASM

const doc = new WasmPdfDocument(encryptedBytes);
doc.authenticate("secret");
const decrypted = doc.save();
// decrypted is an unencrypted Uint8Array
doc.free();

Rust

let mut doc = Pdf::open_with_password("encrypted.pdf", "secret")?;
doc.save("decrypted.pdf")?;

Volver a cifrar con otra contraseña

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("old-protected.pdf", password="oldpass")
doc.save_encrypted("new-protected.pdf", "newuser", "newowner")

WASM

const doc = new WasmPdfDocument(encryptedBytes);
doc.authenticate("oldpass");
const reEncrypted = doc.saveEncryptedToBytes("newuser", "newowner", true, true, true, true);
doc.free();

Rust

let mut doc = Pdf::open_with_password("old-protected.pdf", "oldpass")?;
doc.save_encrypted("new-protected.pdf", "newuser", "newowner")?;

Cifrado por lotes

Cifra todos los PDFs de un directorio:

from pdf_oxide import PdfDocument, PdfError
from pathlib import Path

input_dir = Path("reports/")
output_dir = Path("protected/")
output_dir.mkdir(exist_ok=True)

for pdf_path in input_dir.glob("*.pdf"):
    try:
        doc = PdfDocument(str(pdf_path))
        out_path = output_dir / pdf_path.name
        doc.save_encrypted(
            str(out_path),
            "company2025",
            "admin2025",
            allow_print=True,
            allow_copy=False,
        )
        print(f"Encrypted: {pdf_path.name}")
    except PdfError as e:
        print(f"Failed: {pdf_path.name}: {e}")

Descifrado por lotes

Descifra todos los PDFs con una contraseña conocida:

from pdf_oxide import PdfDocument, PdfError
from pathlib import Path

input_dir = Path("protected/")
output_dir = Path("decrypted/")
output_dir.mkdir(exist_ok=True)

for pdf_path in input_dir.glob("*.pdf"):
    try:
        doc = PdfDocument(str(pdf_path), password="company2025")
        out_path = output_dir / pdf_path.name
        doc.save(str(out_path))
        print(f"Decrypted: {pdf_path.name}")
    except PdfError as e:
        print(f"Failed: {pdf_path.name}: {e}")

Algoritmos compatibles

PDF Oxide admite todos los algoritmos estándar de cifrado de PDF:

Algoritmo Longitud de clave Seguridad
AES-256 256 bits La más fuerte (predeterminada)
AES-128 128 bits Fuerte
RC4-128 128 bits Heredado
RC4-40 40 bits Débil (solo compatibilidad)

save_encrypted() usa AES-256 de forma predeterminada. Si quieres elegir explícitamente el algoritmo, usa la API de Rust con EncryptionConfig.

¿Por qué no pdfplumber o pdfminer?

Ni pdfplumber ni pdfminer saben trabajar con PDFs cifrados:

  • pdfplumber — lanza error con PDFs protegidos por contraseña. Sin soporte de descifrado.
  • pdfminer — soporte de cifrado limitado; falla con archivos cifrados con AES-256.
  • pypdf — permite descifrar, pero es 15× más lento que PDF Oxide para extraer texto después de descifrar.

Si necesitas procesar PDFs cifrados a gran escala, PDF Oxide cubre descifrado y cifrado de forma nativa con AES-128 y AES-256.

Páginas relacionadas