com.vmware.identity.rest.core.util.RequestSigner.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.identity.rest.core.util.RequestSigner.java

Source

/*
 *  Copyright (c) 2012-2015 VMware, Inc.  All Rights Reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not
 *  use this file except in compliance with the License.  You may obtain a copy
 *  of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, without
 *  warranties or conditions of any kind, EITHER EXPRESS OR IMPLIED.  See the
 *  License for the specific language governing permissions and limitations
 *  under the License.
 */
package com.vmware.identity.rest.core.util;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

/**
 * The {@code RequestSigner} class contains utilities for signing requests.
 */
public class RequestSigner {

    private static final String SHA256_WITH_RSA = "SHA256withRSA";

    /**
     * Signs a string using the private key and SHA 256 with RSA signing algorithm, and
     * returns it as a hex-encoded string.
     *
     * @param signingString the string to sign.
     * @param privateKey the private key to sign the string with.
     * @return the signed string in a hex-encoded format.
     * @throws InvalidKeyException if the key is invalid.
     * @throws SignatureException if the signature algorithm is unable to process the input
     * data provided.
     */
    public static String sign(String signingString, PrivateKey privateKey)
            throws InvalidKeyException, SignatureException {
        byte[] bytes = signingString.getBytes(StandardCharsets.UTF_8);

        Signature sig;

        try {
            sig = Signature.getInstance(SHA256_WITH_RSA);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("An error occurred while getting the signature algorithm", e);
        }

        sig.initSign(privateKey);
        sig.update(bytes);

        return Hex.encodeHexString(sig.sign());
    }

    /**
     * Verify a signed request using a hex-formatted string, the string to sign, and a
     * certificate's public key.
     *
     * @param signedRequestHex a hex-encoded string representing the signed request to verify.
     * @param stringToSign the string that will be signed with the public key for
     * verification purposes.
     * @param certificate the certificate containing the public key used for verification.
     * @return true if the signature was verified, false if not.
     * @throws DecoderException if there is an error decoding the hex string.
     * @throws InvalidKeyException if the public key is invalid.
     * @throws SignatureException if the signature algorithm is unable to process the input
     * data provided.
     */
    public static boolean verify(String signedRequestHex, String stringToSign, Certificate certificate)
            throws DecoderException, InvalidKeyException, SignatureException {
        return verify(signedRequestHex, stringToSign, certificate.getPublicKey());
    }

    /**
     * Verify a signed request using a hex-formatted string, the string to sign, and a certificate's public key.
     *
     * @param signedRequestHex a hex-encoded string representing the signed request to verify.
     * @param stringToSign the string that will be signed with the public key for
     * verification purposes.
     * @param publicKey the public key used for verification.
     * @return true if the signature was verified, false if not.
     * @throws DecoderException if there is an error decoding the hex string.
     * @throws InvalidKeyException if the public key is invalid.
     * @throws SignatureException if the signature algorithm is unable to process the input
     * data provided.
     */
    public static boolean verify(String signedRequestHex, String stringToSign, PublicKey publicKey)
            throws DecoderException, InvalidKeyException, SignatureException {
        byte[] signedRequest = Hex.decodeHex(signedRequestHex.toCharArray());

        Signature sig;

        try {
            sig = Signature.getInstance(SHA256_WITH_RSA);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("An error occurred while getting the signature algorithm", e);
        }

        sig.initVerify(publicKey);
        sig.update(stringToSign.getBytes(StandardCharsets.UTF_8));

        return sig.verify(signedRequest);
    }

    /**
     * Create a string for signing using the various necessary components.
     *
     * @param method the HTTP method (e.g. GET, PUT, etc.)
     * @param md5 a MD5 hash of the request entity
     * @param mediaType the media type of a request
     * @param date the timestamp for a request
     * @param uri the uri of a request
     * @return the formatted string to be signed.
     */
    public static String createSigningString(String method, String md5, String mediaType, Date date, URI uri) {
        StringBuilder sb = new StringBuilder();
        sb.append(method).append("\n");
        sb.append(md5).append("\n");
        sb.append(mediaType).append("\n");
        sb.append(getHttpFormattedDate(date)).append("\n");
        sb.append(uri);
        return sb.toString();
    }

    /**
     * Convert a date into the format utilized by web browsers
     * (i.e. "EEE, dd MMM yyyy HH:mm:ss z").
     *
     * @param date the date to convert into a timestamp.
     * @return the formatted timestamp string.
     */
    public static String getHttpFormattedDate(Date date) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
        return dateFormat.format(date);
    }

    /**
     * Computes the MD5 hash of a request entity.
     *
     * @param entity the entity to hash.
     * @return the MD5 hash of the entity.
     */
    public static String computeMD5(String entity) {
        MessageDigest md5;

        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("An error occurred while getting the message digest", e);
        }

        return new String(md5.digest(entity.getBytes()), StandardCharsets.UTF_8);
    }

}