【问题标题】:Asymmetric cryptographic algorithm for large text data大文本数据的非对称密码算法
【发布时间】:2020-10-03 07:26:15
【问题描述】:

我正在寻找满足以下要求的解决方案:

假设有:安装在计算设备上并对其进行控制的应用程序、使用此应用程序的用户以及为应用程序提供一些支持的维护者。应用程序具有配置,例如在文件或数据库中。维护人员在需要时手动更新配置,例如每周更新一次。配置包含例如电子邮件列表,应用程序将其警报发送到。让我们假设,用户不可能以任何方式修改应用程序。虽然,应用程序是用 Java 编写的,所以用户很容易复制和调试它。在内部,应用程序解密应用程序内存中的配置,以便使用配置。

用户应该能够从应用程序内部查看配置。用户将无法更改配置,或使用自己的配置(基本相同),例如更改任何电子邮件或删除现有电子邮件或添加新的。

附加要求,非强制性:没有应用程序不能直接查看配置。我知道这几乎不可能,所以,它至少会很困难,比如在没有应用程序的情况下查看配置所需的解密。

问题:如何做到这一点,有可能吗?

我可以实现的可能解决方案,以及攻击:

1) 使用一些签名。用一些摘要对每个配置进行签名,然后在应用程序中检查摘要。攻击:据我了解,应用程序应使用存储在其中的公钥计算摘要。然后应用程序将计算的摘要与配置提供的摘要进行比较。因此,攻击很简单:用户将修改配置,然后调试应用程序,在应用程序已经计算摘要的地方放置一个断点与存储的摘要进行比较,然后用户可以转储计算的摘要并用这个替换提供的摘要计算一个。

2) 使用混合加密。在这种情况下,攻击是相同的:在解密的对称密钥可用的地方断点,转储此密钥,然后将其用于新的配置加密。

3) 使用非对称加密。维护者使用公钥加密配置,然后应用程序使用私钥解密配置。攻击很简单:用户可以从应用程序中转储私钥并派生新的公钥,然后将其用于加密。

是否有解决方案,例如“用公钥加密,然后用私钥解密”来处理大量数据(最多 10 kb),或者任何其他可能的方式来实现?

谢谢

同事们, 我有一个应用程序可以接收和存储一些只读数据。数据应可供用户阅读,但不可更改。例如,假设存在包含数据的文本文件,其中保留了一些文本可供最终用户阅读但不可编辑。应用程序应定期接收新文件,并以相同的只读模式供用户使用。 因此,用户可以访问本地内容(例如文件内容)。用户可以从应用程序复制内容,保存解密的副本等。我唯一需要防止的是用任何其他数据替换现有数据,包括更改内容或由用户添加新数据。我不需要更改检测,我需要使更改不可能(好的,尽可能难)。 我想,最简单的方法是使用一些密钥加密数据并将公钥包含在应用程序中,以便应用程序可以解密并显示数据,但没有密钥用户将无法更改内容。 我知道,标准 RSA 仅支持要加密的小数据块,通常略小于密钥长度。 (我进行了测试,发现对于 RSA 2048,Java 在 254 字节后抛出异常)我还读到,将源数据拆分为块然后加密这些块并不是一个好主意。我读到建议使用对称密钥(如 AES)进行加密和解密,然后使用 RSA 密钥对加密此 AES 密钥。 在这种情况下,我看到了一个很大的(如我所想的)安全风险——因为我的应用程序是用 Java 编写的,所以很容易调试它并转储解密的 AES 密钥,然后将其用于数据修改,即使没有任何修改应用程序本身。 所以,我的问题是:如何解决这个问题以及在这种情况下使用什么被认为是安全的? 谢谢 更新: 当然,用户可以复制文件并根据需要使用副本。目标是禁止用户更改应用程序使用的数据,而不是这些数据的副本。在非对称加密的情况下,很容易实现 - 我用我的私钥加密数据,传递给应用程序,运行时应用程序用它的公钥解密数据并使用。如果有人想更改数据,应用程序将无法正确解密数据,数据将被破坏,应用程序将无法工作,直到数据被恢复。

【问题讨论】:

  • 如果用户可以复制数据,那么您无法阻止他们更改数据。我根本没有看到加密进入这个问题。
  • 你不想加密它,这很容易绕过。您可以做的最好的事情是在安全环境中签署数据(文件或其他),然后将数据与签名一起分发到您的应用程序中。用于签名的私钥永远不会离开您的安全环境,但公钥已嵌入您的应用程序中。然后,在“提供”内容之前,您验证签名并拒绝提供未通过验证的内容。
  • 这是我的专业领域,因此请避免与我交谈。签名确实使用了非对称加密技术,而且您的加密方案很容易被破解,因为密钥必须正确存在于您的应用程序中。您实际上难以描述的是签名。 ...用它的公钥解密数据... 这就是签名的本质。
  • @PresidentJamesK.Polk 我绝对知道签名的工作原理。在我的用例中,我无法添加任何数据,例如签名。不要生气,但我真的厌倦了这里的大多数答案,比如:“如何使用泵抽水?”回答:“你需要每天洗手两次!”,“使用桶”,“解释你需要这些水做什么”等等。每次我花很多时间来制定我的问题,以防止这些烦人的无意义的答案,而且每次看到很多。唯一的好处是,通常会出现知道真正解决方案的人
  • 如果您可以分发加密文件,那么您可以分发签名文件。有些方案称为 "signature with message recovery" 方案,但它们似乎不适合您的应用程序。

标签: java encryption cryptography rsa public-key-encryption


【解决方案1】:

正如@President James K. Polk 在他的一个 cmets 中所说,在我看来,唯一的解决方案是签署只读数据并仅在数据经过验证后才使用它。在您的“可能的解决方案和攻击”部分中,您写道程序比较了一些很容易被覆盖的数字。通常签名使用 (SHA256-) 对数据进行哈希处理,但您可以在不先对其进行哈希处理的情况下对完整数据进行签名,并且 4 KB 的数据不会给我的桌面 Java 带来性能问题。

我设置了一个完整的工作示例来模拟维护者端和应用程序端,作为一个小礼物,我使用 AES CBC(由签名生成的密钥)加密明文。我知道这种加密模式不是“最好的方式”,因为数据不需要完全保密但不直接可见,这是一个很好的解决方案。

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.util.Arrays;
import java.util.Random;

public class Cyptosystem {
    public static void main(String[] args) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchPaddingException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException {
        System.out.println("Cryptosystem for \nhttps://stackoverflow.com/questions/62361876/asymmetric-cryptographic-algorithm-for-large-text-data/62398723#62398723");
        System.out.println("Warning: this program is experimental and has no proper exception handling");
        byte[] plaintext = new byte[4000]; // content to get secured, provided by maintainers
        byte[] ciphertext = new byte[0]; // encryped plaintext
        byte[] dataForApp = new byte[0]; // initvector | ciphertext
        new Random().nextBytes(plaintext);

        // generate rsa keypair
        System.out.println("generate the RSA keypair");
        KeyPairGenerator rsaGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom random = new SecureRandom();
        rsaGenerator.initialize(4096, random);
        KeyPair rsaKeyPair = rsaGenerator.generateKeyPair();
        PrivateKey rsaPrivateKey = rsaKeyPair.getPrivate(); // for signature
        PublicKey rsaPublicKey = rsaKeyPair.getPublic(); // for verification, implemented in app resources

        System.out.println("sign & encrypt the plaintext");
        // signature done by maintainers
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initSign(rsaPrivateKey);
        sig.update(plaintext);
        byte[] signature = sig.sign(); // provide to app as byte array, hexstring or base64 as you like
        // encrypt plaintext with signature
        byte[] initvector = new byte[16];
        SecureRandom secureRandom = new SecureRandom();
        secureRandom.nextBytes(initvector); // random initvector
        // you can use another aes mode for encryption e.g. gcm
        // you can use a hmac as key derivation ...
        // i'm using sha256 to get a 32 byte long key
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] aeskey = md.digest(signature);
        SecretKeySpec keySpec = new SecretKeySpec(aeskey, "AES");
        IvParameterSpec ivKeySpec = new IvParameterSpec(initvector);
        Cipher aesCipherEnc = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        aesCipherEnc.init(Cipher.ENCRYPT_MODE, keySpec, ivKeySpec);
        ciphertext = aesCipherEnc.doFinal(plaintext);
        // copy iv | ciphertext
        dataForApp = new byte[ciphertext.length + 16]; // initvector length 16 byte
        System.arraycopy(initvector, 0, dataForApp, 0, initvector.length);
        System.arraycopy(ciphertext, 0, dataForApp, initvector.length, ciphertext.length);
        // send the dataForApp to the app (as byte array, hex string, base64 as you like
        System.out.println("dataForApp length: " + dataForApp.length);

        // app side, receive dataForApp & signature, already has public key
        byte[] dataForAppApp = dataForApp.clone();
        byte[] signatureApp = signature.clone();
        System.out.println("decrypt and verify the signature");
        // get initvector & ciphertext
        byte[] initvectorApp = new byte[16];
        byte[] ciphertextApp = new byte[(dataForAppApp.length - 16)];
        System.arraycopy(dataForAppApp, 0, initvectorApp, 0, 16);
        System.arraycopy(dataForAppApp,16, ciphertextApp, 0, (dataForAppApp.length - 16));
        // decrypt data
        MessageDigest mdApp = MessageDigest.getInstance("SHA-256");
        byte[] aeskeyApp = md.digest(signature);
        SecretKeySpec keySpecApp = new SecretKeySpec(aeskeyApp, "AES");
        IvParameterSpec ivKeySpecApp = new IvParameterSpec(initvectorApp);
        Cipher aesCipherDec = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        aesCipherDec.init(Cipher.DECRYPT_MODE, keySpecApp, ivKeySpecApp);
        byte[] decrypttext = aesCipherDec.doFinal(ciphertextApp);
        System.out.println("plaintext equals decrypttext: " +  Arrays.equals(decrypttext, plaintext));
        // don't use the ciphertext as the signature is not verified
        Signature sigApp = Signature.getInstance("SHA256withRSA");
        sigApp.initVerify(rsaPublicKey);
        sigApp.update(decrypttext);
        boolean signatureVerified = sigApp.verify(signatureApp);
        System.out.println("signatureApp verified: " + signatureVerified);
        System.out.println("if verified == true we can use the decrypttext");
    }
}

【讨论】:

  • Java 8 (52.0) RSASignature.class,即使没有源码,idea自动反编译,第142行:return MessageDigest.isEqual(var2, var5);
  • 好吧,现在我终于明白了。谢谢!大声笑)))))只需要真正调试一个例子))))
【解决方案2】:

首先,don't roll your own crypto。密码学非常难,如果你犯了任何错误,它会有一些漏洞,你可以通过使用完善的库来完成繁重的工作来避免。例如,您可以使用libsodium。它有很多抽象,并且可能有你需要的解决方案。

除此之外,让我们讨论一下如何使它更安全:用户需要能够阅读内容,但不能对其进行编辑。 “无法编辑”到底是什么意思?难道他不能在本地修改任何东西,或者只是不能像被授权那样上传到您的服务器吗?

如果是前者,加密对你没有多大帮助 - 你需要能够在本地解密它,因此攻击者总是可以转储你的进程的内存来获取数据 - 当然这很困难,但绝对有可能.只是不允许人们在您的应用程序中编辑/保存/下载将是您可以获得的最强有力的保证。

如果是后者,那么使用身份验证将是可行的方法 - 是一种简单的方法,例如使用用户和密码进行 HTTP 基本身份验证,或者对要上传的文件进行签名。在您的应用程序端处理身份验证将是更实用的方法。

【讨论】:

  • RSA 和 AES 不是我的,它们被认为是工业标准。我想我尽可能具体。通过编辑我的意思是通常意味着什么 - 文件内容的更改。我想这很清楚。感谢您的努力,但您似乎回答了另一个问题,而不是我的问题。将编辑我的问题,使其更加具体,即使对于没有阅读过它的人来说也是如此,哈哈
  • RSA 和 AES 是加密原语,能够对单个块进行编码。要正确使用它们对有效负载进行加密/签名,需要一个明确指定的算法/协议,如果不使用正确的填充、指令和参数,将导致容易破解的加密。真的,不要直接使用它们,如果你真的需要,请使用库。
  • 明确一点:实际上不可能阻止具有系统权限的攻击者更改应用程序中的数据;如果他们愿意,总会有办法直接修改您的地址空间。您能做的最好的事情就是在服务器端验证客户端的响应。
  • 什么反应?什么服务器?有什么特权?你在说什么?
  • 如果存在解决方案,例如,游戏的反作弊将是一个已解决的问题。但事实并非如此,因为无法保证没有修改。
【解决方案3】:

非对称使用公钥进行加密使用私钥进行解密。由于应用程序必须能够解密应用程序需要知道私钥的数据,这就是常用算法的问题,因为对于 RSA 或 ECIES,公钥可以从私钥派生。因此,在使用公钥加密后派生公钥并存储更改/附加的数据并不是真正的问题。

第二件事是 - 你没有指定你的文本会有多“大” - 一些 KB、MB、GB?

几个月前,我测试了一些“后量子安全”的“新”算法,作为示例,我使用了 Bouncy Castle Crypto 提供程序提供的 McEliece Fujisaki 算法(我使用了 1.65 版,bcprov-jdk15to18-165 .jar)。

程序创建一个 50 MB 大字节数组,该数组使用公钥加密并使用私钥解密。 目前我没有找到任何公钥派生方法,所以你肯定需要知道私钥和公钥。 我没有测试更大的字节数组,因为这个参数取决于目标系统的内存(你需要双倍内存 因为完整的数据是在 ciphertextByte 中捕获的,然后在 decryptedtextByte 中再次捕获)。

2020 年 6 月 16 日编辑: 詹姆斯·K·波尔克总统编写了一种方法,可以轻松地从给定的私钥中检索公钥。源代码可在他的 GitHub-Repo (https://github.com/james-k-polk/McEliece/blob/master/McElieceRecoverPublicFromPrivate.java) 中找到,为了以后方便,请在此答案末尾显示。 因此,可以访问私有 McEliece 密钥的每个人都能够使用检索到的公钥加密数据!感谢詹姆斯总统的帮助。

以下是控制台上的输出:

McEliece Fujisaki Pqc Encryption
key generation
PrivateKey length: 4268   algorithm: McEliece-CCA2 format: PKCS#8
PublicKey  length: 103429 algorithm: McEliece-CCA2 format: X.509

initialize cipher for encryption
pt length:    52428800 (50 mb)
ct length:    52429056 (50 mb)

initialize cipher for decryption
dt length:    52428800 (50 mb)

compare plaintext <-> decryptedtext: true

类 McElieceFujisakiPqcEncryptionLargeData.java

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.mceliece.*;
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PrivateKey;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PublicKey;

import java.security.*;
import java.util.Arrays;
import java.util.Random;

public class McElieceFujisakiPqcEncryptionLargeData {
    public static void main(String[] args) throws InvalidCipherTextException {
        System.out.println("McEliece Fujisaki Pqc Encryption");
        if (Security.getProvider("BCPQC") == null) {
            Security.addProvider(new BouncyCastlePQCProvider());
            // used Bouncy Castle: bcprov-jdk15to18-165.jar
        }
        System.out.println("key generation");
        SecureRandom keyRandom = new SecureRandom();
        McElieceCCA2Parameters params = new McElieceCCA2Parameters();
        McElieceCCA2KeyPairGenerator mcElieceCCA2KeyGen = new McElieceCCA2KeyPairGenerator();
        McElieceCCA2KeyGenerationParameters genParam = new McElieceCCA2KeyGenerationParameters(keyRandom, params);
        mcElieceCCA2KeyGen.init(genParam);
        AsymmetricCipherKeyPair pair = mcElieceCCA2KeyGen.generateKeyPair();
        AsymmetricKeyParameter mcEliecePrivateKey = pair.getPrivate();
        AsymmetricKeyParameter mcEliecePublicKey = pair.getPublic();
        PrivateKey privateKey = new BCMcElieceCCA2PrivateKey((McElieceCCA2PrivateKeyParameters) pair.getPrivate()); // conversion neccessary only for key data
        PublicKey publicKey = new BCMcElieceCCA2PublicKey((McElieceCCA2PublicKeyParameters) pair.getPublic()); // conversion neccessary only for key data
        System.out.println("PrivateKey length: " + privateKey.getEncoded().length + "   algorithm: " + privateKey.getAlgorithm() + " format: " + privateKey.getFormat());
        System.out.println("PublicKey  length: " + publicKey.getEncoded().length + " algorithm: " + publicKey.getAlgorithm() + " format: " + publicKey.getFormat());
        // generate cipher for encryption
        System.out.println("\ninitialize cipher for encryption");
        ParametersWithRandom param = new ParametersWithRandom(mcEliecePublicKey, keyRandom);
        McElieceFujisakiCipher mcElieceFujisakiDigestCipher = new McElieceFujisakiCipher();
        mcElieceFujisakiDigestCipher.init(true, param);
        // random plaintext
        byte[] plaintext = new byte[52428800]; // 50 mb, 50 * 1024 * 1024
        new Random().nextBytes(plaintext);
        System.out.println("pt length:    " + plaintext.length + " (" + (plaintext.length / (1024 * 1024)) + " mb)");
        byte[] ciphertext = mcElieceFujisakiDigestCipher.messageEncrypt(plaintext);
        System.out.println("ct length:    " + ciphertext.length + " (" + (ciphertext.length / (1024 * 1024)) + " mb)");
        System.out.println("\ninitialize cipher for decryption");
        mcElieceFujisakiDigestCipher.init(false, mcEliecePrivateKey);
        byte[] decryptedtext = mcElieceFujisakiDigestCipher.messageDecrypt(ciphertext);
        System.out.println("dt length:    " + decryptedtext.length + " (" + (decryptedtext.length / (1024 * 1024)) + " mb)");
        System.out.println("\ncompare plaintext<-> decryptedtext: " + Arrays.equals(plaintext, decryptedtext));
    }
}

James K. Polk 总统的公钥检索课程,可在 MIT-Licence 下获得:

package com.github.jameskpolk;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.pqc.crypto.mceliece.*;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PrivateKey;
import org.bouncycastle.pqc.jcajce.provider.mceliece.BCMcElieceCCA2PublicKey;
import org.bouncycastle.pqc.math.linearalgebra.*;

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;

public class McElieceRecoverPublicFromPrivate {
    private static final SecureRandom RAND = new SecureRandom();

    public static AsymmetricCipherKeyPair generateKeyPair() {
        McElieceCCA2KeyPairGenerator kpg = new McElieceCCA2KeyPairGenerator();
        McElieceCCA2Parameters params = new McElieceCCA2Parameters();
        McElieceCCA2KeyGenerationParameters genParam = new McElieceCCA2KeyGenerationParameters(RAND, params);
        kpg.init(genParam);
        return kpg.generateKeyPair();
    }

    public static McElieceCCA2PublicKeyParameters recoverPubFromPriv(McElieceCCA2PrivateKeyParameters priv) {
        GF2mField field = priv.getField();
        PolynomialGF2mSmallM gp = priv.getGoppaPoly();
        GF2Matrix h = GoppaCode.createCanonicalCheckMatrix(field, gp);
        Permutation p = priv.getP();
        GF2Matrix hp = (GF2Matrix) h.rightMultiply(p);
        GF2Matrix sInv = hp.getLeftSubMatrix();
        GF2Matrix s = (GF2Matrix) sInv.computeInverse();
        GF2Matrix shp = (GF2Matrix)s.rightMultiply(hp);
        GF2Matrix m = shp.getRightSubMatrix();

        GoppaCode.MaMaPe mmp = new GoppaCode.MaMaPe(sInv, m, p);
        GF2Matrix shortH = mmp.getSecondMatrix();
        GF2Matrix shortG = (GF2Matrix) shortH.computeTranspose();
        // generate public key
        return new McElieceCCA2PublicKeyParameters(
                priv.getN(), gp.getDegree(), shortG,
                priv.getDigest());
    }

    public static void main(String[] args) throws Exception{

        // generate a McEliece key pair

        AsymmetricCipherKeyPair bcKeyPair = generateKeyPair();
        McElieceCCA2PrivateKeyParameters bcPriv = (McElieceCCA2PrivateKeyParameters) bcKeyPair.getPrivate();
        BCMcElieceCCA2PrivateKey priv = new BCMcElieceCCA2PrivateKey(bcPriv);

        // get the first public key

        McElieceCCA2PublicKeyParameters bcPub1 = (McElieceCCA2PublicKeyParameters) bcKeyPair.getPublic();
        BCMcElieceCCA2PublicKey pub1 = new BCMcElieceCCA2PublicKey(bcPub1);

        // Now generate a second public key for the private key

        McElieceCCA2PublicKeyParameters bcPub2 = recoverPubFromPriv(bcPriv);
        BCMcElieceCCA2PublicKey pub2 = new BCMcElieceCCA2PublicKey(bcPub2);

        // print some info about sizes

        System.out.printf("Size of encrypted messages in bits(bytes): %d(%d)\n",
                priv.getEncoded().length, priv.getEncoded().length / 8);
        System.out.printf("private key length: %d\n", bcPriv.getK());
        System.out.printf("public key1 length: %d\n", pub1.getEncoded().length);
        System.out.printf("public key2 length: %d\n", pub2.getEncoded().length);

        // now encrypt different messages with each public key.

        String message1 = "Deposits should be made to account # 3.1415929";
        String message2 = "Deposits should be made to account # 2.71828";

        ParametersWithRandom params1 = new ParametersWithRandom(bcPub1, RAND);
        ParametersWithRandom params2 = new ParametersWithRandom(bcPub2, RAND);

        McElieceFujisakiCipher mcElieceFujisakiDigestCipher1 = new McElieceFujisakiCipher();
        McElieceFujisakiCipher mcElieceFujisakiDigestCipher2 = new McElieceFujisakiCipher();
        mcElieceFujisakiDigestCipher1.init(true, params1);
        mcElieceFujisakiDigestCipher2.init(true, params2);

        byte[] ciphertext1 = mcElieceFujisakiDigestCipher1.messageEncrypt(message1.getBytes(StandardCharsets.UTF_8));
        byte[] ciphertext2 = mcElieceFujisakiDigestCipher2.messageEncrypt(message2.getBytes(StandardCharsets.UTF_8));
        System.out.println("ct1 length:    " + ciphertext1.length + " (" + (ciphertext1.length / (1024 * 1024)) + " mb)");
        System.out.println("ct2 length:    " + ciphertext2.length + " (" + (ciphertext2.length / (1024 * 1024)) + " mb)");

        mcElieceFujisakiDigestCipher1.init(false, bcPriv);
        mcElieceFujisakiDigestCipher2.init(false, bcPriv);

        byte[] decryptedtext1 = mcElieceFujisakiDigestCipher1.messageDecrypt(ciphertext1);
        byte[] decryptedtext2 = mcElieceFujisakiDigestCipher2.messageDecrypt(ciphertext2);

        System.out.printf("Decrypted message 1: %s\n", new String(decryptedtext1, StandardCharsets.UTF_8));
        System.out.printf("Decrypted message 2: %s\n", new String(decryptedtext2, StandardCharsets.UTF_8));

    }
}

【讨论】:

  • 示例程序使用 PUBLIC 密钥加密并使用我的答案中所写的 PRIVATE 密钥解密。当有一种算法可以做到这一点时,派生密钥问题将会上升(我没有找到一个,但我不是像其他人一样的加密专家)。 4 MB 的数据绝对不是问题,因为该示例适用于 50 MB 字节数组。 1:40am 表示“东欧”,比德国早 1 小时 :-)
  • 为什么你认为公钥,或者更确切地说是 a 公钥,不能从私钥派生?
  • 我对一般的后量子加密或特别是 McEliece 不太了解。只需查看key generation as described in WIkipedia 中的步骤,我发现生成另一个生成器矩阵 G' 和 G'_hat = S*G'*P 没有障碍。看起来 (G'_hat, t) 是同一私钥的另一个公钥,因此任何使用 (G'_hat, t) 加密的消息也将由同一私钥解密。
  • 我在创建 等效 公钥方面是错误的,但我只能从使用 this code 的实例中恢复实际的公钥。如果你能看一下它是否有意义,我会很感激。谢谢你,我玩 Bouncycastle McEliece 代码玩得很开心。
  • @President James K. Polk:我测试了检索方法,我可以成功地从私钥中检索到有效的公钥。当我在使用智能手机时,我将相应地编辑上面的答案。简而言之:因为我们确实有一个检索功能,所以现在任何可以访问私钥的人都能够加密数据。谢谢,我很高兴在星期天给你带来一些乐趣:-)
【解决方案4】:

我知道,标准的 RSA 只支持要加密的小数据块,

这就是我们使用hybrid cryptosystem 的原因。数据使用对称密码(数据密钥)加密,对称数据密钥使用非对称密码加密。

我不需要更改检测,我需要使更改不可能(好的,尽可能困难)。

如果您无法强制执行任何只读输入/文件系统,那么检测更改是您能做的最好的事情。要么是解密失败,要么是签名失败。

实际上,为了确保数据完整性,我真的会使用签名,而不是纯粹的加密。我看你不想要那个,但最后它会在那里。一些密码/密码模式具有延展性 - 即使在加密且没有任何身份验证(mac、签名)的情况下,数据也可以更改,解密是有效的,您将无法检测到完整性失败。

如果您只是依靠应用程序来检测解密失败后数据是否损坏,那么您正在创建一个完美的解密预言机(破坏安全性)

我想,最简单的方法是使用一些密钥加密数据并将公钥包含在应用程序中,以便应用程序可以解密和显示数据,但如果没有密钥,用户就不会可以更改内容。

在您的应用中硬编码的任何内容都可以视为已公开/公开。您正确识别了风险。如果您有专门的用户,则不会阻止用户更改应用程序中的密钥并传递无效数据。所以 - 对于在客户端运行的任何东西,您可以使完整性更强,但并不完美。最后——你必须对对手的能力做出一些假设。

我用我的私钥加密数据,传递给应用程序,运行时应用程序用它的公钥解密数据,

理论上(数学上)你可以这样做,但大多数当前的库不会让你以错误的方式使用密钥对(私钥用于解密或签名,公共用于加密或验证)。如果你想自己编写这样的解决方案,你就有可能产生你可能不知道的弱点(适当的填充、时间、..)

我认为该方案甚至存在一些弱点(使用私钥加密),但我不记得细节,有些人对该主题有更深入的了解(例如来自 cmets 的 James Polk)

编辑:

创建签名或 MAC 的示例:https://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html

btw - 使用 aes-gcm Java Cipher 实现自动将 mac 标签附加到密文的末尾

【讨论】:

  • 1) 我知道什么是混合加密。我用它。在这种情况下它不适合,因为检索对称密钥非常容易。 2) 是的,所有这些任务都是关于更改检测。唯一的问题 - 如何做到这一点?我不知道如何实施这样的签名。假设每个数据都有一个摘要。用户只需调试应用程序,获取应用程序对变化数据计算的摘要,然后将提供的摘要替换为计算的摘要。
  • 3) 我试过 RSA,它支持。我在“安全”网站上问过,那里的人告诉我有这样的算法,但随后结束了我的问题,哈哈。还尝试了充气城堡的密码,但无法做到这一点。不幸的是,那里的人很生气,他们不知道答案,他们只是给我关于我的要求的毫无意义的建议和其他无用的回复,比如“它非常非常复杂”,而不是任何加密建议跨度>
  • @BbIKTOP 1 - 正确完成后检索密钥并不容易/不可能。 2 - 正如您已经发现的那样,仅摘要(散列)无助于检测更改。您需要签名(rsa 签名是带有适当填充的解密操作的摘要)或经过身份验证的加密(aes-gcm、hmac、..)。 3 - 有些人很生气,因为他们说得对(可能没有详细解释),但你拒绝理解。同样,创建自己的方案并不复杂,但容易产生您可能没有意识到的弱点。
  • @BbIKTOP 您正在更改问题的范围,那么问题可能并且将会关闭。答案仍然假设应用程序的完整性。如果您赋予用户(对手)更改验证/解密密钥的能力,那么您确实无法控制用户硬件上发生的事情。
  • 1) 例如? 2)我的意思是签名摘要,当然。也没有服务器来实现身份验证,如果我理解正确的话 3)我的看法不同。它看起来像:“更改您的要求,以便我的答案有效”
【解决方案5】:

一个有趣的问题。据我从您的问题中可以看出,数据本身并不是秘密。您的问题是用户不应更改数据(或者您应该能够检测到他或她已更改数据)。在这种情况下,散列函数(可能包括加密散列函数)可能是更好的方法。请参阅 https://en.wikipedia.org/wiki/Hash_functionhttps://en.wikipedia.org/wiki/Cryptographic_hash_function。如果您使用哈希函数,那么您始终可以检测用户是否尝试更改数据。

加密哈希函数是单向的,因此您不需要在程序中存储任何密钥。

【讨论】:

  • 不,我不需要检测。我只需要阻止用户更改现有数据并添加他们自己的数据。非对称加密适合我的场景,但它不适用于大数据块。我编辑了问题以反映这一点
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多