MD5是使用哈希算法计算文件或字符串的摘要,将不定长的输入数据转化成128bit的数据,一般在使用的时候需要将它转换成十六进制输出,并且同时输出为小写,是不可还原出原始数据的(目前网站解密方式是采用撞库的方式,不是真正的解密)。
项目有个校验图片一致性的需要,用户上传图片到后台,后台根据一定的规则将图片数据转化成md5值返回给用户,当用户需要使用该图片时需要传入md5做一致性校验,防止用户修改图片。
字符串MD5的iOS代码如下:
-(NSString *)md5:(NSString *)str {
const char *cStr = [str UTF8String];//转换成utf-8
unsigned char result[16];//开辟一个16字节(128位:md5加密出来就是128位/bit)的空间(一个字节=8字位=8个二进制数)
CC_MD5( cStr, strlen(cStr), result);
/*
extern unsigned char *CC_MD5(const void *data, CC_LONG len, unsigned char *md)官方封装好的加密方法
把cStr字符串转换成了32位的16进制数列(这个过程不可逆转) 存储到了result这个空间中
*/
return [NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
/*
x表示十六进制,%02X 意思是不足两位将用0补齐,如果多余两位则不影响
NSLog("%02X", 0x888); //888
NSLog("%02X", 0x4); //04
*/
}
也可以采用循环输出代码更简洁,采用官方提供的md5方法,输出固定长度128位的字符。
Androidd的md5校验代码如下:
public static String stringToMD5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10)
hex.append("0");
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString().toLowerCase();
}
输入同样的字符串可以得到相同的md5结果,但是将图片转成相应的字符串进行md5算法不一致了,原因是图片原始数据转化成字符串必须经过解码处理(转化成png或者jpg),Android和iOS不同的处理机制导致数据不一致,必须采用文件流的方式进行md5转化。
iOS文件流md5转化的相关代码
#define FileHashDefaultChunkSizeForReadingData 1024*8
+(NSString*)getFileMD5WithPath:(NSString*)path
{
return (__bridge_transfer NSString *)FileMD5HashCreateWithPath((__bridge CFStringRef)path, FileHashDefaultChunkSizeForReadingData);
}
CFStringRef FileMD5HashCreateWithPath(CFStringRef filePath,size_t chunkSizeForReadingData) {
// Declare needed variables
CFStringRef result = NULL;
CFReadStreamRef readStream = NULL;
// Get the file URL
CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
(CFStringRef)filePath,
kCFURLPOSIXPathStyle,
(Boolean)false);
// if (!fileURL) goto done;
// Create and open the read stream
readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault,
(CFURLRef)fileURL);
// if (!readStream) goto done;
bool didSucceed = (bool)CFReadStreamOpen(readStream);
// if (!didSucceed) goto done;
// Initialize the hash object
CC_MD5_CTX hashObject;
CC_MD5_Init(&hashObject);
// Make sure chunkSizeForReadingData is valid
if (!chunkSizeForReadingData) {
chunkSizeForReadingData = FileHashDefaultChunkSizeForReadingData;
}
// Feed the data to the hash object
bool hasMoreData = true;
while (hasMoreData) {
uint8_t buffer[chunkSizeForReadingData];
CFIndex readBytesCount = CFReadStreamRead(readStream,(UInt8 *)buffer,(CFIndex)sizeof(buffer));
if (readBytesCount == -1) break;
if (readBytesCount == 0) {
hasMoreData = false;
continue;
}
CC_MD5_Update(&hashObject,(const void *)buffer,(CC_LONG)readBytesCount);
}
// Check if the read operation succeeded
didSucceed = !hasMoreData;
// Compute the hash digest
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5_Final(digest, &hashObject);
// Abort if the read operation failed
if (!didSucceed) goto done;
// Compute the string result
char hash[2 * sizeof(digest) + 1];
for (size_t i = 0; i < sizeof(digest); ++i) {
snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i]));
}
result = CFStringCreateWithCString(kCFAllocatorDefault,(const char *)hash,kCFStringEncodingUTF8);
done:
if (readStream) {
CFReadStreamClose(readStream);
CFRelease(readStream);
}
if (fileURL) {
CFRelease(fileURL);
}
return result;
}
Android文件流md5的相关代码如下:
public static String getMD5(String imagePath) throws NoSuchAlgorithmException, IOException {
InputStream in = new FileInputStream(new File(imagePath));
StringBuffer md5 = new StringBuffer();
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] dataBytes = new byte[1024];
int nread = 0;
while ((nread = in.read(dataBytes)) != -1) {
md.update(dataBytes, 0, nread);
}
byte[] mdbytes = md.digest();
// convert the byte to hex format
for (int i = 0; i < mdbytes.length; i++) {
md5.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
}
return md5.toString().toLowerCase();
}
传入相关图片的路径,图片数据md5出来的数据一致,实现了md5校验的功能,使用md5校验时候要注意是字符串md5转化还是文件md5转化。