【问题标题】:c# RSA encrypt with private keyc# RSA 用私钥加密
【发布时间】:2015-12-25 07:19:52
【问题描述】:

用公钥加密和用私钥解密时加密和解密成功:

使用公钥进行 C# 加密(成功)

   public string EncryptData(string data) {

        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
        rsa.FromXmlString(xml); //public key

        var cipher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);

        return Convert.ToBase64String(cipher );
    }

用私钥进行Java解密(成功)

public static void decrypt() throws Exception{
    byte[] modulusBytes = Base64.getDecoder().decode(mod);
    byte[] dByte = Base64.getDecoder().decode(d);

    BigInteger modulus = new BigInteger(1, (modulusBytes));
    BigInteger exponent = new BigInteger(1, (dByte));

    RSAPrivateKeySpec rsaPrivKey = new RSAPrivateKeySpec(modulus, exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PrivateKey privKey = fact.generatePrivate(rsaPrivKey);

    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.DECRYPT_MODE, privKey);

    byte[] cipherData = Base64.getDecoder().decode(cipherByte);
    byte[] plainBytes = cipher.doFinal(cipherData);


    System.out.println(new String(plainBytes));
} 

问题来了

当c#用私钥加密,java用公钥解密时出现bad padding错误:

使用私钥进行 C# 加密(失败)

public stringEncryptData(string data) {

        RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
        rsa.FromXmlString(xml); //private key

        var cypher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);

        return  Convert.ToBase64String(cypher);
    }

用公钥解密java(失败)

public static void decryptPublic() throws Exception{

    byte[] modulusBytes = Base64.getDecoder().decode(mod);
    byte[] expBytes = Base64.getDecoder().decode(exp);

    BigInteger modulus = new BigInteger(1, (modulusBytes));
    BigInteger exponent = new BigInteger(1, (expBytes));

    RSAPublicKeySpec pubKey = new RSAPublicKeySpec(modulus, exponent);
    KeyFactory fact = KeyFactory.getInstance("RSA");
    PublicKey publicKey = fact.generatePublic(pubKey);
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipher.init(Cipher.DECRYPT_MODE, publicKey );


    byte[] cipherData = Base64.getDecoder().decode(cipherByte);
    byte[] plainBytes = cipher.doFinal(cipherData);

    System.out.println(new String(plainBytes));
} 

我知道公钥应该用于加密,私钥用于解密。但在我的情况下,我需要将公钥发送给多个客户端,以便对其私钥加密的文本进行解密。除客户端外,其他人不应阅读文本。 谁能看到我的代码有什么问题,或者为我的问题提出更好的解决方案。

【问题讨论】:

  • 用公钥加密,用私钥解密。将私钥副本提供给需要解密副本的每个人,但不要尝试反转算法。
  • @WDS 正如我的回答中所述,分发私钥也是一个非常糟糕的主意,因为您完全无法控制其他各方可能与谁共享密钥,并且大大增加了处理泄露的密钥。
  • @CY LIM:你的加密和解密方法应该是镜像的,这意味着如果你在你的 JAVA 解密函数中使用 PKCS1 填充(Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")),你也应该在你的 C# 加密函数中使用 PKCS1 填充var cypher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);var cypher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), RSAEncryptionPadding.Pkcs1);msdn.microsoft.com/en-us/library/mt132684%28v=vs.110%29.aspx
  • “向多个客户端发送公钥” 是错误的。在服务器端,您需要使用 client public key encrypt 并使用 server private key sign。客户端将使用服务器公钥验证签名,并使用自己的私钥解密

标签: java c# encryption cryptography rsa


【解决方案1】:

使用私钥加密/使用公钥解密是 RSA 中的合法操作,然而用于保护数据,而是用于身份验证数据的来源及其完整性。在这种情况下,加密操作通常被称为“签名”。

如您所描述的那样使用私钥加密来保护数据是不安全的,因此不容易做到这一点可能是故意的,旨在防止算法的不正确使用。

按照 cmets 中的建议将您的私钥分发给客户也是不明智的,因为您无法控制他们将密钥传递给谁(意外或其他原因)。

如果您希望对数据进行加密以便可以由多个不同的方解密,那么您应该让他们每个人都为您提供自己的公钥,并使用它为每个客户端分别加密数据。

【讨论】:

  • 第一句话就大错特错了;使用私钥时,RSA 加密未定义。您可以使用私钥进行模幂运算,仅此而已。
  • @MaartenBodewes:断章取义是没有意义的。只需阅读段落中剩下的一句话即可提供预期的含义。
  • @JamesKPolk 不,抱歉,我不同意。 PKCS#1 标准甚至对模幂运算使用不同的名称,只是为了表明签名与加密不同。在意味着生成签名时谈论 RSA 加密只会增加混乱。
【解决方案2】:

只有在使用(安全)填充方案时,RSA 加密才是安全的。 RSA 实验室已在 PKCS#1 标准中指定了 RSA 加密方案(现在是 EMC2 的一部分)。这些已被复制到 RFC 中,例如 RFC 3447: Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1

就本文档而言,加密方案包括 加密操作和解密操作,其中 加密操作从消息中生成密文 收件人的 RSA 公钥解密操作恢复 来自密文的消息与收件人的相应 RSA 私钥

所以用私钥加密是一个未定义的操作。


那么现在该怎么办:

  • 安全地分发私钥而不是公钥
  • 生成密钥对并安全地将公钥传输给发送者
  • 如果您需要身份验证/完整性而不是机密性,请使用签名生成而不是加密

而且,无论您做什么,都要读入公钥基础设施 (PKI)。这是一个非常广泛的主题,您需要先了解它,然后才能应用它。

【讨论】: