web-dev-qa-db-ja.com

RSA公開鍵を使用して、RSA秘密鍵を使用して暗号化された文字列を復号化します

私が得る可能性のある主な答えは、なぜあなたはそれをやりたいのかということです!

残念ながら、私の抗議にもかかわらず、私はそれがほとんど意味をなさないことを知っていても、それをしなければなりません。

秘密鍵を使用して復号化し、公開鍵を使用して暗号化する関数を.Netで記述しています。また、RSAに署名して検証し、これがどのように機能するかを合理的に理解しています。

現在、秘密鍵を使用してRSA暗号化された値が送信されています。秘密鍵は、公開鍵を使用して復号化することで使用可能な値を取得することになっています。

私はこれを行う方法を理解できないようです。私はばかですか?これは普通のことですか?

値を送ってくれた人から、これはPHPでは問題ないと言われました。 PHPまだ使用していません。C++、Java、C#などの主要な言語でそれを実行するためのライブラリが見つかりません。サーバーI .Netの使用に取り組んでいます。

誰かが私を助けてくれることを願っています。

彼らがしていることを変えるように彼らに懇願する以外に、ある種の合理的な解決策があれば素晴らしいでしょう。

これは私の方法です(イリジウムによって指摘された以前の悪い方法から更新されました)が、値を復号化しようとすると例外が発生します

「OAEPパディングのデコード中にエラーが発生しました。」

Rsa.Decrypt(bytes、false)を使用すると、不正なキーの例外が発生します。

public static string DecryptUsingPublic(string dataEncrypted, string publicKey)
    {
        if (dataEncrypted == null) throw new ArgumentNullException("dataEncrypted");
        if (publicKey == null) throw new ArgumentNullException("publicKey");
        try
        {
            RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false);
            RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey);

            byte[] bytes = Convert.FromBase64String(dataEncrypted);
            byte[] decryptedBytes = rsa.Decrypt(bytes, true);

            ArrayList arrayList = new ArrayList();
            arrayList.AddRange(decryptedBytes);

           return Encoding.UTF8.GetString(decryptedBytes);
        }
        catch
        {
            return null;
        }
    }

    private static RSAParameters LoadRsaPublicKey(String publicKeyFilePath, Boolean isFile)
    {
        RSAParameters RSAKeyInfo = new RSAParameters();
        byte[] pubkey = ReadFileKey(publicKeyFilePath, "PUBLIC KEY", isFile);
        byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
        byte[] seq = new byte[15];
        // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
        MemoryStream mem = new MemoryStream(pubkey);
        BinaryReader binr = new BinaryReader(mem);    //wrap Memory Stream with BinaryReader for easy reading
        byte bt = 0;
        ushort twobytes = 0;

        try
        {

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();    //advance 1 byte
            else if (twobytes == 0x8230)
                binr.ReadInt16();   //advance 2 bytes
            else
                return RSAKeyInfo;

            seq = binr.ReadBytes(15);       //read the Sequence OID
            if (!CompareBytearrays(seq, SeqOID))    //make sure Sequence for OID is correct
                return RSAKeyInfo;

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
                binr.ReadByte();    //advance 1 byte
            else if (twobytes == 0x8203)
                binr.ReadInt16();   //advance 2 bytes
            else
                return RSAKeyInfo;

            bt = binr.ReadByte();
            if (bt != 0x00)     //expect null byte next
                return RSAKeyInfo;

            twobytes = binr.ReadUInt16();
            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
                binr.ReadByte();    //advance 1 byte
            else if (twobytes == 0x8230)
                binr.ReadInt16();   //advance 2 bytes
            else
                return RSAKeyInfo;

            twobytes = binr.ReadUInt16();
            byte lowbyte = 0x00;
            byte highbyte = 0x00;

            if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
                lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
            else if (twobytes == 0x8202)
            {
                highbyte = binr.ReadByte(); //advance 2 bytes
                lowbyte = binr.ReadByte();
            }
            else
                return RSAKeyInfo;
            byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
            int modsize = BitConverter.ToInt32(modint, 0);

            byte firstbyte = binr.ReadByte();
            binr.BaseStream.Seek(-1, SeekOrigin.Current);

            if (firstbyte == 0x00)
            {   //if first byte (highest order) of modulus is zero, don't include it
                binr.ReadByte();    //skip this null byte
                modsize -= 1;   //reduce modulus buffer size by 1
            }

            byte[] modulus = binr.ReadBytes(modsize);   //read the modulus bytes

            if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
                return RSAKeyInfo;
            int expbytes = (int)binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
            byte[] exponent = binr.ReadBytes(expbytes);


            RSAKeyInfo.Modulus = modulus;
            RSAKeyInfo.Exponent = exponent;

            return RSAKeyInfo;
        }
        catch (Exception)
        {
            return RSAKeyInfo;
        }

        finally { binr.Close(); }
        //return RSAparams;

    }

 private static RSACryptoServiceProvider InitRSAProvider(RSAParameters rsaParam)
    {
        //
        // Initailize the CSP
        //   Supresses creation of a new key
        //
        CspParameters csp = new CspParameters();
        //csp.KeyContainerName = "RSA Test (OK to Delete)";

        const int PROV_RSA_FULL = 1;
        csp.ProviderType = PROV_RSA_FULL;

        const int AT_KEYEXCHANGE = 1;
        // const int AT_SIGNATURE = 2;
        csp.KeyNumber = AT_KEYEXCHANGE;
        //
        // Initialize the Provider
        //
        RSACryptoServiceProvider rsa =
          new RSACryptoServiceProvider(csp);
        rsa.PersistKeyInCsp = false;

        //
        // The moment of truth...
        //
        rsa.ImportParameters(rsaParam);
        return rsa;
    }

    private static int GetIntegerSize(BinaryReader binr)
    {
        byte bt = 0;
        byte lowbyte = 0x00;
        byte highbyte = 0x00;
        int count = 0;
        bt = binr.ReadByte();
        if (bt != 0x02)     //expect integer
            return 0;
        bt = binr.ReadByte();

        if (bt == 0x81)
            count = binr.ReadByte();    // data size in next byte
        else
            if (bt == 0x82)
            {
                highbyte = binr.ReadByte(); // data size in next 2 bytes
                lowbyte = binr.ReadByte();
                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
                count = BitConverter.ToInt32(modint, 0);
            }
            else
            {
                count = bt;     // we already have the data size
            }

        while (binr.ReadByte() == 0x00)
        {   //remove high order zeros in data
            count -= 1;
        }
        binr.BaseStream.Seek(-1, SeekOrigin.Current);       //last ReadByte wasn't a removed zero, so back up a byte
        return count;
    }

    private static bool CompareBytearrays(byte[] a, byte[] b)
    {
        if (a.Length != b.Length)
            return false;
        int i = 0;
        foreach (byte c in a)
        {
            if (c != b[i])
                return false;
            i++;
        }
        return true;
    }

InitRSAProviderとLoadRsaPublicKeyの上記の2つのメソッドは、PEMキーを文字列として.Netで使用できるようにするためのチュートリアルから取得されました。

10
Ruairi O'Brien

RSA暗号化モードに関するいくつかの情報を見ると、PKCS#1 v1.5(Decrypt(..., false)を呼び出しているために使用している)のように見えます。

"...長さ最大k-11オクテット(kはRSAのオクテット長)のメッセージを操作できます係数)"

(RFC 3447、強調鉱山)。

キーが128バイトであることを示すエラーメッセージに基づくと、128-11 = 117バイトを超えるメッセージに対してPKCS#1 v1.5を使用してRSA(en | de)暗号化を実行できないことを意味します。

RSAを使用してメッセージを直接暗号化する代わりに、対称アルゴリズムを使用してメッセージの本文を暗号化し、RSAを使用して対称暗号化キーのみを暗号化する必要があります。メッセージが適度に短い場合(つまり、キーサイズが117バイト未満)にのみ、RSAを使用してメッセージを直接暗号化することを検討する必要があります。

以下のコメントで示すように、入力がBase64でエンコードされていると仮定して、以下を追加しました。

public string DecryptUsingPublic(string dataEncryptedBase64, string publicKey)
    {
        if (dataEncryptedBase64 == null) throw new ArgumentNullException("dataEncryptedBase64");
        if (publicKey == null) throw new ArgumentNullException("publicKey");
        try
        {
            RSAParameters _publicKey = LoadRsaPublicKey(publicKey, false);
            RSACryptoServiceProvider rsa = InitRSAProvider(_publicKey);

            byte[] bytes = Convert.FromBase64String(dataEncryptedBase64);
            byte[] decryptedBytes = rsa.Decrypt(bytes, false);

            // I assume here that the decrypted data is intended to be a
            // human-readable string, and that it was UTF8 encoded.
            return Encoding.UTF8.GetString(decryptedBytes);
        }
        catch
        {
            return null;
        }
    }
12
Iridium

RSAは.NETに組み込まれています:System.Security.Cryptography.RSA

公開鍵を使用した暗号化と秘密鍵を使用した復号化は、非対称アルゴリズムで行われる最も一般的なことの1つであり、誰でも安全に何かを送信できます。

逆の方法で行う場合:秘密鍵を使用して暗号化し、公開鍵で復号化すると、メッセージが秘密鍵の所有者によって送信されたことが証明されます。しかし、おそらく誰でも公開鍵を入手できるため、メッセージ全体を暗号化する傾向はなく、代わりに秘密鍵を使用してデータのハッシュに署名するだけです。したがって、RSACryptoServiceProviderにはそれを行うためのSign__メソッドとVerify__メソッドがあります。

それでも、パートナーが主張する場合はEncrypt/Decryptメソッドがあります。

そうは言っても、Microsoftの暗号クラスは扱いが少し難しく、特定の領域に欠けていることがわかりました。 弾力がある城のライブラリ をはるかに好みます。

14
Duncan Smart

RSAはnotであり、任意のデータを暗号化することを目的としていません(@Iridiumがすでに言ったように)。制限は使用するパディングによって異なり、パディングの使用は非常に重要です(MSではEncryptValueおよびDecryptValueを直接呼び出すことはできません)。

これを行うrightの方法は、対称暗号(AESなど)を使用して文字列を暗号化し、RSA公開鍵を使用して秘密鍵を暗号化することです。

相手側は、RSA秘密鍵を使用して秘密(AES)鍵を復号化できます。次に、キーを使用して文字列を復号化します。

私は古い(しかしまだ最新の) ブログエントリソースコード(C#)が含まれているという件名を持っています。

6
poupou