Arduino到Android安全蓝牙连接

时间:2014-12-26 01:44:48

标签: android bluetooth cryptography arduino authorization

我使用Adafruit的Arduino Uno和nRF8001主板通过蓝牙连接到Android手机。我将使用它来锁定和解锁锁,我需要确保只有经过验证的设备才能启动锁定和解锁。我已经搜索了一堆,我很难找到一个明确的例子来说明我应该怎么做才能验证连接设备。目前我已将Arduino连接到锁定,每当Android手机连接时,都可以锁定和解锁。

我想到的过程如下:

  1. Android尝试连接到Arduino
  2. Arduino查看请求并将随机字符串发送到Android设备
  3. Android使用共享密钥加密字符串并将其发送回Arduino
  4. Arduino会对加密后的字符串进行解密,并验证它是否与其发送的原始字符串匹配,如果它执行了该操作,它会继续并连接/继续锁定或解锁。
  5. [编辑] 我已经完成了更多的研究和工作,并从security.stackexchange的建议中决定使用AES进行加密。我正在使用this Arduino librarythis Android library我对我在编写Arduino和Android时使用的两个库上应该使用的配置感到有些困惑。

    我设置的Arduino代码用于测试加密解密:

    #include <AES.h>
    
    AES aes ;
    
    byte key[] = 
    {
      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    } ;
    
    byte plain[] =
    {
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    } ;
    
    byte my_iv[] = 
    {
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    } ;
    
    char PassString[] = "This is hard to believe but true";
    char Message[] = "We, the Fairies, blithe and antic Of dimensions not gigantic, Th";
    
    byte cipher [4*N_BLOCK] ;
    byte check [4*N_BLOCK] ;
    
    int bits = 128;
    int blocks = 4;
    
    void loop () 
    {}
    
    
    void setup ()
    {
      Serial.begin (9600) ;
      Serial.println ("Starting encryption...") ;
    
      byte iv [N_BLOCK] ;
    
      // Pass the key into the byte array
      for (int i = 0; i < 32; i++) {
        key[i] = PassString[i];
      }
    
      print_value ("KEY = ", key, 256) ;
    
      for (int i = 0; i < 64; i++) {
        plain[i] = Message[i];
      }
    
      // Set Key
      byte succ = aes.set_key (key, bits) ;
    
      // Encrypt
      for (byte i = 0 ; i < 16 ; i++)
        iv[i] = my_iv[i] ;
      succ = aes.cbc_encrypt (plain, cipher, blocks, iv) ;
    
      // Decrypt
      for (byte i = 0 ; i < 16 ; i++)
        iv[i] = my_iv[i] ;
      succ = aes.cbc_decrypt (cipher, check, blocks, iv) ;
    
      // Prints the plain, ciper, decrypted, and IV
      for (byte ph = 0 ; ph < (blocks == 1 ? 3 : 4) ; ph++)
      {
        for (byte i = 0 ; i < (ph < 3 ? blocks*N_BLOCK : N_BLOCK) ; i++)
        {
          byte val = ph == 0 ? plain[i] : ph == 1 ? cipher[i] : ph == 2 ? check[i] : iv[i] ;
          Serial.print (val>>4, HEX) ; Serial.print (val&15, HEX) ; Serial.print (" ") ;
        }
        Serial.println () ;
      }  
    
    }
    
    char * hex = "0123456789abcdef" ;
    void print_value (char * str, byte * a, int bits)
    {
      Serial.print (str) ;
      bits >>= 3 ; //bits goes from decimal 128 to decimal 16
      for (int i = 0 ; i < bits ; i++)
        {
          // of ex. 0xb9 prints b then 9
          byte b = a[i] ;
          Serial.print (hex [b >> 4]) ;
          Serial.print (hex [b & 15]) ;
        }
      Serial.println () ;
    }
    

    我的Android程序几乎是从上面的链接逐字逐句。

    我可以让他们两个加密和解密他们自己的消息,但他们的设置似乎非常不同,并且无法相互加密。 Android代码似乎涉及更多,如创建密钥和盐。这两个库都非常通用,我不知道如何使它们以相同的方式加密。

    我有几个问题希望能帮助我解决这个问题:

    1. &#34;如何阻止&#34;在Arduino代码中涉及到Android 执行?
    2. 从我读过的内容来解密Arduino上的密文 我需要发送Android,密文,IV和PBE 迭代次数。这是正确的吗?
    3. Android代码接受我的密钥并使用一堆SecretKeys函数 在它上面,我认为随机化并使其更安全。如果我有 把钥匙存放在Arduino上,我还需要打扰吗?
    4. Arduino代码中的PBE迭代计数在哪里?我不是真的 看到图书馆里的任何东西。我需要自己实现吗?一世 看到&#34; test_vectors&#34;图书馆的一个例子 以下一点代码。这是迭代吗?

      for (int j = 0 ; j < 1000 ; j++)
      {
          succ = aes.encrypt (plain, cipher) ;
          aes.copy_n_bytes (plain, cipher, 16) ;
      }
      

1 个答案:

答案 0 :(得分:1)

如果要将加密消息从Android发送到Arduino,您必须确保在两端使用相同的参数。

我从Vladimir Klykov获取了一些测试向量,然后使用Arduino(使用AES library)和Java加密和解密。

两个库都可以设置为CBC,两端使用16字节的十六进制向量,不会出现填充问题。

<强> Arduino的

#include <AES.h>

AES aes ;

//2b7e151628aed2a6abf7158809cf4f3c
byte key[] = {
  0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, 
};

//6bc1bee22e409f96e93d7e117393172a
byte plain[] = {
  0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 
};

//000102030405060708090A0B0C0D0E0F
byte my_iv[] = {
  0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 
};

byte cipher [N_BLOCK] ;
byte check [N_BLOCK] ;

void loop () 
{
}

void setup () 
{
  Serial.begin (115200) ;

  while (!Serial) ;
  Serial.println ("Ready") ;

  Serial.print("N_BLOCK: ") ;
  Serial.println (N_BLOCK) ;

  prekey (128, 2) ;
}

void prekey (int bits, int blocks)
{
  byte iv [N_BLOCK] ;

  long t0 = micros () ;
  byte succ = aes.set_key (key, bits) ;
  long t1 = micros()-t0 ;
  Serial.print ("set_key ") ; Serial.print (bits) ; Serial.print (" ->") ; Serial.print ((int) succ) ;
  Serial.print (" took ") ; Serial.print (t1) ; Serial.println ("us") ;
  t0 = micros () ;
  if (blocks == 1)
    succ = aes.encrypt (plain, cipher) ;
  else
  {
    for (byte i = 0 ; i < 16 ; i++)
      iv[i] = my_iv[i] ;
    succ = aes.cbc_encrypt (plain, cipher, blocks, iv) ;
  }
  t1 = micros () - t0 ;
  Serial.print ("encrypt") ; Serial.print (" ->") ; Serial.print ((int) succ) ;
  Serial.print (" took ") ; Serial.print (t1) ; Serial.println ("us") ;

  t0 = micros () ;
  if (blocks == 1)
    succ = aes.decrypt (cipher, plain) ;
  else
  {
    for (byte i = 0 ; i < 16 ; i++)
      iv[i] = my_iv[i] ;
    succ = aes.cbc_decrypt (cipher, check, blocks, iv) ;
  }
  t1 = micros () - t0 ;
  Serial.print ("decrypt") ; Serial.print (" ->") ; Serial.print ((int) succ) ;
  Serial.print (" took ") ; Serial.print (t1) ; Serial.println ("us") ;

  for (byte ph = 0 ; ph < 5 ; ph++)
  {
    Serial.print(ph == 0 ? "plain:  " : ph == 1 ? "key:    " : ph == 2 ? "iv:     " : ph == 3 ? "enc:    " : "dec:    ") ;
    for (byte i = 0 ; i < (blocks-1)*N_BLOCK ; i++)
    {
      byte val = ph == 0 ? plain[i] : ph == 1 ? key[i] : ph == 2 ? my_iv[i] : ph == 3 ? cipher[i] : check[i] ;
      Serial.print (val>>4, HEX) ; Serial.print (val&15, HEX) ;
    }
    Serial.println () ;
  }  
}

<强>爪哇

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import java.math.*;

public class Encryptor3 {

  public static byte[] hexStringToByteArray(String hexInputString){
    byte[] bts = new byte[hexInputString.length() / 2];

    for (int i = 0; i < bts.length; i++) {
      bts[i] = (byte) Integer.parseInt(hexInputString.substring(2*i, 2*i+2), 16);
    }

    return bts;
  }

  public static String byteArrayToString(byte[] byteArray) {
    StringBuilder str = new StringBuilder();

    for (int i = 0; i < byteArray.length; i++) {
      str.append((char) byteArray[i]);
    }

    return str.toString();
  }

  public static String byteArrayToHexString(byte[] arg) {
    int l = arg.length * 2;
    return String.format("%0"+l+"x", new BigInteger(1, arg));
  }

  public static byte[] encrypt(byte[] key1, byte[] key2, byte[] value) {
    try {
      IvParameterSpec iv = new IvParameterSpec(key2);
      SecretKeySpec skeySpec = new SecretKeySpec(key1, "AES");

      Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
      cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

      byte[] encrypted = cipher.doFinal(value);

      return encrypted;

    } catch (Exception ex) {
      ex.printStackTrace();
    }

    return null;
  }

  public static byte[] decrypt(byte[] key1, byte[] key2, byte[] encrypted) {
    try {
      IvParameterSpec iv = new IvParameterSpec(key2);
      SecretKeySpec skeySpec = new SecretKeySpec(key1, "AES");

      Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
      cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

      byte[] original = cipher.doFinal(encrypted);

      return original;

    } catch (Exception ex) {
      ex.printStackTrace();
    }

    return null;
  }

  public static String toHex(String arg) {
    int l = arg.length() * 2;

    return String.format("%0"+l+"x", new BigInteger(1, arg.getBytes()));
  }

  public static String HexStringToString (String arg) {
    StringBuilder output = new StringBuilder();
    for (int i = 0; i < arg.length(); i+=2) {
      String str = arg.substring(i, i+2);
      output.append((char)Integer.parseInt(str, 16));
    }

    return output.toString();
  }


  public static void main(String[] args) {
    // source: http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors#aes-cbc-128
    String message = "6bc1bee22e409f96e93d7e117393172a"; // 16 byte = 128 bit key
    //String message = toHex("Hello00000000000");
    String key1 =    "2b7e151628aed2a6abf7158809cf4f3c";
    String iv =      "000102030405060708090A0B0C0D0E0F";
    String match =   "7649abac8119b246cee98e9b12e9197d";

    System.out.print("message (hex):         "); System.out.println(message);
    System.out.print("key (hex):             "); System.out.println(key1);
    System.out.print("iv (hex):              "); System.out.println(iv);
    System.out.print("match (hex):           "); System.out.println(match);
    System.out.println();

    byte[] enc_message_ba = encrypt(hexStringToByteArray(key1), hexStringToByteArray(iv), hexStringToByteArray(message));
    System.out.print("Encrypted (hex):       "); System.out.println(byteArrayToHexString(enc_message_ba));
    System.out.println();

    byte[] dec_message_ba = decrypt(hexStringToByteArray(key1), hexStringToByteArray(iv), enc_message_ba);
    System.out.print("Decrypted (hex):       "); System.out.println(byteArrayToHexString(dec_message_ba));
  }
}

Arduino输出

在Arduino中安装AES库并照常运行

Ready
N_BLOCK: 16
set_key 128 ->0 took 596us
encrypt ->0 took 1136us
decrypt ->0 took 1548us
plain:  6BC1BEE22E409F96E93D7E117393172A
key:    2B7E151628AED2A6ABF7158809CF4F3C
iv:     000102030405060708090A0B0C0D0E0F
enc:    7649ABAC8119B246CEE98E9B12E9197D
dec:    6BC1BEE22E409F96E93D7E117393172A

Java输出

将代码放入Encryptor3.java,然后从命令行

macbookproz:aes_cbc_128 matteo$ javac Encryptor3.java 
macbookproz:aes_cbc_128 matteo$ java Encryptor3
message (hex):         6bc1bee22e409f96e93d7e117393172a
key (hex):             2b7e151628aed2a6abf7158809cf4f3c
iv (hex):              000102030405060708090A0B0C0D0E0F
match (hex):           7649abac8119b246cee98e9b12e9197d

Encrypted (hex):       7649abac8119b246cee98e9b12e9197d

Decrypted (hex):       6bc1bee22e409f96e93d7e117393172a