1010import org .bouncycastle .crypto .DataLengthException ;
1111import org .bouncycastle .crypto .Digest ;
1212import org .bouncycastle .crypto .Signer ;
13- import org .bouncycastle .crypto .digests .SHA256Digest ;
14- import org .bouncycastle .crypto .ec .CustomNamedCurves ;
1513import org .bouncycastle .crypto .params .ECCSIPrivateKeyParameters ;
1614import org .bouncycastle .crypto .params .ECCSIPublicKeyParameters ;
1715import org .bouncycastle .crypto .params .ParametersWithRandom ;
1816import org .bouncycastle .math .ec .ECPoint ;
1917import org .bouncycastle .util .Arrays ;
2018import org .bouncycastle .util .BigIntegers ;
2119
20+ /**
21+ * Implementation of Elliptic Curve-based Certificateless Signatures for Identity-Based Encryption (ECCSI)
22+ * as defined in RFC 6507.
23+ * <p>
24+ * This class handles both signature generation and verification using the ECCSI scheme. It supports:
25+ * <ul>
26+ * <li>NIST P-256 (secp256r1) elliptic curve parameters</li>
27+ * <li>SHA-256 hash function</li>
28+ * <li>Certificateless signatures using KMS Public Authentication Key (KPAK)</li>
29+ * <li>Identity-based signatures with Secret Signing Key (SSK)</li>
30+ * </ul>
31+ *
32+ * @see <a href="https://datatracker.ietf.org/doc/html/rfc6507">RFC 6507: Elliptic Curve-Based Certificateless
33+ * Signatures for Identity-Based Encryption (ECCSI)</a>
34+ */
2235public class ECCSISigner
2336 implements Signer
2437{
25- private static final BigInteger q ;
26- private static final ECPoint G ;
27-
28- static
29- {
30- X9ECParameters params = CustomNamedCurves .getByName ("secP256r1" );
31- q = params .getCurve ().getOrder ();
32- G = params .getG ();
33- }
34-
35- private final Digest digest = new SHA256Digest ();
38+ private final BigInteger q ;
39+ private final ECPoint G ;
40+ private final Digest digest ;
3641 private BigInteger j ;
3742 private BigInteger r ;
3843 private ECPoint Y ;
@@ -41,72 +46,26 @@ public class ECCSISigner
4146 private CipherParameters param ;
4247 private ByteArrayOutputStream stream ;
4348 private boolean forSigning ;
49+ private final int N ;
4450
45- public ECCSISigner (ECPoint kpak , byte [] id )
51+
52+ public ECCSISigner (ECPoint kpak , X9ECParameters params , Digest digest , byte [] id )
4653 {
4754 this .kpak = kpak ;
4855 this .id = id ;
56+ this .q = params .getCurve ().getOrder ();
57+ this .G = params .getG ();
58+ this .digest = digest ;
59+ this .digest .reset ();
60+ this .N = (params .getCurve ().getOrder ().bitLength () + 7 ) >> 3 ;
4961 }
5062
5163 @ Override
5264 public void init (boolean forSigning , CipherParameters param )
5365 {
5466 this .forSigning = forSigning ;
5567 this .param = param ;
56- SecureRandom random = null ;
57- if (param instanceof ParametersWithRandom )
58- {
59- random = ((ParametersWithRandom )param ).getRandom ();
60- param = ((ParametersWithRandom )param ).getParameters ();
61- }
62- ECPoint kpak_computed = null ;
63- ECPoint pvt ;
64- if (forSigning )
65- {
66- ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters )param ;
67-
68- j = new BigInteger (256 , random ).mod (q );
69- ECPoint J = G .multiply (j ).normalize ();
70- r = J .getAffineXCoord ().toBigInteger ();
71- pvt = parameters .getPublicKeyParameters ().getPVT ();
72- kpak_computed = G .multiply (parameters .getSSK ());
73- }
74- else
75- {
76- ECCSIPublicKeyParameters parameters = (ECCSIPublicKeyParameters )param ;
77- pvt = parameters .getPVT ();
78- stream = new ByteArrayOutputStream ();
79- }
80-
81- // compute HS
82- byte [] tmp = G .getEncoded (false );
83- digest .update (tmp , 0 , tmp .length );
84- tmp = kpak .getEncoded (false );
85- digest .update (tmp , 0 , tmp .length );
86- digest .update (id , 0 , id .length );
87- tmp = pvt .getEncoded (false );
88- digest .update (tmp , 0 , tmp .length );
89- tmp = new byte [digest .getDigestSize ()];
90- digest .doFinal (tmp , 0 );
91- BigInteger HS = new BigInteger (1 , tmp ).mod (q );
92-
93- //HE = hash( HS || r || M );
94- digest .update (tmp , 0 , tmp .length );
95- if (forSigning )
96- {
97- kpak_computed = kpak_computed .subtract (pvt .multiply (HS )).normalize ();
98- if (!kpak_computed .equals (kpak ))
99- {
100- throw new IllegalArgumentException ("Invalid KPAK" );
101- }
102- byte [] rBytes = BigIntegers .asUnsignedByteArray (32 , r );
103- digest .update (rBytes , 0 , rBytes .length );
104- }
105- else
106- {
107- // Compute Y = HS*PVT + KPAK
108- Y = pvt .multiply (HS ).add (kpak ).normalize ();
109- }
68+ reset ();
11069 }
11170
11271 @ Override
@@ -153,17 +112,17 @@ public byte[] generateSignature()
153112
154113 BigInteger sPrime = denominator .modInverse (q ).multiply (j ).mod (q );
155114
156- return Arrays .concatenate (BigIntegers .asUnsignedByteArray (32 , r ), BigIntegers .asUnsignedByteArray (32 , sPrime ),
115+ return Arrays .concatenate (BigIntegers .asUnsignedByteArray (this . N , r ), BigIntegers .asUnsignedByteArray (this . N , sPrime ),
157116 params .getPublicKeyParameters ().getPVT ().getEncoded (false ));
158117 }
159118
160119 @ Override
161120 public boolean verifySignature (byte [] signature )
162121 {
163- byte [] bytes = Arrays .copyOf (signature , 32 );
164- BigInteger s = new BigInteger (1 , Arrays .copyOfRange (signature , 32 , 64 ));
122+ byte [] bytes = Arrays .copyOf (signature , this . N );
123+ BigInteger s = new BigInteger (1 , Arrays .copyOfRange (signature , this . N , this . N << 1 ));
165124 r = new BigInteger (1 , bytes ).mod (q );
166- digest .update (bytes , 0 , 32 );
125+ digest .update (bytes , 0 , this . N );
167126 bytes = stream .toByteArray ();
168127 digest .update (bytes , 0 , bytes .length );
169128 bytes = new byte [digest .getDigestSize ()];
@@ -185,6 +144,61 @@ public boolean verifySignature(byte[] signature)
185144 @ Override
186145 public void reset ()
187146 {
147+ digest .reset ();
148+ CipherParameters param = this .param ;
149+ SecureRandom random = null ;
150+ if (param instanceof ParametersWithRandom )
151+ {
152+ random = ((ParametersWithRandom )param ).getRandom ();
153+ param = ((ParametersWithRandom )param ).getParameters ();
154+ }
155+ ECPoint kpak_computed = null ;
156+ ECPoint pvt ;
157+ if (forSigning )
158+ {
159+ ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters )param ;
160+ pvt = parameters .getPublicKeyParameters ().getPVT ();
161+ j = new BigInteger (q .bitLength (), random );
162+ ECPoint J = G .multiply (j ).normalize ();
163+ r = J .getAffineXCoord ().toBigInteger ().mod (q );
188164
165+ kpak_computed = G .multiply (parameters .getSSK ());
166+ }
167+ else
168+ {
169+ ECCSIPublicKeyParameters parameters = (ECCSIPublicKeyParameters )param ;
170+ pvt = parameters .getPVT ();
171+ stream = new ByteArrayOutputStream ();
172+ }
173+
174+ // compute HS
175+ byte [] tmp = G .getEncoded (false );
176+ digest .update (tmp , 0 , tmp .length );
177+ tmp = kpak .getEncoded (false );
178+ digest .update (tmp , 0 , tmp .length );
179+ digest .update (id , 0 , id .length );
180+ tmp = pvt .getEncoded (false );
181+ digest .update (tmp , 0 , tmp .length );
182+ tmp = new byte [digest .getDigestSize ()];
183+ digest .doFinal (tmp , 0 );
184+ BigInteger HS = new BigInteger (1 , tmp ).mod (q );
185+
186+ //HE = hash( HS || r || M );
187+ digest .update (tmp , 0 , tmp .length );
188+ if (forSigning )
189+ {
190+ kpak_computed = kpak_computed .subtract (pvt .multiply (HS )).normalize ();
191+ if (!kpak_computed .equals (kpak ))
192+ {
193+ throw new IllegalArgumentException ("Invalid KPAK" );
194+ }
195+ byte [] rBytes = BigIntegers .asUnsignedByteArray (this .N , r );
196+ digest .update (rBytes , 0 , rBytes .length );
197+ }
198+ else
199+ {
200+ // Compute Y = HS*PVT + KPAK
201+ Y = pvt .multiply (HS ).add (kpak ).normalize ();
202+ }
189203 }
190204}
0 commit comments