@@ -61,7 +61,7 @@ public void applyToParams(List<Pair> queryParams, Map<String, String> headerPara
6161 SDK_CORE_LOGGER .debugSign ("VolcstackSign start..." );
6262
6363 try {
64- sign (params , headerParams ,payload );
64+ sign (params , headerParams , payload );
6565 } catch (Exception e ) {
6666 SDK_CORE_LOGGER .error (()->"VolcstackSign exception: " , e );
6767 }
@@ -123,7 +123,7 @@ private List<String> sortHeaderKeys(Map<String, String> headerParams) {
123123 return listHeaderKeys ;
124124 }
125125
126- private void buildCredentialScope (Map <String , String > headerParams ,SignRequest signRequest ){
126+ private void buildCredentialScope (Map <String , String > headerParams , SignRequest signRequest ) {
127127 StringBuilder credentialScope = new StringBuilder ("" );
128128 credentialScope .append (headerParams .get ("X-Date" ).substring (0 , 8 ));
129129 credentialScope .append ("/" );
@@ -135,7 +135,7 @@ private void buildCredentialScope(Map<String, String> headerParams,SignRequest s
135135 SDK_CORE_LOGGER .debugSign ("credentialScope: " + signRequest .credentialScope );
136136 }
137137
138- private void buildStringToSign (Map <String , String > headerParams ,SignRequest signRequest )throws Exception {
138+ private void buildStringToSign (Map <String , String > headerParams , SignRequest signRequest ) throws Exception {
139139 StringBuilder stringToSign = new StringBuilder ("" );
140140 stringToSign .append ("HMAC-SHA256" );
141141 stringToSign .append ("\n " );
@@ -148,7 +148,7 @@ private void buildStringToSign(Map<String, String> headerParams,SignRequest sign
148148 SDK_CORE_LOGGER .debugSign ("stringToSign: " + signRequest .stringToSign );
149149 }
150150
151- private void buildAuthorization (Map <String , String > headerParams ,SignRequest signRequest )throws Exception {
151+ private void buildAuthorization (Map <String , String > headerParams , SignRequest signRequest ) throws Exception {
152152 byte [] signingkeyByte = getHmacSHA256 ("request" , getHmacSHA256 (
153153 service , getHmacSHA256 (
154154 region , getHmacSHA256 (
@@ -186,7 +186,7 @@ private void buildSignedHeaders(Map<String, String> headerParams, SignRequest si
186186 StringBuilder canonicalHeaders = new StringBuilder ("" );
187187 StringBuilder signedHeaders = new StringBuilder ("" );
188188 for (String key : listHeaderKeys ) {
189- if (!key .equalsIgnoreCase ("x-date" )){
189+ if (!key .equalsIgnoreCase ("x-date" )) {
190190 continue ;
191191 }
192192 canonicalHeaders .append (key .toLowerCase ());
@@ -203,9 +203,9 @@ private void buildSignedHeaders(Map<String, String> headerParams, SignRequest si
203203 SDK_CORE_LOGGER .debugSign ("signedHeaders: " + signRequest .signedHeaders );
204204 }
205205
206- private void initHeaders (Map <String ,String > headerParams ){
207- if (!StringUtils .isEmpty (credentials .getSessionToken ())){
208- headerParams .put ("X-Security-Token" ,credentials .getSessionToken ());
206+ private void initHeaders (Map <String , String > headerParams ) {
207+ if (!StringUtils .isEmpty (credentials .getSessionToken ())) {
208+ headerParams .put ("X-Security-Token" , credentials .getSessionToken ());
209209 }
210210 if (!headerParams .containsKey ("X-Date" )) {
211211 SimpleDateFormat sdf = new SimpleDateFormat ("yyyyMMdd'T'HHmmss'Z'" );
@@ -247,17 +247,17 @@ private void sign(Map<String, String> queryParams, Map<String, String> headerPar
247247 // step 1
248248 SDK_CORE_LOGGER .debugSign ("step 1: buildCanonicalRequest start" );
249249 buildCanonicalRequest (queryParams , signRequest );
250- buildSignedHeaders (headerParams ,signRequest );
251- buildPayload (signRequest ,payload );
250+ buildSignedHeaders (headerParams , signRequest );
251+ buildPayload (signRequest , payload );
252252
253253 // step 2
254254 SDK_CORE_LOGGER .debugSign ("step 2: buildCredentialScope and buildStringToSign start" );
255- buildCredentialScope (headerParams ,signRequest );
256- buildStringToSign (headerParams ,signRequest );
255+ buildCredentialScope (headerParams , signRequest );
256+ buildStringToSign (headerParams , signRequest );
257257
258258 // step 3
259259 SDK_CORE_LOGGER .debugSign ("step 3: buildAuthorization start" );
260- buildAuthorization (headerParams ,signRequest );
260+ buildAuthorization (headerParams , signRequest );
261261
262262 }
263263
@@ -324,5 +324,100 @@ public VolcstackSign copy() {
324324 return copySign ;
325325 }
326326
327+
328+
329+ /**
330+ * Generate presigned URL query parameters
331+ *
332+ * @param queryParams Original query parameters
333+ * @return Complete query parameters Map containing all signature information
334+ */
335+ public Map <String , String > presign (Map <String , String > queryParams ) throws Exception {
336+ Map <String , String > presignedParams = new HashMap <>(queryParams );
337+
338+ // Generate timestamp
339+ SimpleDateFormat sdf = new SimpleDateFormat ("yyyyMMdd'T'HHmmss'Z'" );
340+ sdf .setTimeZone (TimeZone .getTimeZone ("GMT" ));
341+ String xDate = sdf .format (new Date ());
342+ String dateStamp = xDate .substring (0 , 8 );
343+
344+ // Build credential scope
345+ String credentialScope = dateStamp + "/" + region + "/" + service + "/request" ;
346+
347+ // Add required query parameters for presigning
348+ presignedParams .put ("X-Date" , xDate );
349+ presignedParams .put ("X-NotSignBody" , "" );
350+ presignedParams .put ("X-Credential" , credentials .getAccessKey () + "/" + credentialScope );
351+ presignedParams .put ("X-Algorithm" , "HMAC-SHA256" );
352+ presignedParams .put ("X-SignedHeaders" , "" );
353+ presignedParams .put ("X-SignedQueries" , "" ); // Set to empty first, will be updated later
354+
355+ // Collect all query parameter keys (excluding X-Security-Token), sort and update X-SignedQueries
356+ List <String > queryKeys = new ArrayList <>(presignedParams .keySet ());
357+ Collections .sort (queryKeys );
358+ presignedParams .put ("X-SignedQueries" , String .join (";" , queryKeys ));
359+
360+ // Important: X-Security-Token must be added after X-SignedQueries calculation is complete
361+ if (!StringUtils .isEmpty (credentials .getSessionToken ())) {
362+ presignedParams .put ("X-Security-Token" , credentials .getSessionToken ());
363+ }
364+
365+ // Re-collect all parameter keys (including X-Security-Token) to build canonical request
366+ List <String > allKeys = new ArrayList <>(presignedParams .keySet ());
367+ Collections .sort (allKeys );
368+
369+ // Build canonical request
370+ StringBuilder canonicalRequest = new StringBuilder ();
371+ canonicalRequest .append (this .method );
372+ canonicalRequest .append ("\n " );
373+ canonicalRequest .append ("/" );
374+ canonicalRequest .append ("\n " );
375+
376+ // Canonical Query String - concatenate all parameters after sorting
377+ StringBuilder canonicalQueryString = new StringBuilder ();
378+ for (String key : allKeys ) {
379+ canonicalQueryString .append (signStringEncoder (key ));
380+ canonicalQueryString .append ("=" );
381+ canonicalQueryString .append (signStringEncoder (presignedParams .get (key )));
382+ canonicalQueryString .append ("&" );
383+ }
384+ canonicalRequest .append (canonicalQueryString .substring (0 , canonicalQueryString .length () - 1 ));
385+ canonicalRequest .append ("\n " );
386+
387+ // Canonical Headers - empty (because X-SignedHeaders is empty)
388+ canonicalRequest .append ("\n " );
389+
390+ // Signed Headers - empty
391+ canonicalRequest .append ("" );
392+ canonicalRequest .append ("\n " );
393+
394+ // Extra newline before Payload Hash (required by volcstack presigning specification)
395+ canonicalRequest .append ("\n " );
396+
397+ // Payload Hash - use hash value of X-NotSignBody (empty string)
398+ canonicalRequest .append (getSHA256 ("" ));
399+
400+ // Build string to sign
401+ StringBuilder stringToSign = new StringBuilder ();
402+ stringToSign .append ("HMAC-SHA256" );
403+ stringToSign .append ("\n " );
404+ stringToSign .append (xDate );
405+ stringToSign .append ("\n " );
406+ stringToSign .append (credentialScope );
407+ stringToSign .append ("\n " );
408+ stringToSign .append (getSHA256 (canonicalRequest .toString ()));
409+
410+ // Calculate signature
411+ byte [] signingKey = getHmacSHA256 ("request" ,
412+ getHmacSHA256 (service ,
413+ getHmacSHA256 (region ,
414+ getHmacSHA256 (dateStamp , credentials .getSecretKey ().getBytes (StandardCharsets .UTF_8 )))));
415+ String signature = getHmacSHA256Hex (stringToSign .toString (), signingKey );
416+
417+ // Add signature to query parameters
418+ presignedParams .put ("X-Signature" , signature );
419+
420+ return presignedParams ;
421+ }
327422}
328423
0 commit comments