Search code examples
javagorsa

Decrypting RSA/ECB/NoPadding Byte Array in Golang


I have a Java code where I send a package with a scrambled key:

protected void write()
{
    writeC(0x00);
    writeD(_sessionId);
    writeD(PROTOCOL_VERSION);
    writeB(_publicKey); --- > scrambled key
    writeB(UNKNOWN_GG);
    writeB(_blowfishKey);
    writeC(0x00);
}

In my Java code, I can correctly decrypt the scrambled key using the following decryption method:

public void run()
{
    final LoginClient client = getClient();
    
    byte[] decrypted = null;
    try
    {
        final Cipher rsaCipher = Cipher.getInstance("RSA/ECB/nopadding");
        rsaCipher.init(Cipher.DECRYPT_MODE, client.getRSAPrivateKey());
        decrypted = rsaCipher.doFinal(_raw, 0x00, 0x80);
    }
    catch (GeneralSecurityException e)
    {
        LOGGER.error("Failed to generate a cipher.", e);
        client.close(LoginFail.REASON_ACCESS_FAILED);
        return;
    }
    
    try
    {
        final String user = new String(decrypted, 0x5E, 14).trim().toLowerCase();
        final String password = new String(decrypted, 0x6C, 16).trim();

        LoginController.getInstance().retrieveAccountInfo(client, user, password);
    }
    catch (Exception e)
    {
        LOGGER.error("Failed to decrypt user/password.", e);
        client.close(LoginFail.REASON_ACCESS_FAILED);
    }
}

In Java, the decryption works correctly with the following inputs:

Scrambled key:

[124, 168, 24, 242, 228, 213, 212, 54, 235, 100, 160, 114, 163, 61, 75, 218, 182, 245, 166, 34, 214, 132, 15, 71, 135, 194, 141, 227, 38, 1, 217, 145, 203, 135, 209, 137, 111, 33, 54, 34, 57, 0, 189, 243, 199, 219, 109, 56, 15, 225, 143, 59, 176, 108, 76, 204, 232, 133, 216, 126, 70, 30, 115, 74, 25, 0, 133, 38, 128, 16, 28, 183, 155, 24, 188, 86, 117, 191, 135, 36, 211, 165, 104, 217, 6, 182, 164, 136, 101, 215, 42, 113, 111, 55, 208, 207, 105, 35, 182, 47, 241, 107, 139, 215, 213, 249, 43, 125, 219, 34, 53, 103, 250, 92, 154, 128, 75, 189, 168, 128, 229, 6, 239, 177, 101, 126, 218, 75]

Java Private Key:

SunRsaSign RSA private CRT key, 1024 bits
  params: null
  modulus: 91851415039980683673835443907833548355613725060429787547716446884026400945844827342368025805543719063805785824109075005634172820422680533076648130137521254244221874431195148950936257983839718841574094615493929797376689026996944801887459231860502077721291214210394902757594184173778070286729141127782416296193
  private exponent: 6487635995240407475566233880851450254636860602479965310563184653343274943593934811905055029279061836006484620592961353145255138101173202734513392349460393317252650745961877082056763589015732603463063131507792018004450541681668317154023863374611531824451608552071840032997073721859882730955156029598795586149
Chave privada (formato Go):
&rsa.PrivateKey{
  PublicKey: rsa.PublicKey{
    N: big.NewInt(0).SetBytes(new byte[] {0, -126, -52, -2, 101, -128, 16, 28, -73, -101, 24, -68, 86, 117, 15, -21, 104, 31, -91, 104, -39, 6, -74, -92, -120, 101, -41, 42, 113, 111, 55, -48, -49, 105, 35, -74, 47, -15, 107, -117, -41, -43, -7, 43, 125, -37, 34, 53, 103, -6, 92, -102, -128, 75, -67, -88, -128, -27, 6, -17, -79, 101, 126, -38, 75, 101, -88, -99, -44, 100, -59, -56, -127, 112, 124, 28, 36, -42, 25, 0, -123, 38, 80, -50, -5, -48, 50, -85, -49, -30, 21, -89, -110, 73, 54, 9, 94, -94, -92, 103, -90, -98, 74, -67, -11, -20, -7, -106, -114, 28, -7, 88, 95, -11, -67, 21, -69, -5, -47, -28, 76, 13, -125, 55, -49, 35, 96, -87, 1, }),
    E: 65537,
  },
  D: big.NewInt(0).SetBytes(new byte[] {9, 61, 27, 118, -27, -35, -67, 121, -122, 125, -17, -57, -105, -117, 33, 80, -128, 115, -71, 125, 34, -55, 65, -61, -44, -107, -89, 12, -50, 19, 62, 47, 120, 56, -86, -124, -90, 99, -73, 67, -18, 124, 36, 3, 70, -72, 26, -41, -106, 74, 118, 43, 59, -82, 80, -71, -111, -13, -76, 120, 110, 55, -48, -119, 74, -39, 58, 44, -63, -103, -73, -80, 57, 1, 64, -60, -33, -53, -21, 53, -45, -7, -42, 113, 60, 12, 38, 24, -66, 9, -111, 18, -108, -38, 30, 114, -77, -83, -9, 34, 88, -44, -114, -79, -54, -17, 44, 2, -36, -117, -74, 122, -62, -103, 14, 115, -39, 18, 73, -69, -77, -62, -111, -127, -102, -12, 90, 101, }),
}

Byte array to decrypt:

[89, 250, 193, 103, 219, 4, 129, 208, 181, 30, 127, 250, 147, 113, 64, 25, 187, 96, 88, 15, 64, 93, 100, 141, 42, 89, 13, 157, 253, 187, 118, 154, 150, 132, 238, 224, 22, 98, 35, 252, 92, 253, 138, 6, 38, 142, 170, 247, 136, 19, 231, 48, 52, 228, 22, 222, 81, 242, 182, 109, 96, 209, 7, 187, 213, 237, 218, 147, 238, 28, 25, 217, 81, 200, 219, 76, 214, 232, 238, 193, 78, 252, 180, 45, 182, 150, 220, 157, 194, 11, 57, 195, 20, 174, 13, 123, 69, 72, 81, 112, 49, 143, 3, 67, 216, 86, 11, 234, 48, 210, 254, 136, 201, 238, 1, 222, 134, 178, 102, 230, 102, 137, 145, 236, 197, 2, 120, 106]

However, when I attempt to replicate the decryption process in Golang using the crypto/rsa package, I encounter issues. The Go implementation looks like this:

func NewScrambledKeyPairTest(bits int) (ScrambledKeyPair, error) {
    scrambledModulusJava := []byte{124, 168, 24, 242, 228, 213, 212, 54, 235, 100, 160, 114, 163, 61, 75, 218, 182, 245, 166, 34, 214, 132, 15, 71, 135, 194, 141, 227, 38, 1, 217, 145, 203, 135, 209, 137, 111, 33, 54, 34, 57, 0, 189, 243, 199, 219, 109, 56, 15, 225, 143, 59, 176, 108, 76, 204, 232, 133, 216, 126, 70, 30, 115, 74, 25, 0, 133, 38, 128, 16, 28, 183, 155, 24, 188, 86, 117, 191, 135, 36, 211, 165, 104, 217, 6, 182, 164, 136, 101, 215, 42, 113, 111, 55, 208, 207, 105, 35, 182, 47, 241, 107, 139, 215, 213, 249, 43, 125, 219, 34, 53, 103, 250, 92, 154, 128, 75, 189, 168, 128, 229, 6, 239, 177, 101, 126, 218, 75}
    privateKey := &rsa.PrivateKey{
        PublicKey: rsa.PublicKey{
            N: big.NewInt(0).SetBytes([]byte{
                0, 130, 204, 254, 101, 128, 16, 28, 185, 155, 24, 188, 86, 117, 15, 235, 104, 31, 165, 104, 217, 6, 182, 164, 136, 101, 215, 42, 113, 111, 55, 208, 207, 105, 35, 182, 47, 241, 107, 139, 215, 213, 249, 43, 125, 219, 34, 53, 103, 250, 92, 154, 128, 75, 189, 168, 128, 229, 6, 239, 177, 101, 126, 218, 75, 101, 168, 157, 212, 100, 197, 200, 129, 112, 124, 28, 36, 214, 25, 0, 133, 38, 80, 206, 251, 208, 50, 171, 207, 226, 21, 169, 146, 73, 54, 9, 94, 162, 164, 103, 166, 158, 74, 189, 245, 236, 249, 150, 142, 28, 249, 88, 95, 245, 189, 21, 187, 251, 209, 228, 76, 13, 131, 55, 207, 35, 96, 169, 1,
            }),
            E: 65537,
        },
        D: big.NewInt(0).SetBytes([]byte{
            9, 61, 27, 118, 229, 221, 189, 121, 134, 125, 239, 199, 151, 139, 33, 80, 128, 115, 185, 125, 34, 201, 65, 195, 212, 149, 167, 12, 206, 19, 62, 47, 120, 56, 170, 132, 166, 99, 185, 67, 238, 124, 36, 3, 70, 184, 26, 215, 150, 74, 118, 43, 59, 174, 80, 185, 145, 243, 180, 120, 110, 55, 208, 137, 74, 217, 58, 44, 193, 153, 183, 176, 57, 1, 64, 196, 223, 203, 235, 53, 211, 249, 214, 113, 60, 12, 38, 24, 190, 9, 145, 18, 148, 218, 30, 114, 179, 173, 247, 34, 88, 212, 142, 177, 202, 239, 44, 2, 220, 139, 182, 122, 194, 153, 14, 115, 217, 18, 73, 187, 179, 194, 145, 129, 154, 244, 90, 101,
        }),
    }

    return ScrambledKeyPair{
        Pair:             privateKey,
        ScrambledModulus: scrambledModulusJava,
    }, nil
}


func (p RequestLogin) Run(client *clients.Client, rand io.Reader) error {
    privateKey := client.PrivateKey()

    c := new(big.Int).SetBytes(p.raw)
    m := new(big.Int)
    m.Exp(c, privateKey.D, privateKey.N)
    em := m.Bytes()

    user := string(em[0x5E : 0x5E+14])
    pwd := string(em[0x6C : 0x6C+16])

    return nil
}

But I'm not having success decrypting these bytes:

[89, 250, 193, 103, 219, 4, 129, 208, 181, 30, 127, 250, 147, 113, 64, 25, 187, 96, 88, 15, 64, 93, 100, 141, 42, 89, 13, 157, 253, 187, 118, 154, 150, 132, 238, 224, 22, 98, 35, 252, 92, 253, 138, 6, 38, 142, 170, 247, 136, 19, 231, 48, 52, 228, 22, 222, 81, 242, 182, 109, 96, 209, 7, 187, 213, 237, 218, 147, 238, 28, 25, 217, 81, 200, 219, 76, 214, 232, 238, 193, 78, 252, 180, 45, 182, 150, 220, 157, 194, 11, 57, 195, 20, 174, 13, 123, 69, 72, 81, 112, 49, 143, 3, 67, 216, 86, 11, 234, 48, 210, 254, 136, 201, 238, 1, 222, 134, 178, 102, 230, 102, 137, 145, 236, 197, 2, 120, 106]

value of these bytes after being decrypted in Java is this:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 0, 0, 97, 100, 109, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97, 100, 109, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

But in Golang I can't because in Java I'm using: Cipher.getInstance("RSA/ECB/nopadding"); e In Golang, I haven't found any function to decrypt a byte slice without padding.


Solution

  • Your code is not directly executable and you do not describe exactly what the problem is (wrong result, exception...). When I run the decryption (modular exponentiation) with your data:

    ciphertextBytes := ([]byte{89, 250, 193, 103, 219, 4, 129, 208, 181, 30, 127, 250, 147, 113, 64, 25, 187, 96, 88, 15, 64, 93, 100, 141, 42, 89, 13, 157, 253, 187, 118, 154, 150, 132, 238, 224, 22, 98, 35, 252, 92, 253, 138, 6, 38, 142, 170, 247, 136, 19, 231, 48, 52, 228, 22, 222, 81, 242, 182, 109, 96, 209, 7, 187, 213, 237, 218, 147, 238, 28, 25, 217, 81, 200, 219, 76, 214, 232, 238, 193, 78, 252, 180, 45, 182, 150, 220, 157, 194, 11, 57, 195, 20, 174, 13, 123, 69, 72, 81, 112, 49, 143, 3, 67, 216, 86, 11, 234, 48, 210, 254, 136, 201, 238, 1, 222, 134, 178, 102, 230, 102, 137, 145, 236, 197, 2, 120, 106})[0:128]
    modulusInt := "91851415039980683673835443907833548355613725060429787547716446884026400945844827342368025805543719063805785824109075005634172820422680533076648130137521254244221874431195148950936257983839718841574094615493929797376689026996944801887459231860502077721291214210394902757594184173778070286729141127782416296193"
    privateExpInt := "6487635995240407475566233880851450254636860602479965310563184653343274943593934811905055029279061836006484620592961353145255138101173202734513392349460393317252650745961877082056763589015732603463063131507792018004450541681668317154023863374611531824451608552071840032997073721859882730955156029598795586149"
    
    c := new(big.Int)
    c.SetBytes(ciphertextBytes)
    n := new(big.Int)
    n.SetString(modulusInt, 10)
    d := new(big.Int)
    d.SetString(privateExpInt, 10)
    
    decrypted := new(big.Int).Exp(c, d, n).Bytes()
    fmt.Println(hex.EncodeToString(decrypted)) // 24000061646d696e00000000000000000061646d696e000000000000000000000000000000
    

    I get a correct result (hex encoded):

    24000061646d696e00000000000000000061646d696e000000000000000000000000000000
    

    The only difference to the result of the Java code is that the Java code automatically pads the result from the front with 0x00 values up to the key size/modulus, whereas this does of course not happen with direct modular exponentiation.

    To fix this, you can simply pad yourself so that the indices are correct, e.g.:

    decryptedPadded := append(make([]byte, 128-len(decrypted)), decrypted...)
    fmt.Println(hex.EncodeToString(decryptedPadded[0x5E : 0x5E+14])) // 61646d696e000000000000000000
    fmt.Println(string(decryptedPadded[0x5E : 0x5E+14])) // admin
    

    Security: As already mentioned in the comment, RSA encryption without padding is insecure. In addition, a key size of at least 2048 bits (256 bytes) should be used nowadays.


    Edit: The code linked in your comment uses the wrong byte arrays for modulus and private exponent. The correct values can be output as follows, e.g. for the modulus:

    modulusInt := "91851415039980683673835443907833548355613725060429787547716446884026400945844827342368025805543719063805785824109075005634172820422680533076648130137521254244221874431195148950936257983839718841574094615493929797376689026996944801887459231860502077721291214210394902757594184173778070286729141127782416296193"
    n := new(big.Int)
    n.SetString(modulusInt, 10)
    fmt.Println(n.Bytes()) // [130 204 254 101 128 16 28 183 155 24 188 86 117 15 235 104 31 165 104 217 6 182 164 136 101 215 42 113 111 55 208 207 105 35 182 47 241 107 139 215 213 249 43 125 219 34 53 103 250 92 154 128 75 189 168 128 229 6 239 177 101 126 218 75 101 168 157 212 100 197 200 129 112 124 28 36 214 25 0 133 38 80 206 251 208 50 171 207 226 21 167 146 73 54 9 94 162 164 103 166 158 74 189 245 236 249 150 142 28 249 88 95 245 189 21 187 251 209 228 76 13 131 55 207 35 96 169 1]
    

    Likewise for the private exponent. When comparing with your values, you will find deviations in a few bytes.

    This results in the following for the private key:

    privateKey := &rsa.PrivateKey{
        PublicKey: rsa.PublicKey{
            N: big.NewInt(0).SetBytes([]byte{130, 204, 254, 101, 128, 16, 28, 183, 155, 24, 188, 86, 117, 15, 235, 104, 31, 165, 104, 217, 6, 182, 164, 136, 101, 215, 42, 113, 111, 55, 208, 207, 105, 35, 182, 47, 241, 107, 139, 215, 213, 249, 43, 125, 219, 34, 53, 103, 250, 92, 154, 128, 75, 189, 168, 128, 229, 6, 239, 177, 101, 126, 218, 75, 101, 168, 157, 212, 100, 197, 200, 129, 112, 124, 28, 36, 214, 25, 0, 133, 38, 80, 206, 251, 208, 50, 171, 207, 226, 21, 167, 146, 73, 54, 9, 94, 162, 164, 103, 166, 158, 74, 189, 245, 236, 249, 150, 142, 28, 249, 88, 95, 245, 189, 21, 187, 251, 209, 228, 76, 13, 131, 55, 207, 35, 96, 169, 1}),
            E: 65537,
        },
        D: big.NewInt(0).SetBytes([]byte{9, 61, 27, 118, 229, 221, 189, 121, 134, 125, 239, 199, 151, 139, 33, 80, 128, 115, 185, 125, 34, 201, 65, 195, 212, 149, 167, 12, 206, 19, 62, 47, 120, 56, 170, 132, 166, 99, 183, 67, 238, 124, 36, 3, 70, 184, 26, 215, 150, 74, 118, 43, 59, 174, 80, 185, 145, 243, 180, 120, 110, 55, 208, 137, 74, 217, 58, 44, 193, 153, 183, 176, 57, 1, 64, 196, 223, 203, 235, 53, 211, 249, 214, 113, 60, 12, 38, 24, 190, 9, 145, 18, 148, 218, 30, 114, 179, 173, 247, 34, 88, 212, 142, 177, 202, 239, 44, 2, 220, 139, 182, 122, 194, 153, 14, 115, 217, 18, 73, 187, 179, 194, 145, 129, 154, 244, 90, 101}),
    }
    

    With this, decryption works.