org.jclouds.joyent.blobstore.JoyentBlobRequestSigner.java Source code

Java tutorial

Introduction

Here is the source code for org.jclouds.joyent.blobstore.JoyentBlobRequestSigner.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.jclouds.joyent.blobstore;

import com.google.common.base.Supplier;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.util.encoders.Base64;
import org.jclouds.domain.Credentials;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.joyent.JoyentConstants;

import javax.inject.Singleton;
import javax.ws.rs.core.HttpHeaders;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * @author vitaly.rudenya
 */
@Singleton
public class JoyentBlobRequestSigner implements HttpRequestFilter {

    private static final Log LOG = LogFactory.getLog(JoyentBlobRequestSigner.class);
    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy zzz",
            Locale.ENGLISH);
    private static final String AUTHZ_HEADER = "Signature keyId=\"/%s/keys/%s\",algorithm=\"rsa-sha256\","
            + "signature=\"%s\"";

    private static final String SIGNING_ALGORITHM = "SHA256WithRSAEncryption";
    private static final String AUTHZ_SIGNING_STRING = "date: %s";

    private volatile KeyPair keyPair_;
    private String identity;

    @Inject
    @Named(JoyentConstants.JOYENT_CERT_FINGERPRINT)
    private String fingerPrint_;

    @Inject
    @Named(JoyentConstants.JOYENT_CERT_CLASSPATH)
    private String certClasspath_;

    @Inject
    public JoyentBlobRequestSigner(@org.jclouds.location.Provider Supplier<Credentials> creds) {
        identity = creds.get().identity;
    }

    @Override
    public HttpRequest filter(HttpRequest request) throws HttpException {
        LOG.debug("signing request: " + request.getHeaders());

        if (keyPair_ == null) {
            synchronized (this) {
                if (keyPair_ == null) {
                    try {
                        keyPair_ = getKeyPair(certClasspath_);
                    } catch (IOException e) {
                        LOG.error(e);
                        throw new HttpException("Can't load key pair", e);
                    }
                }
            }
        }

        HttpRequest res = request;

        String reqURL = request.getEndpoint().toString();
        String path = request.getEndpoint().getPath();
        String storPath = "/" + identity + "/" + JoyentConstants.STOR_PATH;
        if (!path.startsWith(storPath)) {
            int pathInd = reqURL.lastIndexOf(path);
            String newUrl = reqURL.substring(0, pathInd) + storPath + reqURL.substring(pathInd);
            res = res.toBuilder().endpoint(newUrl).build();
        }

        String date = request.getFirstHeaderOrNull(HttpHeaders.DATE);
        if (date == null) {
            Date now = Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime();
            date = DATE_FORMAT.format(now);
            LOG.debug("setting date header: " + date);
            res = res.toBuilder().addHeader(HttpHeaders.DATE, date).build();
        }
        try {
            Signature sig = Signature.getInstance(SIGNING_ALGORITHM);
            sig.initSign(keyPair_.getPrivate());
            String signingString = String.format(AUTHZ_SIGNING_STRING, date);
            sig.update(signingString.getBytes("UTF-8"));
            byte[] signedDate = sig.sign();
            byte[] encodedSignedDate = Base64.encode(signedDate);
            String authzHeader = String.format(AUTHZ_HEADER, identity, fingerPrint_, new String(encodedSignedDate));
            res = res.toBuilder().addHeader(HttpHeaders.AUTHORIZATION, authzHeader).build();
        } catch (NoSuchAlgorithmException e) {
            throw new HttpException("invalid algorithm", e);
        } catch (InvalidKeyException e) {
            throw new HttpException("invalid key", e);
        } catch (SignatureException e) {
            throw new HttpException("invalid signature", e);
        } catch (UnsupportedEncodingException e) {
            throw new HttpException("invalid encoding", e);
        }

        return res;
    }

    private static KeyPair getKeyPair(String keyPath) throws IOException {
        BufferedReader br = new BufferedReader(
                new InputStreamReader(JoyentBlobRequestSigner.class.getResourceAsStream(keyPath)));
        Security.addProvider(new BouncyCastleProvider());
        PEMReader pemReader = new PEMReader(br);
        KeyPair kp = (KeyPair) pemReader.readObject();
        pemReader.close();
        return kp;
    }
}