Ed25519 cms verify noattr fail

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Ed25519 cms verify noattr fail

Weijun Wang
I'm trying out bcprov/bcpkix 1.65 and see this problem.

First create a keypair for eddsa:

  keytool -keystore ks -keypass changeit -storepass changeit -storetype pkcs12 \
          -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
          -providerpath bcprov-debug-jdk15on-165.jar \
          -genkeypair -alias ed25519 -dname CN=ed25519 -keyalg ed25519 -keysize 255 -sigalg ed25519

Then run the program below to sign (without SignedAttributes in SignerInfo) and verify, and there is an error.

I am not familiar with CMS APIs in BC, sorry I am not calling the right methods.

Note:

1. If attr = true, then the program runs fine.

2. getBC() returns the BC provider. I'm sure you have easier way to find it.

Thanks,
Weijun


import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.*;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.List;

public class T4 {

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

        Security.insertProviderAt(getBC(), 1);

        byte[] msg = "hello world".getBytes();
        byte[] sig = null;
        boolean attr = false;
        String alias = "ed25519";
        String alg = "ed25519";
        PrivateKey pk = null;
        X509Certificate xc = null;

        KeyStore ks = KeyStore.getInstance("pkcs12");
        try (FileInputStream fs = new FileInputStream("ks")) {
            ks.load(fs, "changeit".toCharArray());
            xc = (X509Certificate) ks.getCertificate(alias);
            pk = (PrivateKey)ks.getKey(alias, "changeit".toCharArray());
        }

        var calculator = new JcaDigestCalculatorProviderBuilder()
                .setProvider("BC").build();

        Store certs = new JcaCertStore(List.of(xc));
        ContentSigner sha1Signer = new JcaContentSignerBuilder(alg)
                .setProvider("BC").build(pk);

        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        gen.addSignerInfoGenerator(
                new JcaSignerInfoGeneratorBuilder(
                        calculator)
                        .setDirectSignature(!attr)
                        .build(sha1Signer, xc));
        gen.addCertificates(certs);

        CMSSignedData sigData = gen.generate(
                new CMSProcessableByteArray(msg), false);
        sig = sigData.getEncoded();

        CMSSignedDataParser sp = new CMSSignedDataParser(
                calculator,
                new CMSTypedStream(new ByteArrayInputStream(msg)),
                sig);
        sp.getSignedContent().drain();
        Store<X509CertificateHolder> certStore = sp.getCertificates();
        SignerInformationStore signers = sp.getSignerInfos();
        for (SignerInformation signer : signers.getSigners()) {
            Collection<X509CertificateHolder> certCollection
                    = certStore.getMatches(signer.getSID());
            for (var cert : certCollection) {
                var verifier = new JcaSimpleSignerInfoVerifierBuilder()
                        .setProvider("BC").build(cert);
                if (!signer.verify(verifier)) {
                    throw new RuntimeException("failed with " + cert.getSubject());
                }
            }
        }
    }
}


Reply | Threaded
Open this post in threaded view
|

Re: Ed25519 cms verify noattr fail

David Hook-3

Okay, so in when attr is true the signature is generated in a single
shot using the standard signature mechanism. If attr is false then it's
a direct signature. There's a catch in this case, which is mentioned RFC
8419 section 4. Pure EdDSA, which is what is used in CMS, requires two
passes over the data, prior to now all the signature algorithms we've
seen are ones where you can compute a hash separately and then use that
to verify the signature, in this case you need to compute the hash with
some header information, plus the data, which is a bit tricky to do in
one pass as the SignerInfo structures come after the signed data. At the
moment if you want to make use of direct signatures with Edwards Curves
and BC, you need to use the non-streaming API. We still need to come up
with a plan for the streaming case.

Apologies,

David

On 24/5/20 8:23 pm, Weijun Wang wrote:

> I'm trying out bcprov/bcpkix 1.65 and see this problem.
>
> First create a keypair for eddsa:
>
>   keytool -keystore ks -keypass changeit -storepass changeit -storetype pkcs12 \
>           -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
>           -providerpath bcprov-debug-jdk15on-165.jar \
>           -genkeypair -alias ed25519 -dname CN=ed25519 -keyalg ed25519 -keysize 255 -sigalg ed25519
>
> Then run the program below to sign (without SignedAttributes in SignerInfo) and verify, and there is an error.
>
> I am not familiar with CMS APIs in BC, sorry I am not calling the right methods.
>
> Note:
>
> 1. If attr = true, then the program runs fine.
>
> 2. getBC() returns the BC provider. I'm sure you have easier way to find it.
>
> Thanks,
> Weijun
>
>
> import org.bouncycastle.cert.X509CertificateHolder;
> import org.bouncycastle.cert.jcajce.JcaCertStore;
> import org.bouncycastle.cms.*;
> import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
> import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
> import org.bouncycastle.operator.ContentSigner;
> import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
> import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
> import org.bouncycastle.util.Store;
>
> import java.io.ByteArrayInputStream;
> import java.io.FileInputStream;
> import java.security.*;
> import java.security.cert.X509Certificate;
> import java.util.Collection;
> import java.util.List;
>
> public class T4 {
>
>     public static void main(String[] args) throws Exception {
>
>         Security.insertProviderAt(getBC(), 1);
>
>         byte[] msg = "hello world".getBytes();
>         byte[] sig = null;
>         boolean attr = false;
>         String alias = "ed25519";
>         String alg = "ed25519";
>         PrivateKey pk = null;
>         X509Certificate xc = null;
>
>         KeyStore ks = KeyStore.getInstance("pkcs12");
>         try (FileInputStream fs = new FileInputStream("ks")) {
>             ks.load(fs, "changeit".toCharArray());
>             xc = (X509Certificate) ks.getCertificate(alias);
>             pk = (PrivateKey)ks.getKey(alias, "changeit".toCharArray());
>         }
>
>         var calculator = new JcaDigestCalculatorProviderBuilder()
>                 .setProvider("BC").build();
>
>         Store certs = new JcaCertStore(List.of(xc));
>         ContentSigner sha1Signer = new JcaContentSignerBuilder(alg)
>                 .setProvider("BC").build(pk);
>
>         CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
>         gen.addSignerInfoGenerator(
>                 new JcaSignerInfoGeneratorBuilder(
>                         calculator)
>                         .setDirectSignature(!attr)
>                         .build(sha1Signer, xc));
>         gen.addCertificates(certs);
>
>         CMSSignedData sigData = gen.generate(
>                 new CMSProcessableByteArray(msg), false);
>         sig = sigData.getEncoded();
>
>         CMSSignedDataParser sp = new CMSSignedDataParser(
>                 calculator,
>                 new CMSTypedStream(new ByteArrayInputStream(msg)),
>                 sig);
>         sp.getSignedContent().drain();
>         Store<X509CertificateHolder> certStore = sp.getCertificates();
>         SignerInformationStore signers = sp.getSignerInfos();
>         for (SignerInformation signer : signers.getSigners()) {
>             Collection<X509CertificateHolder> certCollection
>                     = certStore.getMatches(signer.getSID());
>             for (var cert : certCollection) {
>                 var verifier = new JcaSimpleSignerInfoVerifierBuilder()
>                         .setProvider("BC").build(cert);
>                 if (!signer.verify(verifier)) {
>                     throw new RuntimeException("failed with " + cert.getSubject());
>                 }
>             }
>         }
>     }
> }
>
>


Reply | Threaded
Open this post in threaded view
|

Re: Ed25519 cms verify noattr fail

David Hook-3

CMSSignedDataGenerator and CMSSignedData.

There's some examples in:

https://github.com/bcgit/bc-java/blob/master/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java

Regards,

David

On 27/5/20 2:51 pm, Weijun Wang wrote:

> How do I use the non-streaming API?
>
> Also, I noticed that even if I encapsulate the data inside the signature and thus do not need to provide the signedContent argument to CMSSignedDataParser, the verification is still failing. Is it because of the same reason?
>
> Thanks,
> Weijun
>
>> On May 27, 2020, at 12:24 PM, David Hook <[hidden email]> wrote:
>>
>>
>> Okay, so in when attr is true the signature is generated in a single
>> shot using the standard signature mechanism. If attr is false then it's
>> a direct signature. There's a catch in this case, which is mentioned RFC
>> 8419 section 4. Pure EdDSA, which is what is used in CMS, requires two
>> passes over the data, prior to now all the signature algorithms we've
>> seen are ones where you can compute a hash separately and then use that
>> to verify the signature, in this case you need to compute the hash with
>> some header information, plus the data, which is a bit tricky to do in
>> one pass as the SignerInfo structures come after the signed data. At the
>> moment if you want to make use of direct signatures with Edwards Curves
>> and BC, you need to use the non-streaming API. We still need to come up
>> with a plan for the streaming case.
>>
>> Apologies,
>>
>> David
>>
>> On 24/5/20 8:23 pm, Weijun Wang wrote:
>>> I'm trying out bcprov/bcpkix 1.65 and see this problem.
>>>
>>> First create a keypair for eddsa:
>>>
>>>  keytool -keystore ks -keypass changeit -storepass changeit -storetype pkcs12 \
>>>          -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider \
>>>          -providerpath bcprov-debug-jdk15on-165.jar \
>>>          -genkeypair -alias ed25519 -dname CN=ed25519 -keyalg ed25519 -keysize 255 -sigalg ed25519
>>>
>>> Then run the program below to sign (without SignedAttributes in SignerInfo) and verify, and there is an error.
>>>
>>> I am not familiar with CMS APIs in BC, sorry I am not calling the right methods.
>>>
>>> Note:
>>>
>>> 1. If attr = true, then the program runs fine.
>>>
>>> 2. getBC() returns the BC provider. I'm sure you have easier way to find it.
>>>
>>> Thanks,
>>> Weijun
>>>
>>>
>>> import org.bouncycastle.cert.X509CertificateHolder;
>>> import org.bouncycastle.cert.jcajce.JcaCertStore;
>>> import org.bouncycastle.cms.*;
>>> import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
>>> import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
>>> import org.bouncycastle.operator.ContentSigner;
>>> import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
>>> import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
>>> import org.bouncycastle.util.Store;
>>>
>>> import java.io.ByteArrayInputStream;
>>> import java.io.FileInputStream;
>>> import java.security.*;
>>> import java.security.cert.X509Certificate;
>>> import java.util.Collection;
>>> import java.util.List;
>>>
>>> public class T4 {
>>>
>>>    public static void main(String[] args) throws Exception {
>>>
>>>        Security.insertProviderAt(getBC(), 1);
>>>
>>>        byte[] msg = "hello world".getBytes();
>>>        byte[] sig = null;
>>>        boolean attr = false;
>>>        String alias = "ed25519";
>>>        String alg = "ed25519";
>>>        PrivateKey pk = null;
>>>        X509Certificate xc = null;
>>>
>>>        KeyStore ks = KeyStore.getInstance("pkcs12");
>>>        try (FileInputStream fs = new FileInputStream("ks")) {
>>>            ks.load(fs, "changeit".toCharArray());
>>>            xc = (X509Certificate) ks.getCertificate(alias);
>>>            pk = (PrivateKey)ks.getKey(alias, "changeit".toCharArray());
>>>        }
>>>
>>>        var calculator = new JcaDigestCalculatorProviderBuilder()
>>>                .setProvider("BC").build();
>>>
>>>        Store certs = new JcaCertStore(List.of(xc));
>>>        ContentSigner sha1Signer = new JcaContentSignerBuilder(alg)
>>>                .setProvider("BC").build(pk);
>>>
>>>        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
>>>        gen.addSignerInfoGenerator(
>>>                new JcaSignerInfoGeneratorBuilder(
>>>                        calculator)
>>>                        .setDirectSignature(!attr)
>>>                        .build(sha1Signer, xc));
>>>        gen.addCertificates(certs);
>>>
>>>        CMSSignedData sigData = gen.generate(
>>>                new CMSProcessableByteArray(msg), false);
>>>        sig = sigData.getEncoded();
>>>
>>>        CMSSignedDataParser sp = new CMSSignedDataParser(
>>>                calculator,
>>>                new CMSTypedStream(new ByteArrayInputStream(msg)),
>>>                sig);
>>>        sp.getSignedContent().drain();
>>>        Store<X509CertificateHolder> certStore = sp.getCertificates();
>>>        SignerInformationStore signers = sp.getSignerInfos();
>>>        for (SignerInformation signer : signers.getSigners()) {
>>>            Collection<X509CertificateHolder> certCollection
>>>                    = certStore.getMatches(signer.getSID());
>>>            for (var cert : certCollection) {
>>>                var verifier = new JcaSimpleSignerInfoVerifierBuilder()
>>>                        .setProvider("BC").build(cert);
>>>                if (!signer.verify(verifier)) {
>>>                    throw new RuntimeException("failed with " + cert.getSubject());
>>>                }
>>>            }
>>>        }
>>>    }
>>> }
>>>
>>>