全部产品
弹性计算 会员服务 网络 安全 移动云 数加·大数据分析及展现 数加·大数据应用 管理与监控 云通信 阿里云办公 培训与认证 智能硬件
存储与CDN 数据库 域名与网站(万网) 应用服务 数加·人工智能 数加·大数据基础服务 互联网中间件 视频服务 开发者工具 解决方案 物联网 更多
阿里云物联网套件

CCP设备接入

更新时间:2017-07-12 21:22:31

设备认证流程

注意: 认证需要的参数可以在 iot.console.aliyun.com ,获取产品证书和设备证书 productKeyproductSecretdeviceNamedeviceSecret 详细请参考控制台使用手册中的创建产品添加设备

1.设备获取服务器的公钥证书以及接入服务器的ip地址等信息

  • 第一步,设备授权认证。发送一个GET/POST请求到http://iot-auth.aliyun.com/iot/auth, 参数如下:

    参数名 是否必传 描述
    deviceName 必须 用户注册设备的自定义名称,简称设备名称, 例如:b15f5e5063064fffaccc1e56ad59c29
    productKey 必须 用户在阿里云IoT控制台上创建的产品的productKey, 例如: 12345
    sign 必须 签名参数计算的规则如下:
    1. 将所有提交给服务器的参数(sign除外), 按照字母顺序排序, 然后将参数值依次拼接起来. 例如:deviceNameb15f5e5063064fffaccc1e56ad59c29productKey123456signMethodMD5... 假定变量名content
    2. 然后在对这个拼接之后的值(content)进行Hmac签名, 加密使用的key = productSecret+deviceSecret.
    3. 得到结果转16进制字符串并转大写 例如:DF2F6A209E06D25FA154DBE9771ED987
    4. 如果使用MD5,sign=md5(productSecret+content+deviceSecret),最后转大写
    signMethod 可选 签名算法,HmacMD5(默认) 或 HmacSHA1 或 MD5
    resFlag 可选 返回结果类型,默认all,可选 cert:只返回证书,ip:只返回数据服务器地址
    time 可选 时间戳, yyyy-MM-dd hh:mm:ss 格式,用于混淆加密结果,使得每次签名不一样
  • 服务器返回json结构:

{“servers”:”8.8.8.8:8080|8001”,”pubkey”:”pem key经过base64的字符串”,”pkVersion”:”1.0”,”deviceId”:”设备对应阿里云的全局id”,”success”:true,”sign”:”服务器签名”}

  1. 获得到的公钥证书需要base64decode,结果是一个pem格式的字符串(x509证书),其中pkVersion是当前证书版本.
  2. 其中servers是用于CCP连接的数据通道服务器地址,端口有多个选择
  3. 至此, 设备端已经完成了认证和获取接入服务器的ip地址
  4. 服务器返回的sign使用以上一致的签名策略对servers、pubkey、pkVersion参数(success除外)签名,供客户端验证(防止dns劫持风险),证书和数据服务器ip可以缓存客户端,注意ip可能会变化,当设备连接不上时需要重新认证获取最新配置。

2.确认会话的sessionId, 简称: sid

  • 第二步,设备会话握手,使用公钥传输客户端私钥,同时返回会话id。 发送一个GET/POST请求到http://iot-auth.aliyun.com/iot/sid, 参数如下:

    参数名 是否必传 描述
    pkVersion 可选 证书版本(如果不传,则默认使用最新版本证书校验)
    signMethod 可选 签名算法,HmacMD5(默认) 或 HmacSHA1 或 MD5
    seedKey 必须 1.由客户端随机生成16位byte数组
    2. 利用获取到的服务器公钥证书对这个进行 RSA/ECB/PKCS1Padding 加密
    3. 将加密之后的结果进行 Base64 Encode .
    具体可参考下方示例代码。
    data 必须 1. json结构的字符串, 原始格式: {deviceName:”xxxx”, productKey:”xxxxx”}
    2. 组装好json格式之后做 AES-128-ECB, PKCS5 加密, Aes的key使用seedKey二进制数组,最后进行 Base64 Encode.
    3. 详情参考下方示例代码
    sign 必须 对发送给服务器的参数进行签名,规则参考第一步
  • 注意: 最终发送HTTP请求前需要对所有url参数值做urlencode,utf8编码. base64结果通常含有=号之类的特殊字符

  • 返回结果的处理:

    1. {"sid":"/GuyJBskn9R1p9DRoyWfcgAjyfjYRMZm1EqDASVd/Uf5N6JOfZFYg2+juz8Wff+S","success":true,"sign":xxxx}
  1. 利用base64 decode将sid转化成bytes
  2. 使用 AES-128-ECB, PKCS5 解密, Aes的key使用seedKey
  3. 最终得到解密后的 sid
  4. 解密后的sid用于接入服务器凭证进行数据传输,参考 设备接入流程。
  5. sid和seedkey可缓存客户端本地,有效期24小时。

    注意,服务器同时返回了sign签名供客户端验证,防止dns劫持。sign的内容只有sid一个字段,签名规则参考第一步。

  • 错误码

    • 当有异常时返回json错误格式:
      1. {"errorCode":"InvalidSign","message":"illegle sign!","success":false}
      • 其中errorCode有: InvalidSign:签名错误,CertExpired:证书过期,InvalidPara:参数错误,Reject:非法请求,Unknow:系统异常
  • seedkey rsa算法的参考代码

    1. //java示例代码
    2. byte[] seedkey = new byte[16];
    3. new Random().nextBytes(seedkey);
    4. Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    5. CertificateFactory cf = CertificateFactory.getInstance("X.509");
    6. X509Certificate cert = (X509Certificate) cf
    7. .generateCertificate(new ByteArrayInputStream(pk.getBytes()));
    8. PublicKey publicKey = cert.getPublicKey();
    9. cipher.init(Cipher.ENCRYPT_MODE, publicKey);
    10. //公钥加密
    11. byte[] b1 = cipher.doFinal(seedkey);
    12. String seedKey2server = base64(b1);
    13. seedKey2server = UrlEncode.encode(seedKey2server,"utf-8");
    14. //seedKey2server 就是最终发送服务器的值
  • aes算法的参考代码
    1. String data = "{deviceId:\" xxxx\", productKey:\"12345 \"}";
    2. SecretKeySpec aesKey = new SecretKeySpec(seedKey, "AES");
    3. cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    4. cipher.init(Cipher.ENCRYPT_MODE, aesKey);
    5. byte[] b2 = cipher.doFinal(date.getBytes("utf-8"));
    6. //base64
    7. String data2server = base64(b2);
    8. data2server = UrlEncode.encode(data2server,"utf-8");
  • 嵌入式C的加解密开源库可以参考 https://tls.mbed.org/source-code,详见CCP的C版SDK代码。

附录:可变长算法

CCP协议中字段类型为可变长数值,都需要使用该算法来实现。

The algorithm for encoding a decimal number (X) into the variable length encoding scheme is as follows:

  1. do
  2. digit = X MOD 128
  3. X = X DIV 128
  4. // if there are more digits to encode, set the top bit of this digit
  5. if ( X > 0 )
  6. digit = digit OR 0x80
  7. endif
  8. 'output' digit
  9. while ( X> 0 )

where MOD is the modulo operator (% in C), DIV is integer division (/ in C), and OR is bit-wise or (| in C).The algorithm for decoding the Number Length field is as follows:

  1. multiplier = 1
  2. value = 0
  3. do
  4. digit = 'next digit from stream'
  5. value += (digit AND 127) * multiplier
  6. multiplier *= 128
  7. while ((digit AND 128) != 0)

where AND is the bit-wise and operator (& in C).LS When this algorithm terminates, value contains the Remaining Length in bytes.

本文导读目录