55import java .security .GeneralSecurityException ;
66import java .security .PrivateKey ;
77import java .security .Provider ;
8+ import java .security .PublicKey ;
89import java .security .SecureRandom ;
910import java .security .Signature ;
1011import java .security .SignatureException ;
2728import org .bouncycastle .asn1 .pkcs .PrivateKeyInfo ;
2829import org .bouncycastle .asn1 .pkcs .RSASSAPSSparams ;
2930import org .bouncycastle .asn1 .x509 .AlgorithmIdentifier ;
31+ import org .bouncycastle .asn1 .x509 .SubjectPublicKeyInfo ;
3032import org .bouncycastle .jcajce .CompositePrivateKey ;
3133import org .bouncycastle .jcajce .io .OutputStreamFactory ;
3234import org .bouncycastle .jcajce .spec .CompositeAlgorithmSpec ;
4143import org .bouncycastle .operator .OperatorCreationException ;
4244import org .bouncycastle .operator .RuntimeOperatorException ;
4345import org .bouncycastle .operator .SignatureAlgorithmIdentifierFinder ;
46+ import org .bouncycastle .pqc .crypto .lms .LMSigParameters ;
47+ import org .bouncycastle .util .Pack ;
4448import org .bouncycastle .util .Strings ;
4549import org .bouncycastle .util .io .TeeOutputStream ;
4650
51+ /**
52+ * General builder class for ContentSigner operators based on the JCA.
53+ */
4754public class JcaContentSignerBuilder
4855{
4956 private static final Set isAlgIdFromPrivate = new HashSet ();
@@ -65,30 +72,76 @@ public class JcaContentSignerBuilder
6572
6673 private OperatorHelper helper = new OperatorHelper (new DefaultJcaJceHelper ());
6774 private SecureRandom random ;
68- private DigestAlgorithmIdentifierFinder digestAlgIdFinder ;
6975
7076 private AlgorithmIdentifier sigAlgId ;
7177 private AlgorithmParameterSpec sigAlgSpec ;
7278
79+ /**
80+ * Construct a basic content signer where the signature algorithm name
81+ * tells us all we need to know.
82+ *
83+ * @param signatureAlgorithm the signature algorithm we perform.
84+ */
7385 public JcaContentSignerBuilder (String signatureAlgorithm )
7486 {
7587 this (signatureAlgorithm , (AlgorithmIdentifier )null );
7688 }
7789
78- public JcaContentSignerBuilder (String signatureAlgorithm , AlgorithmIdentifier signatureDigestAlgorithmID )
90+ //
91+ // at the moment LMS is the only algorithm like this, we can wing it with other public keys.
92+ //
93+ private static AlgorithmIdentifier getSigDigAlgId (PublicKey publicKey )
7994 {
80- this .signatureAlgorithm = signatureAlgorithm ;
81- this .signatureDigestAlgorithm = signatureDigestAlgorithmID ;
82- this .digestAlgIdFinder = null ;
95+ byte [] encoded = publicKey .getEncoded ();
96+ SubjectPublicKeyInfo subInfo = SubjectPublicKeyInfo .getInstance (encoded );
97+
98+ if (subInfo .getAlgorithm ().getAlgorithm ().equals (PKCSObjectIdentifiers .id_alg_hss_lms_hashsig ))
99+ {
100+ byte [] keyData = subInfo .getPublicKeyData ().getOctets ();
101+
102+ int type = Pack .bigEndianToInt (keyData , 4 );
103+ LMSigParameters sigParams = LMSigParameters .getParametersForType (type );
104+
105+ return new AlgorithmIdentifier (sigParams .getDigestOID ());
106+ }
107+
108+ return null ;
83109 }
84110
85- public JcaContentSignerBuilder (String signatureAlgorithm , DigestAlgorithmIdentifierFinder digestAlgIdFinder )
111+ /**
112+ * Constructor which calculates the digest algorithm used from the public key, if necessary.
113+ * <p>
114+ * Some PKIX operations, such as CMS signing, require the digest algorithm used for in the
115+ * signature. Some algorithms, such as LMS, use different digests with different parameter sets but the same OID
116+ * is used to represent the signature. In this case we either need to be told what digest is associated
117+ * with the parameter set, or we need the public key so we can work it out.
118+ * </p>
119+ *
120+ * @param signatureAlgorithm the signature algorithm we perform.
121+ * @param verificationKey the public key associated with our private key.
122+ */
123+ public JcaContentSignerBuilder (String signatureAlgorithm , PublicKey verificationKey )
86124 {
87- this .signatureAlgorithm = signatureAlgorithm ;
88- this .signatureDigestAlgorithm = null ;
89- this .digestAlgIdFinder = digestAlgIdFinder ;
125+ this (signatureAlgorithm , getSigDigAlgId (verificationKey ));
90126 }
91127
128+ /**
129+ * Constructor which includes the digest algorithm identifier used.
130+ * <p>
131+ * Some PKIX operations, such as CMS signing, require the digest algorithm used for in the
132+ * signature, this constructor allows the digest algorithm identifier to
133+ * be explicitly specified.
134+ * </p>
135+ *
136+ * @param signatureAlgorithm the signature algorithm we perform.
137+ * @param signatureDigestAlgorithmID the public key associated with our private key.
138+ */
139+ public JcaContentSignerBuilder (String signatureAlgorithm , AlgorithmIdentifier signatureDigestAlgorithmID )
140+ {
141+ this .signatureAlgorithm = signatureAlgorithm ;
142+ this .signatureDigestAlgorithm = signatureDigestAlgorithmID ;
143+ }
144+
92145 public JcaContentSignerBuilder (String signatureAlgorithm , AlgorithmParameterSpec sigParamSpec )
93146 {
94147 this (signatureAlgorithm , sigParamSpec , null );
@@ -158,6 +211,7 @@ public ContentSigner build(PrivateKey privateKey)
158211 {
159212 this .sigAlgId = getSigAlgId (privateKey );
160213 }
214+
161215 final AlgorithmIdentifier signatureAlgId = sigAlgId ;
162216 final Signature sig = helper .createSignature (sigAlgId );
163217
@@ -197,11 +251,11 @@ public byte[] getSignature()
197251 }
198252 };
199253
200- if (signatureDigestAlgorithm != null || digestAlgIdFinder != null )
254+ if (signatureDigestAlgorithm != null )
201255 {
202256 return new ExtendedContentSigner ()
203257 {
204- private final AlgorithmIdentifier digestAlgorithm = ( signatureDigestAlgorithm != null ) ? signatureDigestAlgorithm : digestAlgIdFinder . find ( contentSigner . getAlgorithmIdentifier ()) ;
258+ private final AlgorithmIdentifier digestAlgorithm = signatureDigestAlgorithm ;
205259 private final ContentSigner signer = contentSigner ;
206260
207261 public AlgorithmIdentifier getDigestAlgorithmIdentifier ()
0 commit comments