cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
175
Views
1
Helpful
2
Replies

Cisco ACI backup file MD5 calculation is missing 0's ?

vv0bbLeS
Level 1
Level 1

Hello all,

Long story but I had a lab upgrade from 5.2 to 6.0.5(M) fail on older APIC-M2's, and Cisco TAC suggested a fabric rebuild. Since I have newer APIC-M4's, I decided to install 5.3.2b on my APIC-M4's, and rebuild my lab fabric using those. All was going well until I needed to import a tar.gz backup file from my 5.2 version APIC-M2's. The import had a blank value in a certain field (monitoringUser=""), and the 5.3 APIC-M4's didn't like that blank value. So I thought no problem, I'll extract the tar.gz file down to the actual .xml file, edit the field to be something like monitoringUser="test" (to get rid of the empty string). Then, in Cisco ACI 5.3, there is also a .xml.md5 file that contains the MD5 hash of the .xml file, so I thought no big deal, I'll just calculate a new MD5 hash for my updated .xml file, put that new hash into the .xml.md5 file, and zip everything back up into a tar.gz ball and upload to the APIC for import. Only to find out that the APIC didn't like the new MD5 hash.

I thought hmm OK maybe I calculated that MD5 hash incorrectly. So to be sure, I took the unmodified .xml file, calculated the MD5 hash on my Windows machine using certutil , and I discovered that certutil has extra 0's in the MD5 hash (but not just certutil, python's hashlib also adds extra 0's). I repeated this process for several .xml backup files to be sure I wasn't crazy, and here are the results (there are spaces in the Cisco hashes where the missing 0's are):

Cisco ACI: 25e7a2d057c47d d292fad20e9261e9d
Windows:   25e7a2d057c47d0d292fad20e9261e9d


Cisco ACI: abd1df6f37a8 6e71c8d2a3eec 2bceb
Windows:   abd1df6f37a806e71c8d2a3eec02bceb


Cisco ACI: 16aac3e3a9f6d52f49aeb89cc5 1be5f
Windows:   16aac3e3a9f6d52f49aeb89cc501be5f


Cisco ACI: 81d7642011af374e 2bcf9d1fe75 dd6
Windows:   81d7642011af374e02bcf9d1fe750dd6


Cisco ACI:   9be37da7ac4cd95ddc6a7b1bbf1cf1
Windows:   009be37da7ac4cd95ddc6a7b1bbf1cf1

 

 

As you can see, Cisco ACI's MD5 hash calculation is missing some 0's (but not all 0's as in the first example).

So my question is, how can I replicate Cisco ACI's md5 hash calculation? Does it use a salt of some kind? I've even tried logging onto the APIC and running the APIC's own mc5sum command against the raw .xml file and it produces the same hash as my Windows machine, so even that doesn't help me. I would like to be able to make a small change in the .xml backup file, calculate the new MD5 hash that Cisco ACI wants, and zip everything up and import it back into ACI. Can I do this?

0xD2A6762E
1 Accepted Solution

Accepted Solutions

@robert2003b  thanks for your reply, I think I may have tracked this one down. The answer is that the MD5 hash that Cisco ACI calculates for its backup files is not prepended with 0's to reach a certain length.

How I reached that conclusion: Since Windows certutil and Python's hashlib weren't calculating the MD5 hash the same as Cisco was, I discovered that Java has a broken method that can also generate MD5 hashes with missing 0's because Java's Integer.toHexString method "will not prepend 0's to the length you implicitly desire."

Zeros missing from md5 hash in java - Stack Overflow

In other words, programs like certutil, Python's hashlib library, and even the md5sum program on a Cisco ACI APIC, they all will prepend the MD5 with 0's so the MD5 hash is a certain length (from my initial post above, 32 characters long). It appears that Cisco ACI doesn't prepend the MD5 hash when calculating it for ACI backups. 


To replicate this, I found a Java script that, with a little modification, creates the same MD5 hashes that Cisco ACI does for its backup files:

How to Generate MD5 Checksum for Files in Java? - GeeksforGeeks

The code sample on that page will generate MD5 hashes the same as Windows and all the others - 32 characters long. However, with a little modification using that Integer.toHexString method we saw above, we can change the java code so it won't prepend the MD5 hash with 0's, so the MD5 hash will be the same as for Cisc ACI backups. So, if we take the code from the link above and we comment out lines 76-79 and replace them with this single line:

 

 

sb.append(Integer.toHexString((bytes[i]& 0xFF)));

 

 

we can achieve what we want - an MD5 hash that isn't prepended with 0's, so it will be the same as Cisco's ACI hash for its backup files. The full java code is pasted below for reference:

// Java program to Generate MD5 Checksum for Files
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
 
public class GFG {
 
    // this method gives a NoSuchAlgorithmException in case
    // we pass a string which doesn't have any hashing
    // algorithm in its correspondence
   
    public static void main(String[] args)
        throws IOException, NoSuchAlgorithmException
    {
 
        // create a file object referencing any file from
        // the system of which checksum is to be generated
        File file = new File("C:\\ftp\\ACI-BACKUP-FILE.xml");
 
        // instantiate a MessageDigest Object by passing
        // string "MD5" this means that this object will use
        // MD5 hashing algorithm to generate the checksum
        MessageDigest mdigest = MessageDigest.getInstance("MD5");
 
        // Get the checksum
        String checksum = checksum(mdigest, file);
 
        // print out the checksum
        System.out.println(checksum);
    }
 
    // this method return the complete  hash of the file
    // passed
    private static String checksum(MessageDigest digest,
                                   File file)
        throws IOException
    {
        // Get file input stream for reading the file
        // content
        FileInputStream fis = new FileInputStream(file);
 
        // Create byte array to read data in chunks
        byte[] byteArray = new byte[1024];
        int bytesCount = 0;
 
        // read the data from file and update that data in
        // the message digest
        while ((bytesCount = fis.read(byteArray)) != -1)
        {
            digest.update(byteArray, 0, bytesCount);
        };
 
        // close the input stream
        fis.close();
 
        // store the bytes returned by the digest() method
        byte[] bytes = digest.digest();
 
        // this array of bytes has bytes in decimal format
        // so we need to convert it into hexadecimal format
 
        // for this we create an object of StringBuilder
        // since it allows us to update the string i.e. its
        // mutable
        StringBuilder sb = new StringBuilder();
       
        // loop through the bytes array
        for (int i = 0; i < bytes.length; i++) {
           
            // the following line converts the decimal into
            // hexadecimal format and appends that to the
            // StringBuilder object
            //sb.append(Integer
                    //.toString((bytes[i] & 0xff) + 0x100, 16)
                    //.substring(1));
			//sb.append(Integer.toHexString((bytes[i]& 0xFF) | 0x100).substring(1));
			sb.append(Integer.toHexString((bytes[i]& 0xFF)));
        }
 
        // finally we return the complete hash
        return sb.toString();
    }
}

 

 

 

0xD2A6762E

View solution in original post

2 Replies 2

robert2003b
Level 1
Level 1

It seems you’ve encountered a unique challenge with the MD5 hash calculation in Cisco ACI. The discrepancies you’re noticing with the missing zeros could be due to a difference in how Cisco ACI formats or processes the MD5 hash output compared to standard tools like certutil or Python’s hashlib. Ensure that the formatting of the hash, especially concerning leading zeros, matches that of Cisco ACI. It’s possible that Cisco ACI may be using a different string representation of the hash. While it’s not typical for MD5 hash calculations to use a salt (as they are not used for cryptographic security in this context), it’s worth checking if Cisco ACI incorporates any additional data into the hash calculation. 

@robert2003b  thanks for your reply, I think I may have tracked this one down. The answer is that the MD5 hash that Cisco ACI calculates for its backup files is not prepended with 0's to reach a certain length.

How I reached that conclusion: Since Windows certutil and Python's hashlib weren't calculating the MD5 hash the same as Cisco was, I discovered that Java has a broken method that can also generate MD5 hashes with missing 0's because Java's Integer.toHexString method "will not prepend 0's to the length you implicitly desire."

Zeros missing from md5 hash in java - Stack Overflow

In other words, programs like certutil, Python's hashlib library, and even the md5sum program on a Cisco ACI APIC, they all will prepend the MD5 with 0's so the MD5 hash is a certain length (from my initial post above, 32 characters long). It appears that Cisco ACI doesn't prepend the MD5 hash when calculating it for ACI backups. 


To replicate this, I found a Java script that, with a little modification, creates the same MD5 hashes that Cisco ACI does for its backup files:

How to Generate MD5 Checksum for Files in Java? - GeeksforGeeks

The code sample on that page will generate MD5 hashes the same as Windows and all the others - 32 characters long. However, with a little modification using that Integer.toHexString method we saw above, we can change the java code so it won't prepend the MD5 hash with 0's, so the MD5 hash will be the same as for Cisc ACI backups. So, if we take the code from the link above and we comment out lines 76-79 and replace them with this single line:

 

 

sb.append(Integer.toHexString((bytes[i]& 0xFF)));

 

 

we can achieve what we want - an MD5 hash that isn't prepended with 0's, so it will be the same as Cisco's ACI hash for its backup files. The full java code is pasted below for reference:

// Java program to Generate MD5 Checksum for Files
 
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
 
public class GFG {
 
    // this method gives a NoSuchAlgorithmException in case
    // we pass a string which doesn't have any hashing
    // algorithm in its correspondence
   
    public static void main(String[] args)
        throws IOException, NoSuchAlgorithmException
    {
 
        // create a file object referencing any file from
        // the system of which checksum is to be generated
        File file = new File("C:\\ftp\\ACI-BACKUP-FILE.xml");
 
        // instantiate a MessageDigest Object by passing
        // string "MD5" this means that this object will use
        // MD5 hashing algorithm to generate the checksum
        MessageDigest mdigest = MessageDigest.getInstance("MD5");
 
        // Get the checksum
        String checksum = checksum(mdigest, file);
 
        // print out the checksum
        System.out.println(checksum);
    }
 
    // this method return the complete  hash of the file
    // passed
    private static String checksum(MessageDigest digest,
                                   File file)
        throws IOException
    {
        // Get file input stream for reading the file
        // content
        FileInputStream fis = new FileInputStream(file);
 
        // Create byte array to read data in chunks
        byte[] byteArray = new byte[1024];
        int bytesCount = 0;
 
        // read the data from file and update that data in
        // the message digest
        while ((bytesCount = fis.read(byteArray)) != -1)
        {
            digest.update(byteArray, 0, bytesCount);
        };
 
        // close the input stream
        fis.close();
 
        // store the bytes returned by the digest() method
        byte[] bytes = digest.digest();
 
        // this array of bytes has bytes in decimal format
        // so we need to convert it into hexadecimal format
 
        // for this we create an object of StringBuilder
        // since it allows us to update the string i.e. its
        // mutable
        StringBuilder sb = new StringBuilder();
       
        // loop through the bytes array
        for (int i = 0; i < bytes.length; i++) {
           
            // the following line converts the decimal into
            // hexadecimal format and appends that to the
            // StringBuilder object
            //sb.append(Integer
                    //.toString((bytes[i] & 0xff) + 0x100, 16)
                    //.substring(1));
			//sb.append(Integer.toHexString((bytes[i]& 0xFF) | 0x100).substring(1));
			sb.append(Integer.toHexString((bytes[i]& 0xFF)));
        }
 
        // finally we return the complete hash
        return sb.toString();
    }
}

 

 

 

0xD2A6762E

Save 25% on Day-2 Operations Add-On License