All Products
Search
Document Center

Object Storage Service:Example of uploading signature headers in OSS using Java

Last Updated:Mar 20, 2026

Use the OSS software development kit (SDK) whenever possible — it handles signature computation automatically. If you cannot use the SDK and need to call the PutObject API directly over HTTP, this topic shows you how to construct the Authorization request header manually in Java using HMAC-SHA1.

Prerequisites

Before you begin, ensure that you have:

  • JDK 1.8

  • Apache Commons Codec library added to your project (provides org.apache.commons.codec.binary.Base64)

Maven:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version>
</dependency>

Gradle:

implementation 'commons-codec:commons-codec:1.15'

How it works

Signing a PutObject request involves four steps:

  1. Build the StringToSign — Concatenate the HTTP method, empty Content-MD5 and Content-Type fields, the GMT-formatted Date, and the CanonicalizedResource.

  2. Compute the HMAC-SHA1 signature — Sign the StringToSign using your AccessKey secret.

  3. Base64-encode the signature — Encode the raw HMAC bytes to produce a printable signature string.

  4. Set the Authorization header — Combine your AccessKey ID and the signature in the format OSS <AccessKeyID>:<Signature>.

StringToSign format

The StringToSign for a PutObject request follows this structure:

StringToSign =
  VERB + "\n"
  + Content-MD5 + "\n"       (empty in this example)
  + Content-Type + "\n"      (empty in this example)
  + Date + "\n"
  + CanonicalizedResource

For the example in this topic, the StringToSign looks like:

PUT\n
\n
\n
Thu, 22 May 2025 12:00:00 GMT\n
/xx/panda/102283/111.txt
Note: The two blank lines correspond to the empty Content-MD5 and Content-Type fields. Both must be present even when empty.

Sample code

import org.apache.commons.codec.binary.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
public class OssSignHeader {
private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
private final static String CHARSET_UTF8 = "utf8";
private final static String ALGORITHM = "HmacSHA1";
public static String hmacSha1(String data, String key) {
try {
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
mac.init(keySpec);
byte[] rawHmac;
rawHmac = mac.doFinal(data.getBytes(CHARSET_UTF8));
return new String(Base64.encodeBase64(rawHmac));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static String buildSignData(String Date,String VERB,String CanonicalizedResource){
String signData = "PUT" + "\n"+ "\n"+ "\n"
+ Date + "\n"
 + CanonicalizedResource;
return signData;
}
public static String getGMTDate(){
Calendar cd = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
String str = sdf.format(cd.getTime());
return str;
}
public static void main(String[] args) throws Exception{
String date = getGMTDate();
String ossBucket= "your bucket name";
 String accessKeyId= "your AccessKey";
 String secretAccessKey= "your AccessSecret";
 String resourcePath = "/xx/panda/102283/111.txt";
 String resourcePath1 = "panda/102283/111.txt";
String VERB = "GET";
 String url = "http://"+ossBucket+".oss-cn-hangzhou.aliyuncs.com/";
 String Signature = (hmacSha1(buildSignData(date,VERB,resourcePath),secretAccessKey));
 String Authorization = "OSS " + accessKeyId + ":" + Signature;
System.out.println(Authorization);
Map<String,String> head = new HashMap<String,String>();
head.put("Date",date);
head.put("Authorization",Authorization);
 URL url1 = new URL(url + resourcePath1);
HttpURLConnection connection ;
StringBuffer sbuffer=null;
try {
// Add request content
connection= (HttpURLConnection) url1.openConnection();
// Set HTTP connection properties
connection.setDoOutput(true);// within the HTTP body, it needs to be set to true, which is false by default.
connection.setRequestMethod("PUT"); // submit HTTP-provided functions such as GET, POST, DELETE, and PUT as needed.
// connection. Setusecvisits (false);// specify the cache. Note: The request method must be set to post.
// connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("Date", date); // Set the URL and domain of the requested server, for example, bucket*. **. ***. ***.
connection.setRequestProperty("Authorization", Authorization);// Set the request format in Json. You can also set the format in XML.
// connection.setRequestProperty("Content-Length", obj.toString().getBytes().length + ""); // set the Length of the requested object.



connection.setReadTimeout(10000);// Set the read timeout.
connection.setConnectTimeout(10000);// Set the connection timeout.
connection.connect();
 OutputStream out = connection.getOutputStream();// write data to the object output stream, which will be stored in the memory buffer.
out.write(new String("test data").getBytes()); // out.write(new String("test data").getBytes()); // refresh the output stream of the object to write any bytes to the potential stream.
out.flush();
// Close the flow object. At this time, no more data can be written to the object output stream, and previously written data exists in the memory buffer.
out.close();
// Read the response.
if (connection.getResponseCode()==200)      {
// Get an input stream from the server.
InputStreamReader inputStream =new InputStreamReader(connection.getInputStream());// call the getInputStream() function of the HttpURLConnection object to send the complete HTTP request message encapsulated in the memory buffer to the server.
BufferedReader reader = new BufferedReader(inputStream);



String lines;
sbuffer= new StringBuffer("");



while ((lines = reader.readLine()) != null) {
lines = new String(lines.getBytes(), "utf-8");
sbuffer.append(lines);                }
reader.close();
}else{
System.out.println(connection.getResponseCode());
}
// Disconnect.
connection.disconnect();
System.out.println("OK  "+url1);
} catch (IOException e) {
e.printStackTrace();
}
}
}

Replace the placeholder values before running the code:

PlaceholderDescriptionExample
"your bucket name"Your OSS bucket namemy-bucket
"your AccessKey"Your AccessKey IDLTAI5tXxx
"your AccessSecret"Your AccessKey secretxXxXxXx
Note: The buildSignData() method hardcodes "PUT" as the HTTP method in the StringToSign, while main() sets VERB = "GET" and connection.setRequestMethod("PUT"). This inconsistency exists in the source code. Adapt both the StringToSign construction and the connection method to match the actual HTTP method used in your implementation.

What's next