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 环境中不太可能)或不匹配的密钥引起的。
检查您是否使用了匹配的密钥。
我不是加密专家,但我正在尝试使用 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 环境中不太可能)或不匹配的密钥引起的。
检查您是否使用了匹配的密钥。