叮咚智能音箱开发 ,用C# 实现开通状态回调解密教程
智能音箱是目前热火的智能产品,有很多家企业推出自己的智能音箱,并且进行了开放平台,允许第三方开发者,基于开放平台进行应用开发,丰富自己产品功能。
本人则购买一台 京东与讯飞合作的产品,叮咚智能音箱。并且在里面开发一个小应用“小军找手机”,
但在开发过程中遇到一个坑,利用C#开发语言,实现叮咚开放平台 的加解密。因为叮咚平台是用JAVA加密,我主用C#语言。
平台在第三方应用中需要进行参数设置,见下图,要求设置密钥,即128个字符的字符串。因官方采用JAVA语言开发,如果第三方应用采用.NET 开发,则按页面中的JAVA的方式无法正确解密回明文的state ,即json:{"userid":"用户标识","operation":"oauth","timestamp":"时间戳,毫秒","appid":"应用的APPID"} 。这段字符串的反解。
经过群里伙伴的帮助,终于实现反解。现将教程公布如下:
再次感谢 群中 @SuperCokou @智家e物联 @华仔爱技术
最重要的https://yq.aliyun.com/articles/2513 本页的原创者 华仔爱技术
官方应用对state参数的解密过程
1.应用将接收到的数据进行Url解码,state_base64 = UrlDecoder.decode(state,"UTF-8")
2.应用将解码后到的数据进行Base64解码 state_mi = Base64.decode(state_base64)
3.应用使用应用配置的AesKey秘钥进行数据的解密 state = AES.decrypt(state_mi,AesKey)
应用.NET进行参数解密的过程
1.用附的JAVA代码 见下,将代码中key='12345578 中的变量key ,替换成你自己所用的回调地址参数秘钥,即参数配置中的秘钥。
2.在DOS环境,运行javac TestGenAESByteKey和java TestGenAESByteKey 然后得到类似这种的字符串 a0UcXq pLnhoWpzpHYcl1+A==
以上两步,在第三方应用的代码方法之外使用,因为一些底层加密的原因,具体的不作讨论。
3.下面 C# 代码 Decrypt方法 反解出 state。这部分在第三方应用中
附JAVA代码:
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import sun.misc.BASE64Encoder;
public class TestGenAESByteKey{
/**
* @param args
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
*/
public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException {
String key="12345678123456768123456781234567681234567812345676812345678123456768123456781234567681234567812345676812345678123456768123456781";
KeyGenerator kgen = KeyGenerator.getInstance("AES");
java.security.SecureRandom random = java.security.SecureRandom.getInstance("SHA1PRNG");
random.setSeed(key.getBytes());
kgen.init(128, random);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
BASE64Encoder coder = new BASE64Encoder();
System.out.println(coder.encode(enCodeFormat));
}
}
附C#代码
/// <summary>
/// 服务开启关闭回调
/// </summary>
/// <param name="state"></param>
/// <returns></returns>
public ContentResult OpenService(string state)
{
string state_base64 = HttpUtility.UrlDecode(state,Encoding.UTF8);
String decrypt_state = Decrypt(state_base64);
///下面为具体的业务逻辑
}
private static string Decrypt(string toDecrypt)
{
byte[] keyArray = Convert.FromBase64String(@"a0UcbepLnhoWpzpHYcl1+A==");
byte[] inputBuffer = Convert.FromBase64String(toDecrypt);
RijndaelManaged rDel = new RijndaelManaged();
rDel.Key = keyArray;
rDel.Mode = CipherMode.ECB;
rDel.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = rDel.CreateDecryptor();
byte[] resultArray = cTransform.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
return Encoding.UTF8.GetString(resultArray);
}
[王老师玩ESP32系列]第十四课,ESP32使用RTOS之任务传参
By 物物互动2017.9.30 10:44
/* structure that hold data*/ typedef struct{ int sender; int counter; }Data; /* this variable hold queue handle */ xQueueHandle xQueue; void setup() { Serial.begin(112500); /* create the queue which size can contains 5 elements of Data */ xQueue = xQueueCreate(5, sizeof(Data)); xTaskCreate( sendTask1, /* Task function. */ "sendTask1", /* name of task. */ 10000, /* Stack size of task */ NULL, /* parameter of the task */ 2, /* priority of the task */ NULL); /* Task handle to keep track of created task */ xTaskCreate( sendTask2, /* Task function. */ "sendTask2", /* name of task. */ 10000, /* Stack size of task */ NULL, /* parameter of the task */ 2, /* priority of the task */ NULL); /* Task handle to keep track of created task */ xTaskCreate( receiveTask, /* Task function. */ "receiveTask", /* name of task. */ 10000, /* Stack size of task */ NULL, /* parameter of the task */ 1, /* priority of the task */ NULL); /* Task handle to keep track of created task */ } void loop() { } void sendTask1( void * parameter ) { /* keep the status of sending data */ BaseType_t xStatus; /* time to block the task until the queue has free space */ const TickType_t xTicksToWait = pdMS_TO_TICKS(100); /* create data to send */ Data data; /* sender 1 has id is 1 */ data.sender = 1; data.counter = 1; for(;;){ Serial.println("sendTask1 is sending data"); /* send data to front of the queue */ xStatus = xQueueSendToFront( xQueue, &data, xTicksToWait ); /* check whether sending is ok or not */ if( xStatus == pdPASS ) { /* increase counter of sender 1 */ data.counter = data.counter + 1; } /* we delay here so that receiveTask has chance to receive data */ delay(1000); } vTaskDelete( NULL ); } /* this task is similar to sendTask1 */ void sendTask2( void * parameter ) { BaseType_t xStatus; const TickType_t xTicksToWait = pdMS_TO_TICKS(100); Data data; data.sender = 2; data.counter = 1; for(;;){ Serial.println("sendTask2 is sending data"); xStatus = xQueueSendToFront( xQueue, &data, xTicksToWait ); if( xStatus == pdPASS ) { data.counter = data.counter + 1; } delay(1000); } vTaskDelete( NULL ); } void receiveTask( void * parameter ) { /* keep the status of receiving data */ BaseType_t xStatus; /* time to block the task until data is available */ const TickType_t xTicksToWait = pdMS_TO_TICKS(100); Data data; for(;;){ /* receive data from the queue */ xStatus = xQueueReceive( xQueue, &data, xTicksToWait ); /* check whether receiving is ok or not */ if(xStatus == pdPASS){ /* print the data to terminal */ Serial.print("receiveTask got data: "); Serial.print("sender = "); Serial.print(data.sender); Serial.print(" counter = "); Serial.println(data.counter); } } vTaskDelete( NULL ); }