Skip to content

Commit 0f08ecd

Browse files
committed
added PKCS12 example using PBMac1
additional PQC tests added support for AES_WRAP_PAD to CMS.
1 parent bffd6a7 commit 0f08ecd

File tree

10 files changed

+378
-21
lines changed

10 files changed

+378
-21
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package org.bouncycastle.asn1.x509;
2+
3+
import org.bouncycastle.asn1.ASN1EncodableVector;
4+
import org.bouncycastle.asn1.ASN1Object;
5+
import org.bouncycastle.asn1.ASN1Primitive;
6+
import org.bouncycastle.asn1.ASN1Sequence;
7+
import org.bouncycastle.asn1.DERSequence;
8+
import org.bouncycastle.asn1.pkcs.IssuerAndSerialNumber;
9+
10+
/**
11+
* <pre>
12+
* PrivateKeyStatement ::= SEQUENCE {
13+
* signer IssuerAndSerialNumber,
14+
* cert Certificate OPTIONAL }
15+
* </pre>
16+
*/
17+
public class PrivateKeyStatement
18+
extends ASN1Object
19+
{
20+
private final IssuerAndSerialNumber signer;
21+
private final Certificate cert;
22+
23+
public static PrivateKeyStatement getInstance(Object obj)
24+
{
25+
if (obj instanceof PrivateKeyStatement)
26+
{
27+
return (PrivateKeyStatement)obj;
28+
}
29+
30+
if (obj != null)
31+
{
32+
return new PrivateKeyStatement(ASN1Sequence.getInstance(obj));
33+
}
34+
35+
return null;
36+
}
37+
38+
private PrivateKeyStatement(ASN1Sequence seq)
39+
{
40+
if (seq.size() == 1)
41+
{
42+
this.signer = IssuerAndSerialNumber.getInstance(seq.getObjectAt(0));
43+
this.cert = null;
44+
}
45+
else if (seq.size() == 2)
46+
{
47+
this.signer = IssuerAndSerialNumber.getInstance(seq.getObjectAt(0));
48+
this.cert = Certificate.getInstance(seq.getObjectAt(1));
49+
}
50+
else
51+
{
52+
throw new IllegalArgumentException("unknown sequence in PrivateKeyStatement");
53+
}
54+
}
55+
56+
public PrivateKeyStatement(IssuerAndSerialNumber signer)
57+
{
58+
this.signer = signer;
59+
this.cert = null;
60+
}
61+
62+
public PrivateKeyStatement(Certificate cert)
63+
{
64+
this.signer = new IssuerAndSerialNumber(cert.getIssuer(), cert.getSerialNumber().getValue());
65+
this.cert = cert;
66+
}
67+
68+
public IssuerAndSerialNumber getSigner()
69+
{
70+
return signer;
71+
}
72+
73+
public Certificate getCert()
74+
{
75+
return cert;
76+
}
77+
78+
public ASN1Primitive toASN1Primitive()
79+
{
80+
ASN1EncodableVector v = new ASN1EncodableVector(2);
81+
82+
v.add(signer);
83+
84+
if (cert != null)
85+
{
86+
v.add(cert);
87+
}
88+
89+
return new DERSequence(v);
90+
}
91+
}

core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,25 @@ public interface X509AttributeIdentifiers
77
/**
88
* @deprecated use id_at_role
99
*/
10-
static final ASN1ObjectIdentifier RoleSyntax = new ASN1ObjectIdentifier("2.5.4.72");
10+
ASN1ObjectIdentifier RoleSyntax = new ASN1ObjectIdentifier("2.5.4.72");
1111

12-
static final ASN1ObjectIdentifier id_pe_ac_auditIdentity = X509ObjectIdentifiers.id_pe.branch("4");
13-
static final ASN1ObjectIdentifier id_pe_aaControls = X509ObjectIdentifiers.id_pe.branch("6");
14-
static final ASN1ObjectIdentifier id_pe_ac_proxying = X509ObjectIdentifiers.id_pe.branch("10");
12+
ASN1ObjectIdentifier id_pe_ac_auditIdentity = X509ObjectIdentifiers.id_pe.branch("4");
13+
ASN1ObjectIdentifier id_pe_aaControls = X509ObjectIdentifiers.id_pe.branch("6");
14+
ASN1ObjectIdentifier id_pe_ac_proxying = X509ObjectIdentifiers.id_pe.branch("10");
1515

16-
static final ASN1ObjectIdentifier id_ce_targetInformation= X509ObjectIdentifiers.id_ce.branch("55");
16+
ASN1ObjectIdentifier id_ce_targetInformation = X509ObjectIdentifiers.id_ce.branch("55");
1717

18-
static final ASN1ObjectIdentifier id_aca = X509ObjectIdentifiers.id_pkix.branch("10");
18+
ASN1ObjectIdentifier id_aca = X509ObjectIdentifiers.id_pkix.branch("10");
1919

20-
static final ASN1ObjectIdentifier id_aca_authenticationInfo = id_aca.branch("1");
21-
static final ASN1ObjectIdentifier id_aca_accessIdentity = id_aca.branch("2");
22-
static final ASN1ObjectIdentifier id_aca_chargingIdentity = id_aca.branch("3");
23-
static final ASN1ObjectIdentifier id_aca_group = id_aca.branch("4");
20+
ASN1ObjectIdentifier id_aca_authenticationInfo = id_aca.branch("1");
21+
ASN1ObjectIdentifier id_aca_accessIdentity = id_aca.branch("2");
22+
ASN1ObjectIdentifier id_aca_chargingIdentity = id_aca.branch("3");
23+
ASN1ObjectIdentifier id_aca_group = id_aca.branch("4");
2424
// { id-aca 5 } is reserved
25-
static final ASN1ObjectIdentifier id_aca_encAttrs = id_aca.branch("6");
25+
ASN1ObjectIdentifier id_aca_encAttrs = id_aca.branch("6");
2626

27-
static final ASN1ObjectIdentifier id_at_role = new ASN1ObjectIdentifier("2.5.4.72");
28-
static final ASN1ObjectIdentifier id_at_clearance = new ASN1ObjectIdentifier("2.5.1.5.55");
27+
ASN1ObjectIdentifier id_at_role = new ASN1ObjectIdentifier("2.5.4.72");
28+
ASN1ObjectIdentifier id_at_clearance = new ASN1ObjectIdentifier("2.5.1.5.55");
29+
30+
ASN1ObjectIdentifier id_at_privateKeyStatement = new ASN1ObjectIdentifier("1.3.6.1.4.1.22112.2.1");
2931
}

pkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRepMessageBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public CertificateRepMessageBuilder(X509CertificateHolder... caCerts)
3030
this.caCerts[i] = new CMPCertificate(caCerts[i].toASN1Structure());
3131
}
3232
}
33+
3334
public CertificateRepMessageBuilder addCertificateResponse(CertificateResponse response)
3435
{
3536
responses.add(response.toASN1Structure());

pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ public class CMSAlgorithm
4646
public static final ASN1ObjectIdentifier AES128_WRAP = NISTObjectIdentifiers.id_aes128_wrap.intern();
4747
public static final ASN1ObjectIdentifier AES192_WRAP = NISTObjectIdentifiers.id_aes192_wrap.intern();
4848
public static final ASN1ObjectIdentifier AES256_WRAP = NISTObjectIdentifiers.id_aes256_wrap.intern();
49+
public static final ASN1ObjectIdentifier AES128_WRAP_PAD = NISTObjectIdentifiers.id_aes128_wrap_pad.intern();
50+
public static final ASN1ObjectIdentifier AES192_WRAP_PAD = NISTObjectIdentifiers.id_aes192_wrap_pad.intern();
51+
public static final ASN1ObjectIdentifier AES256_WRAP_PAD = NISTObjectIdentifiers.id_aes256_wrap_pad.intern();
4952
public static final ASN1ObjectIdentifier CAMELLIA128_WRAP = NTTObjectIdentifiers.id_camellia128_wrap.intern();
5053
public static final ASN1ObjectIdentifier CAMELLIA192_WRAP = NTTObjectIdentifiers.id_camellia192_wrap.intern();
5154
public static final ASN1ObjectIdentifier CAMELLIA256_WRAP = NTTObjectIdentifiers.id_camellia256_wrap.intern();

pkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ class CMSUtils
5252
wrapAlgNames.put(CMSAlgorithm.AES128_WRAP, "AESWRAP");
5353
wrapAlgNames.put(CMSAlgorithm.AES192_WRAP, "AESWRAP");
5454
wrapAlgNames.put(CMSAlgorithm.AES256_WRAP, "AESWRAP");
55+
wrapAlgNames.put(CMSAlgorithm.AES128_WRAP_PAD, "AES-KWP");
56+
wrapAlgNames.put(CMSAlgorithm.AES192_WRAP_PAD, "AES-KWP");
57+
wrapAlgNames.put(CMSAlgorithm.AES256_WRAP_PAD, "AES-KWP");
5558
}
5659

5760
static
@@ -264,15 +267,15 @@ static Cipher createAsymmetricWrapper(JcaJceHelper helper, ASN1ObjectIdentifier
264267
public static int getKekSize(ASN1ObjectIdentifier symWrapAlg)
265268
{
266269
// TODO: add table
267-
if (symWrapAlg.equals(CMSAlgorithm.AES256_WRAP))
270+
if (symWrapAlg.equals(CMSAlgorithm.AES256_WRAP) || symWrapAlg.equals(CMSAlgorithm.AES256_WRAP_PAD))
268271
{
269272
return 32;
270273
}
271-
else if (symWrapAlg.equals(CMSAlgorithm.AES128_WRAP))
274+
else if (symWrapAlg.equals(CMSAlgorithm.AES128_WRAP) || symWrapAlg.equals(CMSAlgorithm.AES128_WRAP_PAD))
272275
{
273276
return 16;
274277
}
275-
else if (symWrapAlg.equals(CMSAlgorithm.AES192_WRAP))
278+
else if (symWrapAlg.equals(CMSAlgorithm.AES192_WRAP) || symWrapAlg.equals(CMSAlgorithm.AES192_WRAP_PAD))
276279
{
277280
return 24;
278281
}

pkix/src/main/java/org/bouncycastle/pkcs/bc/BcPKCS12PBMac1CalculatorBuilder.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.bouncycastle.pkcs.bc;
22

3+
import java.io.IOException;
4+
35
import org.bouncycastle.asn1.pkcs.PBKDF2Params;
46
import org.bouncycastle.asn1.pkcs.PBMAC1Params;
57
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -8,8 +10,6 @@
810
import org.bouncycastle.operator.OperatorCreationException;
911
import org.bouncycastle.pkcs.PKCS12MacCalculatorBuilder;
1012

11-
import java.io.IOException;
12-
1313
public class BcPKCS12PBMac1CalculatorBuilder
1414
implements PKCS12MacCalculatorBuilder
1515
{
@@ -27,7 +27,11 @@ public BcPKCS12PBMac1CalculatorBuilder(PBMAC1Params pbeMacParams) throws IOExcep
2727
throw new IOException("Key length must be present when using PBMAC1.");
2828
}
2929
}
30-
// TODO handle other cases
30+
else
31+
{
32+
// TODO: add scrypt support.
33+
throw new IllegalArgumentException("unrecognised PBKDF");
34+
}
3135
}
3236

3337
@Override
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package org.bouncycastle.pkcs.test;
2+
3+
import java.math.BigInteger;
4+
import java.security.KeyPair;
5+
import java.security.KeyPairGenerator;
6+
import java.security.PrivateKey;
7+
import java.security.PublicKey;
8+
import java.security.Security;
9+
import java.util.Date;
10+
11+
import junit.framework.TestCase;
12+
import org.bouncycastle.asn1.x500.X500Name;
13+
import org.bouncycastle.asn1.x509.BasicConstraints;
14+
import org.bouncycastle.asn1.x509.Extension;
15+
import org.bouncycastle.asn1.x509.PrivateKeyStatement;
16+
import org.bouncycastle.asn1.x509.X509AttributeIdentifiers;
17+
import org.bouncycastle.cert.CertException;
18+
import org.bouncycastle.cert.CertIOException;
19+
import org.bouncycastle.cert.X509CertificateHolder;
20+
import org.bouncycastle.cert.X509v3CertificateBuilder;
21+
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
22+
import org.bouncycastle.jcajce.spec.MLDSAParameterSpec;
23+
import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
24+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
25+
import org.bouncycastle.operator.ContentSigner;
26+
import org.bouncycastle.operator.ContentVerifierProvider;
27+
import org.bouncycastle.operator.OperatorCreationException;
28+
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
29+
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
30+
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
31+
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
32+
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
33+
34+
public class PQCPKCS10Test
35+
extends TestCase
36+
{
37+
public void setUp()
38+
{
39+
Security.addProvider(new BouncyCastleProvider());
40+
}
41+
42+
public void testKEMPKCS10()
43+
throws Exception
44+
{
45+
KeyPairGenerator dilKpGen = KeyPairGenerator.getInstance("ML-DSA", "BC");
46+
47+
dilKpGen.initialize(MLDSAParameterSpec.ml_dsa_65);
48+
49+
KeyPair dilKp = dilKpGen.generateKeyPair();
50+
51+
X509CertificateHolder sigCert = makeV3Certificate("CN=ML-KEM Client", dilKp);
52+
53+
KeyPairGenerator kemKpGen = KeyPairGenerator.getInstance("ML-KEM", "BC");
54+
55+
kemKpGen.initialize(MLKEMParameterSpec.ml_kem_768);
56+
57+
KeyPair kemKp = kemKpGen.generateKeyPair();
58+
59+
PKCS10CertificationRequestBuilder pkcs10Builder = new JcaPKCS10CertificationRequestBuilder(
60+
new X500Name("CN=ML-KEM Client"), kemKp.getPublic());
61+
62+
pkcs10Builder.addAttribute(X509AttributeIdentifiers.id_at_privateKeyStatement,
63+
new PrivateKeyStatement(sigCert.toASN1Structure()));
64+
65+
PKCS10CertificationRequest request = pkcs10Builder.build(
66+
new JcaContentSignerBuilder("ML-DSA").setProvider("BC").build(dilKp.getPrivate()));
67+
68+
assertTrue(request.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(sigCert.getSubjectPublicKeyInfo())));
69+
}
70+
71+
private static X509CertificateHolder makeV3Certificate(String _subDN, KeyPair issKP)
72+
throws OperatorCreationException, CertException, CertIOException
73+
{
74+
PrivateKey issPriv = issKP.getPrivate();
75+
PublicKey issPub = issKP.getPublic();
76+
77+
X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
78+
new X500Name(_subDN),
79+
BigInteger.valueOf(System.currentTimeMillis()),
80+
new Date(System.currentTimeMillis() - 5000L),
81+
new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
82+
new X500Name(_subDN),
83+
issKP.getPublic());
84+
85+
certGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0));
86+
87+
ContentSigner signer = new JcaContentSignerBuilder("ML-DSA").build(issPriv);
88+
89+
X509CertificateHolder certHolder = certGen.build(signer);
90+
91+
ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().build(issPub);
92+
93+
assertTrue(certHolder.isSignatureValid(verifier));
94+
95+
return certHolder;
96+
}
97+
}

0 commit comments

Comments
 (0)