Encryption/Decryption 使用 bouncycastle-java 和 RSAES-OAEP

Encryption/Decryption with bouncycastle-java and RSAES-OAEP

我不是加密专家,但我正在尝试使用 bouncycastle 1.67 创建一个 CMSEnvelopedDataGenerator,其中会话密钥使用 [=47= 加密]RSAES-OAEP (1.2.840.113549.1.1.7)

现在我的代码是这样的:

 CMSEnvelopedDataGenerator envelopedGenerator = new CMSEnvelopedDataGenerator();
 
 JcaAlgorithmParametersConverter paramsConverter = new JcaAlgorithmParametersConverter();
 OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);

 AlgorithmIdentifier algorithmIdentifier;    
 algorithmIdentifier = paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, oaepSpec);
 JceKeyTransRecipientInfoGenerator recipent = new JceKeyTransRecipientInfoGenerator(receiverCert, algorithmIdentifier).setProvider("BC");    
 
 # encrypt
 CMSEnvelopedData envelopedData;
 envelopedData = envelopedGenerator.generate(
     new CMSProcessableByteArray(encodedSignedData),  
     new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_CBC).setProvider("BC").build()
 )

它贯穿始终,但是当我通过 openssl asn1parse 检查它时,我看到了

115:d=6  hl=2 l=   9 prim: OBJECT            :rsaesOaep
126:d=6  hl=2 l=  47 cons: SEQUENCE
128:d=7  hl=2 l=  15 cons: cont [ 0 ]
130:d=8  hl=2 l=  13 cons: SEQUENCE
132:d=9  hl=2 l=   9 prim: OBJECT            :sha256
143:d=9  hl=2 l=   0 prim: NULL
145:d=7  hl=2 l=  28 cons: cont [ 1 ]
147:d=8  hl=2 l=  26 cons: SEQUENCE
149:d=9  hl=2 l=   9 prim: OBJECT            :mgf1
160:d=9  hl=2 l=  13 cons: SEQUENCE
162:d=10 hl=2 l=   9 prim: OBJECT            :sha256

然后是十六进制转储。 在我的参考文件中,它是这样的:

115:d=6  hl=2 l=   9 prim: OBJECT            :rsaesOaep
126:d=6  hl=2 l=  43 cons: SEQUENCE
128:d=7  hl=2 l=  13 cons: cont [ 0 ]
130:d=8  hl=2 l=  11 cons: SEQUENCE
132:d=9  hl=2 l=   9 prim: OBJECT            :sha256
143:d=7  hl=2 l=  26 cons: cont [ 1 ]
145:d=8  hl=2 l=  24 cons: SEQUENCE
147:d=9  hl=2 l=   9 prim: OBJECT            :mgf1
158:d=9  hl=2 l=  11 cons: SEQUENCE
160:d=10 hl=2 l=   9 prim: OBJECT            :sha256

我文件的第 143 行是

143:d=9  hl=2 l=   0 prim: NULL

我不确定那是从哪里来的。

当我使用适用于我的参考文件的解密代码时,出现以下异常情况

 exception unwrapping key: bad padding: unable to decrypt block

Caused by: org.bouncycastle.cms.CMSException: exception unwrapping key: bad padding: unable to decrypt block
 at org.bouncycastle.cms.jcajce.JceKeyTransRecipient.extractSecretKey(Unknown Source)
 at org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient.getRecipientOperator(Unknown Source)
 at org.bouncycastle.cms.KeyTransRecipientInformation.getRecipientOperator(Unknown Source)
 at org.bouncycastle.cms.RecipientInformation.getContentStream(Unknown Source)

Caused by: org.bouncycastle.operator.OperatorException: bad padding: unable to decrypt block
at org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper.generateUnwrappedKey(Unknown Source)

Caused by: org.bouncycastle.jcajce.provider.util.BadBlockException: unable to decrypt block
at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.getOutput(Unknown Source)
at org.bouncycastle.jcajce.provider.asymmetric.rsa.CipherSpi.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)

Caused by: org.bouncycastle.crypto.InvalidCipherTextException: data wrong
at org.bouncycastle.crypto.encodings.OAEPEncoding.decodeBlock(Unknown Source)
at org.bouncycastle.crypto.encodings.OAEPEncoding.processBlock(Unknown Source)

希望不多,就是少了

编辑:

我错误生成的文件 recipient.getKeyEncryptionAlgorithm().getParameters() 导致

[[0][2.16.840.1.101.3.4.2.1, NULL], [1][1.2.840.113549.1.1.8, [2.16.840.1.101.3.4.2.1, NULL]]]

中的正确文件
[[0][2.16.840.1.101.3.4.2.1], [1][1.2.840.113549.1.1.8, [2.16.840.1.101.3.4.2.1]]]

SHA-256 值的这些错误 NULL 来自哪里。

您在 BC 创建的消息中只提到了一个 'extra' NULL,但实际上有两个,第二个位于您从 post 编辑的数据中排除的第一行。 post 中的(不同)长度字段以及 getParameters() 的显示清楚地表明了这一点。

那些NULL没有错

这些 NULL 是 OAEP 参数结构中哈希算法的参数,是标准所要求的。来自 RFC 3447 = PKCS1v2.1,这是第一个包含 SHA-2 的版本(2003 年,紧接在 2002 年的 FIPS 186-2 之后)在 A.2.1:

The parameters field ... shall have a value of type RSAES-OAEP-params:

      RSAES-OAEP-params ::= SEQUENCE {
          hashAlgorithm     [0] HashAlgorithm    DEFAULT sha1,
          maskGenAlgorithm  [1] MaskGenAlgorithm DEFAULT mgf1SHA1,
          pSourceAlgorithm  [2] PSourceAlgorithm DEFAULT pSpecifiedEmpty
      }
...
         HashAlgorithm ::= AlgorithmIdentifier {
            {OAEP-PSSDigestAlgorithms}
         }

         OAEP-PSSDigestAlgorithms    ALGORITHM-IDENTIFIER ::= {
             { OID id-sha1 PARAMETERS NULL   }|
             { OID id-sha256 PARAMETERS NULL }|
             { OID id-sha384 PARAMETERS NULL }|
             { OID id-sha512 PARAMETERS NULL },
             ...  -- Allows for future expansion --
         }
...
         MaskGenAlgorithm ::= AlgorithmIdentifier {
            {PKCS1MGFAlgorithms}
         }
         PKCS1MGFAlgorithms    ALGORITHM-IDENTIFIER ::= {
             { OID id-mgf1 PARAMETERS HashAlgorithm },
             ...  -- Allows for future expansion --
         }

注意这两个哈希规范——标签的外部哈希和 MGF1 参数中的内部哈希——由信息集定义 HashAlgorithm 以及该集合中的所有定义值,包括 SHA-256,具有明确的 NULL 参数,不会被省略,因为通用 X.509 ASN.1 允许(比较 RFC5280 4.1.1.2,它使用较旧的预信息集表示法)。

请注意,A.2.3 中的 PSS 也是如此,A.2.4 中 RSASSA-PKCS1-v1_5 中的 DigestInfo 的哈希算法集稍大一些。这和 v2.0 中的等效规定(不包括 2.0 中没有的 PSS,并且符号略有不同)可能是对 PKCS1v1.5 at 10.1.2 的反应,仅使签名 DigestInfo 参数成为 'should'(即使在 2119 年之后也是小写,大概是因为这是 RSALabs 文本而不是 IETF),这导致实现中的变化导致有时实际上正确的签名无法验证,这被认为是需要修复的坏事。

因此您的 'reference' 文件在技术上违反了标准。然而,由于这些散列算法实际上并不使用参数——这就是它们用 NULL 编码的原因——BouncyCastle 可以很容易地容忍并接受省略的大小写。我测试了一个有效的结构,它确实可以双向工作。 (如果它甚至可以使用一些不合适的值,比如插入在那里的八位字节字符串,我也不会感到惊讶,但我没有测试它。)

即使参数编码错误,也不会导致您遇到的异常——它要么是显式 decoding/parsing 错误,例如 'required field missing',要么是实例化错误,例如 'invalid parameters for algorithm x'。在没有错误的情况下,'bad padding' 是由损坏、篡改或其他错误数据(在 CMS 环境中不太可能)或不匹配的密钥引起的。

检查您是否使用了匹配的密钥。