Search code examples
c#androidadbbouncycastlepublic-key

ADB Public Key Format


I'm trying to implement ADB protocol in C#. When i tried using AUTH(2), the device always says that my signature is wrong by replying AUTH(1) over and over again until i sent AUTH(3) (which will trigger the allow this pc dialog). I have saved the public key to the phone by checking the "Always allow from this PC" option on the device.

I am suspecting that my public key format i sent is wrong because seeing the size my implementation sends vs the original ADB's is different. The public keys example below does not use the same private key.

ADB:

000022: Bulk or Interrupt Transfer (DOWN), 2019-08-16 08:23:37,2615937 +0,0000492 (1. Device: SAMSUNG_Android (Galaxy series, misc. (MTP mode)))
Pipe Handle: Control PipeSend 0x2bd bytes to the device
 51 41 41 41 41 45 39 37 36 35 78 52 52 44 4D 45   QAAAAE9765xRRDME
 6B 43 47 59 4F 7A 43 63 72 43 68 71 62 53 73 56   kCGYOzCcrChqbSsV
 6D 32 4A 35 57 46 58 71 4A 78 59 79 4B 41 63 55   m2J5WFXqJxYyKAcU
 2B 51 66 57 6A 57 7A 35 54 78 68 58 32 45 46 43   +QfWjWz5TxhX2EFC
 52 4E 4F 38 55 56 4A 4D 68 49 44 33 49 58 72 47   RNO8UVJMhID3IXrG
 38 49 48 54 77 41 4F 76 6D 78 2F 33 67 4F 41 73   8IHTwAOvmx/3gOAs
 55 4F 53 36 4A 2F 5A 74 43 6C 38 38 35 4E 72 64   UOS6J/ZtCl885Nrd
 67 66 30 63 61 33 36 6E 70 51 62 43 54 42 56 45   gf0ca36npQbCTBVE
 56 59 7A 54 52 49 65 59 4A 63 69 65 78 30 37 4F   VYzTRIeYJciex07O
 77 38 38 51 6A 52 4C 53 76 63 53 62 52 56 6D 72   w88QjRLSvcSbRVmr
 73 78 47 70 32 4C 7A 77 47 69 6E 77 43 4D 48 46   sxGp2LzwGinwCMHF
 45 79 69 64 7A 52 42 2B 73 74 4F 2B 66 67 6D 44   EyidzRB+stO+fgmD
 47 64 50 4D 31 51 74 59 47 73 51 74 65 53 76 54   GdPM1QtYGsQteSvT
 36 79 56 57 66 73 73 70 72 6C 65 33 49 6B 74 45   6yVWfssprle3IktE
 32 53 64 4B 4E 63 62 49 44 57 39 5A 56 67 46 2F   2SdKNcbIDW9ZVgF/
 2B 4B 79 6B 4C 69 43 37 77 4F 72 4B 4E 46 4E 49   +KykLiC7wOrKNFNI
 61 45 4C 4B 52 50 37 33 33 31 73 30 76 67 75 34   aELKRP7331s0vgu4
 57 61 76 6F 39 39 66 79 69 43 66 41 48 65 64 45   Wavo99fyiCfAHedE
 6E 42 45 7A 4B 41 50 52 53 42 36 6A 74 39 6D 34   nBEzKAPRSB6jt9m4
 4B 67 73 4D 4F 54 48 67 45 37 45 7A 53 6D 42 4C   KgsMOTHgE7EzSmBL
 72 64 38 51 31 58 44 34 52 75 77 61 6F 76 37 47   rd8Q1XD4Ruwaov7G
 46 2B 31 52 78 42 44 35 73 61 38 6B 6F 4B 6E 53   F+1RxBD5sa8koKnS
 50 68 6E 75 35 48 78 76 71 57 73 49 78 38 70 54   Phnu5HxvqWsIx8pT
 42 59 51 45 79 2B 57 33 51 71 2F 6E 66 32 6B 46   BYQEy+W3Qq/nf2kF
 70 41 33 61 6D 45 63 2F 53 71 4E 2B 30 46 73 55   pA3amEc/SqN+0FsU
 31 48 71 52 78 64 59 5A 68 68 62 66 58 69 52 70   1HqRxdYZhhbfXiRp
 4B 67 74 4E 70 31 50 43 55 2B 46 2F 55 32 2B 67   KgtNp1PCU+F/U2+g
 64 58 39 6C 68 38 52 31 6C 59 7A 79 38 2F 4A 4C   dX9lh8R1lYzy8/JL
 65 5A 6F 4F 37 34 31 47 34 34 54 75 38 4A 69 4F   eZoO741G44Tu8JiO
 69 59 74 6D 43 32 58 42 39 54 52 51 36 49 74 71   iYtmC2XB9TRQ6Itq
 46 30 36 67 4D 53 32 78 71 6C 50 6B 54 58 4F 6D   F06gMS2xqlPkTXOm
 4E 4B 71 72 31 39 76 4C 79 64 66 34 42 5A 34 62   NKqr19vLydf4BZ4b
 57 53 58 4A 71 54 39 66 2B 54 47 39 75 48 79 56   WSXJqT9f+TG9uHyV
 31 77 35 6E 68 6F 34 44 7A 68 38 6D 65 73 44 55   1w5nho4Dzh8mesDU
 6C 30 2F 71 72 71 48 45 4F 73 57 6C 4B 50 72 57   l0/qrqHEOsWlKPrW
 4F 59 2F 5A 42 41 56 4B 4F 6A 6D 34 5A 57 67 5A   OY/ZBAVKOjm4ZWgZ
 76 71 42 56 61 35 71 57 67 4A 44 68 50 37 37 72   vqBVa5qWgJDhP77r
 44 68 47 69 2B 50 34 4C 57 45 35 31 49 6A 44 44   DhGi+P4LWE51IjDD
 43 48 6F 59 42 4B 4C 6E 5A 4A 41 42 37 55 39 61   CHoYBKLnZJAB7U9a
 44 31 7A 54 4E 52 31 47 42 31 65 58 73 50 35 4D   D1zTNR1GB1eXsP5M
 77 42 66 70 30 34 4C 5A 47 71 77 70 30 52 36 59   wBfp04LZGqwp0R6Y
 4E 31 54 79 4E 4B 5A 47 74 65 71 42 4E 4F 51 37   N1TyNKZGteqBNOQ7
 62 5A 59 76 39 46 55 76 45 76 72 47 66 38 64 62   bZYv9FUvEvrGf8db
 6D 31 73 69 63 77 45 41 41 51 41 3D 00            m1sicwEAAQA=.

Mine:

000061: Bulk or Interrupt Transfer (DOWN), 2019-08-16 08:27:20,0783520 +0,0001867 (1. Device: SAMSUNG_Android (Galaxy series, misc. (MTP mode)))
Pipe Handle: Control PipeSend 0x15d bytes to the device
 41 51 41 42 70 47 65 58 30 4F 38 79 6A 37 63 6B   AQABpGeX0O8yj7ck
 62 42 68 79 75 75 4B 76 6E 79 4F 59 44 55 49 56   bBhyuuKvnyOYDUIV
 31 66 39 6A 59 4B 52 78 78 6B 38 4D 68 39 38 34   1f9jYKRxxk8Mh984
 4B 7A 2F 75 46 69 55 61 54 47 68 70 59 6A 6D 2F   Kz/uFiUaTGhpYjm/
 7A 79 42 4A 75 5A 68 71 6E 6C 43 34 4A 6E 65 62   zyBJuZhqnlC4Jneb
 75 31 6B 75 79 48 61 4F 69 38 65 71 34 63 6A 4A   u1kuyHaOi8eq4cjJ
 4F 79 62 63 5A 2F 4A 7A 56 43 4B 75 61 78 4D 36   OybcZ/JzVCKuaxM6
 48 58 47 4B 59 70 39 74 47 63 51 6C 59 6F 36 63   HXGKYp9tGcQlYo6c
 64 53 61 53 59 33 50 6D 49 47 38 74 6D 69 56 69   dSaSY3PmIG8tmiVi
 61 62 4C 30 35 56 78 70 51 65 50 48 4E 37 32 2F   abL05VxpQePHN72/
 2B 64 37 75 73 48 36 52 54 41 35 2F 66 58 79 52   +d7usH6RTA5/fXyR
 6B 54 78 33 69 2B 73 36 73 34 35 58 65 50 43 77   kTx3i+s6s45XePCw
 65 32 5A 71 63 4E 44 2F 5A 61 4B 73 73 43 38 6F   e2ZqcND/ZaKssC8o
 4C 74 5A 4F 6A 54 35 4E 6D 44 58 34 4E 30 38 49   LtZOjT5NmDX4N08I
 6C 49 30 71 61 76 46 31 2F 4F 49 64 38 65 4D 70   lI0qavF1/OId8eMp
 35 64 38 4F 71 61 31 48 74 47 70 64 4D 7A 68 43   5d8Oqa1HtGpdMzhC
 47 6B 69 49 2F 33 63 77 6A 48 39 71 61 58 39 5A   GkiI/3cwjH9qaX9Z
 76 4D 38 4F 61 6D 76 6C 76 68 4B 36 44 47 5A 39   vM8OamvlvhK6DGZ9
 62 47 74 61 73 50 76 59 78 46 69 51 73 51 6A 46   bGtasPvYxFiQsQjF
 6B 56 72 79 75 76 6D 6D 43 74 70 61 32 42 2B 4A   kVryuvmmCtpa2B+J
 78 6F 38 6F 54 37 6B 70 4B 31 69 6D 6A 45 73 6E   xo8oT7kpK1imjEsn
 74 7A 6E 44 75 73 49 62 4B 77 3D 3D 00            tznDusIbKw==.

Here is how i generate the key pair and data signing using BouncyCastle library:

public static string GetBase64Key(AsymmetricKeyParameter key)
{
    var publicKey = (RsaKeyParameters)key;

    using (MemoryStream ms = new MemoryStream())
    {
        byte[] buff = publicKey.Exponent.ToByteArrayUnsigned();
        ms.Write(buff, 0, buff.Length);

        buff = publicKey.Modulus.ToByteArrayUnsigned();
        ms.Write(buff, 0, buff.Length);

        ms.Flush();
        return Convert.ToBase64String(ms.ToArray());
    }
}

public static AsymmetricCipherKeyPair LoadKeyPair(string file)
{
    using (StreamReader reader = new StreamReader(file))
    {
        PemReader pem = new PemReader(reader);
        return pem.ReadObject() as AsymmetricCipherKeyPair;
    }
}

public static void SaveKeyPair(AsymmetricCipherKeyPair pair, string file)
{
    using (StreamWriter writer = new StreamWriter(file))
    {
        PemWriter pem = new PemWriter(writer);
        pem.WriteObject(pair.Private);
        pem.WriteObject(pair.Public);
    }
}

public static AsymmetricCipherKeyPair GenerateKeyPair(int keySize)
{
    CryptoApiRandomGenerator randomGenerator = new CryptoApiRandomGenerator();
    SecureRandom secureRandom = new SecureRandom(randomGenerator);
    var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keySize);
    var keyPairGenerator = new RsaKeyPairGenerator();
    keyPairGenerator.Init(keyGenerationParameters);
    return keyPairGenerator.GenerateKeyPair();
}

public static byte[] SignDataSHA1(byte[] data, AsymmetricKeyParameter privateKey)
{
    ISigner signer = SignerUtilities.GetSigner("SHA1WITHRSA");
    signer.Init(true, privateKey);
    signer.BlockUpdate(data, 0, data.Length);
    return signer.GenerateSignature();
}

And this is how i used those helper functions:

// Initialiizing key pair.
AsymmetricCipherKeyPair pair;

if (File.Exists(RSA_CONTAINER_NAME))
{
    pair = CryptoHelper.LoadKeyPair(RSA_CONTAINER_NAME);
}
else
{
    pair = CryptoHelper.GenerateKeyPair(RSA_KEY_STRENGTH);
    CryptoHelper.SaveKeyPair(pair, RSA_CONTAINER_NAME);
}

// Signing token.
byte[] signedData = CryptoHelper.SignDataSHA1(token, pair.Private);

// Writing data.
Message msg = new Message();
msg.Command = Message.A_AUTH;
msg.Arg0 = 2; // Private key signing.
msg.Data = signedData;
msg.DataLength = signedData.Length;
await SendMessageAsync(device, msg);

// Reading response.
Message reply = await ReadMessageAsync(device);
byte[] replyBody = new byte[reply.DataLength];
await device.ReadAsync(replyBody, 0, replyBody.Length);

if (reply.Command == Message.A_AUTH)
{
    // Sending rsa public key.
    string pem = CryptoHelper.GetBase64Key(pair.Public) + "\0";
    byte[] publicKey = Encoding.ASCII.GetBytes(pem);
    msg = new Message();
    msg.Command = Message.A_AUTH;
    msg.Arg0 = 3; // RSAPUBLICKEY
    msg.Data = publicKey;
    msg.DataLength = publicKey.Length;
    await SendMessageAsync(device, msg);

    // Reading response.
    reply = await ReadMessageAsync(device);
    replyBody = new byte[reply.DataLength];
    await device.ReadAsync(replyBody, 0, replyBody.Length);
}

if (reply.Command != Message.A_CNXN)
{
    // Something went wrong.
    throw new Exception("Authentication error");
}

// Done.

Any help would be highly appreciated.

Note: I have completed the steps until i got the token to sign from the device for brevity. I also have tried PEM format (without including the BEGIN and END lines), didn't work either.


Solution

  • I finally managed to fix my authentication problem. I decided to include pre generated public and private key to my app (not generated automatically anymore). I also installed bouncy castle library for C# to parse private key provided by adb. Public key problem fixed.

    Turned out that my signing method is also wrong. Here is the correct one (using bouncy castle):

    public static byte[] SignDataSHA1(byte[] data, AsymmetricKeyParameter privateKey)
    {
        // Converting bouncy castle key to native csp.
        RSAParameters rsaParam = DotNetUtilities.ToRSAParameters(privateKey as RsaPrivateCrtKeyParameters);
    
        using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
        {
            rsa.ImportParameters(rsaParam);
    
            // Signing data.
            return rsa.SignHash(data, CryptoConfig.MapNameToOID("SHA1"));
        }
    }
    

    I basically just tell the signer that the data already hashed and voila, it worked.