如何使用openssl通过公钥加密大文件

91

我该如何使用公钥加密大文件,以便除拥有私钥的人之外,没有其他人能够解密它?

我可以生成RSA公钥和私钥,但当涉及到使用以下命令加密大文件时:

openssl rsautl -encrypt -pubin -inkey public.pem -in myLargeFile.xml -out myLargeFile_encrypted.xml

我该如何进行解密操作呢?我使用以下命令创建了我的私钥和公钥:

openssl genrsa -out private.pem 1024
openssl rsa -in private.pem -out public.pem -outform PEM -pubout

我遇到了这个错误:

RSA operation error
3020:error:0406D06E:rsa routines:RSA_padding_add_PKCS1_type_2:data too large for key size:.\crypto\rsa\rsa_pk1.c:151:

我尝试使用大小从1024到1200位的密钥,但没有成功,出现了相同的错误。

8个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
92

公钥加密并不适用于加密任意长度的文件。通常使用对称密码(例如AES)进行普通加密。每次生成一个新的随机对称密钥,使用该密钥进行加密,然后使用RSA密码(公钥)将其加密。密文以及加密的对称密钥一起传输给接收者。接收者使用其私钥解密对称密钥,然后使用对称密钥解密消息。

私钥从未共享,仅使用公钥加密随机对称密码。


1
但是非对称加密的重点是防止私密秘钥的共享,然而这种方法却会导致密钥的共享,即使它被非对称加密所加密。使用非对称加密算法如RSA来加密共享密钥与使用对称算法相比,是否有任何优势呢? - techno
3
@techno:你并未分享任何秘密。你生成一个一次性的随机密钥,用它来加密一条消息,然后将其丢弃。你发送消息时所附带的密钥仅适用于该消息。 - n. m.
1
是的,但是特定消息的一次性密钥是共享的吗?使用非对称加密算法(如RSA)加密共享密钥与使用对称算法相比是否有优势呢? - techno
7
是的。你也分享信息本身。如果你害怕分享密钥,那就不要分享信息。 - n. m.
1
从性能角度来看,“公钥加密不适用于加密任意长度的文件。”使用接收者(或者是预期读取该文件的人)的公钥对整个文件进行RSA加密会带来其他缺点吗? - cYrus
显示剩余4条评论

79

在OpenSSL和命令行中,实现安全高效的编码任何文件的解决方案:

你需要准备一些X.509证书以PEM格式加密文件。

加密文件:

openssl smime -encrypt -binary -aes-256-cbc -in plainfile.zip -out encrypted.zip.enc -outform DER yourSslCertificate.pem

说明:

  • smime - S/MIME工具的ssl命令 (smime(1))
  • -encrypt - 文件处理的选择方法
  • -binary - 使用安全的文件处理方式。通常输入消息会按照S/MIME规范要求被转换为“规范”格式,而这个开关可以禁用它。对于所有二进制文件(如图像、声音、ZIP存档等),这是必需的。
  • -aes-256-cbc - 选定AES加密中的256位密钥长度的密码算法进行加密(强度高)。如果未指定,则使用40位RC2(非常薄弱)。(支持的密码算法
  • -in plainfile.zip - 输入文件名
  • -out encrypted.zip.enc - 输出文件名
  • -outform DER - 将输出文件编码为二进制格式。如果未指定,则文件将通过base64编码,并且文件大小将增加30%。
  • yourSslCertificate.pem - 你的证书文件名。应该使用PEM格式。

该命令能够高效地强加密任何格式的大文件。
已知问题: 当你尝试加密大于600MB的文件时,会发生错误。虽然不会抛出错误,但加密后的文件将会损坏。请务必验证每个文件!(或使用PGP - 它对带有公钥的文件加密有更好的支持)

解密文件:

openssl smime -decrypt -binary -in encrypted.zip.enc -inform DER -out decrypted.zip -inkey private.key -passin pass:your_password

什么是什么:

  • -inform DER - 与上述-outform相同
  • -inkey private.key - 指定您的私钥文件名。它应该是PEM格式,并且可以由密码加密。
  • -passin pass:your_password - 您的私钥加密密码。(口令参数

4
结果证明,我需要执行-aes256而不是-aes-256-cbc - Brian Armstrong
14
“那个命令可以非常有效地加密任何大小或格式的文件”,但是糟糕的是,在最后一段你承认它根本无法加密大文件。也许你不应该如此夸大其词,声称它是终极解决方案并适用于所有文件,因为最终你的解决方案对于大文件根本不起作用。你应该先说明你的解决方案的限制和范围,再做结论。 - Timo
3
关于大于600MB的文件存在的已知问题,是否有关于该问题的错误报告?如果你只有公钥可用,那么验证加密文件是不可能的。 - Sampo
1
我曾使用这种方法来创建数据库备份。然而,一旦备份文件大小达到约1.5GB,我就无法解密该文件。如上所述,有关此问题的错误报告。请谨慎使用。 - Joe J
1
我无法使用公共密钥(如问题所述)使其工作。它需要一个证书。 - Alleo
显示剩余4条评论

48

我发现在http://www.czeskis.com/random/openssl-encrypt-file.html找到的指示很有用。

用你的示例文件名来解释这个链接网站的步骤:

生成对称密钥,因为你可以使用它加密大文件

openssl rand -base64 32 > key.bin

使用对称密钥加密大文件

openssl enc -aes-256-cbc -salt -in myLargeFile.xml \
  -out myLargeFile.xml.enc -pass file:./key.bin

加密对称密钥,以便您可以安全地将其发送到其他人

openssl rsautl -encrypt -inkey public.pem -pubin -in key.bin -out key.bin.enc

销毁未加密的对称密钥,以免被他人发现。

shred -u key.bin

此时,您将加密的对称密钥(key.bin.enc)和加密的大文件(myLargeFile.xml.enc)发送给另一个人。

然后,另一个人可以使用他们的私钥解密对称密钥。

openssl rsautl -decrypt -inkey private.pem -in key.bin.enc -out key.bin

现在他们可以使用对称密钥解密文件。

openssl enc -d -aes-256-cbc -in myLargeFile.xml.enc \
  -out myLargeFile.xml -pass file:./key.bin
而且,您已经完成了。另一个人拥有解密后的文件,它已经被安全地发送了。

4
这是最佳解决方案。本应成为排名最高且被接受的答案。 - King

31

使用rsautl不能直接加密大文件。相反,可以按照以下步骤操作:

  1. 使用openssl rand生成密钥,例如:openssl rand 32 -out keyfile
  2. 使用openssl rsautl加密密钥文件。
  3. 使用openssl enc加密数据,使用步骤1生成的密钥。
  4. 将加密后的密钥文件与加密后的数据打包在一起。接收方需要使用其私钥解密密钥,然后使用所得到的密钥解密数据。

26

使用smime加密非常大的文件并不建议,因为您可能可以使用“-stream”选项加密大文件,但由于硬件限制,无法解密生成的文件 参见:解密大文件的问题

如上所述,公钥加密不适用于加密任意长度的文件。因此,以下命令将生成一个密码短语,使用对称加密算法来加密文件,然后使用非对称(公钥)加密算法加密密码短语。请注意:smime包括使用主公钥和备份键来加密密码短语。建议使用备份公/私钥对。

随机密码生成

将RANDFILE值设置为当前用户可以访问的文件,生成passwd.txt文件,并清除设置。

export OLD_RANDFILE=$RANDFILE
RANDFILE=~/rand1
openssl rand -base64 2048 > passwd.txt
rm ~/rand1
export RANDFILE=$OLD_RANDFILE

加密

使用以下命令将文件使用passwd.txt的内容作为密码,AES256加密到一个base64 (-a选项) 文件。使用一个主公钥和备份密钥,对passwd.txt进行非对称加密,将其加密为XXLarge.crypt.pass文件。

openssl enc -aes-256-cbc -a -salt -in XXLarge.data -out XXLarge.crypt -pass file:passwd.txt
openssl smime -encrypt -binary -in passwd.txt -out XXLarge.crypt.pass -aes256 PublicKey1.pem PublicBackupKey.pem
rm passwd.txt

解密

解密会将XXLarge.crypt.pass解密为passwd.tmp,将XXLarge.crypt解密为XXLarge2.data,并删除passwd.tmp文件。

openssl smime -decrypt -binary -in XXLarge.crypt.pass -out passwd.tmp -aes256 -recip PublicKey1.pem -inkey PublicKey1.key
openssl enc -d -aes-256-cbc -a -in XXLarge.crypt -out XXLarge2.data -pass file:passwd.tmp
rm passwd.tmp

这已经针对>5GB的文件进行了测试。

5365295400 Nov 17 10:07 XXLarge.data
7265504220 Nov 17 10:03 XXLarge.crypt
      5673 Nov 17 10:03 XXLarge.crypt.pass
5365295400 Nov 17 10:07 XXLarge2.data

这只是我的个人看法:为了使加密文件大小更小(没有Base64编码),我会放弃使用“-a”,并添加“-pbkdf2”以获得更好的密钥派生效果(在 2021 年,openssl enc 对简单的“-pass”进行了投诉)。 - Ivin
“smime” 不是已经使用对称密码进行加密,然后使用 RSA 加密密钥了吗?单独的加密是否只是为了解决 smime 子命令在处理大文件时无法正常工作的问题? - Jan Hudec
在您的结果和我的测试中,加密文件比原始文件(已经压缩)大约30%。请参见XXLarge.crypt与XXLarge.data。是否有任何方法可以使用openssl参数来减小文件大小? - Marius

9
在对 n. '代词' m. 的回答进行更详细的解释之前, 公钥加密并不适用于加密任意长度的文件。我们使用对称密码(比如AES)进行普通的加密。每次生成一个新的随机的对称密钥,用它来加密数据,然后再用RSA密码(公钥)加密这个对称密钥。密文和加密后的对称密钥将一起传输给接收方。接收方使用自己的私钥解密对称密钥,然后再使用该对称密钥来解密消息。 下面是加密流程
+---------------------+      +--------------------+
|                     |      |                    |
| generate random key |      |   the large file   |
|        (R)          |      |        (F)         |
|                     |      |                    |
+--------+--------+---+      +----------+---------+
         |        |                     |
         |        +------------------+  |
         |                           |  |
         v                           v  v
+--------+------------+     +--------+--+------------+
|                     |     |                        |
| encrypt (R) with    |     | encrypt (F)            |
| your RSA public key |     | with symmetric key (R) |
|                     |     |                        |
|  ASym(PublicKey, R) |     |     EF = Sym(F, R)     |
|                     |     |                        |
+----------+----------+     +------------+-----------+
           |                             |
           +------------+ +--------------+
                        | |
                        v v
         +--------------+-+---------------+
         |                                |
         |   send this files to the peer  |
         |                                |
         |     ASym(PublicKey, R) + EF    |
         |                                |
         +--------------------------------+

解密的流程如下:

   +----------------+        +--------------------+
   |                |        |                    |
   | EF = Sym(F, R) |        | ASym(PublicKey, R) |
   |                |        |                    |
   +-----+----------+        +---------+----------+
         |                             |
         |                             |
         |                             v
         |   +-------------------------+-----------------+
         |   |                                           |
         |   |             restore key (R)               |
         |   |                                           |
         |   | R <= ASym(PrivateKey, ASym(PublicKey, R)) |
         |   |                                           |
         |   +---------------------+---------------------+
         |                         |
         v                         v
     +---+-------------------------+---+
     |                                 |
     |       restore the file (F)      |
     |                                 |
     |      F <= Sym(Sym(F, R), R)     |
     |                                 |
     +---------------------------------+
此外,您可以使用以下命令:
# generate random symmetric key
openssl rand -base64 32 > /config/key.bin

# encryption
openssl rsautl -encrypt -pubin -inkey /config/public_key.pem -in /config/key.bin -out /config/key.bin.enc
openssl aes-256-cbc -a -pbkdf2 -salt -in  $file_name -out $file_name.enc -kfile /config/key.bin

# now you can send these files: $file_name.enc + /config/key.bin.enc

# decryption
openssl rsautl -decrypt -inkey /config/private_key.pem -in /config/key.bin.enc -out /config/key.bin
openssl aes-256-cbc -d -a -pbkdf2 -in $file_name.enc -out $file_name -kfile /config/key.bin

1
最好使用-kfile /config/key.bin而不是-k $(cat /config/key.bin),否则密码会在进程列表(任务管理器)中泄漏。 - Yeti

4

如果要使用openssl smime安全地加密大文件(>600MB),则需要将每个文件拆分成小块:

# Splits large file into 500MB pieces
split -b 500M -d -a 4 INPUT_FILE_NAME input.part.

# Encrypts each piece
find -maxdepth 1 -type f -name 'input.part.*' | sort | xargs -I % openssl smime -encrypt -binary -aes-256-cbc -in % -out %.enc -outform DER PUBLIC_PEM_FILE

为了提供信息,以下是如何解密和组合所有部分的方法:

# Decrypts each piece
find -maxdepth 1 -type f -name 'input.part.*.enc' | sort | xargs -I % openssl smime -decrypt -in % -binary -inform DEM -inkey PRIVATE_PEM_FILE -out %.dec

# Puts all together again
find -maxdepth 1 -type f -name 'input.part.*.dec' | sort | xargs cat > RESTORED_FILE_NAME

2
也许你应该查看这个问题的已接受答案(如何使用公钥/私钥在PHP中加密数据?)。与手动解决RSA消息大小限制(或特性)不同,它展示了如何使用OpenSSL的S / mime功能来完成相同的事情,而不需要手动处理对称密钥。请注意保留HTML标记。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,