`

baidu云存储java sdk源码阅读之安全

阅读更多

         本身想写一个例子程序的,发现官方的文档比较简单,所以想写一系列的java sdk阅读文章加强对bcs api和参数的认识,然后再写一个例子出来,今天我们先看一下操作的安全部分的代码实现,它位于:com.baidu.inf.iis.bcs.auth包下面,类也很简单,但是之前对java安全和加解密不熟悉,所以还花了点时间,今天将这个阅读过程记录下来。

       首先我来看最简单的三个类,即就是BCSCredentials.java、BCSSignCondition.java、SigningAlgorithm.java,这个就不详细讲解了,BCSCredentials里面包含AK和SK是对这连个属性的封装,BCSSignCondition从命名来看,就是Sign生成的条件,里面包含时间、ip、和size,至于这个size,到目前不懂是什么玩意,是什么的大小,从官方给的sdk来看实际上是condition对象整个没使用,最后一个就是加密算法的一个枚举类,里面有两种最常用的消息摘要算法HmacSHA1和HmacSHA256。关于这两种算法我在这里也简单的介绍一下,也是今天刚了解,算是现学现卖。

       无论是HmacSHA1还是HmacSHA256均是HMAC算法家族的成员之一,HMAC(Hash Message Authentication Code),散列消息鉴别码,基于密钥的Hash算法的认证协议,它的原理是用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。使用一个密钥生成一个固定大小的小数据块,即MAC,并将其加入到消息中,然后传输。接收方利用与发送方共享的密钥进行鉴别认证等。认证过程如下所示:



 

            如果大家待会还有疑问的,看完代码之后就能彻底的明白,谁是密钥,谁是公共函数,谁是数据等等。

       直接上代码吧,这个在网上没找到代码,只能反编译后看,虽然累点,但是还能看出来点东西。

      

package com.baidu.inf.iis.bcs.auth;

import com.baidu.inf.iis.bcs.http.BCSHttpRequest;
import com.baidu.inf.iis.bcs.http.DefaultBCSHttpRequest;
import com.baidu.inf.iis.bcs.http.HttpMethodName;
import com.baidu.inf.iis.bcs.model.BCSClientException;
import com.baidu.inf.iis.bcs.request.BaiduBCSRequest;
import com.baidu.inf.iis.bcs.utils.ServiceUtils;
import java.io.PrintStream;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class BCSSigner
{
  public static void main(String[] paramArrayOfString)
    throws URISyntaxException
  {
    1 local1 = new BaiduBCSRequest("bucket", "object", HttpMethodName.GET)
    {
    };
    BCSSignCondition localBCSSignCondition = new BCSSignCondition();
    localBCSSignCondition.setIp("192.168.1.1");
    localBCSSignCondition.setSize(Long.valueOf(1234L));
    localBCSSignCondition.setTime(Long.valueOf(4321L));

    BCSCredentials localBCSCredentials = new BCSCredentials("akakak", "sksksk");

    DefaultBCSHttpRequest localDefaultBCSHttpRequest = new DefaultBCSHttpRequest();
    localDefaultBCSHttpRequest.setHttpMethod(local1.getHttpMethod());
    localDefaultBCSHttpRequest.setEndpoint("10.81.2.114:8685");

    sign(local1, localDefaultBCSHttpRequest, localBCSCredentials, localBCSSignCondition);
    System.out.println(localDefaultBCSHttpRequest.toString());
  }

  public static void sign(BaiduBCSRequest paramBaiduBCSRequest, BCSHttpRequest paramBCSHttpRequest, BCSCredentials paramBCSCredentials) {
    sign(paramBaiduBCSRequest, paramBCSHttpRequest, paramBCSCredentials, null);
  }

  public static void sign(BaiduBCSRequest paramBaiduBCSRequest, BCSHttpRequest paramBCSHttpRequest, BCSCredentials paramBCSCredentials, BCSSignCondition paramBCSSignCondition) {
    StringBuilder localStringBuilder1 = new StringBuilder();
    StringBuilder localStringBuilder2 = new StringBuilder();

    if (null == paramBaiduBCSRequest.getHttpMethod()) {
      throw new BCSClientException("Sign failed! Param: httpMethod, bucket, object can not be empty!");
    }
    if (null == paramBaiduBCSRequest.getBucket()) {
      throw new BCSClientException("Sign failed! Param: httpMethod, bucket, object can not be empty!");
    }
    if ((null == paramBaiduBCSRequest.getObject()) || (0 == paramBaiduBCSRequest.getObject().length())) {
      throw new BCSClientException("Sign failed! Param: httpMethod, bucket, object can not be empty!");
    }
    localStringBuilder1.append("MBO");
    localStringBuilder2.append("Method=").append(paramBaiduBCSRequest.getHttpMethod().toString()).append("\n");
    localStringBuilder2.append("Bucket=").append(paramBaiduBCSRequest.getBucket()).append("\n");
    localStringBuilder2.append("Object=").append(paramBaiduBCSRequest.getObject()).append("\n");

    if (paramBCSSignCondition != null) {
      if (0 != paramBCSSignCondition.getIp().length()) {
        localStringBuilder1.append("I");
        localStringBuilder2.append("Ip=").append(paramBCSSignCondition.getIp()).append("\n");
      }
      if (paramBCSSignCondition.getTime().longValue() > 0L) {
        localStringBuilder1.append("T");
        localStringBuilder2.append("Time=").append(paramBCSSignCondition.getTime()).append("\n");
        paramBCSHttpRequest.addParameter("time", String.valueOf(paramBCSSignCondition.getTime()));
      }
      if (paramBCSSignCondition.getSize().longValue() > 0L) {
        localStringBuilder1.append("S");
        localStringBuilder2.append("Size=").append(paramBCSSignCondition.getSize()).append("\n");
        paramBCSHttpRequest.addParameter("size", String.valueOf(paramBCSSignCondition.getSize()));
      }
    }
    localStringBuilder2.insert(0, "\n");
    localStringBuilder2.insert(0, localStringBuilder1.toString());

    byte[] arrayOfByte = new byte[0];
    try {
      SecretKeySpec localSecretKeySpec = new SecretKeySpec(paramBCSCredentials.getSecretKey().getBytes(), SigningAlgorithm.HmacSHA1.toString());

      Mac localMac = Mac.getInstance(localSecretKeySpec.getAlgorithm());
      localMac.init(localSecretKeySpec);
      arrayOfByte = localMac.doFinal(ServiceUtils.toByteArray(localStringBuilder2.toString()));
      paramBCSHttpRequest.addParameter("sign", ":" + paramBCSCredentials.getAccessKey() + ":" + ServiceUtils.toBase64(arrayOfByte));
    }
    catch (NoSuchAlgorithmException localNoSuchAlgorithmException) {
      throw new BCSClientException("NoSuchAlgorithmException. Sign bcs failed!", localNoSuchAlgorithmException);
    } catch (InvalidKeyException localInvalidKeyException) {
      throw new BCSClientException("InvalidKeyException. Sign bcs failed!", localInvalidKeyException);
    } catch (RuntimeException localRuntimeException) {
      throw new BCSClientException("Sign bcs failed!", localRuntimeException);
    }
  }
}

           从代码结构来看就sign函数在做事,我们来看,首先构造了两个StringBuffer用于盛放后来生成的东西,从意图上来看,第一个buffer放的是一些标志位,第一个buffer放的是签名的内容。 从代码来看每次操作的三个参数是必须的,bucket、object、httpMethod,从代码来看httpMethod就是标准的HTTP方法。

 

package com.baidu.inf.iis.bcs.http;

public enum HttpMethodName
{
  GET, POST, PUT, DELETE, HEAD;
}
          我们继续来看,这个是比较简单的,首先会放一个标志MBO,我们终于知道MBO是什么东西,Method、Object、Bucket。即就是
buffer1里面得数据是:MBO
buffe2里面得数据是:
      Method=GET
      Bucket=bucket
      Object=object
      我们注意到作者在这里面特意用了x平台的换行符,而不是system.newline,估计也是为了和服务器端实现的简单性。
       后面紧接着有个判断,paramBCSSignCondition如果不为null的时候进下面的一个代码块,当遇到ip数据的时候,给buffer1添加一个标志:I,buffer2里面放数据。举个例子,condition的数据如果是下面的数据:
       
BCSSignCondition condition = new BCSSignCondition();
condition.setIp("192.168.1.1");
condition.setSize(1234L);
condition.setTime(4321L);
         那么现在buffer1和buffer2里面的数据将如下所示:
        buffer1: MBOIST
buffe2里面得数据是:
       Method=GET
       Bucket=bucket
       Object=object
       Ip=192.168.1.1
       Time=4321
       Size=1234
        我们接着来看,实际上是对buffer2的数据做了变换,在buffer2的开头插入了buffer1的数据: 那么现在buffer1的数据是不动的,buffer2里面得数据将会变成下面的内容:
       MBOITS
       Method=GET
       Bucket=bucket
       Object=object
       Ip=192.168.1.1
      Time=4321
       Size=1234
        我们接着来看,最后实现了加密:
  SecretKeySpec localSecretKeySpec = new SecretKeySpec(paramBCSCredentials.getSecretKey().getBytes(), SigningAlgorithm.HmacSHA1.toString());
           这个实际上是根据AK和HmacSHA1算法生成密钥。
        
 Mac localMac = Mac.getInstance(localSecretKeySpec.getAlgorithm());
 localMac.init(localSecretKeySpec);
              这个是根据指定的算法生成mac对象,当然了,这块代码作者写的并不是很好,我在看的时候就有点晕,为什么要根据生成密钥的算法生成mac对象呢,如果这个地方写成localMac.init(SigningAlgorithm.HmacSHA1.toString());就比较好看了,还有发现作者对枚举都没有实现toString而是在外面调用toString,这个习惯也不大好。 下面的init方法实际上是根据密钥初始化mac。
        
arrayOfByte = localMac.doFinal(ServiceUtils.toByteArray(localStringBuilder2.toString()));
 paramBCSHttpRequest.addParameter("sign", ":" + paramBCSCredentials.getAccessKey() + ":" + ServiceUtils.toBase64(arrayOfByte));
 
         最后这个代码实际上是利用doFinal加密函数对buffer2里面得数据进行加密,并生成一个20位的mac。当然这里又使用base64进行了编码,最后将sign作为http的一个参数传递过去。
        我们在利用例子说明一下:假设buffer1和buffer2里面的数据如下:
       buffer1里面得数据是:
          MBOIST
       buffe2里面得数据是:
       Method=GET
       Bucket=bucket
       Object=object
       Ip=192.168.1.1
       Time=4321
       Size=1234
       最后生成的sign是:MBOITS:akakak:52sIR4vwpC4FKKOERHLHJGS0vYk=
      最前面是MBO和里面相应的参数,中间是一个冒号,冒号后面是AK,又是一个冒号,最后是根据SK作为密钥,采用HmacSHA1生成的一个20位的mac再经过BASE64编码后的结果,好了这个整个方法我就分析完了。
       在最后我们再来看一下这个方法,这个方法是void类型,说明是没有返回值的,有四个参数BaiduBCSRequest,BCSHttpRequest,BCSCredentials,BCSSignCondition。其中BCSSignCondition不是必须得,BaiduBCSRequest就是我们通常操作的request对象,在这个函数里面要用它的bucket、object、httpMethodName属性,BCSCredentials里面携带的是AK和FK信息,Condition里面是生成sign的额外的一些非必填的参数。返回值是通过BCSHttpRequest的addParameter进行隐身返回的。总共传递了三个参数,sign是必须得,time和size参数如果有的时候会填写。
      
httpRequest.addParameter("time", String.valueOf(signCondition.getTime()));
httpRequest.addParameter("size", String.valueOf(signCondition.getSize()));
httpRequest.addParameter("sign", signFlags.append(":").append(credentials.getAccessKey()).append(":").append(ServiceUtils.toBase64(data))
					.toString());
         我们可能要发问了,为什么IP存在的时候没有向服务器端传递呢,我想可能服务器端为了避免出错,可能从请求列表里面获的了ip信息了,不过在目前复杂的网络条件下,这种方式是不是可取呢,我感觉应该还是信任客户端的ip,然后根据传递过来的ip数据进行数据签名验证,否则极有可能导致验证不通过。
        最后在附近里面我上传了bcs的jar和反编译工具,希望对大家有所帮助。对于这个过程官方文档讲的也很明白,感兴趣的可以看看:官方文档       
  • 大小: 25.3 KB
分享到:
评论

相关推荐

    Java SDK源代码

    Java SDK源代码是Java开发工具包的原始代码,包含了Java平台标准版(Java SE)的核心类库和其他组件的实现细节。这些源代码对于Java开发者来说是极其宝贵的资源,因为它们揭示了Java API背后的工作原理,有助于理解...

    云存储java sdk源代码.zip

    本话题主要围绕“云存储Java SDK源代码”展开,我们将探讨云存储的基本概念、Java SDK的作用、如何使用Java SDK进行云存储开发,以及如何理解和分析提供的源代码。 首先,云存储是指将数据存储在远程服务器上,用户...

    快盘JAVA版SDK源代码.zip

    【标题】"快盘JAVA版SDK源代码.zip" 指的是一个包含了Java版本的快盘SDK(Software Development Kit)的源代码压缩包。快盘SDK通常是由云存储服务提供商提供的,它允许开发者通过编程接口与快盘服务进行交互,实现...

    bce-java-sdk

    同时,由于SDK是开源的,开发者还可以查看源代码,学习最佳实践,并根据需要进行定制。 总之,“bce-java-sdk”是一个强大的工具,它为Java开发者提供了与百度云服务无缝对接的能力。通过使用SDK中的demo和公共包,...

    微信支付接口java sdk

    `src`目录存放着源代码,`docs`可能包含了SDK的使用文档或API参考,帮助开发者更好地理解和使用SDK。 `README.md`文件通常包含项目的简介、安装指南、使用示例和注意事项,是快速上手的重要参考资料。`....

    AWS S3 SDK for Java开发指南V1.0

    在开始使用AWS S3 JAVA SDK之前,开发者应该对对象存储有一定的理解,并且已经阅读过《XSKY EOS应用与开发指南》。SDK提供了一系列的接口和类,以便开发者进行Bucket管理和Object管理。 **环境搭建**: 1. **AWS S3...

    微信APIV3 JAVA SDK集成(JSAPI支付、商家转账到零钱、订单退款、多商户配置)超简单、新鲜,附源码

    此API V3版本针对JAVA环境提供了SDK,使得JAVA开发者能够轻松地集成微信的各种功能,包括但不限于JSAPI支付、商家转账到零钱、订单退款以及多商户配置等。这篇文章将详细介绍这些功能的集成过程,并提供源码示例。 ...

    海康安防系统SDK及实例源码_win64_java_海康门禁_C#_海康_海康安防系统SDK及实例源码_

    对于Java开发者,可以通过阅读源码学习如何在Java环境中调用海康安防的API;同样,C#开发者也可以从中学习如何在.NET框架下使用SDK。 3. **门禁系统** 海康门禁系统是海康安防解决方案的一部分,它提供了门禁控制...

    Java SDK.zip

    javac是Java SDK中的核心工具,它负责将源代码转换成平台无关的字节码。 3. **Javadoc工具**:用于自动生成Java类库的API文档。开发者可以使用Javadoc命令来解析源代码中的注释,生成HTML格式的文档,方便其他...

    ucloud接口java-sdk开发

    `ucloud-java-sdk-master`这个压缩包文件包含了UCloud Java SDK的源码,开发者可以下载并导入到自己的项目中。通过引入SDK,你可以直接调用预定义的类和方法来执行各种操作,如创建、删除、查询云服务器(UHost)、...

    云计算 02.Java SDK for FusionCompute

    描述:R5版本FusionCompute JAVA API接口,提供随需使用的虚拟化计算能力,以及为支撑计算能力而必须配套的存储及网络能力。 1.文档放在doc中,包含开发指南和接口参考。 2.程序放在software中,提供形态为Java ...

    baidu_bcs_sdk_java-master.zip_BCE_BCS

    通过"baidu_bcs_sdk_java-master"这个压缩包,开发者可以获得完整的Java SDK源代码,进一步了解其内部实现,也可以直接在项目中引用,快速构建与BCS服务交互的功能。使用这个SDK,开发者可以创建自己的应用程序,...

    腾讯微博开放平台Java开发SDK

    在实际开发中,`src`目录包含了SDK的源代码,开发者可以查看并学习其内部实现逻辑;`WebContent`目录可能包含示例Web应用的资源文件,如HTML、CSS和JavaScript,展示了如何在Web环境下集成SDK;`dist`目录通常存放的...

    aws-java-sdk-core-1.11.46.zip

    这个SDK是亚马逊Web服务(AWS)提供的用于Java开发人员的工具集,允许他们轻松地与AWS的各种服务进行交互,包括存储、计算、数据库、分析等。 【描述】"scalatestplus-play.zip, scalatestplus播放scalatest+play" ...

    taobao-sdk-java

    `taobao-sdk-java-20110512-source.jar` 文件则包含的是SDK的源代码,这对于开发者来说是极有价值的资源。通过查看源代码,开发者可以更深入地理解API的工作原理,进行调试,甚至自定义或扩展SDK的功能。这对于解决...

    Openstack-java-sdk-master

    "Openstack-java-sdk-master"很可能是这个项目的主分支,包含了完整的源代码和必要的资源文件,使得开发者能够更容易地在Java环境中集成和使用OpenStack的服务。 OpenStack的Java SDK提供了一系列的类和方法,用于...

    大华SDK+java+4G网络摄像头

    这一步骤涉及到跨平台的编程技巧,确保Java代码能够正确地与底层硬件交互。 对于"手机触发相机进行录像"这个需求,我们需要在手机端编写一个控制程序,可能通过HTTP请求或者自定义的API向服务器发送指令。服务器端...

    微信SDK 开发源代码

    微信SDK开发源代码是针对移动应用开发者的重要资源,它提供了集成微信功能的接口和示例,使得开发者可以方便地在自己的应用程序中实现微信登录、分享、支付等服务。本源代码包显然包含了创建一个Android应用的基本...

    java 商城源代码

    【Java商城源代码】是一个基于Java编程语言的电子商务平台开发项目,它提供了完整的商城系统实现,可以帮助开发者快速搭建一个功能完善的在线购物网站。这个源代码集合涵盖了从用户界面设计、商品管理、订单处理到...

Global site tag (gtag.js) - Google Analytics