All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.nosql.driver.iam.FederationRequestHelper Maven / Gradle / Ivy

There is a newer version: 5.4.16
Show newest version
/*-
 * Copyright (c) 2011, 2020 Oracle and/or its affiliates.  All rights reserved.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 *  https://oss.oracle.com/licenses/upl/
 */

package oracle.nosql.driver.iam;

import static oracle.nosql.driver.iam.Utils.*;
import static oracle.nosql.driver.util.HttpConstants.*;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import java.util.Set;
import java.util.logging.Logger;

import javax.net.ssl.SSLException;

import oracle.nosql.driver.httpclient.HttpClient;
import oracle.nosql.driver.iam.CertificateSupplier.X509CertificateKeyPair;
import oracle.nosql.driver.util.HttpRequestUtil;
import oracle.nosql.driver.util.HttpRequestUtil.HttpResponse;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;

import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;

/**
 * @hidden
 * Internal use only
 */
class FederationRequestHelper {
    /* signing headers used to obtain security token */
    private static final String SIGNING_HEADERS =
        "date (request-target) content-length content-type x-content-sha256";
    private static final String APP_JSON = "application/json";

    static String getSecurityToken(URI endpoint,
                                   int timeoutMs,
                                   String tenantId,
                                   X509CertificateKeyPair pair,
                                   String body,
                                   Logger logger) {

        HttpClient client = null;
        try {
            client = buildHttpClient(endpoint, logger);
            CharBuffer charBuf = CharBuffer.wrap(body);
            ByteBuffer buf = StandardCharsets.UTF_8.encode(charBuf);
            byte[] payloadByte = new byte[buf.remaining()];
            buf.get(payloadByte);

            HttpResponse response = HttpRequestUtil.doPostRequest(
                client, endpoint.toString(),
                headers(tenantId, endpoint, payloadByte, pair, logger),
                payloadByte, timeoutMs, logger);

            int responseCode = response.getStatusCode();
            if (responseCode > 299) {
                throw new IllegalStateException(
                    String.format(
                        "Error getting security token from IAM, " +
                        "status code %d, response \n%s",
                        response.getStatusCode(),
                        response.getOutput()));
            }
            logTrace(logger, "Federation response " + response.getOutput());
            return parseResponse(response.getOutput());
        } finally {
            if (client != null) {
                client.shutdown();
            }
        }
    }

    private static HttpClient buildHttpClient(URI endpoint, Logger logger) {
        String scheme = endpoint.getScheme();
        if (scheme == null) {
            throw new IllegalArgumentException(
                "Unable to find URL scheme, invalid URL " +
                 endpoint.toString());
        }
        if (scheme.equalsIgnoreCase("http")) {
            return new HttpClient(endpoint.getHost(), endpoint.getPort(),
                                  0, 0, 0, null, "FederationClient", logger);
        }

        SslContext sslCtx = null;
        try {
            sslCtx = SslContextBuilder.forClient().build();
        } catch (SSLException se) {
            throw new IllegalStateException(
                "Unable to build SSL context for http client", se);
        }

        return new HttpClient(endpoint.getHost(), 443, 0, 0, 0,
                              sslCtx, "FederationClient", logger);
    }

    /*
     * Request body:
     * {
     *  "intermediateCertificates": [
     *    "interCert1",
     *    "interCert3",
     *    "interCert2"
     *  ],
     *  "certificate": "certificate",
     *  "publicKey": "publicKey",
     *  "purpose": "purpose"
     * }
     */
    static String getFederationRequestBody(String publicKey,
                                           String certificate,
                                           Set interCerts,
                                           String purpose) {

        try {
            StringWriter sw = new StringWriter();
            JsonGenerator gen = createGenerator(sw);
            gen.writeStartObject();
            gen.writeStringField("publicKey", publicKey);
            gen.writeStringField("certificate", certificate);
            gen.writeStringField("purpose", purpose);

            gen.writeFieldName("intermediateCertificates");
            gen.writeStartArray();
            for (String interCert : interCerts) {
                gen.writeString(interCert);
            }
            gen.writeEndArray();
            gen.writeEndObject();
            gen.close();

            return sw.toString();
        } catch (IOException ioe) {
            throw new IllegalStateException(
                "Error getting federation request body", ioe);
        }
    }

    private static HttpHeaders headers(String tenantId,
                                       URI endpoint,
                                       byte[] body,
                                       X509CertificateKeyPair pair,
                                       Logger logger) {

        String date = createFormatter().format(new Date());
        String bodySha = calculateBodySHA256(body);
        StringBuilder sign = new StringBuilder();
        sign.append(DATE).append(HEADER_DELIMITER)
            .append(date).append("\n")
            .append(REQUEST_TARGET).append(HEADER_DELIMITER)
            .append("post ").append(endpoint.getPath()).append("\n")
            .append(CONTENT_LENGTH.toLowerCase()).append(HEADER_DELIMITER)
            .append(Integer.toString(body.length)).append("\n")
            .append(CONTENT_TYPE.toLowerCase()).append(HEADER_DELIMITER)
            .append(APP_JSON).append("\n")
            .append(CONTENT_SHA).append(HEADER_DELIMITER)
            .append(bodySha);

        logTrace(logger, "Federation request signing content " +
                 sign.toString());
        String signature;
        try {
             signature = sign(sign.toString(), pair.getKey());
        } catch (Exception e) {
            return null;
        }
        String authHeader = String.format(
            SIGNATURE_HEADER_FORMAT,
            SIGNING_HEADERS,
            keyId(tenantId, pair),
            RSA,
            signature,
            SINGATURE_VERSION);

        logTrace(logger, "Federation request authorization header " +
                 authHeader);
        HttpHeaders headers = new DefaultHttpHeaders();
        return headers
            .add(HttpHeaderNames.HOST, endpoint.getHost())
            .set(CONTENT_TYPE.toLowerCase(), APP_JSON)
            .set(CONTENT_SHA, bodySha)
            .set(CONTENT_LENGTH.toLowerCase(), body.length)
            .set(DATE, date)
            .set(AUTHORIZATION.toLowerCase(), authHeader);
    }

    private static String keyId(String tenantId, X509CertificateKeyPair pair) {
        return String.format("%s/fed-x509/%s",
                             tenantId, Utils.getFingerPrint(pair));
    }

    private static String calculateBodySHA256(byte[] body) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            digest.update(body);
            byte[] hash = digest.digest();
            return new String(Base64.getEncoder().encodeToString(hash));
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("Algorithm SHA-256 unavailable", e);
        }
    }

    /*
     * Response:
     * { "token": "...."}
     */
    private static String parseResponse(String response) {
        try {
            JsonParser parser = createParser(response);
            if (parser.getCurrentToken() == null) {
                parser.nextToken();
            }
            while (parser.getCurrentToken() != null) {
                String field = findField(response, parser, "token");
                if (field != null) {
                    parser.nextToken();
                    return parser.getText();
                }
            }
            throw new IllegalStateException(
                "Unable to find security token in " + response);
        } catch (IOException ioe) {
            throw new IllegalStateException(
                "Error parsing security token " + response +
                " " + ioe.getMessage());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy