首页 > 其他分享 >Android问题解决:android.util.Base64.encode 导致签名不匹配 SignatureDoesNotMatch

Android问题解决:android.util.Base64.encode 导致签名不匹配 SignatureDoesNotMatch

时间:2023-06-08 14:05:21浏览次数:52  
标签:NO URL Base64 util flag SignatureDoesNotMatch Signature encode



文章目录

  • 前文:遇到问题
  • 一问:为什么SignatureDoesNotMatch
  • 二问:为什么SignatureDoesNotMatch
  • 三问:Signature请求参数为什么多了%0A
  • 四问:Signature为什么多了换行
  • 五问:android.util.Base64.encode的用法


前文:遇到问题

在折腾《ESP32-C3入门教程——导读》时,需要对接阿里云物联网平台。
想要通过Android App直接调用阿里云物联网云端API
阿里云并没有直接提供对应的云端开发的Android SDK,就只能自己写了。
结果就遇到了这么一个大坑。签名不匹配,校验不通过。

今天博文,咱们换种写法,5why分析法。
话说每个问题多问几个why,在第5个why的时候,你就能找到根源。

SignatureDoesNotMatch : Specified signature is not matched with our calculation

{
	    "RequestId":"DD7C6D23-8236-5152-85FB-A7869342B749",
	    "Message":"Specified signature is not matched with our calculation. server string to sign is:XXXXXXXXX",
	    "Recommend":"https://troubleshoot.api.aliyun.com?q=SignatureDoesNotMatch&product=Iot",
	    "HostId":"iot.cn-shanghai.aliyuncs.com",
	    "Code":"SignatureDoesNotMatch"
    }

Android问题解决:android.util.Base64.encode 导致签名不匹配 SignatureDoesNotMatch_DEFAULT

一问:为什么SignatureDoesNotMatch

分析:

  • 为什么SignatureDoesNotMatch 签名不匹配
  • 大概率是Signature计算错误?
  • 直接看签名机制其中的Signature参数的计算过程

验证:
做个对比测试,验证我们计算的Signature是否正确

  • 下载官方提供的java sdk
  • 设定一模一样的参数,分别运行java和android
  • 打印输出,皆是:seuSaBh1s8fZ4MGayjxYv4wc3JY=

二问:为什么SignatureDoesNotMatch

分析:
第一步排查直接失败,方向错误,没法接着深挖。

  • 换个方向继续问:Signature签名正确,为什么还会提示SignatureDoesNotMatch
  • 难道是提交给阿里云的请求字符串错误?

验证:
同上一样,做一个对比测试,验证我们最终得到的请求字符串是否一致。

  • 下载官方提供的java sdk
  • 设定一模一样的参数,分别运行java和android
  • 打印输出,分别如下:

http://iot.cn-shanghai.aliyuncs.com/?SignatureVersion=1.0&Action=QueryDevicePropertyStatus&Format=JSON&SignatureNonce=2E68FD84-A0BE-4D86-BDB2-7578F20290E6&Version=2018-01-20&AccessKeyId=XXXXXXXXX&Signature=seuSaBh1s8fZ4MGayjxYv4wc3JY%3D%0A&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&Timestamp=2022-04-21T15%3A30%3A27Z&ProductKey=XXXXXXXXX&DeviceName=XXXXXXXXX

https://iot.cn-shanghai.aliyuncs.com/?AccessKeyId=XXXXXXXXX&Action=QueryDevicePropertyStatus&DeviceName=XXXXXXXXX&Format=JSON&ProductKey=XXXXXXXXX&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=2E68FD84-A0BE-4D86-BDB2-7578F20290E6&SignatureVersion=1.0&Timestamp=2022-04-21T15%3A30%3A27Z&Version=2018-01-20&Signature=seuSaBh1s8fZ4MGayjxYv4wc3JY%3D

  • 经过细致的观察和对比,很快发现Android打印输出的是Signature=seuSaBh1s8fZ4MGayjxYv4wc3JY%3D%0A
  • 而Java打印输出的是Signature=seuSaBh1s8fZ4MGayjxYv4wc3JY%3D

三问:Signature请求参数为什么多了%0A

分析:

  • Signature原先的计算结果是seuSaBh1s8fZ4MGayjxYv4wc3JY=
  • 根据签名机制要求,需要将其按照RFC3986的规则进行URL编码后,再添加到请求参数中,即完成对请求签名的过程。
  • seuSaBh1s8fZ4MGayjxYv4wc3JY=进行URL编码后得到seuSaBh1s8fZ4MGayjxYv4wc3JY%3D
  • 那么java程序运行的结果是没毛病的,Android运行结果为什么多了%0A
  • 由URL编码得知,%0A是换行符经过URL编码得来的。
  • 那么得到的结论是Android计算Signature的结果是多了一个换行符。
  • 这也反推到第一个为什么的猜测是正确的,只是因为换行符靠打印是看不出来的。

四问:Signature为什么多了换行

分析:

  • android的Signature计算代码很多也是借鉴签名机制这边的Java代码示例。
  • 基本没有修改,唯一修改的地方在于return new String(Base64.encodeBase64(bytes, false), CHARSET_UTF8);
  • 因为android没有该函数,所以我将其改成return new String(Base64.encode(bytes, Base64.DEFAULT), CHARSET_UTF8);
  • 如果代码中哪里有问题,那么最有可能出问题的地方就在这里。

验证:

  • return new String(Base64.encode(bytes, Base64.DEFAULT), CHARSET_UTF8);
  • 改成return new String(Base64.encode(bytes, Base64.NO_WRAP), CHARSET_UTF8);
  • 问题解决,撒花~

五问:android.util.Base64.encode的用法

分析:

但是,我们不禁还是要问:为什么呢?

android.util.Base64.java的源码中得到

/**
 * Utilities for encoding and decoding the Base64 representation of
 * binary data.  See RFCs <a
 * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a
 * href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>.
 */
public class Base64 {
    /**
     * Default values for encoder/decoder flags.
     */
    public static final int DEFAULT = 0;

    /**
     * Encoder flag bit to omit the padding '=' characters at the end
     * of the output (if any).
     */
    public static final int NO_PADDING = 1;

    /**
     * Encoder flag bit to omit all line terminators (i.e., the output
     * will be on one long line).
     */
    public static final int NO_WRAP = 2;

    /**
     * Encoder flag bit to indicate lines should be terminated with a
     * CRLF pair instead of just an LF.  Has no effect if {@code
     * NO_WRAP} is specified as well.
     */
    public static final int CRLF = 4;

    /**
     * Encoder/decoder flag bit to indicate using the "URL and
     * filename safe" variant of Base64 (see RFC 3548 section 4) where
     * {@code -} and {@code _} are used in place of {@code +} and
     * {@code /}.
     */
    public static final int URL_SAFE = 8;

    /**
     * Flag to pass to {@link Base64OutputStream} to indicate that it
     * should not close the output stream it is wrapping when it
     * itself is closed.
     */
    public static final int NO_CLOSE = 16;
   
   ......

flags

注释

说明

DEFAULT

Default values for encoder/decoder flags.

默认

NO_PADDING

Encoder flag bit to omit the padding ‘=’ characters at the end of the output (if any).

省略末尾填充的=

NO_WRAP

Encoder flag bit to omit all line terminators (i.e., the output will be on one long line).

省略所有的终止符

CRLF

Encoder flag bit to indicate lines should be terminated with a CRLF pair instead of just an LF. Has no effect if NO_WRAP is specified as well.

指示行的编码器标志位用CRLF替代LF

URL_SAFE

Encoder/decoder flag bit to indicate using the “URL and filename safe” variant of Base64 (see RFC 3548 section 4) where - and _ are used in place of + and /

URL和文件名安全方式,替换其中不符合url安全的字符如+和/

NO_CLOSE

Flag to pass to Base64OutputStream to indicate that it should not close the output stream it is wrapping when it itself is closed.

传递给Base64OutputStream标记以指示它本身关闭时不应关闭它正在包装的输出流

在这里大概明白了,

  • DEFAULT默认会在base64字符串结果添加换行。
  • 而选择NO_WRAP 会省略掉这个结尾的换行。
  • 所以,问题就出在这。
  • 该问题刚好在5why分析法的第五个为什么结束的时候,完美结题!

觉得好,就一键三连呗(点赞+收藏+关注)


标签:NO,URL,Base64,util,flag,SignatureDoesNotMatch,Signature,encode
From: https://blog.51cto.com/u_16081772/6439462

相关文章

  • RsaUtils
    importorg.apache.commons.codec.binary.Base64;importjavax.crypto.Cipher;importjava.io.ByteArrayOutputStream;importjava.security.*;importjava.security.interfaces.RSAPrivateKey;importjava.security.interfaces.RSAPublicKey;importjava.security.spec......
  • Django修改数据库时出错 django.db.utils.OperationalError: (1091, "Can't DROP 'con
    记录下简单的处理方法:报错信息:django.db.utils.OperationalError:(1091,"Can'tDROP'content';checkthatcolumn/keyexists")可能数据库中的字段结构已经完成了此字段的修改但是在  pythonmanage.pymakemigrations新生成的migrations/0002_auto_20191011_2104.py......
  • 判断非String对象是否为null,小伙竟然用StringUtils.isEmpty(obj+"")
    我在代码走查时,发现下面的代码。其中Line133行的StringUtils.isEmpty(levyId+"")引起了我的注意。levyId是Long,你这样判断Long是否为null,靠谱吗?  答案是:不靠谱!当levyId是null时,levyId+""的值是什么?是字符串null哟~~显然,StringUtils.isEmpty("null")是false。所以,还是老......
  • utils
    1.kills.sh #!/bin/shNAME=$1#$1运行时输入参数为文件名称if[-z"$NAME"];thenecho"STRINGisempty"NAME="aa"fiecho$NAMEID=`ps-ef|grep"$NAME"|grep-v"$0"|grep-v"grep"|awk......
  • utils.js
    加减乘除运算/***@description:加法运算*@param{*}arg1*@param{*}arg2*@param{*}number展示小数点后位数*@return{*}*/exportfunctionoperationAdd(arg1,arg2,number=2){letl1=0,l2=0,m,c;try{l1=arg1.toS......
  • Java中HttpClientUtil工具类
     HttpClientUtil包含get和post方法。发送HttpPost或HttpGet请求一共三个步骤:1、创建CloseableHttpClient对象,用于执行excute方法2、创建HttpPost或者HttpGet请求对象3、执行请求,判断返回状态,接收响应对象  publicclassHttpClientUtil{/****编码集*/......
  • Java中HttpClientUtil工具类
     HttpClientUtil包含get和post方法。发送HttpPost或HttpGet请求一共三个步骤:1、创建CloseableHttpClient对象,用于执行excute方法2、创建HttpPost或者HttpGet请求对象3、执行请求,判断返回状态,接收响应对象  publicclassHttpClientUtil{/****编码集*/......
  • Java中HttpClientUtil工具类
    ​ HttpClientUtil包含get和post方法。发送HttpPost或HttpGet请求一共三个步骤:1、创建CloseableHttpClient对象,用于执行excute方法2、创建HttpPost或者HttpGet请求对象3、执行请求,判断返回状态,接收响应对象  publicclassHttpClientUtil{/****编码集......
  • Java中HttpClientUtil工具类
    ​ HttpClientUtil包含get和post方法。发送HttpPost或HttpGet请求一共三个步骤:1、创建CloseableHttpClient对象,用于执行excute方法2、创建HttpPost或者HttpGet请求对象3、执行请求,判断返回状态,接收响应对象  publicclassHttpClientUtil{/****编码集......
  • Python 标准类库-因特网数据处理之Base64数据编码
    该模块提供将二进制数据编码为可打印ASCII字符并将这种编码解码回二进制数据的功能。它为RFC3548中指定的编码提供编码和解码功能。定义了Base16、Base32和Base64算法,以及事实上的标准Ascii85和Base85编码。RFC3548编码适用于对二进制数据进行编码,以便可以安全地通过电子邮件发......