gpt4 book ai didi

java - 使用 PGP Bouncy CaSTLe 依赖项创建 CipherOutputStream

转载 作者:行者123 更新时间:2023-12-03 11:17:16 27 4
gpt4 key购买 nike

我想创建一个 OutputStream来自另一个 OutputStream其中新OutputStream将自动加密我写入的内容 OutputStream .我想使用 Bouncy CaSTLe,因为我已经将该依赖项用于其他功能。
我在互联网上看到了如何使用 Bouncy CaSTLe 加密数据的各种问题,但答案要么加密了给定的 File (我不使用文件,我使用 OutputStream s)或者有大量代码需要复制粘贴。我不敢相信这一定是那么困难。
这是我的设置:

  • 我正在使用这个 Bouncy CaSTLe 依赖项 (V1.68)
  • 我正在使用 Java 8
  • 我有一个由 https://pgpkeygen.com/ 生成的公钥和私钥.算法为 RSA, key 大小为 1024。
  • 我将公钥和私钥保存为我机器上的文件
  • 我想确保下面的测试通过

  • 我注释掉了一些代码,Cipher 上的 init 函数(代码编译,但测试失败)。我不知道应该在 init 函数中放入什么作为第二个参数。
    读取函数来自: https://github.com/jordanbaucke/PGP-Sign-and-Encrypt/blob/472d8932df303d6861ec494a3e942ea268eaf25f/src/SignAndEncrypt.java#L272 .只有 testEncryptDecryptWithoutSigning 是我写的。
    代码:
    @Test
    void testEncryptDecryptWithoutSigning() throws Exception {
    // The data will be written to this property
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    Security.addProvider(new BouncyCastleProvider());

    PGPSecretKey privateKey = readSecretKey(pathToFile("privatekey0"));
    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    //cipher.init(Cipher.ENCRYPT_MODE, privateKey);

    CipherOutputStream os = new CipherOutputStream(baos, cipher);
    // I also need to use a PrintWriter
    PrintWriter printWriter =
    new PrintWriter(new BufferedWriter(new OutputStreamWriter(
    os,
    StandardCharsets.UTF_8.name())));

    // This is an example of super secret data to write
    String data = "Some very sensitive data";

    printWriter.print(data);
    printWriter.close();

    // At this point, the data is 'inside' the byte array property
    // Assert the text is encrypted
    if (baos.toString(StandardCharsets.UTF_8.name()).equals(data)) {
    throw new RuntimeException("baos not encrypted");
    }

    PGPSecretKey publicKey = readSecretKey(pathToFile("publickey0"));
    //cipher.init(Cipher.DECRYPT_MODE, publicKey);

    ByteArrayInputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
    ByteArrayOutputStream decrypted = new ByteArrayOutputStream();

    // Decrypt the stream, but how?

    if (!decrypted.toString(StandardCharsets.UTF_8.name()).equals(data)) {
    throw new RuntimeException("Not successfully decrypted");
    }
    }

    static PGPSecretKey readSecretKey(InputStream input) throws IOException, PGPException
    {
    PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(
    PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());

    //
    // we just loop through the collection till we find a key suitable for encryption, in the real
    // world you would probably want to be a bit smarter about this.
    //

    Iterator keyRingIter = pgpSec.getKeyRings();
    while (keyRingIter.hasNext())
    {
    PGPSecretKeyRing keyRing = (PGPSecretKeyRing)keyRingIter.next();

    Iterator keyIter = keyRing.getSecretKeys();
    while (keyIter.hasNext())
    {
    PGPSecretKey key = (PGPSecretKey)keyIter.next();

    if (key.isSigningKey())
    {
    return key;
    }
    }
    }

    throw new IllegalArgumentException("Can't find signing key in key ring.");
    }

    static PGPSecretKey readSecretKey(String fileName) throws IOException, PGPException
    {
    InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));
    PGPSecretKey secKey = readSecretKey(keyIn);
    keyIn.close();
    return secKey;
    }

    static PGPPublicKey readPublicKey(String fileName) throws IOException, PGPException
    {
    InputStream keyIn = new BufferedInputStream(new FileInputStream(fileName));
    PGPPublicKey pubKey = readPublicKey(keyIn);
    keyIn.close();
    return pubKey;
    }

    /**
    * A simple routine that opens a key ring file and loads the first available key
    * suitable for encryption.
    *
    * @param input data stream containing the public key data
    * @return the first public key found.
    * @throws IOException
    * @throws PGPException
    */
    static PGPPublicKey readPublicKey(InputStream input) throws IOException, PGPException
    {
    PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(
    PGPUtil.getDecoderStream(input), new JcaKeyFingerprintCalculator());

    //
    // we just loop through the collection till we find a key suitable for encryption, in the real
    // world you would probably want to be a bit smarter about this.
    //

    Iterator keyRingIter = pgpPub.getKeyRings();
    while (keyRingIter.hasNext())
    {
    PGPPublicKeyRing keyRing = (PGPPublicKeyRing)keyRingIter.next();

    Iterator keyIter = keyRing.getPublicKeys();
    while (keyIter.hasNext())
    {
    PGPPublicKey key = (PGPPublicKey)keyIter.next();

    if (key.isEncryptionKey())
    {
    return key;
    }
    }
    }

    throw new IllegalArgumentException("Can't find encryption key in key ring.");
    }

    最佳答案

    作为初步,该网站不会生成 key 对,而是生成三个。从历史上看,在 PGP 中,实际的加密 key 和 key 对以及 PGP 用户所谓的 key 之间一直存在一些歧义,因为给定用户(或实体或角色等)拥有一个“主”或“主” key 是很常见的,并且一个或多个与该主 key 相关联的子 key 。对于 DSA+ElG key ,技术上需要使用子 key (而不是主 key )进行加密;对于 RSA,这样做被认为是一种很好的做法,因为分开管理(例如可能撤销)这些 key 通常会更好。有些人还认为使用子 key 而不是主 key 来签署数据是一种很好的做法,并且仅将主 key 用于签署 key (PGP 称之为认证 - C),但有些人不这样做。当 PGP 用户和文档谈论“ key ”时,他们通常指的是一组主 key 及其(所有)子 key ,他们说主 key 或子 key (或加密子 key 或签名子 key )表示特定的实际 key 。
    当您选择 RSA 时,该网站会生成一个使用 SCEA 的主 key ( key 对)——即所有用途——以及每个使用 SEA 的两个子 key ——所有用途都对一个子 key 有效。这是荒谬的;如果主 key 支持签名和加密,大多数 PGP 程序将永远不会使用任何子 key ,即使它没有或您覆盖它,子 key 之间也没有有意义的区别,也没有合理的方法来选择使用哪个。
    而 BouncyCaSTLe 通过更改术语加剧了这种情况:大多数 PGP 程序使用 key 作为实际 key 或一组主 key 加上子 key ,“公共(public)”和“ secret ” key 指的是每个 key 或组的一半,以及'keyring' 指代您存储的所有 key 组,通常在一个文件中,该文件可能用于许多不同的人或实体。然而,Bouncy 将主 key 及其子 key (以公共(public)或 secret 形式)的组称为 KeyRing,将可能包含多个组的文件称为 KeyRingCollection,它们都有 Public 和 Secret 变体。反正 ...
    你的第一个问题是你把它倒退了。 public key cryptography我们用公钥(一半)加密,用私钥(一半)解密,PGP(因此 BCPG)称之为 secret 。此外,因为 PGP 中的私钥/ secret key 是密码加密的,所以要使用它,我们必须先解密它。 (在 JKS 和 PKCS12 等“普通”JCA keystore 中也是如此,但在其他 keystore 中不一定如此。)
    你的第二个问题是类型。 尽管给定非对称算法的(特定)PGP key 在语义上只是该算法的 key ,加上一些元数据(身份、偏好和信任/签名信息),但 BCPG 中用于 PGP key 的 Java 对象(类)不在Java 加密体系结构 (JCA) 中用于 key 的对象的类型层次结构。简单来说,org.bouncycastle.openpgp.PGPPublicKey不是 java.security.PublicKey 的子类.所以这些关键对象must be converted到与 JCA 一起使用的 JCA 兼容对象。
    通过这些更改和一些添加,以下代码有效(FSVO 工作):

    static void SO66155608BCPGPRawStream (String[] args) throws Exception {
    byte[] plain = "testdata".getBytes(StandardCharsets.UTF_8);

    PGPPublicKey p1 = null;
    FileInputStream is = new FileInputStream (args[0]);
    Iterator<PGPPublicKeyRing> i1 = new JcaPGPPublicKeyRingCollection (PGPUtil.getDecoderStream(is)).getKeyRings();
    for( Iterator<PGPPublicKey> j1 = i1.next().getPublicKeys(); j1.hasNext(); ){
    PGPPublicKey t1 = j1.next();
    if( t1.isEncryptionKey() ){ p1 = t1; break; }
    }
    is.close();
    if( p1 == null ) throw new Exception ("no encryption key");
    PublicKey k1 = new JcaPGPKeyConverter().getPublicKey(p1);

    Cipher c1 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    c1.init(Cipher.ENCRYPT_MODE, k1);
    ByteArrayOutputStream b1 = new ByteArrayOutputStream();
    CipherOutputStream s1 = new CipherOutputStream(b1,c1);
    s1.write(plain);
    s1.close();
    byte[] cipher = b1.toByteArray();
    long id = p1.getKeyID();
    System.out.println("keyid="+Long.toString(id,16)+" "+Arrays.toString(cipher));
    if( Arrays.equals(cipher,plain) ) throw new Exception ("didn't encrypt!");

    PGPSecretKey p2 = null;
    is = new FileInputStream (args[1]);
    Iterator<PGPSecretKeyRing> i2 = new JcaPGPSecretKeyRingCollection (PGPUtil.getDecoderStream(is)).getKeyRings();
    for( Iterator<PGPSecretKey> j2 = i2.next().getSecretKeys(); j2.hasNext(); ){
    PGPSecretKey t2 = j2.next();
    if( t2.getKeyID() == id ){ p2 = t2; break; }
    }
    is.close();
    if( p2 == null ) throw new Exception ("no decryption key");
    PGPPrivateKey p3 = p2.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder().build(args[2].toCharArray()));
    PrivateKey k2 = new JcaPGPKeyConverter().getPrivateKey(p3);

    Cipher c2 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    c2.init(Cipher.DECRYPT_MODE, k2);
    ByteArrayInputStream b2 = new ByteArrayInputStream(cipher);
    CipherInputStream s2 = new CipherInputStream(b2,c2);
    byte[] back = new byte[cipher.length]; // definitely more than needed
    int actual = s2.read(back);
    s2.close();
    System.out.println ("Result->" + new String(back,0,actual,StandardCharsets.UTF_8));
    }
    (我发现将代码放在执行顺序中的一个地方会更清晰,但是您可以将其分解为多个部分,而无需进行实质性更改。)
    我保留了您的逻辑(来自 Bouncy 示例),即从第一组中选择第一个具有加密功能的公钥(无论是 master 还是 sub),其中一个上面的 Bouncy 错误地调用了 KeyRing;由于您使用的网站上面给出了主 key SCEA,因此这始终是主 key 。不可能根据是否允许加密来类似地选择 secret /私钥,并且在任何情况下都不能保证公钥文件将始终处于相同的顺序,因此选择解密 key 的正确方法是匹配来自用于加密的 key 的 keyid。
    此外,现代加密算法(像 RSA 这样的非对称和像 AES 或“3DES”这样的对称)产生的数据是任意位模式的,尤其是大多数无效的 UTF-8,因此将这些字节“解码”为 UTF-8 以进行比较明文通常会破坏您的数据;如果你想要这个(不必要的)检查,你应该像我展示的那样比较字节数组。
    最后,如果您不知道,非对称算法通常不用于加密大数据或可变大小的数据,而这正是您通常使用 Java 流的目的;这也在维基百科文章中进行了解释。这种方法直接使用 RSA PKCS1-v1_5,使用 1024 位 key ,只能处理 117 个字节的数据(可能少于 117 个字符,具体取决于)。
    如果您希望结果与任何真正的 PGP 实现兼容或互操作,那绝对不是——这意味着从 PGP key 格式转换的工作被浪费了,因为您可以直接在首先,遵循 Oracle 网站上的基本教程或 Stack 上的数百个示例。如果要与GPG或类似的互操作,则需要使用BCPG类进行PGP格式的加密和解密,它可以在纯字节流上分层,但与JCA的 Cipher{Input,Output}Stream完全不同且不兼容。 .

    关于java - 使用 PGP Bouncy CaSTLe 依赖项创建 CipherOutputStream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66155608/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com