本身想写一个例子程序的,发现官方的文档比较简单,所以想写一系列的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。即就是