Note:
- This tutorial requires access to Oracle Cloud. To sign up for a free account, see Get started with Oracle Cloud Infrastructure Free Tier.
- It uses example values for Oracle Cloud Infrastructure credentials, tenancy, and compartments. When completing your lab, substitute these values with ones specific to your cloud environment.
Manage Oracle Cloud Infrastructure REST APIs Programmatically using Shell and Java Code
Introduction
Oracle Cloud Infrastructure(OCI) provide cloud infrastructure to customers to host their mission critical applications on cloud. OCI also provides customer with the ways to access OCI services either through console, or through APIs. For connecting through APIs, customer can use SDKs based on the programming language fo their choice like Java, Python or Ruby. But their are few cases where customer can not use SDK, and can not use any programming language, but instead want to connect through APIs directly either from a tool, database or third party application where they can not install SDK. Here comes the usage of REST APIs comes handy. Customers can leverage REST APIs provided by OCI for almost all services, and use them in their tools directly by just updating the endpoints and taking care of authorization and authentication.
These days, every organization want to leverage the power of REST APIs in their applications and create an innovative offering for their customers without the need of installing and managing any SDK.
In this tutorial, we will discuss and understand how OCI can help organizations across the world to leverage the power of REST APIs with OCI. In this tutorial, we will demonstrate example of REST APIs based on shell scripting and Java code.
Objectives
-
Set up OCI API keys for the user based on which REST APIs will connect to the OCI tenancy.
-
Create use case to download or upload a binary file to OCI Object Storage using REST APIs.
-
Set up a shell and Java application, write appropriate code to call and display OCI REST APIs.
Prerequisites
-
Access to OCI and policies allowed in Oracle Cloud Infrastructure Identity and Access Management (OCI IAM). The user should have access to corresponding resources which they want to create or update using REST APIs.
-
Knowledge of shell scripting and Java application development.
-
Basic understanding of agile methodology of software development.
Task 1: Set up Oracle Cloud Infrastructure API Keys
-
Log in to the OCI tenancy with the service account or the admin user and go to the User settings.
Or
Go to the user from the OCI IAM console. Ensure the user has all the required access for creating and updating infrastructure on OCI.
-
Go to the Resources section, click API Keys and Add API Key. Select for either downloading the new public and private keys, upload your public key or paste your public key.
-
Save the keys at a convenient location, and save the configuration file as shown in the following page. You will need this configuration file and private key when connecting to your tenancy using REST APIs.
Note:
-
You will need same
configuration parameters
to connect to your OCI region and tenancy. You will replace the parameters in Java code and shell scripts accordingly as we dive deeper into the tutorial. -
To call OCI services from OCI Compute instances, OCI provides a more secure way using instance principals. Avoid using user API keys as best practices. For more information, see Calling Services from an Instance.
-
Task 2: Create and Run Shell Script to Upload or Put a Binary File from OCI Object Storage using REST APIs
-
Go to file system and create a shell file. For this example, file named as
PUT_OBJECT_REST_API.sh
. -
Open the file with your choice of editor and paste the following code in the shell file.
-
PUT_OBJECT_REST_API.sh
:#!/bin/bash ########################## Fill these in with your values ########################## #OCID of the tenancy calls are being made in to tenancy_ocid="ocid1.tenancy.oc1..aaaaaaaackopa" # namespace of the tenancy namespace="ocitenancyname" # OCID of the user making the rest call user_ocid="ocid1.user.oc1..aaaaaaaafelqsoffcjh" # path to the private PEM format key for this user privateKeyPath="/home/lovelesh/.oci/oci_api_key.pem" # fingerprint of the private key for this user fingerprint="72:d4:6c:f8:89" #bucket name for uplaod/ put bucket="ocibucketname" #file name for upload/ put object="test_pdf.pdf" # The REST api you want to call, with any required paramters. rest_api="/n/$namespace/b/$bucket/o/$object" # The host you want to make the call against host="objectstorage.us-ashburn-1.oraclecloud.com" # the file containing the data you want to POST to the rest endpoint body="/home/lovelesh/.oci/test_pdf.pdf" #################################################################################### # extra headers required for a POST/PUT request method="put" content_type="application/octet-stream" body_arg=(--data-binary @${body}) content_sha256="$(openssl dgst -binary -sha256 < $body | openssl enc -e -base64)"; content_sha256_header="x-content-sha256: $content_sha256" content_length="$(wc -c < $body | xargs)"; content_length_header="content-length: $content_length" headers="(request-target) date host" # add on the extra fields required for a POST/PUT headers=$headers" x-content-sha256 content-type content-length" content_type_header="content-type: $content_type"; date=`date -u "+%a, %d %h %Y %H:%M:%S GMT"` date_header="date: $date" host_header="host: $host" request_target="(request-target): $method $rest_api" # note the order of items. The order in the signing_string matches the order in the headers, including the extra POST fields signing_string="$request_target\n$date_header\n$host_header" # add on the extra fields required for a POST/PUT signing_string="$signing_string\n$content_sha256_header\n$content_type_header\n$content_length_header" signature=`printf '%b' "$signing_string" | openssl dgst -sha256 -sign $privateKeyPath | openssl enc -e -base64 | tr -d '\n'` set -x curl -v -X $(echo $method | tr [:lower:] [:upper:]) --data-binary "@$body" -sS https://$host$rest_api -H "date: $date" -H "x-content-sha256: $content_sha256" -H "content-type: $content_type" -H "content-length: $content_length" -H "Authorization: Signature version=\"1\",keyId=\"$tenancy_ocid/$user_ocid/$fingerprint\",algorithm=\"rsa-sha256\",headers=\"$headers\",signature=\"$signature\""
-
-
Now we have all the code pasted in shell file, let us work on updating the required values and understand their functionality.
tenancy_ocid="ocid1.tenancy.oc1..aaaaaaaackopa" ... fingerprint="72:d4:6c:f8:89"
All these values are responsible to make a secure connection with your OCI tenancy. Get these values from the config file.
rest_api="/n/$namespace/b/$bucket/o/$object" host="objectstorage.us-ashburn-1.oraclecloud.com"
This is the rest_api and host endpoint, prepare it as per your requirement as this will change for each and every service. The example shown here is endpoint for OCI Object Storage Service for ashburn region.
headers=$headers" x-content-sha256 content-type content-length" content_type_header="content-type: $content_type"; date=`date -u "+%a, %d %h %Y %H:%M:%S GMT"` date_header="date: $date" host_header="host: $host" request_target="(request-target): $method $rest_api"
Here we have created the headers for REST API along with content type and other required parameters. Note that here we have definitions for method_type, date, and host headers as for post call we need detailed header set.
signing_string="$signing_string\n$content_sha256_header\n$content_type_header\n$content_length_header" curl -v -X $(echo $method | tr [:lower:] [:upper:]) --data-binary "@$body" ...
Finally, we are creating the signing string, and then making the REST API call using curl. Note that OCI need the API calls to be signed, and that’s why we are doing it here.
-
After you have updated the values as per your configuration, run the shell script, you should see the script executing all good, and the file is pushed to OCI Object Storage.
Note:
-
Make sure you have updated the shell file permission to executable by changing it to 755 or manage it yourself.
-
You can similarly also do this on Windows using powershell, or use the same script on Windows by installing a WSL(Windows Subsystem for Linux) like Ubuntu and run it from Ubuntu WSL on Windows. For more information, see Windows Subsystem for Linux.
-
Task 3: Create and Run Java Code to Upload or Put a Binary File from OCI Object Storage using REST APIs
-
Go to file system and create a java file. For this example, file named as
PUT_OBJECT_REST_API.java
. -
Open the file with your choice of editor and paste the following code in the shell file.
-
PUT_OBJECT_REST_API.java
:import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.Signature; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Base64; // NOTE - Feel free to remove logging as needed. Currently logs the signing string, host, date, and response. public class PUT_OBJECT_REST_API { private static final String apiKeyFilePath = "C:\\Users\\User1\\Folder\\privatekey.pem" private static final String tenancyId = "ocid1.tenancy.oc1..aaaaaaaackopa27emaz4uteg4q7"; // OCI Tenancy ID private static final String userId = "ocid1.user.oc1..aaaaaaaafelqsoffcjq"; // OCI User ID private static final String keyFingerprint = "72:d4:6c:f8:89:74:aa"; // OCI fingerprint private static final String region = "us-ashburn-1"; // OCI Region private static final String namespace = "ocitenancyname"; // OCI bucket namespace private static final String bucketName = "ocibucketname"; // OCI bucket NAME public static void main(String[] args) { try { String filePath = "D:\\file\\location\\test_pdf.pdf"; //args.length > 0 ? args[0] : ""; // File to upload (path) PrivateKey privateKey = loadPrivateKey(apiKeyFilePath); String fileName = Paths.get(filePath).getFileName().toString(); String targetUri = "https://objectstorage." + region + ".oraclecloud.com/n/" + namespace + "/b/" + bucketName + "/o/" + fileName; HttpRequest request = buildRequest(targetUri, filePath, privateKey); HttpClient client = HttpClient.newHttpClient(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("Response status code: " + response.statusCode()); System.out.println("Response body: " + response.body()); } catch (Exception e) { e.printStackTrace(); } } private static PrivateKey loadPrivateKey(String filePath) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { String key = new String(Files.readAllBytes(Paths.get(filePath))) .replace("-----BEGIN PRIVATE KEY-----", "") .replace("-----END PRIVATE KEY-----", "") .replaceAll("\\s+", ""); // Remove all whitespace byte[] decodedKey = Base64.getDecoder().decode(key); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decodedKey); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate(spec); } private static HttpRequest buildRequest(String targetUri, String filePath, PrivateKey privateKey) throws Exception { String method = "put"; String path = "/n/" + namespace + "/b/" + bucketName + "/o/" + Paths.get(filePath).getFileName(); String host = "objectstorage." + region + ".oraclecloud.com"; String date = DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)); byte[] fileContent = Files.readAllBytes(Paths.get(filePath)); // Read file content as byte array String signingString = "(request-target): " + method.toLowerCase() + " " + path + "\n" + "host: " + host + "\n" + "date: " + date; System.out.println("Signing String: \n" + signingString); String signature = signRequest(privateKey, signingString); String authorizationHeader = "Signature version=\"1\",headers=\"(request-target) host date\",keyId=\"" + tenancyId + "/" + userId + "/" + keyFingerprint + "\",algorithm=\"rsa-sha256\",signature=\"" + signature + "\""; return HttpRequest.newBuilder() .uri(URI.create(targetUri)) .header("Content-Type", "application/octet-stream") .header("date", date) .header("Authorization", authorizationHeader) .PUT(HttpRequest.BodyPublishers.ofByteArray(fileContent)) .build(); } private static String signRequest(PrivateKey privateKey, String dataToSign) throws Exception { Signature signatureInstance = Signature.getInstance("SHA256withRSA"); signatureInstance.initSign(privateKey); signatureInstance.update(dataToSign.getBytes()); byte[] signature = signatureInstance.sign(); return Base64.getEncoder().encodeToString(signature); } }
-
-
Now we have all the code pasted in java file, let us work on updating the required values and understand their functionality.
private static final String apiKeyFilePath = "C:\\Users\\User1\\Folder\\privatekey.pem" private static final String tenancyId = "ocid1.tenancy.oc1..aaaaaaaackopa27emaz4uteg4q7"; // OCI Tenancy ID private static final String userId = "ocid1.user.oc1..aaaaaaaafelqsoffcjq"; // OCI User ID private static final String keyFingerprint = "72:d4:6c:f8:89:74:aa"; // OCI fingerprint private static final String region = "us-ashburn-1"; // OCI Region private static final String namespace = "ocitenancyname"; // OCI bucket namespace private static final String bucketName = "ocibucketname"; // OCI bucket NAME
All these values are responsible to make a secure connection with your OCI tenancy. Get these values from the config file.
String targetUri = "https://objectstorage." + region + ".oraclecloud.com/n/" + namespace + "/b/" + bucketName + "/o/" + fileName; HttpRequest request = buildRequest(targetUri, filePath, privateKey);
This is the rest_api and host endpoint, prepare it as per your requirement as this will change for each and every service. The example shown here is endpoint for OCI Object Storage Service for ashburn region.
private static HttpRequest buildRequest
We have created the headers for REST API along with content type and other required parameters. Note that here we have definitions for method_type, date, and host headers as for post call we need detailed header set.
private static String signRequest
Finally, we are creating the signing string, and then making the REST API call. Note that OCI need the API calls to be signed, and that is why we are doing it here.
-
After you have updated the values as per your configuration, run the java code, you should see the script executing all good, and the file is pushed to OCI Object Storage.
Note: Make sure you have Java installed with all the required packages as needed by the code.
Related Links
Acknowledgments
- Author - Lovelesh Saxena (Principal Software Engineering Architect)
More Learning Resources
Explore other labs on docs.oracle.com/learn or access more free learning content on the Oracle Learning YouTube channel. Additionally, visit education.oracle.com/learning-explorer to become an Oracle Learning Explorer.
For product documentation, visit Oracle Help Center.
Manage Oracle Cloud Infrastructure REST APIs Programmatically using Shell and Java Code
G11957-01
July 2024