weidai11 / cryptopp

free C++ class library of cryptographic schemes
https://cryptopp.com
Other
4.67k stars 1.47k forks source link

Loading RSA private key file containing OID "rsaPSS" throws BERDecode error #1181

Open luckyPandaBear opened 1 year ago

luckyPandaBear commented 1 year ago

RSAPrivKeys.zip

When importing RSA private key files as attached, Crypto++ seems to support structures containing the OID for "rsaEncryption" (1 2 840 113549 1 1 1) only. Whenever a private key file is presented indicating the OID for "rsaPSS" (1 2 840 113549 1 1 10), the Load() function throws a "BERDecode error" exception.

Sample code:

CryptoPP::InvertibleRSAFunction rsa;
CryptoPP::FileSource fs("privKey.pkcs8", true);
rsa.Load(fs);

The same behaviour applies to:

rsa.BERDecode(fs);

Why is it not possible to import key files with "rsaPSS" OID included?

noloader commented 9 months ago

The problems is here in rsa.cpp:

OID RSAFunction::GetAlgorithmID() const
{
    return ASN1::rsaEncryption();
}

rsaEncryption is OID 1.2.840.113549.1.1.1. It causes the exception in PKCS8PrivateKey::BERDecode when BERDecodeAndCheck is called.

I think the quickest workaround is to add a RSA_PSS, which uses the expected OID:

$ git diff
diff --git a/rsa.h b/rsa.h
index 3f2312ee..330fc33f 100644
--- a/rsa.h
+++ b/rsa.h
@@ -155,6 +155,26 @@ public:
        Integer PreimageBound() const {return ++(m_n>>1);}
 };

+/// \brief RSA trapdoor function using the public key
+/// \since Crypto++ 1.0
+class CRYPTOPP_DLL RSAFunction_PSS : public RSAFunction
+{
+public:
+       OID GetAlgorithmID() const {
+               return ASN1::rsassa_pss();
+       }
+};
+
+/// \brief RSA trapdoor function using the private key
+/// \since Crypto++ 1.0
+class CRYPTOPP_DLL InvertibleRSAFunction_PSS : public InvertibleRSAFunction
+{
+public:
+       OID GetAlgorithmID() const {
+               return ASN1::rsassa_pss();
+       }
+};
+
 /// \brief RSA algorithm
 /// \since Crypto++ 1.0
 struct CRYPTOPP_DLL RSA
@@ -164,6 +184,15 @@ struct CRYPTOPP_DLL RSA
        typedef InvertibleRSAFunction PrivateKey;
 };

+/// \brief RSA algorithm
+/// \since Crypto++ 1.0
+struct CRYPTOPP_DLL RSA_PSS
+{
+       CRYPTOPP_STATIC_CONSTEXPR const char* CRYPTOPP_API StaticAlgorithmName() {return "RSA";}
+       typedef RSAFunction_PSS PublicKey;
+       typedef InvertibleRSAFunction_PSS PrivateKey;
+};
+
 /// \brief RSA encryption algorithm
 /// \tparam STANDARD signature standard
 /// \sa <a href="http://www.weidai.com/scan-mirror/ca.html#RSA">RSA cryptosystem</a>

And then in your program:

RSA_PSS::PrivateKey rsa;
FileSource fs("privKey.pkcs8", true);
rsa.Load(fs);

A new RSA_PSS class is not the only way to solve the problem. Here is another way, which is mostly a copy/paste/modify of PKCS8PrivateKey::BERDecode.

#include "config.h"
#include "files.h"
#include "oids.h"
#include "asn.h"
#include "rsa.h"

using namespace CryptoPP;

bool BERDecodeAlgorithmParameters(BufferedTransformation &bt)
{
    BERDecodeNull(bt);
    return false;
}

RSA::PrivateKey LoadAndCheck(BufferedTransformation& bt, const OID& oid /*expected*/)
{
    BERSequenceDecoder privateKeyInfo(bt);
        word32 version;
        BERDecodeUnsigned<word32>(privateKeyInfo, version, INTEGER, 0, 0);  // check version

        BERSequenceDecoder algorithm(privateKeyInfo);
            oid.BERDecodeAndCheck(algorithm);
            bool parametersPresent = algorithm.EndReached() ? false : BERDecodeAlgorithmParameters(algorithm);
        algorithm.MessageEnd();

        BERGeneralDecoder octetString(privateKeyInfo, OCTET_STRING);
            RSA::PrivateKey key;
            key.BERDecodePrivateKey(octetString, parametersPresent, octetString.RemainingLength());
        octetString.MessageEnd();

    privateKeyInfo.MessageEnd();

    return key;
}

int main(int argc, char* argv[])
{
    FileSource fs("RSAPrivPSS.pkcs8", true);
    RSA::PrivateKey key = LoadAndCheck(fs, ASN1::rsassa_pss());
    return 0;
}