JavaScript检测是否支持WebP图片(出自Google官方文档)

function check_webp_feature(feature, callback) { var kTestImages = { lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA", lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==", alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==", animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA" }; var img = new Image(); img.onload = function () { var result = (img.width > 0) && (img.height > 0); callback(feature, result); }; img.onerror = function () { callback(feature, false); }; img.src = "data:image/webp;base64," + kTestImages[feature]; }

December 26, 2019 · 1 min · holdsky

快速排序C++实现

自己都忘了什么时候写的了,@_@ #pragma once #include <list> #include <vector> #include <stack> using namespace std; struct Bound { size_t low; size_t high; Bound(size_t lowind,size_t highind) { this->low=lowind; this->high=highind; } }; template <typename T> void QSort(vector<T> & vay) //从小到大排序 { size_t len=vay.size(); if( len > 2) { stack<Bound> boundS; Bound bd(0,len-1); boundS.push(bd); size_t low,high; while( ! boundS.empty() ) //当栈不为空时 { bd=boundS.top();//取栈顶元素 low=bd.low; high=bd.high; boundS.pop();//弹出栈顶元素 low++;//用数组的排序部分的首个元素作为关键值,进行比较 while( true ) //进行二分数组排序 { while( low < high && vay[low] <= vay[bd.low] ) //用数组的排序部分的首个元素作为关键值,进行比较 { low++; } while( low < high && vay[high] > vay[bd.low] ) //vay[high] > vay[bd.low] 不能用 >=,因为要二分数组的排序部分,保证两部分的排他性 { high--; } if( low < high ) {//交换低索引和高索引元素 T tmp=vay[low]; vay[low]=vay[high]; vay[high]=tmp; } else {//此时,low == high 为真 break; } } //此时,low == high 为真 if( vay[low] > vay[bd.low] ) { low--; } //将关键值插入大元素和小元素的分界 T tmp=vay[low]; vay[low]=vay[bd.low]; vay[bd.low]=tmp; if( low - bd.low > 1 ) //若小元素部分元素个数大于1个,继续排序 { Bound newbd(bd.low,low-1); boundS.push(newbd); } if( bd.high - low > 1)//若大元素部分元素个数大于1个,继续排序 { Bound newbd(low+1,bd.high); boundS.push(newbd); } } } else if( len == 2) //若数组长度为2,直接比较 { if( vay[0] > vay[1] ) { T tmp=vay[0]; vay[0]=vay[1]; vay[1]=tmp; } } //若数组长度小于2,不用排序 } template <typename T> struct BoundIter { BoundIter(typename list<T>::iterator lowiter,typename list<T>::iterator highiter) { this->low=lowiter; this->high=highiter; } typename list<T>::iterator low; typename list<T>::iterator high; }; template <typename T> void QSort(list<T> & lst) //从小到大排序 { if( lst.size() > 2) { stack<BoundIter<T>> boundIterS; BoundIter<T> bd(lst.begin(),(--lst.end())); boundIterS.push(bd); list<T>::iterator low,high; while( ! boundIterS.empty() ) //当栈不为空时 { bd=boundIterS.top();//取栈顶元素 low=bd.low; high=bd.high; boundIterS.pop();//弹出栈顶元素 low++;//用数组的排序部分的首个元素作为关键值,进行比较 while( true ) //进行二分数组排序 { while( low != high && *low <= *(bd.low) ) //用数组的排序部分的首个元素作为关键值,进行比较 { low++; } while( low != high && *high > *(bd.low) ) //*high > *(bd.low) 不能用 >=,因为要二分数组的排序部分,保证两部分的排他性 { high--; } if( low != high ) {//交换低索引和高索引元素 T tmp=*low; *low=*high; *high=tmp; } else {//此时,low == high 为真 break; } } //此时,low == high 为真 if( *low > *bd.low ) { low--; } //将关键值插入大元素和小元素的分界 T tmp=*low; *low=*bd.low; *bd.low=tmp; if( low != bd.low ) {//若小元素部分元素个数大于1个,继续排序 low--; if( low != bd.low ) { BoundIter<T> newbd(bd.low,low); boundIterS.push(newbd); } low++; } if( low != bd.high) {//若大元素部分元素个数大于1个,继续排序 low++; if( low != bd.high ) { BoundIter<T> newbd(low,bd.high); boundIterS.push(newbd); } } } } else if( lst.size() == 2) //若数组长度为2,直接比较 { list<T>::iterator iter0=lst.begin(); list<T>::iterator iter1=(++lst.begin()); if( *iter0 > *iter1 ) { T tmp=*iter0; *iter0=*iter1; *iter1=tmp; } } //若数组长度小于2,不用排序 }

December 25, 2019 · 2 min · holdsky

学习笔记:WindowManager显示Android全局悬浮窗口

我用Android手机装了个电商软件,抢购用。自己手机的状态栏不能显示秒级别的时间,只能精确到分钟。为了能准确的把握抢购时间,自己边学习边开发了一个时间显示悬浮窗。 WindowManager 参考文章 WindowManager可以在其他应用最上层,甚至手机桌面最上层显示窗口。 使用Context.getSystemService(Context.WINDOW_SERVICE)来获取WindowManager。 API 17推出了Presentation,它将自动获取display的Context和WindowManager,可以方便地在另一个display上显示窗口 使用WindowManager继承自基类的addView方法和removeView方法来显示和隐藏窗口 WindowManager实现悬浮窗需要声明权限,在manifest中添加如下权限: 在MIUI上需要在设置中打开本应用的”显示悬浮窗”开关,并且重启应用,否则悬浮窗只能显示在本应用界面内,不能显示在手机桌面上。 具体过程 创建工程 用Android Studio创建一个Android Basic Activity工程 添加权限 修改 AndroidManifest.xml,添加下面结构的数据 <?xml version="1.0" encoding="utf-8"?> <manifest > <!-- 显示顶层浮窗权限 --> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"> </uses-permission> </manifest> 修改代码 给MainActivity添加两个属性 public class MainActivity extends AppCompatActivity { TextView textView; //用于显示时间 Timer timer;//用于定时刷新时间 ... } Android Basic Activity工程创建后,自带一个按钮,给按钮添加点击事件的处理 final View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) { final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); // 类型 TYPE_SYSTEM_OVERLAY ,当悬浮窗口在其他App窗口上面时,不会隔断触摸事件,其他App能正常使用。 //若要隔断触摸事件,可以使用TYPE_SYSTEM_ALERT params.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; // 设置flag int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件 // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口 // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按 params.flags = flags; // 不设置这个弹出框的透明遮罩显示为黑色 params.format = PixelFormat.TRANSLUCENT; params.width = 300; params.height = 50; params.gravity = Gravity.TOP; TextView top=new TextView(view.getContext()); //控件字体位置位于左边 top.setGravity(Gravity.LEFT); top.setText("悬浮窗口"); WindowManager windowManager = (WindowManager)view.getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE); windowManager.addView(top,params); textView = top; startTimer();//启动定时器(实现在后面) } }; FloatingActionButton fab = findViewById(R.id.fab); //添加点击事件处理 fab.setOnClickListener(listener); 定时器刷新时间 ...

December 18, 2019 · 3 min · holdsky

编程之路总结:加密与解密--RSA要点

RSA加密算法是一种非对称加密算法. 对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用RSA加密的信息的可靠性就肯定会极度下降。 密钥 RSA为非对称加密算法,密钥分为私钥和公钥。 其中,公钥可以由私钥导出(意思就是,拥有私钥,也就拥有了公钥),而公钥不能导出私钥。 密钥存储格式 比较常用的格式有ASN1.DER 和 PEM ASN1.DER 是公钥或者私钥的采用ASN1.DER方式序列化,其结果是一串二进制数据 PEM 为Base64格式的文本,主要内容为ASN1.DER数据的Base64表达。另外,PEM允许使用指定的对称加密算法将加密,如DES等。 PEM格式的样例,中间的Base64文本其实就是ASN1.DER的Base64表示 -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA04KNnZEBO3Ajk+nI4x/VKdoL F+yNv/KI0wCNF1J9do8Ra+MXUdDr+Nbehf6JYLuwHfur2mhw0gTT9lTLM2mD0qHo rbMs/XtSbXc9eP3xwJ/hU0V76LqtKw+y7GSgJ9XiyWS848DO2fV7uHdpwgRWCfHu R8fcUO0SrOHLj5g1HwIDAQAB -----END PUBLIC KEY----- 密钥长度 密钥长度越长,安全性越好,加密解密所需时间越长。 RSA长度有512、1024 、 2048 、 4096,一般使用时的最低长度为1024。 参考 文章,密钥长度增长一倍,公钥操作所需时间增加约 4 倍,私钥操作所需时间增加约 8 倍,公私钥生成时间约增长 16 倍。 加密与解密 RSA的加密密钥可以是公钥、也可以是私钥;解密亦然。 公钥加密,私钥解密 私钥加密,公钥解密 块加密与补全模式 RSA也是一种分块加密算法,单次加密长度的上限时密钥长度,而当RSA使用PKCS1 补全模式时, padding 占用了 11 个字节,因此单次计算块长度约束 待加密的字节数不能超过密钥的比特长度值除以 8 再减去 11 例如,密钥长度是1024比特,那么单次块最大长度 (1024 / 8 -11) = 117 字节 签名与验签 对于非对称加密算法来说,除了常规的加解密外,还可以用来签名与验签。 签名用私钥 验签用公钥 另外,就RSA来讲,签名的摘要算法可以使用 MD5、 SHA1、 SHA2系列(如SHA256)

December 17, 2019 · 1 min · holdsky

Base64:一个仅基于C++的实现

#pragma once //从ATL的base64库整理而来 //整理的目的是提供一个仅基于标准c++的base64库,提高代码的可移植性 #include <string> namespace Crypt { class Base64 { public: enum Base64_Flag { ATL_BASE64_FLAG_NONE = 0, // 0000 0000 标准的编码方式(RFC2045) ATL_BASE64_FLAG_NOPAD = 1 , // 0000 0001 没有补全( = ) ATL_BASE64_FLAG_NOCRLF = 2 // 0000 0010 没有换行 ( crlf ) } ; //计算编码后的长度 static int EncodeGetRequiredLength(int nSrcLen, Base64_Flag dwFlags = ATL_BASE64_FLAG_NONE) { int nRet = static_cast<int>( (nSrcLen<<2) /3); if ((dwFlags & ATL_BASE64_FLAG_NOPAD) == 0)// 需要补全 = nRet += nSrcLen % 3; int nOnLastLine = nRet % 76; if (nOnLastLine) { if ( (nOnLastLine & 0x03) ) //nOnLastLine % 4 nRet += 4-(nOnLastLine & 0x03 ); } if ((dwFlags & ATL_BASE64_FLAG_NOCRLF) == 0) //需要换行符 { nRet += ((nRet / 76 + 1) << 1) ;//(nRet / 76 + 1) * 2, 回车换行个数(+1是为了取最多值),根据rfc2045,编码后一行最多76个字符(不含换行) } return nRet; } inline static int DecodeGetRequiredLength(int nSrcLen) { return ((nSrcLen*3)>>2)+2; //(nSrcLen*3)/4+2 } //pbSrcData 要编码的原始数据 //nSrcLen 原始数据长度 //szDest 编码后的输出 //pnEncodeRealLen 编码后的实际长度 static bool Encode(const unsigned char *pbSrcData,int nSrcLen,char* szDest,int *pnEncodeRealLen,Base64_Flag dwFlags = ATL_BASE64_FLAG_NONE) { static const char *s_chBase64EncodingTable="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if (!pbSrcData || !szDest || !pnEncodeRealLen) { return false; } int nWritten = 0 ; //编码后的长度,EncodeGetRequiredLength获得的长度通常大于实际长度 int nLen1 = (nSrcLen/3) << 2; // (nSrcLen/3)*4 int nLen2 = nLen1/76 ; //RFC2045 base64编码后,每行最多76个字符 int nLen3 = 19 ; for (int i=0; i<=nLen2; i++) { if (i==nLen2) nLen3 = (nLen1%76)/4; for (int j=0; j<nLen3; j++) { unsigned long dwCurr = 0; for (int n=0; n<3; n++) { dwCurr |= *pbSrcData++; dwCurr <<= 8; } for (int k=0; k<4; k++) { unsigned char b = (unsigned char)(dwCurr>>26); *szDest++ = s_chBase64EncodingTable[b]; dwCurr <<= 6; } } nWritten+= nLen3*4; if ((dwFlags & ATL_BASE64_FLAG_NOCRLF)==0) { *szDest++ = '\r'; *szDest++ = '\n'; nWritten+= 2; } } if (nWritten && (dwFlags & ATL_BASE64_FLAG_NOCRLF)==0) { szDest-= 2; nWritten -= 2; } nLen2 = (nSrcLen%3) ? (nSrcLen%3 + 1) : 0; if (nLen2) { unsigned long dwCurr = 0; for (int n=0; n<3; n++) { if (n<(nSrcLen%3)) dwCurr |= *pbSrcData++; dwCurr <<= 8; } for (int k=0; k<nLen2; k++) { unsigned char b = (unsigned char)(dwCurr>>26); *szDest++ = s_chBase64EncodingTable[b]; dwCurr <<= 6; } nWritten+= nLen2; if ((dwFlags & ATL_BASE64_FLAG_NOPAD)==0) { nLen3 = nLen2 ? 4-nLen2 : 0; for (int j=0; j<nLen3; j++) { *szDest++ = '='; } nWritten+= nLen3; } } *pnEncodeRealLen = nWritten; return true; } //szSrc 要解码的数据 //nSrcLen 要解码数据的长度 //pbDest 解码后的输出 //pnDecodeRealLen 解码后的字节长度 static bool Decode(const char* szSrc, int nSrcLen, unsigned char *pbDest, int *pnDecodeRealLen) { // walk the source buffer // each four character sequence is converted to 3 bytes // CRLFs and =, and any characters not in the encoding table // are skiped // returns -1 if the character is invalid // or should be skipped // otherwise, returns the 6-bit code for the character // from the encoding table static const int base64_decode_table[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; if (szSrc == 0 || pnDecodeRealLen == 0) { return false; } const char* szSrcEnd = szSrc + nSrcLen; int nWritten = 0; while (szSrc < szSrcEnd &&(*szSrc) != 0) { unsigned long dwCurr = 0; int i; int nBits = 0; for (i=0; i<4; i++) { if (szSrc >= szSrcEnd) break; int nCh = base64_decode_table[(unsigned char)(*szSrc)]; //强转为unsigned char,防止运行时抛出异常 szSrc++; if (nCh == -1) { // skip this char i--; continue; } dwCurr <<= 6; dwCurr |= nCh; nBits += 6; } // dwCurr has the 3 bytes to write to the output buffer // left to right dwCurr <<= 24-nBits; for (i=0; i<nBits/8; i++) { *pbDest = (unsigned char) ((dwCurr & 0x00ff0000) >> 16); pbDest++; dwCurr <<= 8; nWritten++; } } *pnDecodeRealLen = nWritten; return true; } static bool Encode(const std::string & in , std::string & out,Base64_Flag dwFlags = ATL_BASE64_FLAG_NONE) { int len = EncodeGetRequiredLength(static_cast<int>(in.size()),dwFlags); out.assign(len,0); bool ret=Encode((const unsigned char*)in.c_str(),static_cast<int>(in.size()),(char*)out.c_str(),&len,dwFlags); out.resize(static_cast<size_t>(len)); return ret; } static bool Encode( const unsigned char * in ,int in_len,std::string & out ,Base64_Flag dwFlags = ATL_BASE64_FLAG_NONE) { int len = EncodeGetRequiredLength(in_len,dwFlags); out.assign(len,0); bool ret=Encode(in ,in_len,(char*)out.c_str(),&len,dwFlags); out.resize(static_cast<size_t>(len)); return ret; } static bool Decode(const std::string & in, std::string & out ) { out.assign(in.size(),0); int len=static_cast<int>(out.size()); bool ret = Decode(in.c_str(),static_cast<int>(in.size()), (unsigned char*) out.c_str() , &len); out.resize(static_cast<size_t>(len)); return ret; } static bool Decode(const char * in ,int in_len , std::string & out ) { out.assign( static_cast<size_t>(in_len),0); int outlen=static_cast<int>(out.size()); bool ret = Decode(in ,in_len, (unsigned char*) out.c_str() , &outlen); out.resize(static_cast<size_t>(outlen)); return ret; } }; }

December 13, 2019 · 5 min · holdsky

Objective-C在升序数组中使用二分法查找元素

//在升序数组中,查找插入索引 ,插入索引是第一个恰当的位置(多个相同元素的第一个元素位置) //existSame ,输出YES时,返回的索引是相等的元素索引,输出NO,返回的索引是需要插入的位置索引 NSUInteger FindInsertIndex(NSArray * ascSortedItmes , id obj, BOOL &existSame, int(^CompareTwoItem)(id item, id obj)) { existSame = NO; NSUInteger count = [ascSortedItmes count]; if (count > 0) { NSUInteger midIndex ,startIndex = 0 , endIndex = count -1 , safeN = count; while (safeN--) //safeN 保证while一定会结束 ,可以去掉这个判断 { midIndex = ((startIndex + endIndex) >> 1); // (startIndex + endIndex) / 2 id item = ascSortedItmes[midIndex]; int cmp = CompareTwoItem(item ,obj); if (cmp >0 ) // item > obj { if (endIndex == startIndex // 此时 endIndex == midIndex == startIndex || midIndex == 0 ) { return midIndex; } endIndex = midIndex -1; } else if (cmp == 0) //处理相等情况,确保插入的索引是第一个恰当的索引(多个相同元素的第一个元素位置) { if (midIndex == startIndex) //此时,startIndex到endIndex最多两个元素 { existSame = YES; return midIndex; } endIndex = midIndex; } else // item < obj { startIndex = midIndex + 1; if (startIndex > endIndex) // 此时 midIndex == endIndex { return (startIndex < count) ? startIndex : count; } } } ASSERT(0);//上面查找算法有问题 return count;//表示附加到末尾 } else { return 0; } }

December 13, 2019 · 1 min · holdsky

编程之路总结:加密与解密

先摘录一段百度百科的文字: 密码学(在西欧语文中,源于希腊语kryptós“隐藏的”,和gráphein“书写”)是研究如何隐密地传递信息的学科. 密码学的历史很长,人类使用密码的历史几乎与使用文字的时间一样长。古典密码学、近代密码学、现代密码学……详细请阅读 这里 在现代,密码技术大部分都和计算机紧密相关,因为计算了实在是太大了。做过基础应用开发的话,一般会对密码工具或加密工具有所了解,接触一些 CryptoXXX 或 XXXCrypto 工具库。Crypto是不是和希腊语kryptós很像?😄😄😄 好了,说些我懂的吧。我从软件编程的角度谈下自己的体会。 算法分类 在学习和使用Crypto类的加解密工具库时,尤其是在查看和使用其提供API时,要明白这后面对应的是什么算法、在什么场景下使用。 当前,我们常用的算法可以分为三类: 摘要算法,也称散列、哈希算法,也有叫做单向加密算法的。严格来说,这不是加密算法—-摘要算法是一种操作不可逆的算法,不能反向解密出原始数据。它应用的场景往往是完整性、真实性校验,例如数字签名。 对称加密算法。指加密和解密的密钥都是同一个,通常是加密方和解密方预先共同约定一个密钥。在非对称加密算法 非对称加密算法,公钥加密算法。这和对称加密算法相对应,指加密密钥和解密密钥不是同一个。著名的应用场景就是TLS/SSL(HTTPS的 S 指的是TLS/SSL) 摘要(Digest) 也叫杂凑、哈希;就是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。例如MD5是将数据散列到128比特(16字节)上。 MD5 英文 MD5 Message-Digest Algorithm. 是第5代摘要算法,前面还有MD2、MD4。 MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。–摘自百度百科。 算法输入单次计算块输出MD2任意数据,包括空数据512比特(64字节)128比特(16字节)MD4任意数据,包括空数据512比特(64字节)128比特(16字节)MD5任意数据,包括空数据512比特(64字节)128比特(16字节) MD5的计算结果不存在32位、16位之分,其结果就是16字节、128比特。32位、16位之说是极其不专业的。 MD5已经被证明不安全,但这并不影响我们继续使用,只需要我们将应用范围缩小到不涉及安全方面就可以了。 例如, 我们仍然可以使用MD5来校验非核心文件的完整性。 用MD5将URL映射到本地文件名,以帮助实现缓存功能。(通常在客户端使用MD5时,是不考虑MD5的碰撞概率的)。 SHA系列 英语 Secure Hash Algorithm,缩写为SHA 算法输入单次计算块输出SHA10 ~ (2^64 - 1)字节,包括空数据512比特(64字节)160比特(20字节)SHA2240 ~ (2^64 - 1)字节,包括空数据512比特(64字节)224比特(28字节)SHA2560 ~ (2^64 - 1)字节,包括空数据512比特(64字节)256比特(32字节)SHA3840 ~ (2^128 - 1)字节,包括空数据1024比特(128字节)284比特(48字节)SHA5120 ~ (2^128 - 1)字节,包括空数据1024比特(128字节)512比特(64字节) SHA224、SHA256、SHA384、SHA512统称为SHA2。当前,SHA1已经被证明不安全了(Google曾公布过两幅不同的有意义的图片,但其SHA1值是一样的,这在法律上是极其可怕的事)。 SHA2在安全领域仍然继续服役着。 SHA系列的最新标准已经有了SHA3系列,详情可以参考维基百科 SHA3 SM3 我国SM3密码算法正式成为ISO/IEC国际标准 SM3是中华人民共和国政府采用的一种密码散列函数标准,相关标准为“GM/T 0004-2012 《SM3密码杂凑算法》”。 在商用密码体系中,SM3主要用于数字签名及验证、消息认证码生成及验证、随机数生成等,其算法公开。据国家密码管理局表示,其安全性及效率与SHA256相当。 SM3 算法的哈希值大小为 256 比特。 ...

December 13, 2019 · 1 min · holdsky

学习笔记:uglifyjs 混淆JavaScript

安装 sudo npm install uglify-js -g -g 全局安装 简单使用 uglifyjs 文件1 文件2 文件3 -c -m -o 输出文件 # 例如 uglifyjs a.js b.js c.js -c -m -o result.js -c 压缩输入文件(删除空白字符,注释等,而非常见的zip压缩) -m 混淆符号,这个是轻量的混淆,把长符号替换成端符号 -o 输出文件

December 9, 2019 · 1 min · holdsky

翻译:系统工程之配置基准

本文谷歌翻译. 正文 为程序生命周期中的特定事件建立了配置基准,并有助于程序的“获取程序基准”(APB)的绩效部分。 总体技术基准汇总到APB中,并包含以下配置基准: A Configuration Baseline is established for specific events in a programs life-cycle and contributes to the performance portion of a program’s Acquisition Program Baseline (APB). The overall Technical Baseline rolls up into the APB and consists of the following configuration baselines: 功能基准(Functional Baseline) 定义所需的系统功能,以描述整个系统的功能和接口特性,并进行验证以证明实现了这些指定功能特性。 该基准来自功能开发文档(CDD),通常包括整个系统的详细功能性能规范以及验证和验证总体系统性能所需的测试。 通常在系统功能审查(SFR)中建立功能基线并将其置于配置控制之下。 通常使用系统验证审查(SVR)和/或功能配置审核(FCA)对其进行验证。 Definition of the required system functionality describing functional and interface characteristics of the overall system, and the verification required to demonstrate the achievement of those specified functional characteristics. This baseline is derived from the Capability Development Document (CDD) and normally includes a detailed functional performance specification for the overall system and the tests necessary to verify and validate overall system performance. ...

December 6, 2019 · 3 min · holdsky

JSCore/V8环境中实现JavaScript的Console功能

JavaScript 原生中默认是没有 Console 对象,这是宿主对象提供的内置对象。 在webview中已经内置实现了,但是在JSCore这样的JS引擎中没有实现相关功能,因此为了前端同学调试方便和代码可移植性,有必要手动实现一个。 大体的实现思路如下: 注入全局的日志打印函数 我们可以注入一个全局的日志打印函数,假设名字叫做 NativePrint,然后创建Console对象,将Console对象的日志输出全部转发到这个 NativePrint 上。 NativePrint 接收两个参数,一个是日志类型,一个日志内容. iOS JSContext示例 JSContext * context = [JSContext new]; context[@"NativePrint"] = ^(NSString *logType , id logContext) { //打印输出 } Android V8示例 import com.eclipsesource.v8.V8; JavaVoidCallback callback = new JavaVoidCallback() { @Override public void invoke(final V8Object receiver, final V8Array parameters) { //解析parameters ,打印输出 } } V8 runtime = V8.createV8Runtime(); runtime.registerJavaMethod(callback, "NativePrint"); 注入Console对象 可变参数 对于JavaScript的方法来说,都有一个 arguments 参数列表(列表名字就是arguments),所以我们可以定义可变参数函数 javascript代码 注入NativePrint方法后,我们再注入一个全局的Console对象, 例如 console = { default:this, __Stringformat : function (values) { function __toString(s) { if (s === null || s === undefined) { return ""; } return s.toString(); } if (values.length == 0) { return ""; } else if (values.length == 1) { return __toString(values[0]); } index = 0; return values[0].replace(/%([a-zA-Z_])/g, function (match) { index = index +1; if (values.length > index) { return __toString(values[index]); } else { return ""; } }); }, info : function () { ffpd_log("info",this.__Stringformat(arguments)); }, warn : function () { ffpd_log("warn",this.__Stringformat(arguments)); }, //其他Console方法 }; 使用示例 ...

December 5, 2019 · 1 min · holdsky