当前位置: 首页 > 知识库问答 >
问题:

Bouncy Castle:多个PGPSignature在同一时间范围内相同(~1秒)

徐绪
2023-03-14
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;
import java.util.Iterator;

import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPKeyPair;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPair;
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;

public class PgpSimpleSigner {

    private static final DateTimeFormatter FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS" + "SSS" + "SSS");

    public         final PGPPrivateKey     privateKey;
    public         final PGPPublicKey      publicKey;

    private PgpSimpleSigner() throws IOException, PGPException {

        final RSAKeyGenerationParameters kgp = new RSAKeyGenerationParameters(BigInteger.valueOf(0x10001), new SecureRandom(), 2048, 12);
        final RSAKeyPairGenerator        kpg = new RSAKeyPairGenerator();
                                         kpg.init(kgp);

        final PGPKeyPair  keyPair = new BcPGPKeyPair(PGPPublicKey.RSA_SIGN, kpg.generateKeyPair(), new Date());

        this.privateKey = keyPair.getPrivateKey();
        this.publicKey  = keyPair.getPublicKey();
    }

    private PGPSignature sign(final String signMeString) throws Exception {

        final int keyAlgorithm  = PublicKeyAlgorithmTags.RSA_SIGN;
        final int hashAlgorithm = PGPUtil.SHA256;

        final JcaPGPContentSignerBuilder csb = new JcaPGPContentSignerBuilder(keyAlgorithm, hashAlgorithm);
                                         csb.setProvider(new BouncyCastleProvider());

        final PGPSignatureGenerator          sGen  = new PGPSignatureGenerator(csb);
        final PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();

        sGen.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, this.privateKey);

        final Iterator<String> it = this.publicKey.getUserIDs();

        if (it.hasNext()) {
            spGen.addSignerUserID(false, it.next());
            sGen .setHashedSubpackets(spGen.generate());
        }

        sGen.update(signMeString.getBytes());

        return sGen.generate();
        /*
         * The above logic based on Method
         * signFile(String, InputStream, OutputStream, char[], String)
         * in
         * org.bouncycastle.openpgp.examples.ClearSignedFileProcessor.
         * 
         * ...but without the complicated CR/LF & Whitespace logic
         * as we know our input String is RFC 4880 compliant.
         */
    }

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

        final PgpSimpleSigner pgpSimpleSigner = new PgpSimpleSigner();

        byte[] bcSigBytesPrev = {};
        long   t0             = System.nanoTime();

        while (true) {
            final long         nsSinceDelta = System.nanoTime() - t0;
            final PGPSignature bcSig        = pgpSimpleSigner.sign("Sign me, I'm RFC 4880 compliant");
            final byte[]       bcSigBytes   = bcSig.getSignature();

            if (Arrays.compare(bcSigBytesPrev,  bcSigBytes) != 0) {
                               bcSigBytesPrev = bcSigBytes;
                
                System.out.println(FMT.format(ZonedDateTime.now()) + "\t" + nsSinceDelta + "\t" + Base64.getEncoder().encodeToString(bcSigBytes));

                t0 = System.nanoTime();
            }
        }
    }
}

共有1个答案

计燕七
2023-03-14

检查org.bouncycastle.bcpg.sig.SignatuRecreationTime.TimeTobytes()的源代码,了解日期值如何添加到字节数组中进行签名:

protected static byte[] timeToBytes(
    Date    date)
{
    byte[]    data = new byte[4];
    long        t = date.getTime() / 1000;
    
    data[0] = (byte)(t >> 24);
    data[1] = (byte)(t >> 16);
    data[2] = (byte)(t >> 8);
    data[3] = (byte)t;
    
    return data;
}

如您所见,使用的是最小到秒的当前时间(date.getTime()返回毫秒,/1000“删除”毫秒部分)。

看起来这是符合RFC4880-5.9的。文字数据包(标记11)部分:

 - A four-octet number that indicates a date associated with the
   literal data.  Commonly, the date might be the modification date
   of a file, or the time the packet was created, or a zero that
   indicates no specific time.

所以它看起来像是在按指定的方式工作。

 类似资料:
  • 问题内容: 因此,我试图生成的是特定时间范围内的所有小时。 因此,鉴于范围从上午11点到下午2:00,我将得到: 我试图避免必须在商店每隔特定的小时存储一次,而只存储范围(我需要将小时数与其他时间进行比较) 谢谢 问题答案: 如果您有数字表(如果没有,请单击链接以创建一个表)… 如果这是专门的,则可以创建仅包含24个值的小时表。

  • 我想使用ManagedBean类Users。java,创建两个具有不同作用域的bean实例。我尝试了两种方法: 1.为用户使用ManagedBean和SessionScope注释(这将创建一个带有会话范围的用户),并在faces-config.xml声明另一个来自用户的MenagedBean,带有请求范围。 注意:“组件”来自Spring框架,暂时可以忽略。 在faces-config.xml:

  • 我正在尝试编写一个素数筛生成器,我将其转换为打印列表,然后在给定范围内打印素数。我很确定我的配对数是正确的,但出于某种原因,我在素数列表中得到了一些非素数的额外值。(我马上发现了这一点,因为我在输出中的最后一个值是3599,这不是素数)。我不确定我是否有某种逻辑错误,所以任何帮助都很棒。

  • 我有一个付费应用程序我的数据库是MYSQL,我想知道有多少设备是登录在同一时间与同一用户&通过 知道吗?

  • 问题内容: 我有一个使用测试范围的库L v1.0.0的项目A。项目A还依赖于项目B(具有范围编译),而B则可传递地依赖于库L v1.0.0(具有范围编译)。 为什么项目A的库L的最终范围是“测试”?它在运行时导致我NotClassDefFoundError。似乎库L上项目A的依赖项定义覆盖了L上的传递性依赖项。 怎么了 我的项目A仅将L用于单元测试,因此我定义了“测试”范围的依赖项。但是,最后,我

  • 问题内容: 我想从一维numpy数组(或向量)中选择多个不相邻的范围。 假设: 当然,这可行: 这可以通过单个索引获取: 但是,如果我要选择范围,该怎么办? 我试过了: 有没有简单的方法可以执行此操作,或者我需要分别生成它们并进行连接? 问题答案: 您需要在索引之前或之后进行串联。 使它变得容易 扩展切片并将其连接。 您可以混合切片和列表: 在索引之前进行连接可能比在之后进行连接要快,但是对于这样