关于密码学:Tongsuo-840pre2-发布

公布工夫:2023 年 8 月 17 日 公布内容:Tongsuo 8.4.0-pre2 下载地址:https://github.com/Tongsuo-Project/Tongsuo/releases/tag/8.4.0-pre2 本次公布重要更新: 铜锁 8.4.0 的第二个预公布版本(-pre2),欢送下载试用。铜锁的 8.4.0 是一个大版本公布,其中包含了对多种半同态算法、零常识证实算法和硬件加速能力的反对,并删除了不罕用的算法以及进行了破绽的修复重要更新(8.4.0-pre1 ~ 8.4.0-pre2):实现基于 64 位平台架构的 SM2 算法性能优化实现基于 SM2 曲线参数特化的疾速模约减和疾速模逆元算法修复多个安全漏洞重要更新(8.3.0 ~ 8.4.0):修复多个安全漏洞删除多个不罕用的算法实现基于 64 位平台架构的 SM2 算法性能优化实现基于 SM2 曲线参数特化的疾速模约减和疾速模逆元算法反对零常识证实算法-bulletproofs (r1cs)反对零常识证实算法-bulletproofs (range proof)ZUC 算法优化以及 speed 反对 ZUC 算法s_server 反对配置多证书测试TLCP SNI性能SSL_connection_is_ntls 改成应用预读形式判断是否为 NTLSPaillier 反对硬件加速(蚂蚁隐衷计算减速硬件)BIGNUM 运算反对 method 机制反对半同态加密算法 Twisted-EC-ElGamal反对半同态加密算法 EC-ElGamal 命令行反对半同态加密算法-Paillier删除对 VMS 零碎的反对反对新个性 - 增加导出符号前缀删除 PA-RISC 架构代码删除 SPARC 架构代码反对椭圆曲线(EC)点计算硬件加速 API(蚂蚁隐衷计算减速硬件)修复2处SM2签名算法的实现 bug [0x9527-zhou]根底代码迁徙到 OpenSSL 3.0.2

August 23, 2023 · 1 min · jiezi

关于密码学:铜锁-SM2-算法性能优化实践三|快速模逆元算法实现

模逆元的概念在数学上,乘法逆元有一个更加广为人知的别名“倒数”。也就是说,对于实数 a,其倒数 a-1 满足 a*a-1=1。因为 1 是实数域的乘法单位元,故而倒数 a-1 为实数 a 的乘法逆元。 而在无限域模运算中,乘法逆元的定义要更简单一些:如果 a 和 m 都是正整数且满足 gcd(a,m)=1,那么在模 m 意义下,存在一个整数 b,使得 ab≡1(mod m),此时 b 就是 a 在模 m 运算下的逆元,常示意为 a-1,简称为模逆元。 因为引入了模运算,在实数域上简略求倒数取得逆元的办法并不能间接求得无限域上的模逆元,须要改用更为简单的形式求解运算后果。 在 SM2 数字签名算法中,存在两个必须计算模逆元的步骤:一是将椭圆曲线公钥点 PK 由仿射坐标转化为雅可比坐标时,须要计算 z 轴坐标 z1 对素数域参数 p 的模逆元;二是在签名计算过程中,须要求解私钥 dA+1 对椭圆曲线阶数 n 的模逆元。因为模逆元的计算非常复杂,是椭圆曲线中开销最大的单体运算 (无任何优化时计算耗时是模乘法的数千倍) ,且运算处于数字签名算法流程的要害链路上, 因而是制约 SM2 数字签名性能的瓶颈之一。目前,铜锁我的项目为 SM2 数字签名算法提供了通用的模逆元优化实现,尚未针对 SM2 曲线实现特定优化,存在能够进一步优化晋升的空间。 优化计划比拟选型在数学上,有两种算法能够疾速地计算模逆元,它们别离是拓展欧几里得算法和基于费马小定理的求解算法。上面咱们简要介绍这两类算法,探讨各算法的优缺点以及在铜锁的工程实现中抉择基于费马小定理的求解算法的起因。 1 拓展欧几里得算法求解模逆元用于求解最大公约数的欧几里得算法最早在欧几里得的《几何本来》中被提出,拓展欧几里得算法是对这一古老算法的扩大。在求解最大公约数的根底上,拓展欧几里得算法通过收集辗转相除过程中的余式,求得线性方程 ax+by=gcd(a,b) 的整数解。因为阶数 n 是一个质数,因而 gcd(a,n)=1,那么该线性方程即转化为 ax+ny=1,恰巧是同余线性方程 ax≡1(mod n) 的个别示意模式,所求的 x 即为 a 的模 n 逆元。 对于古代计算机而言,拓展欧几里得算法的一个毛病是在计算过程中存在大量除法运算,而 CPU 在解决除法运算时的效率通常比其余根本运算 (如加、减、乘) 要低得多。针对这一缺点,约瑟夫 · 斯提芬于 1967 年提出了二进制拓展欧几里得算法[1], 该算法用简略的移位操作和减法代替了简单的除法运算。上面是利用二进制拓展欧几里得算法求模 n 逆元的伪代码: 拓展欧几里得算法求解模逆元的长处是:能够求解任意模数下的逆元,不受模数是否为素数的限度;算法效率高,相较于费马小定理求模逆元有肯定的性能劣势。然而,拓展欧几里得算法相应的也存在一些毛病:实现代码较为简单,容易出错;代码中有大量的分支和判断语句,难以实现恒定工夫 (Constant time) 算法,对于侧信道攻打的抵制较弱,在密码学算法中可能会导致对于私钥或明文的信息产生透露。 ...

August 23, 2023 · 2 min · jiezi

关于密码学:铜锁-Bulletproofs-Range-使用教程

文|王祖熙(花名:金九) 蚂蚁团体开发工程师 负责国产化明码库 Tongsuo 的开发和保护专一于密码学、高性能网络、网络安全等畛域 本文 5316 字 浏览 12 分钟 背景在许多利用场景中,当须要证实一个值满足特定的范畴条件、又不心愿走漏该值的具体大小时,传统的证实办法可能须要裸露更多的信息。而范畴证实能够提供一种更高效、更隐衷爱护的形式来实现这一指标。 Bulletproofs Range Proof (范畴证实) 就是这样一种高效的范畴证实技术,Range Proof 也是 Bulletproofs 算法的一个重要利用,用于证实一个值位于指定的范畴内:[0,2n-1] (n 为指数,是 Range Proof 的重要参数) 。 在加密货币交易中,范畴证实能够用于证实交易金额在特定范畴内,以确保交易的合法性和爱护用户隐衷,这能够避免歹意用户创立有效的交易或泄露敏感的交易金额信息。而 Bulletproofs 具备 Proof 较小的特点,能够大大减小交易成本。以下是一些应用 Bulletproofs 的区块链我的项目: Monero:Monero (门罗币) 是一种基于隐衷的加密货币,它应用了 Bulletproofs 技术来实现交易的隐衷和匿名性。Bulletproofs 能够帮忙减小 Monero 交易的大小,进步交易的效率,并爱护用户的隐衷。Grin:Grin 是一种基于 MimbleWimble 协定的加密货币,它应用了 Bulletproofs 技术来实现交易的隐衷和可扩展性。Bulletproofs 在 Grin 中被用于构建零常识证实,以验证交易的有效性和爱护用户的隐衷。Zcoin:Zcoin 是一种基于零常识证实的隐衷加密货币,它应用了 Bulletproofs 技术来改良其零常识证实计划。Bulletproofs 能够帮忙 Zcoin 实现更高效和更紧凑的证实,进步隐衷爱护的性能。Bulletproofs 的零常识证实和范畴证实个性使其成为进步区块链隐衷和性能的无力工具,因而在隐衷爱护和可扩展性方面具备宽泛的利用后劲。 命令行公共参数 (-ppgen/-pp)生成公共参数$ tongsuo bulletproofs -ppgen -out ./pp.pem -curve_name sm2 -gens_capacity 16 -party_capacity 4参数阐明: ...

June 29, 2023 · 6 min · jiezi

关于密码学:铜锁支持-Bulletproofs-算法

背景零常识证实 (ZKP,Zero Knowledge Proof) 是隐衷计算和区块链畛域中十分重要的密码学技术,可能在证实者不向验证者提供任何有用信息的状况下,使验证者置信某个论断是正确的。零常识证实于 1985 年提出,至今 30 多年,但目前支流的零常识证实算法仅有 zk-SNARKs、zk-STARKs 和 Bulletproofs。其中 zk-SNARKs 于 2013 年提出,因其常数级的验证工夫和证实大小较小,在区块链中获取了宽泛的利用,但其原理和计算非常复杂,还须要 trusted-setup,耦合度和复杂度都比拟高,在一些场景中并不实用;zk-STARKs 解决了 zk-SNARKs 须要 trusted-setup 的问题,但其生成 zk-STARKs 的证实 size 较大,验证 zk-STARKs 的证实计算复杂度较高,因而不论是 zk-SNARKs 还是 zk-STARKs,都有较高的门槛。而 2018 年提出的 Bulletproofs 算法,其设计指标是克服传统计划的毛病并提供更高效、更紧凑的零常识证实计划。它具备以下几个劣势: 高效性:Bulletproofs 算法具备线性的验证复杂度,这意味着验证一个证实所需的计算量与证实的大小成正比,具备较高的效率。紧凑性:Bulletproofs 生成的证实绝对较小,通常是输出规模的对数级别。相比于传统计划,它们更能无效地传输和存储,缩小了通信和存储老本。这对于在无限的计算和存储资源下进行零常识证实十分有价值。通用性:Bulletproofs 是通用的,能够用于证实范畴 (Range) 和证实常识 (R1CS) 的零常识证实。它能够利用于各种不同的利用场景,包含 Web3 畛域的窃密交易、身份验证和隐衷爱护等。兼容性:Bulletproofs 与现有的区块链技术兼容。它能够与现有的密码学原语和协定配合应用,如零常识范畴证实 (Range Proof) 和加密哈希函数。这使得 Bulletproofs 可能与现有的零碎和平台集成,并为其提供更高效和牢靠的零常识证实能力。综上所述,Bulletproofs 诞生的背景是解决传统零常识证实计划的限度和毛病,它通过提供高效、紧凑和通用的证实计划来解决这些问题。Bulletproofs 具备高效性、紧凑性、通用性和兼容性等劣势,使其成为在各种隐衷爱护和认证利用中宽泛应用的零常识证实算法。目前可实现 Bulletproofs 算法的开源我的项目中,数量很少且品质个别,门槛也较高。铜锁作为根底明码库,十分有必要实现零常识证实算法,特地是 Bulletproofs 算法,以将其利用到更多的畛域,解决隐衷计算和区块链中的难题。 概念公共参数Bulletproofs 的公共参数也有事后设置的过程,但与 zk-SNARKs 的 trusted-setup 有着较大区别,所谓 trusted-setup 是指在协定进行证实和认证之前,须要生成和设置一些公共的参数,然而在生成这些公共参数的过程中,生成了一些不能公开的两头数据,须要在实现 setup 之后把这部分不能公开的数据删除,否则任何一方拿到这些数据都可能破解协定。而 Bulletproofs 的公共参数生成叫做 setup,而不是 trusted-setup。在 Bulletproofs 的公共参数生成过程中,参与者执行特定的计算,并共享生成的公共参数,而后,其余验证者能够应用这些公共参数来验证证实的有效性。所以,证实者和验证者须要在雷同的公共参数下进行,否则无奈验证通过。 ...

June 29, 2023 · 4 min · jiezi

关于密码学:密码学概念科普加密算法数字签名散列函数HMAC

明码散列函数明码散列函数 (Cryptographic hash function),是一个单向函数,输出音讯,输入摘要。次要特点是: 只能依据音讯计算摘要,很难依据摘要反推音讯扭转音讯,摘要肯定会跟着扭转对于不同的音讯,计算出的摘要简直不可能雷同依据散列函数的上述特点,能够利用在保留明码、数据防篡改和完整性爱护、数字签名等方面,前面介绍其余概念的时候也会提到。 在网上下载文件时,常常会提供 MD5 值供校验。因为文件理论可能是从世界各地的镜像站下载的,有可能会被篡改,所以下载实现之后计算一下 MD5 看是否统一,就晓得是否被篡改了。 个别零碎在设计时,都不会间接保留明码原文,避免明码透露。这时能够应用散列函数保留原始明码的摘要。依据散列函数的特点,即使摘要透露了,也很难反推出明码是什么。只有正确的输出了原始明码,能力计算出完全相同的摘要。 尽管散列函数是单向的,但能够应用彩虹表用空间换工夫。就是把常见的组合的摘要都计算出来,这样拿到一个摘要之后,去比对一下就有可能找到原始信息。例如能够去搜索引擎上搜寻一下 456b7016a916a4b178dd72b947c152b7 这个字符串,很容易就能晓得他的原文是什么。 为了防止被彩虹表攻打,能够在计算摘要时,增加一点随机的货色进去再计算摘要,这个货色叫盐值 (salt)。因为盐值足够长且是随机的,这样想通过当时计算的彩虹表去碰撞就会更艰难了。例如下面那个字符串很容易能搜到是 admin 的 MD5 值。而如果咱们给 admin 前面拼接上 P6R8ERaZ,再计算出 adminP6R8ERaZ 的 MD5 值 70a1a6831709d7e7cb6cc35ccdfdbd39,再去搜寻这个值就很难找到他的原文了。下面只是个例子,理论利用中,盐值不是简略的增加到原始音讯的前面。 常见的散列函数包含 MD5、SHA-1、SHA-2 等。SHA-2 蕴含 SHA-256/224、SHA-512/384 等。其中 MD5、SHA-1 曾经被证实不平安,举荐应用 SHA-2 系列的。 本文的例子中都应用 MD5,是因为 MD5 生成的摘要是 128 位的,更短一点好写。 MAC/HMAC下面提到下载文件时可能会提供文件的 MD5,供校验文件是否被篡改。这样做有个前提是这个 MD5 自身不会被篡改。例如下载的文件放在镜像站,而 MD5 自身是放在 HTTPS 的源网站的。 但很多时候摘要自身也是不能平安传递的,这时如果还应用摘要去校验就失去了意义,因为篡改音讯的人间接把摘要一起篡改了就行了。 这种状况能够应用音讯认证码 (Message authentication code, MAC)。MAC 是依据一个密钥,应用一个算法,把音讯计算成一个摘要。通信的另一方,应用雷同的密钥和算法,计算出摘要,比照是否统一,能够验证音讯是否被篡改。 将音讯和 MAC 一起传递,如果音讯被篡改了,但中间人没有密钥,无奈计算出新的 MAC,音讯接管方就能校验出音讯被篡改了。 理论应用中通常应用的是 HMAC (keyed-hash message authentication code),也就是应用明码散列函数,联合一个密钥,去计算音讯的摘要。 ...

June 25, 2023 · 3 min · jiezi

关于密码学:国产加密算法概述与应用

国产环境为什么要推广国密算法自主可控,不必依赖于国外的技术,更符合国情安全性更高,目前来说国密算法不会被破解,重要信息不会存在泄露危险性能更好暂且不管下面的阐述是否是睁着眼睛说的,至多第一点是不可否认的 定义国产明码算法(国密算法)是指国家明码局认定的国产商用明码算法,实现商用明码算法的加密、解密和认证等性能 国产商业环境目前次要应用公开的SM2(非对称算法)、SM3(哈希算法)、SM4(对称算法) 罕用国密算法SM1分组加密算法,分组长度,密钥长度都是128bit对称加密算法算法平安窃密水平和AES相当算法不公开,以IP核模式存在于芯片中,须要通过加密芯片接口调用,采纳硬件实现SM2非对称加密算法,基于椭圆曲线明码的公钥明码算法规范,其秘钥长度256bit,蕴含数字签名、密钥替换和公钥加密用于替换RSA/DH/ECDSA/ECDH等国内算法SM2采纳的是ECC 256位的一种,其平安强度比RSA 2048位高,且运算速度快于RSASM3明码杂凑算法,报文摘要算法,不可逆在SHA-256根底上改良实现的一种算法,采纳Merkle-Damgard构造,音讯分组长度为512bit,输入的摘要值长度为256bit用于代替MD5/SHA-1/SHA-2等国内算法实用于数字签名和验证、音讯认证码的生成与验证以及随机数的生成SM4分组对称明码算法AES算法具备雷同的密钥长度、分组长度,都是128bit用于代替DES/AES等国内算法算法蕴含几种常见加密解密模式 ECB模式 次要逻辑 把数据依照肯定长度字节分段进行加密或者解密,最初一段有余固定长度的话补0或者补F最初把计算出来的数据连在一起就失去明文或者密文长处 简略有利于并行计算误差不会被传递毛病 不能暗藏明文的模式可能对明文进行主动攻击CBC模式 是一种循环模式 次要逻辑 把数据依照固定长度分组,最初一组数据长度有余的话填充指定数据第一组数据与初始化向量iv进行异或运算,产生的后果执行加密失去第一组密文第二组数据与第一组加密后果进行异或后的后果,执行加密失去第二组密文最初把所有分组密文拼接在一起失去加密后果长处 不容易主动攻击安全性好于ECB,是SSL、IPSec的规范毛病 不利于并行计算误差传递须要初始化向量IVSM9用椭圆曲线对实现的基于标识的数字签名算法、密钥替换协定、密钥封装机制和公钥加密与解密算法,包含数字签名生成算法和验证算法,并给出了数字签名与验证算法及其相应的流程python国密算法举荐与利用开源我的项目 https://github.com/duanhongyi/gmsslSM2与SM3算法因为sm2加密须要生成一个sm2对应的椭圆曲线对应的公钥和私钥,然而gmssl库没有封装一个随机生成公钥私钥的函数,所以须要拜访一个第三方的网址去获取生成的公钥私钥,网址如下 https://const.net.cn/tool/sm2/genkey/把生成的公钥和私钥输出作为参数,填入如下代码 初始化定义,非对称加密须要提供公钥和私钥参数 from gmssl import sm2, funcprivate_key = '00B9AB0B828FF68872F21A837FC303668428DEA11DCD1B24429D0C99E24EED83D5'public_key = 'B9C9A6E04E9C91F7BA880429273747D7EF5DDEB0BB2FF6317EB00BEF331A83081A6994B8993F3F5D6EADDDB81872266C87C018FB4162F5AF347B483E24620207'sm2_crypt = sm2.CryptSM2( public_key=public_key, private_key=private_key, asn1=True)加密和解密 data = b"test"for _ in range(5): enc_data = sm2_crypt.encrypt(data) print(f'encrypted data len {len(enc_data.hex())}: {enc_data.hex()}') dec_data = sm2_crypt.decrypt(enc_data) print(f'decrypted data len {len(dec_data)}: {dec_data}\n') assert dec_data == data生成的打印输出如下,生成密文的长度都是固定的200,发现每次生成的密文都是不同的,在加密过程中退出了salt,防止了雷同明文映射到雷同密文造成的不安全性 encrypted data len 200: 42a29aa33143985e14ae37ccdba89704595008e561af51cb7d88d2c9663468492b0d08a431fa5cd5b92e785ffd9cb858a69d71da82ddb3ddfeaa6e5e765e25cfb68bd4b307c63bc7fe844045159e8cbdbecbe4e3c31359c5f87669abb1dc6e431ad82632decrypted data len 4: b'test'encrypted data len 200: a797d8e1a205e1e50d60895b29d056eecd6d8e0f58ececf283680d695a007c24c2f182ce9601a500e3b755f6f530d3490b4efc43c242b9bdfff64253099792e8a7c4fa9692a59961fc7b6ef35a738a27d1c262f7e4a39190c64e1b8c2ff7c9ffa48748a6decrypted data len 4: b'test'encrypted data len 200: cd010a36cb272c9fc1f355e9e707554c972ba0ec27fe171a137b7d9165a7fe7f9913633333b1290909ce28f73973aa84fe666a97335c6d12f17fd724c87cc2bd37bc4581118fe049cd08ef963ffce0d84cd779f7c6b875c17a88b864115bca71a43cfbd9decrypted data len 4: b'test'encrypted data len 200: 88f5c05e7830c8b70320f2c40673d9aa0fd961535214825765d81e85e9c47e6bdb064086c1b462b2a735aa82ea5ed9668f8611a16c811247ff9dad92f9d572f983d5d2dcc26ae808fdfa4fb7044245bf9066ea11a28fb18208091e060da7c62ea6145266decrypted data len 4: b'test'encrypted data len 200: a5ef6804476fe7442c181aa70df95eae31c4ff59fb70002f7dfca1e8d50f987c8917ce0c9754c19ec5696bfcd71ff686847ca5c62f8c49654388ff1b463393aed40d3a53876587c53aa33aee466248d0fceb4f6c97ada7b25194d0990275b97c81fa32d1decrypted data len 4: b'test'签名和验证 ...

April 20, 2023 · 2 min · jiezi

关于密码学:CCC-数字钥匙学习笔记-车主配对命令

整顿了一下CCC组织的汽车数字钥匙Release 3中对于车主配对Owner Paring,过程的APDU指令和数据阐明。根本能够算是在车端的角度进行车主配对操作。外面的章节表格编号,都依照CCC数字钥匙Release 3文档中的编号走,不便未来检索对照。 车主配对命令5.1 车主配对的指令表5-1:车主配对命令集 命令Ins Byte(HEX)实现方SELECTA4Digital Key FrameworkSPAKE2+REQUEST30Digital Key FrameworkSPAKE2+VERIFY32Digital Key FrameworkWRITE DATAD4Digital Key FrameworkGET DATACADigital Key FrameworkGET RESPONSEC0Digital Key FrameworkOP CONTROL FLOW3CDigital Key Framework参见表15-15.3.2Digital Key Applet表5-2 根本状态字 SW1SW2形容6700长度错6A80数据域的参数错6A82文件找不到6B00P1 P2错6C00Le错6D00INS错6E00CLA错9000胜利5.1.1 SELECT指令汽车向钥匙设施发送SELECT AID指令。Digital Key framework AID为 A000000809434343444B467631。 当Digital Key framework被选中,设施该当依照表5-3返回数据。 钥匙设施该当向车辆批示以后配对状态,可能状态有: 未配对配对模式开始且配对口令曾经输出SELECT指令用来抉择Digital Key applet实例(应用实例AID)在15.3.2.1定义。 C-APDU: 00 A4 04 00 Lc [Digital_Key_Framework_AID] 00 R-APDU: [表 5-3]90 00 表 5-3 SELECT指令响应 Tag长度(bytes)形容是否必须5A2*nn反对SPAKE2+协定版本(ver.high\ver.low)必须5C2*mm反对的Digital Key applet协定版本(ver.high\ver.low)必须D4100 = 未配对<br/>02 = 配对模式开始且配对口令曾经输出必须钥匙设施该当返回所有反对的的SPAKE2+和反对的Digital Key applet协定版本,每个版本应用2字节大端编码的数据。 ...

April 12, 2023 · 4 min · jiezi

关于密码学:Tongsuo铜锁|开放原子开源基金会拜访篇

2023 年 3 月,铜锁密码学开源我的项目的研发和经营团队访问了位于北京的凋谢原子开源基金会(以下简称基金会),并就 2023 年开源我的项目经营打算进行了深刻沟通。 双方同意在我的项目经营层面开展更加踊跃的交换,并在 2023 年中对基金会主导的重点工作进行全面共建。 铜锁我的项目团队将积极参与正在推动的“凋谢原子训练营”、“凋谢原子开源大赛”等主题流动,致力让铜锁开源社区更好的倒退,并为社区用户和开发者们带来更大的价值。 本次铜锁我的项目团队也设计了一个小小的考察问卷,心愿能收到大家的反馈,让咱们能够更好的服务更多的开发者和用户,成为让千家万户都能开箱即用的“铜锁”。 轻轻通知正在看文章的读者们,每位认真填写问卷的小伙伴都有机会取得如下铜锁精美周边之一哦! 扫描下方二维码 或点击文末“铜锁考察问卷”即可参加问卷填写

April 6, 2023 · 1 min · jiezi

关于密码学:密码学哈希为什么要将盐加在明文后面

家喻户晓,在做音讯认证或者签名时,仅应用hash函数安全性是不高的,容易蒙受字典和暴力破解(https://www.cmd5.com/)。所以通常会应用带密钥或加盐的哈希算法作为音讯认证或者口令存储,正如题目所说,咱们在检索互联网上对于加盐的实现时,内容往往都是在明文前面加上随机值: 那做音讯认证的密钥或者盐可不可以加在明文后面呢? 这就引出本文的攻击方式。 MD5 算法计算逻辑为了分明这个攻击方式原理,须要先理解下md5的计算逻辑。 md5算法实质上是一种压缩算法,将长度小于2^64bit的任意字符压缩成128bit固定长度字符。同AES之类的分组加密算法一样,md5也须要进行分组计算。 如图所示,md5的计算须要通过两个步骤: 分组&填充具体计算1.分组&填充首先会对明文依照512bit的长度进行分组,最初一个分组可能会产生长度有余512bit,或者刚刚512bit。 无论最初一个分组的长度是否刚好等于512bit,依照填充规定都须要进行填充,具体细节: 假如明文刚好能被512整除,须要新增一个分组,在开端8*8=64bit 依照小端存储放入原始明文的长度,分组两头残余的bit 依照10000000的形式进行填充,造成一个总长512bit的新分组。假如整除512bit余数大于0 且余数大于512-8*8 =448 bit 则须要持续填充10000(0x80000.....)至下一个分组的448bit,残余的bit依照小端存储填充原始明文长度余数小于448bit,开端填充原始明文长度,两头残余局部填充填充10000(0x80000.....)以上两个步骤用代码示意即为: m_l = len(message) # 原始音讯长度 length = struct.pack('<Q', (m_l) * 8) # 长度转化为小端 unsigned long long 8B blank_padding = b"" message += b'\x80' # 10000000 # 此分组不足以填充长度时 if 56 < len(message) < 64: blank_padding += b'\x00' * (56 + 64 - len(message)) # 填充至下一个分组 # 分组能填充长度 else: blank_padding = b'\x00' * (56 - len(message) % 64) # 本分组填充 if len(blank_padding) > 0: # 填充10000 message += blank_padding # 填充长度 message += length2. 具体计算其实具体的计算过程,咱们不必关注,把这个过程当做一个黑盒(关注细节的能够关注文末github地址)就能够: ...

March 13, 2023 · 1 min · jiezi

关于密码学:Tongsuo|铜锁再获-IOS-和-Linux-平台商用密码认证资质

文|杨洋(花名:凯申 ) 铜锁开源明码库创始人蚂蚁团体高级技术专家 文|张成龙(花名:刻一) 蚂蚁团体技术专家 本文 1657 字 浏览 5 分钟 点击查看原文《Tongsuo|铜锁再获 IOS 和 Linux 平台商用明码认证资质》 2022 年底,咱们借助文章《Tongsuo | 寰球首个商用明码产品认证的密码学开源我的项目》,向外界颁布铜锁明码库取得了软件明码模块平安一级的资质,也就是铜锁的 Android 平台一级资质。 2023 兔年伊始,铜锁再次取得了两个商用明码认证证书,即《BabaSSL IOS 端软件明码模块》和《利用安全软件明码模块(Linux 版)》,别离对应 IOS 平台和 Linux 平台,同先前取得的 Android 平台资质一样,此次取得的两个国密资质也无偿凋谢给铜锁的用户应用。 自此,铜锁开源明码库集齐了 Android、IOS 两个挪动客户端和更罕用的 Linux 平台的商用明码/国密资质,用户能够集成铜锁明码模块于本人的产品和零碎中,实现对商用明码技术应用的合规。 全副资质 PDF 和高清图片版点击最下方“浏览原文”或输出下方链接即可下载。 https://www.yuque.com/tsdoc/m... 铜锁明码库的资质状况阐明 截止 2023 年 1 月底,铜锁明码库一共获得了国家明码管理局商用明码检测核心颁发的三个商用明码产品认证证书,也即俗称的国密资质。这三个资质均属于 GM/T 0028《明码模块平安技术要求》平安一级。 BabaSSL 挪动端软件明码模块 实用平台:Android 产品名称和版本号:ANTGROUP-BabaSSL-MC 8.2.1 持有单位:蚂蚁科技集团股份有限公司 颁发日期:2022.11.30 有效期至:2027.11.29 BabaSSL IOS 端软件明码模块 实用平台:IOS 产品名称和版本号:ANTGROUP-BabaSSL-MIOSC 8.3.0 持有单位:蚂蚁科技集团股份有限公司 颁发日期:2023.1.17 有效期至:2028.1.16 利用安全软件明码模块(Linux 版) ...

February 1, 2023 · 1 min · jiezi

关于密码学:PrivacIN-Week3课程回顾-韩博士讲授ZK电路开发-zkEVM设计

2022年7月23日9:30,隐衷学院PrivacyIN首期ZK训练营第三课——《Applied ZK In Practice》准时开课。本期课程由香港科技大学在读博士韩思远讲授,以ZK电路开发和zkEVM设计为主题,围绕零常识证实利用电路设计及zkEVM构建思路开展,帮忙学员学懂ZKP电路设计。此次培训连续小班授课,数十名国内外密码学及相干畛域的专家学者加入了本次ZK技术培训。 背景常识 基于零常识证实(ZKP)的利用实现,次要是通过将个别的计算问题转化为电路问题,而后构建基于零常识证实零碎的电路束缚或其余保障计算个性的束缚,来实现可验证计算和零知识性。在确定的零常识证实零碎(协定)上,ZKP利用的构建核心内容则是业务电路的设计和开发,因而理解利用电路设计思路、保障安全性和高效性都是十分重要的议题。 课程次要内容 简略介绍了基于零常识证实的利用零碎设计所波及的背景常识后,韩思远老师以开发者的视角,为大家介绍了ZKP利用波及的难题。 他示意:残缺了解零常识证实(ZKP)利用开发是非常复杂的,须要了解多种经典ZKP协定设计原理、深度的密码学和数学知识、难以抉择的ZKP框架、艰涩难懂的术语和概念等,ZKP利用零碎设计常识曲线较高,初学者是很难入门的。 ZKP零碎 从程序设计视角,ZKP零碎个别能够划分为前端frontend和后端backend两个局部。 ZKP零碎的Frontend局部的次要应用低级别语言来示意高级别语言,例如能够将一个个别地计算问题应用较低级别的电路语言示意,如R1CS电路束缚构建计算等(比方circom应用R1CS形容其前端电路)。 ZKP零碎的Backend局部即密码学证实零碎,次要将frontend构建低级别的语言形容的电路,转换为生成证实和验证正确性等,比方罕用的backend零碎协定有Groth16和Plonk等。韩思远博士示意,基于frontend和backend开发拆散的思路,开发一个基于ZKP的利用零碎,在密码学专家选定适合ZKP证实零碎作为backend后,开发人员的工作次要思考的是如何应用frontend提供的低级语言来形容和实现利用业务逻辑,而无需关注ZKP证实零碎后端。 R1CS的创立 zkSNARKs零碎比拟罕用的是R1CS(rank1-constraint-system),R1CS定义构造形如:Ai、Bi、Ci别离为乘法电路门的左操作符(线)的系数向量、右操作符(线)的系数向量、输入系数向量,S为输出变量或witness,R1CS次要关注乘法门。(留神:系数向量的元素示意对应的变量(或线)是否参加该电路门计算)在进行实现某个计算业务时候,构建R1CS模式束缚电路方程组和QAP十分重要,课堂中以Vitalik的提供的范例,即有函数f(x)=x^3 + x + 5,如何示意为能够用来构建该函数证实proof的R1CS电路束缚。创立R1CS和QAP(Quadratic Arithmetic Programs)的次要步骤: 1.计算业务应用对立的计算表达式示意,并示意为电路,其中如if,switch,循环等语句须要转换;(本例无需转换) 2.计算表达式拍平(flatten),将计算表达式应用算数电路门示意成电路circuit,并依照电路门开展、每个电路门一行,电路门的输入都定义新的变量(两头变量)。例如z=x+y能够应用一个加法门示意,z=x+5y能够应用一个加法门和一个乘法门示意;本例中f(x)拍平后的变量:one,~out(为35), x, y, sym_1, sym_2为,其中one为常量,~out为输入,x, y, sym_1, sym_2形成witness,x也是惟一的私密输出变量。 3.每个约束方程转换或合并为乘法门约束方程(多个加法能够在一个约束方程中),所有乘法门形成R1CS束缚方程组;(留神:本例prover曾经晓得一个解x=3,并得出其余witness取值,如y=27, sym_1=9, sym_2=27) 4.对R1CS束缚方程组,应用多项式插值别离求解每个变量的系数多项式,如本范例能够失去Ai、Bi、Ci(其中i=0,1,2,3)多项式;(留神:系数多项式求解个别应用随机数作为x值,y值则示意对应的变量在计算这个乘法门中是否起作用了) 5.将所有变量的系数多项式向量Ai、Bi、Ci(其中i=0,1,2,3)形成的矩阵多项式与变量向量相乘,再向量点积,就能够示意为对立的多项式,且该多项式可被“多项式插值“作为根的可擦除多项式Z(x)整除,其与Z(x)形成QAP; 课堂中,韩博士应用Vitalik提供的范例进行重点解说,即给定一个多项式f(x),如何编写电路束缚和构建QAP,而个别的zkSNARK大多应用Bilinear-Pairing对QAP进行测验来实现ZKP计算。 R1CS的异或运算 R1CS实用于算数门电路,其次要应用+、-、*示意电路运算,其不间接反对布尔电路的异或运算。实现ZKP的多比特位的异或运算须要转换为算术电路来实现,并增加适合的束缚来实现。比方一个8bits的异或运算:A XOR B = C,其中A=11000000,B=1011111, C=01111111。实现的思路是将多个bit位转化为算术电路元素数组,证实这些bit的关系满足A XOR B = C, 于是转化为证实如下约束条件: 1.这些数组中的元素为0或1,即a(a-1)=0,a可取值0或1; 2.这些数组中的元素示意的是形成A、B、C的比特位,即元素依照低位到高位累加; 3.A XOR B=C,对每个bit位异或运算,比特的异或运算:a xor b = a+b-2ab Plonk电路束缚 Plonk协定应用区别于传统R1CS的电路束缚模式,其应用一个对立的电路表达式:,示意算术门和输出门。 Plonk中将束缚次要分为门束缚(运算和输出)、线束缚(复制束缚),而后别离对这两类束缚构建零常识证实和验证。 ...

October 18, 2022 · 1 min · jiezi

关于密码学:PrivacyIN-Week1课程回顾-张宇鹏博导开讲零知识证明密码学基础研究导论

前言 隐衷学院【PrivacyIN】第一期ZK训练营课程精讲内容上线啦,本期课堂邀请到美国德州农工大学(Texas A&M University)计算机科学与工程学院的助理传授张宇鹏,次要介绍ZK实践及相干利用,课堂主题为《Basics of ZK Cryptography & Research Overview》。 此次授课采取小班授课,邀请了数十名来自国内外密码学及相干畛域的专家学者作为学员,加入了高强度的90分钟的密码学培训课堂。 课程精讲全文 零常识证实(Zero-Knowledge-Proofs)由Goldwasser、Micali和 Rackoff在1985年提出,零常识证实是一种在证实者和验证者之间的密码学协定,证实者向验证者证实其领有一个解(witness)可能解决某个计算问题(statement),同时又不泄露任何对于该解(witness)的额定信息。 零常识证实(Zero-Knowledge-Proofs)既不泄露隐衷又保障正确性的能力,使得零常识证实成为一个弱小的工具,并广泛应用各种畛域中,如电子投票、匿名凭证、群签名、可验证外包计算等,尤其随着WEB3技术的倒退,数字货币与区块链技术十分严密地分割起来。 本期课堂中张宇鹏老师围绕零常识证实开展,次要内容有:证实零碎和零常识证实定义、零常识证实发展史、零常识证实及区块链利用等。 张宇鹏老师首先以证实「勾股定理」为范例形容了传统意义上的证实问题,即给出一个B为直角的三角形ABC,证实正确,证实的办法是通过B点画一条垂直线交AC于D点,而后通过三角形类似关系证实勾股定理关系满足。 这里即证实的申明statement,证实的过程(推演步骤)即prove。statement能够示意任何计算问题,传统statement证实是通过提供所有的证实推演步骤,而后进行正确性检测验证来实现的,这里会泄露信息(常识knowledge)。 在证实零碎中,prover/verifier交互证实计算模型是十分典型的,张宇鹏老师例举一个色彩测验的例子,该证实中prover宣称「晓得一张领有不同色彩的纸」作为statement。 通过引入随机挑战,verifier生成随机数以确定是否翻动纸张来进行随机挑战,prover收到挑战则答复是否翻动了纸张,如果是诚恳的prover将总是可能以正确(100%的概率)答复verifier是否翻动了纸张,否则盲猜只有1/2的概率可能正确答复verifier的挑战。反复该过程很屡次(比方100次或以上),如果prover每次都可能正确答复verifer的挑战,则极大概率(简直为100%)地证实了prover晓得正确的答案,即证实了prover宣称的「晓得一张领有不同色彩的纸」statement;如果prover不晓得答案,则prover将以极低的概率(如100次挑战,猜对的概率简直能够疏忽)猜对,则证实其prover在扯谎或是作恶者。 规范的证实零碎中,次要有证实者prover、验证者verifier和公共计算C,其中prover领有数据data,prover生成一个证实proof示意其应用计算表达式C计算失去后果result,而后将计算证实proof发送给verifer;verifer收到计算证实proof进行验证证实是否正确。 证实零碎具备次要特点: 正确性(correctness):诚恳的证实者能够以极高的验证正确性(简直为1) 齐备性(soundness):如果证实者不诚实或作恶,则通过验证的概率是极小的(能够疏忽的概率) 一个高效的证实零碎的次要指标高效的验证工夫和较小的证实proof大小,也即具备succinct个性。 基于证实零碎上,构建一个既能证实计算正确性,proof中又不泄露任何prover原来的数据,即具备零知识性(zero-knowledge),这样的证实零碎就是零常识证实ZKP零碎。 课程中张宇鹏老师回顾了零常识证实问题的发展史,介绍了最早由Goldwasser、Micali和 Rackoff的提出零常识证实零碎以及晚期相干的简单计算实践,包含交互证实(Interactive Proof)、概率检测证实(Probabilistically checkable proofs)、零常识(Zero-knowledge)等。 零常识证实零碎比拟里程碑意义的冲破则是Pinocchio协定,Pinocchio协定是第一个靠近实用的简洁、非交互的零常识证实零碎zkSNARK(SNARK=Succint Non-interactive ARgument of Knowledge),它反对通用计算,将计算问题转换为R1CS(Rank1 Constraint System)模式,而后将R1CS转换到QAP(Quadratic Arithmetic Programs)来实现简洁的多项式证实和验证,Pinocchio协定的呈现标记着零常识证实开始由实践倒退到实用阶段,这也是Groth16协定的根底。 接着,张宇鹏老师对以后零常识证实零碎依靠的密码学技术进行分类,次要分为:双线线性配对(Bilinear Pairing)、平安多方计算(Secure Computation)、离散对数(Discrete-log)、交互式谕示证实Interactive oracle proof、交互式证实Interactive proof、格明码(Lattice)等。 目前零常识证实零碎性能曾经靠近实用,以后最佳的证实零碎在百万级别计算电路上体现曾经性能十分好了(比方生成证实),但另外一方面证实大小和验证工夫则跟具体密码学技术结构形式有很大关系。 进一步地,张宇鹏老师简略摸索了明码货币、认证和区块链的特点,并针对Bitcoin隐衷性,剖析了波及的隐衷泄露问题,示意其隐衷保护性是数据公开的、弱伪匿名,提出能够应用零常识证实来创立数据合法性证实来解决问题的思路。 张宇鹏老师还剖析了以后区块链的扩容问题(Scalability),即区块链零碎解决交易的能力十分低效(比方Bitcoin: 5tx/s,Ethereum: ~30 tx/s)的问题,并介绍了十分炽热的ZK-Rollup技术,示意其次要是应用高效零常识验证算法,将批量交易聚合构建简洁证实,并执行高效的验证来实现的。 邻近课堂完结,张宇鹏老师持续剖析和探讨了隐衷链ZCash和ZK-Rollup的一些差别,帮忙大家进一步了解它们的特点、概念和设计指标。 自在探讨环节,张宇鹏急躁地为学员解答了一系列密码学根底和零常识证实的相干问题。 对于PrivacyIN ...

October 12, 2022 · 1 min · jiezi

关于密码学:PrivacyIN隐私学院第二期训练营开启报名-聚焦门限签名及安全多方计算

PrivacyIN 隐衷学院 (Privacy Institution) 致力于建设凋谢的明码和隐衷技术布道和钻研社区,并联结寰球顶尖的学者、隐衷技术开发者推动ZK(零常识证实)、MPC(平安多方计算)、FHE(全同态明码)的翻新和落OP地。 为了推动隐衷在下一代多方计算场景中的翻新和落地,PrivacyIN隐衷学院打算围绕古代密码学技术发展技术培训、钻研社区和我的项目翻新孵化。以此升高开发者实践协定利用门槛,进步明码钻研人员的工程创新能力,独特保护一个凋谢的明码隐衷技术社区。 PrivacyIN第二期训练营开启报名! TSS(Threshold Signature Scheme 门限签名) 是MPC(平安多方计算)密钥治理的重要钻研方向,能够反对多方治理私钥和资产。相比拟多签协定(Multi Sign),在安全性和老本方面有着更显著的劣势。 PrivacyIN 隐衷学院的发起方LatticeX具备该畛域深厚的钻研背景,由LatticeX发动的Open TSS我的项目正在建设一个凋谢的TSS协定钻研、工程翻新社区。Open TSS目前在MPC钻研畛域与西北大学、南洋理工大学、香港大学等多所出名院校开展单干,以自发表的密码学顶会论文为实践根底,积攒了扎实的技术实力、工程能力和隐衷计算生态社区,目前已开源Open TSS相干代码,并反对行业内多个MPC钱包的翻新落地,提供生态赋能能力。 Open TSS官网:https://opentss.com 本期课程将聚焦TSS,为学员们带来高质量、高密度、重实际的优质课程。 课程工夫: 本次课程会严格筛选出20名学员对象进行小班式辅导授课,期待学员的要求如下: 每周能投入至多10个小时相熟根本密码学协定,或者纯熟应用Rust, Golang, Solidity等编程语言课前实现预习工作,包含经典论文浏览和工程项目钻研实现每节课后代码工作的提交训练营课程内容通过长时间打磨,对开发者敌对。训练营全程完全免费。顺利毕业的学生将获PrivacyIN提供的多样化激励,帮忙每一名开发者学懂MPC、用懂TSS,并能够领有本人创立的我的项目。如果您的我的项目具备后劲,可取得Prizes及Grants孵化赞助,让我的项目更进一步。 精细化课程 TSS训练营课程细节提前看 PrivacyIN第二期隐衷开发者课程内容定位于MPC实践倒退介绍和TSS工程开发实际,次要课程内容: MPC综述-次要介绍平安多方计算(MPC)的密码学根底和倒退综述TSS 综述-门限签名(TSS)协定的历史停顿和钻研倒退方向TSS 产业利用最佳实际-门限签名在去中心化钱包、托管等web3利用中最佳实际Open TSS Hands-on-Open TSS开源框架入手训练师资雄厚 训练营师资团队公开 本期TSS训练营由汪骁传授、薛陆地传授、LatticeX首席产品官Kyle Song领衔。 汪骁现任西北大学计算机科学系助理传授。在马里兰大学获得博士学位后,他曾在麻省理工学院和波士顿大学从事博士后工作,钻研领域涵盖计算机平安、隐衷和密码学。他目前的钻研方向包含可实用平安多方计算、零常识证实、不经意随机拜访机(ORAM)和后量子密码学。2017年,汪骁荣获ACM CCS最佳论文奖。 薛陆地是香港大学金融科技学院和计算机科学系钻研助理传授。 在此之前是中科院IIE密码学研究员。 Kyle Song目前负责LatticeX 基金会的产品策略和技术生态,推动明码隐衷计算在行业内的翻新和落地。在此之前,为Google Blockchain CoE开创成员,并且曾率领OKCoin公链钻研团队,推动公链在分片扩容、隐衷畛域的翻新。长期专一和钻研密码学、区块链、云计算畛域的穿插利用和翻新。 三位老师将为参加的开发者奉上一场丰盛、实用、干货满满的实际课程。 把握TSS能力 就在此刻! 现报名通道已全面开启!报名通道: https://docs.google.com/forms... 咱们将在您报名后与您取得联系,诚挚欢送一起奔赴密码学的世界,开启建设数字世界的大门!咱们始终置信在夯实数字世界地基的路上,隐衷计算是极其重要的奠基者。 报名详情请关注LatticeX基金会推特:https://twitter.com/LatticeX_SGP

September 21, 2022 · 1 min · jiezi

关于密码学:异或的4种堪称神奇的运用场景

原创:扣钉日记(微信公众号ID:codelogs),欢送分享,转载请保留出处。简介家喻户晓,编程语言个别都内置了3种位运算符&(AND)、|(OR)、~(NOT),用来实现位运算,但其实还有一种十分罕用的位运算,即异或^(XOR),数学中罕用⊕示意。 异或的运算逻辑如下: 1 ⊕ 1 = 0 1 ⊕ 0 = 1 0 ⊕ 1 = 1 0 ⊕ 0 = 0 简略来说,异或的个性是,两个值雷同,后果为0,不同则后果为1,所以才叫异或嘛,两个值相异再或起来,不就是1嘛 因为异或非凡的运算个性,使其能够实现一些神奇的操作,如下: 实现加减法加解密密钥替换数据备份那就来一起看看吧! 运算定律任何值与本身异或,后果为0 x ^ x = 0任何值与0异或,后果为其本身 x ^ 0 = x交换律 x ^ y ^ z = x ^ z ^ y结合律 x ^ y ^ z = x ^ (y ^ z)异或的运算定律比较简单,就不写数学证实了,感兴趣可到网上搜寻。 实现加减法XOR的第一种使用场景就是实现加减法,在咱们上小学时,应该都学过进位加法与退位减法来做加减运算,咱们来温习一下吧。 进位加法: 当某一位上两数之和大于10时,须要进位,即高位上要多加一个1。 退位减法: 当某一位上两数相减不够时,须要退位,即从高位上借(减)一个1,来当作10用。 异或其实与这个相似,不过它不会产生进位与退位,如下: 如二进制加法01 + 01 = 10,而异或运算是01 ^ 01 = 00,它其实做了加法,但高位不进位,所以异或又称不进位加法。如二进制减法10 - 01 = 01,而异或运算是10 ^ 01 = 11,也能够看做个位0向高位借了一个1当2来用,但高位不减1,所以异或也能够称为不退位减法。利用异或的这个个性,只有再解决一下进位和退位问题,就能够实现加减法了,如下是java代码实现: ...

September 10, 2022 · 3 min · jiezi

关于密码学:PrivacyIN公开课视频-汪骁基于ZK协议的机器学习隐私保护设计

PrivacyIN首次公开课邀请到美国西北大学助理传授汪骁负责主讲人,解说ZKsystem (Mystique)。 汪骁是西北大学计算机科学系助理传授,在马里兰大学获得博士学位后,他曾在麻省理工学院和波士顿大学从事博士后工作,钻研领域涵盖计算机平安、隐衷和密码学。他目前的钻研方向包含可实用平安多方计算、零常识证实、不经意随机拜访机(ORAM)和后量子密码学。2017年,汪骁荣获ACM CCS最佳论文奖。 本期讲座汪骁传授将带来该ZKsystem的精讲,该零碎能够在算术和布尔值之间、公开提交值和公有验证值之间进行高效转换,指标是大规模AI神经网络推理。 https://www.bilibili.com/vide...

August 11, 2022 · 1 min · jiezi

关于密码学:PrivacyIN-隐私学院首期ZK训练营毕设启动-学术投资导师全程护航

实践+实际PrivacyIN ZK训练营百人同参 由LatticeX基金会发动的PrivacyIN隐衷学院首期ZK训练营已完结近一个月的课程。密集而又富裕应用性、实践性的课程广受密码学爱好者推崇。 在第一周与第二周的课程中,德克萨斯农工大学计算机科学与工程系助理传授张宇鹏活泼地解说了 ZKP密码学基础知识,并提供了以后ZKP的钻研概述,并与同学一并摸索zkSNARKs经典协定(Groth16、Plonk、Stark),深入浅出地解析了协定实现的基本原理。 第三周课程中,来自香港科技大学的韩思远博士以实际为导向,详解了ZKP电路利用导论,次要包含 L2 电路开发概述、zkEVM钻研、ZK经典电路剖析等。大量的案例实战让同学之间产生了热烈的探讨。 在最初一周的课程中,资深密码学工程师Kevin详解了电路利用实际,并应用 Snarkjs+Circom 构建一个繁难的 ZKP 神经网络预测的利用实例,公布到区块链网络。工作流程其中包含:参数构建、电路设计、zkey生成、验证合约、交易和验证等。充沛晋升了同学的实际感知及能力。 本次训练营学生中不乏来自Google、Tiktok、推特等知名企业的开发者,亦有来自斯坦福、东京大学、清华等高校的学生。并且有上百名场内线上参加学习的同学,与训练营的同学一起认真听课并高质量实现课后作业,PrivacyIN高密度,强实际的课程掀起了寰球密码学学习热潮。 值得一提的是,为使寰球更多的学生、开发者参加到密码学的学习中,PrivacyIN邀请到美国西北大学助理传授汪骁参加首期密码学公开课授课,介绍了基于ZK协定的机器学习隐衷爱护设计,详细分析了Mystique协定的构造和解决的问题。后续PrivacyIN将邀请更多寰球顶尖密码学专家、学者、工程师为开发者带来富裕启发及应用性的公开课。 首期ZK训练营毕设将启动学术、投资导师全程护航 在完结了近一个月的课程后,ZK训练营毕业设计曾经启动,毕业设计的设定不仅是测验阶段性学习成绩,更是将实践与实际交融的一次摸索。 在此期间,训练营内的任课教师、学术带头人及机构投资导师将全程参加,旨在发现毕业设计中不一样的「火花」,与此同时,参加毕业设计的同学都将取得丰富激励,极其优良的毕业设计作品有机会取得一线投资机构的青眼,助力成为ZK畛域守业的第一站。 PrivacyIN后续将围绕古代密码学技术发展系列技术培训、钻研社区和我的项目翻新孵化。以此升高开发者实践协定利用门槛,进步明码钻研人员的工程创新能力,独特保护一个凋谢的明码隐衷技术社区。 PrivacyIN第二期课程正在筹备,将如期发展寰球报名,敬请期待!对ZK训练营课程及毕业设计感兴趣的同学们,可分割咱们理解详情。 联系方式:LatticeX基金会推特https://twitter.com/LatticeX_SGP

August 11, 2022 · 1 min · jiezi

关于密码学:论文分享FED-BN使用LOCAL-BATCH-NORMALIZATION方法解决Noniid问题

更多干货内容,请关注公众号:隐语的小剧场 本次分享内容基于ICLR 2021收录的一篇文章:《FED BN: FEDERATED LEARNING ON NON-IID FEATURES VIA LOCAL BATCH NORMALIZATION》,这篇论文次要探讨了应用LOCAL BATCH NORMALIZATION办法解决Non-iid问题。围绕这篇论文的分享将分为4个局部: 1、BATCH NORMALIZATION及其解决Non-iid问题的办法; 2、Non-iid问题及罕用的sota解决办法; 3、FED BN及其解决Non-iid问题的办法; 4、围绕论文试验剖析FED BN成果与价值。 话题一、BATCH NORMALIZATION及其解决Non-iid问题的办法 1. BATCH NORMALIZATION BATCH NORMALIZATION是在明文机器学习中比拟罕用且成果较好的一种办法,它能够很好的解决feature scaling的问题,包含层与层之间internal Covariate Shift 的问题。 那么什么是feature shift呢,咱们来举个例子 如图工作是辨认图片内容是不是玫瑰花,右侧绿色小圈是玫瑰花,红色则不是,蓝色则表分类边界。尽管高低2组图的工作雷同,然而肉眼可见其特色散布显著不同。这种散布状态在训练时就会导致收敛艰难,须要更多的步骤来对消scale不同所带来的影响能力最终收敛。这种feature scale差别在和权重进行矩阵相乘时,会产生一些偏离较大的差别值,这个差别值在网络中会影响前面的层,并且偏离越大影响越显著。BN的作用则是将输出标准化,放大 scale 的范畴。其后果就是能够进步梯度的收敛水平,并晋升训练速度。 那么BN是怎么来的呢? 在早些期间,网络绝对扁平的时候有一种办法Whiten(白化):对输出数据做单位方差的正态分布转换,能够减速网络收敛。在深度网络中随着网络深度的一直减少,每层特征值散布会逐步的向激活函数的输入区间的高低两端(激活函数饱和区间)凑近,如此持续就会导致梯度隐没,影响训练收敛。一些工作包含Relu, Resnet和BN等就是尝试解决这个问题。既然whiten能够减速收敛,那么是否深度网络中对每一层做whiten就能够减速收敛?由这种构想便产生了BN,其作用就是保障机器学习的iid假如,保障每个隐层节点的激活输出散布固定,升高feature shift对模型训练造成的影响。 那BN怎么做? 训练阶段 1、取batch数据计算均值 2、取batch数据计算方差 3、做normalize 4、通过训练失去scale 和shift参数 这里normalize在后的过程中还波及了两个参数:及,一个对应scale,一个对应shift,是对其进行一个平移操作的参数。为什么须要这两个参数?咱们以sigmoid激活函数来进行举例;论文作者在设计时默认将BN增加在激活函数之前,如果在得出normalize 后不增加线性变动,则会导致大部分输出后果停留在线性空间。但线性空间的多层叠加却是有效的,相较之一层线性网络没有任何区别,即减弱了整个网络的表达能力。故而须要增加及参数进行转换,使其找到线性和非线性之间的平衡点,如此既能享受到非线性表白的益处,又能够防止它落在两端影响收敛速度。 在预测流程中,则没有batch 的概念,咱们须要应用全副的待预测数据求均值,而后求抽样方差,最初应用这两个参数,对其进行转化并在预测中应用。 长处小结: 1、能够极大地晋升训练速度及收敛速度,能够应用较大的学习率 2、能够加强学习的成果 3、调参过程会变简略,因为BN的应用对初始值的敏感度升高 毛病小结: 1、训练和预测的后果存在些许的不统一 2、单个batch的计算须要减少计算量 话题二、iid iid,即独立同散布,假如训练数据和测试数据满足雷同散布,是通过训练数据取得的模型可能在测试集上取得较好成果的基本前提保障。然而在局部联邦场景下,数据源不同使得这一假如很难被满足。对于iid的分类参考相干论文可划分为五种情景: 1、Feature distribution skew,即特色散布偏移(以前文玫瑰花辨认为例,x 的特色不统一,但label 散布统一); ...

August 9, 2022 · 1 min · jiezi

关于密码学:PrivacyIN隐私学院-详尽解析zkEVM的电路设计技巧-附视频

PrivacyIN隐衷学院 (Privacy Institution) 是由LatticeX基金会发动,致力于建设凋谢的明码和隐衷技术布道和钻研社区,并联结寰球顶尖的学者、隐衷技术开发者推动ZK(零常识证实)、MPC(平安多方计算)、FHE(全同态明码)的翻新和落地。 为了推动隐衷在下一代多方计算场景中的翻新和落地,PrivacyIN隐衷学院打算围绕古代密码学技术发展技术培训、钻研社区和我的项目翻新孵化。以此升高开发者实践协定利用门槛,进步明码钻研人员的工程创新能力,独特保护一个凋谢的明码隐衷技术社区。 自7月9日PrivacyIN隐衷学院首期ZK训练营开课伊始,便收到了几百名密码学爱好者报名参加训练营,PrivacyIN最终遴选出寰球不同地区20位同学参加这次丰盛、实用、干货满满的密码学实际课程。学生中不乏来自Google、Tiktok、推特等知名企业的开发者,亦有来自斯坦福、东京大学、清华等高校的学生。 本期课程由香港科技大学的韩思远博士以实际为导向,介绍了zkEVM的电路设计技巧,包含程序表白,EVM架构以及zkEVM设计中的优化思路 https://www.bilibili.com/vide...

August 5, 2022 · 1 min · jiezi

关于密码学:密码学系列之PEM和PKCS7PKCS8PKCS12

简介PEM是一种常见的保留key或者证书的格局,PEM格局的文件一般来说后缀是以.pem结尾的。那么PEM到底是什么呢?它和罕用的证书格局PKCS7和PKCS12有什么关系呢?一起来看看吧。 PEMPEM尽管应用来存储证书或者密钥的,然而PEM本来是和email相关联的,因为PEM的全称是Privacy-Enhanced Mail,最后是为邮件的隐衷加强而创立的,是在1993年由IETF制订的规范。尽管最终的协定规范并没有被宽泛采纳,然而其中定义的文本编码却被宽泛的应用,最终由IETF在RFC 7468中正式化。 之前咱们介绍过一种协定描述语言ASN.1,ASN.1通常被用来定义协定中的数据结构,而后通过应用DER编码来对这些数据进行序列化,然而DER编码是二进制的格局,二进制文件在某些状况下不不便进行传输或者展现,不然说某些只反对ASCII编码的状况,所以须要一种能够讲DER格局转换成为文本格式的形式。 这种形式就叫做PEM。PEM应用的办法也很简略,就是对DER编码过后的二进制数据应用base64编码,将其转换成为文本文件。 在PEM中有固定的文件头和文件结尾符。文件头是以'-----BEGIN'+label+'-----'开始,文件结尾是以'-----END'+label+'-----'完结。 其中label示意的是编码的音讯类型,通常能够取这些值:CERTIFICATE, CERTIFICATE REQUEST, PRIVATE KEY 和 X509 CRL。 上面是一个PEM的例子,示意其内容是一个证书: -----BEGIN CERTIFICATE KEY----------END CERTIFICATE KEY-----尽管PEM格局的文件通常以.pem完结,然而也能够应用 ".cer" 或者 ".crt" 示意一个证书,应用".key"示意是一个密钥。 另外, 一个PEM文件中能够蕴含多个内容,比方对于证书来说,通常来说可能须要一些额定的信息比方证书链,这样一个证书链能够存储在一个PEM文件中。 PKCS7PKCS7是Public-Key Cryptography Standards系列的一员,次要用来存储签名或者加密后的数据,比方证书或者CRL。PKCS7能够用原始的DER格局进行存储,也能够应用PEM格局进行存储。 如果以PEM格局进行存储,那么文件的结尾和结尾别离是: ‑‑‑‑‑BEGIN PKCS7‑‑‑‑‑‑‑‑‑‑END PKCS7‑‑‑‑‑在windows中PKCS7通常以.p7b结尾。 PKCS7的操作能够通过openssl命令来进行。 比方将一个PKCS7的文件从PEM格局转换成为DER格局: openssl pkcs7 -in file.pem -outform DER -out file.der从一个文件中提取出所有的证书到另外一个文件: openssl pkcs7 -in file.pem -print_certs -out certs.pemPKCS8PKCS8也是Public-Key Cryptography Standards系列的一员,它次要用来存储私钥。 私钥首先会应用PKCS #5的规范进行加密,而后将其进行base64编码,转换成为PEM格局进行存储。 所以说PKCS8的格局就是PEM,然而外面存储的内容是通过加密过后的私钥。 PKCS12PKCS12也是Public-Key Cryptography Standards系列的一员,PKCS12能够看做是PKCS7的扩大,在PKCS12中能够存储证书,私钥或者CRL。和PKCS7相比,PKCS12能够额定存储私钥。 PKCS12的文件是以.p12 或者 .pfx结尾的。在JDK9中,PKCS12是默认的密钥存储格局。 PKCS12的格局和PEM相比会简单的多,在须要的时候,咱们能够应用OPENSSL将PKCS12格局转换成为PEM格局: openssl pkcs12 -nocerts -nodes -in cert.p12 -out private.pem当然也能够从PEM到PKCS12: ...

August 4, 2022 · 1 min · jiezi

关于密码学:隐语计算技术|私有信息检索PIR及其应用场景

1. The Problem of Private Information Retrival PIR 全称为 Private Information Retrival,直观的翻译名字为“公有信息检索”。已知的最早提出 PIR 的文章是 1995 年 Benny Chor, et. al. [1]。在文章最开始的时候,提到了在传统的 query 场景中,咱们有一个 client 发送 query,有 server 回复后果。 从形象角度来看,咱们能够试图爱护: Server 数据的安全性Client query 的安全性在 [1] 之前,有许多工作在钻研如何爱护 server 端 DB 数据的安全性,在此不再赘述。Benny Chor, et. al. [1] 提出了一个问题:咱们是否能够在 query 场景中爱护 client query 的隐衷性?因为过后分布式数据库存储的倒退,因而他们提出了一个基于 replicated DB (and non-colluding) 的计划。 这就是 PIR 的场景性问题,依据事实中不同的 client 以及 server 的假如,咱们能够把协定进行分类。 2. PIR 场景的分类 咱们能够看到,PIR 场景中的实体有 client 以及 server,并且 client 向 server 发送了一个须要爱护隐衷的 query ,server 向 client 返回一个 query 的最终后果。 ...

August 1, 2022 · 2 min · jiezi

关于密码学:转PrivacyIN隐私学院-解析零知识证明的基本密码学基础

PrivacyIN隐衷学院 (Privacy Institution) 是由LatticeX基金会发动,致力于建设凋谢的明码和隐衷技术布道和钻研社区,并联结寰球顶尖的学者、隐衷技术开发者推动ZK(零常识证实)、MPC(平安多方计算)、FHE(全同态明码)的翻新和落地。 为了推动隐衷在下一代多方计算场景中的翻新和落地,PrivacyIN隐衷学院打算围绕古代密码学技术发展技术培训、钻研社区和我的项目翻新孵化。以此升高开发者实践协定利用门槛,进步明码钻研人员的工程创新能力,独特保护一个凋谢的明码隐衷技术社区。 本期课程由德克萨斯农工大学计算机科学与工程系助理传授张宇鹏活泼地介绍了ZK协定(零常识证实)的根本密码学根底,ZK协定的研究进展综述以及行业利用案列。 https://www.bilibili.com/vide...

July 30, 2022 · 1 min · jiezi

关于密码学:转PrivacyIN隐私学院-详尽解析ZK重要协议的设计理念

PrivacyIN隐衷学院 (Privacy Institution) 是由LatticeX基金会发动,致力于建设凋谢的明码和隐衷技术布道和钻研社区,并联结寰球顶尖的学者、隐衷技术开发者推动ZK(零常识证实)、MPC(平安多方计算)、FHE(全同态明码)的翻新和落地。 为了推动隐衷在下一代多方计算场景中的翻新和落地,PrivacyIN隐衷学院打算围绕古代密码学技术发展技术培训、钻研社区和我的项目翻新孵化。以此升高开发者实践协定利用门槛,进步明码钻研人员的工程创新能力,独特保护一个凋谢的明码隐衷技术社区。 本期课程由德克萨斯农工大学计算机科学与工程系助理传授张宇鹏具体介绍了ZK重要协定的设计理念,包含:Groth16 Plonk Stark。不同电路满足不同的场景,从性能、可信启动等角度进行比照。 https://www.bilibili.com/vide...

July 30, 2022 · 1 min · jiezi

关于密码学:有一种密码学专用语言叫做ASN1

简介ASN.1是一种跨平台的数据序列化的接口描述语言。可能很多人没有据说过ASN.1, 然而置信有过跨平台编程教训的同学可能都听过protocol buffers和Apache Thrift,尽管ASN.1和下面两个语言相比不是那么闻名,然而ASN.1的呈现要比他们早的多,早在1984年ASN.1就呈现了。 和他们相比ASN.1并没有提供繁多的开源实现,而是作为一种标准来供第三方供应商实现的。ASN.1次要用在定义各种根底协定中,比方罕用的LDAP,PKCS,GSM,X.500等。 ASN.1是一种和平台、语言无关的描述语言,能够应用很多ASN.1的翻译工具,将ASN.1翻译成为C, C++, Java等代码。 ASN.1的例子既然ASN.1是一个描述语言,那么咱们先来看一个直观的例子。ASN.1的根底是module, 咱们看一下ASN.1中module的例子: StudentCards DEFINITIONS AUTOMATIC TAGS ::= BEGINStudentCard ::= SEQUENCE {dateOfBirthday DATE,student StudentInfo}StudentInfo ::= SEQUENCE {studentName VisibleString (SIZE (3..50)),homeAddress Address,contactPhone NumericString (SIZE (7..12))}Address::= SEQUENCE {street VisibleString (SIZE (5 .. 50)) OPTIONAL,city VisibleString (SIZE (2..30)),state VisibleString (SIZE(2) ^ FROM ("A".."Z")),zipCode NumericString (SIZE(5 | 9))}END下面的例子中,咱们应用ASN.1定义了一个StudentCard,最外层的以BEGIN和END突围的就是module。StudentCards是module的名字,首字母必须大写。 其中::= 是一个赋值符号。 module中能够有多个type, type的名字也必须首字母大写,例如下面的StudentCard,StudentInfo等等。 每个type中定义了它的组成组件,组件的名字首字母必须小写,这些组件的名字又叫做identifiers。 下面的dateOfBirthday前面接的DATE是ASN.1中内置的类型。而student前面的StudentInfo是一个自定义类型,并且同样蕴含在module中。 StudentInfo中的studentName是一个VisibleString,这个String的限度是size在3到50之间。 下面咱们定义module的时候在module前面加上了AUTOMATIC TAGS,这是什么意思呢? 在ASN.1中,tags是ASN.1音讯中每个组件的外部标识符,以Address为例,咱们心愿给Address中的每个属性都指定一个外部的标识符,如下所示: Address::= SEQUENCE {street [0] VisibleString (SIZE (5 .. 50)) OPTIONAL,city [1] VisibleString (SIZE (2..30)),state [2] VisibleString (SIZE(2) ^ FROM ("A".."Z")),zipCode [3] NumericString (SIZE(5 | 9))}这外面的[0] [1] 就是标识符,当然,咱们能够在定义module的时候手动指定这些tags,然而如果咱们应用AUTOMATIC TAGS,这些标识符会主动创立,从而防止了手动创立标识符可能带来的问题。 ...

July 28, 2022 · 2 min · jiezi

关于密码学:分享自己开发的在线密码学小工具

分享本人开发的在线密码学小工具 继续更新... https://istommao.github.io/Cr...

July 17, 2022 · 1 min · jiezi

关于密码学:密码学系列之使用openssl检测网站是否支持ocsp

简介OCSP在线证书状态协定是为了替换CRL而提出来的。对于古代web服务器来说个别都是反对OCSP的,OCSP也是古代web服务器的标配。 然而OCSP stapling却不是所有的web服务器都反对。然而事实工作中咱们可能须要晓得具体某个网站对OCSP的反对水平。 反对OCSP stapling的网站怎么判断一个web站点是否反对OCSP stapling呢? 最简略的办法就是去第三方网站查看网站的证书信息。比方咱们之前提到过的entrust.ssllabs.com,通过输出对应的网站信息,在Protocol Details一节中,能够找到网站是否反对OCSP stapling的具体信息,如下所示: 能够看到这个网站是开启了OCSP stapling的。然而事实上这个世界上的绝大部分网站是没有开启OCSP stapling的。 那么除了在第三方网站上查看OCSP stapling之外,还有没有其余方法呢? 事实上咱们能够应用openssl神器轻松的做到这一点。当然前提是这个网站反对https。 接下来咱们会具体解说从获取服务器的证书到验证服务器是否反对OCSP stapling的一整套流程。 本文要验证的网站是微软的官网www.squarespace.com,这是一个反对OCSP stapling的网站。 获取服务器的证书要校验服务器是否反对OSCP,咱们首先须要获取到这个服务器的证书,能够用openssl提供的 openssl s_client -connect来实现这个工作。 openssl s_client -connect www.squarespace.com:443这个命令会输入建设连贯的所有内容,其中蕴含了要拜访网站的证书信息。 因为咱们只须要网站的证书,所以须要把-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----之间的内容保留即可。 那么最终的命令如下: openssl s_client -connect www.squarespace.com:443 | sed -n '/-----BEGIN/,/-----END/p' > ca.pem这里咱们应用一个sed -n命令从输入中截取以-----BEGIN结尾和以-----END结尾的数据。 最终咱们失去了网站的证书。 除了网站自身的证书之外,网站的证书自身是由其余的证书来签发的,这些证书叫做intermediate certificate,咱们须要获取到整个证书链。 同样应用openssl的openssl s_client -showcerts命令能够获取所有的证书链: openssl s_client -showcerts -connect www.squarespace.com:443 | sed -n '/-----BEGIN/,/-----END/p' > chain.pem如果你关上chain.pem文件能够发现,文件外面有两个证书,最下面的一个就是服务器自身的证书,而第二个就是用于签名服务器证书的intermediate certificate。 获取OCSP responder地址如果证书中蕴含有OCSP responder的地址,那么能够用上面的命令来获取: openssl x509 -noout -ocsp_uri -in ca.pem 咱们能够失去网站的ocsp responder地址是:http://ocsp.digicert.com。 ...

July 11, 2022 · 2 min · jiezi

关于密码学:PrivacyIN隐私学院开始报名啦打造密码学全球私塾

转自:PrivacyIN隐衷学院 PrivacyIN隐衷学院 (Privacy Institution) 致力于建设凋谢的明码和隐衷技术布道和钻研社区,并联结寰球顶尖的学者、隐衷技术开发者推动ZK(零常识证实)、MPC(平安多方计算)、FHE(全同态明码)的翻新和落地。 为了推动隐衷在下一代多方计算场景中的翻新和落地,PrivacyIN隐衷学院打算围绕古代密码学技术发展技术培训、钻研社区和我的项目翻新孵化。以此升高开发者实践协定利用门槛,进步明码钻研人员的工程创新能力,独特保护一个凋谢的明码隐衷技术社区。  首期ZK训练营正在招募中! 基于隐衷技术布道的初衷,PrivacyIN首期ZK训练营报名现已在寰球开启!报名工夫2022.6.22–2022.7.6,课程工夫为2022.7.8 -2022.8.12,每周一次课程。 本次课程会严格筛选出不超过20名学员对象进行小班式辅导授课,期待学员的要求如下: ●      每周能投入至多10个小时●      相熟根本密码学协定,或者纯熟应用Rust, Golang, Solidity等编程语言●      课前实现预习工作,包含经典论文浏览和工程项目钻研●      实现每节课后代码工作的提交●      课程设计的展现和代码分享 训练营课程内容通过长时间打磨,对开发者敌对。训练营全程完全免费。顺利毕业的学生将获PrivacyIN提供的多样化激励,帮忙每一名开发者取得ZK的能力,并能够领有本人创立的我的项目。如果您的我的项目具备后劲,可取得Prizes及Grants孵化赞助,让我的项目更进一步。  训练营课程表公开! PrivacyIN首期隐衷开发者课程内容定位于ZK实践倒退介绍和工程开发实际,次要课程内容: ZK密码学根底和钻研概述ZK经典协定基本原理(Groth16Plonk Stark)ZK电路利用介绍(L2/ZKEVM,ZK隐衷电路)ZK Hands-on 毕业我的项目提交和展现  训练营师资团队 首期ZK训练营由张宇鹏传授、韩思远博士、资深工程师Kelvin Wong领衔。 张宇鹏传授是加州大学伯克利分校的博士后研究员,德克萨斯农工大学计算机科学与工程系的助理传授。张宇鹏传授发表过多篇学术论文,并具备丰盛的密码学实战经验,此次张宇鹏传授将会把本人的钻研教训融入至课程中。 韩思远是香港科技大学博士,其钻研方向为区块链存储优化、基于ZK的区块链/数据隐衷爱护零碎等,负责本次课程讲师。 Kelvin Wong是资深隐衷计算工程师,次要参加实现Rosetta隐衷机器学习平台,MPC算法协定等,具备多年我的项目开发实战经验,负责本次课程助教。 三位老师将为参加的开发者奉上一场丰盛、实用、干货满满的实际课程。  立刻参加把握ZK常识吧! 现报名通道已全面开启!https://docs.google.com/forms... 咱们将在您报名后与您取得联系,诚挚欢送一起奔赴密码学的世界,开启建设数字世界的大门!咱们始终置信在夯实数字世界地基的路上,隐衷计算是极其重要的奠基者。  

June 24, 2022 · 1 min · jiezi

关于密码学:密码学系列之PKI的证书格式表示X509

简介在PKI(public key infrastructure)公钥设施根底体系中,所有的所有操作都是围绕着证书和密钥的,它提供了创立、治理、散发、应用、存储和撤销数字证书以及治理公钥加密所需的一组角色、策略、硬件、软件和程序。 有了密钥,就能够依据密钥制作证书了。要想证书能够被宽泛的应用,一个通用的规范必定是少不了的,在PKI体系中,这个证书的规范就叫做X.509。 X.509 规范定义了公钥证书最罕用的格局。 一个证书的例子证书中最次要就是公钥信息,从证书中提取公钥,能力应用公钥去解密发送者应用私钥加密过的数据。公钥信息是证书的外围。 除了公钥之外,证书蕴含了很多其余的信息,比方蕴含了身份信息(主机名、组织或集体等)。 创立证书非常简单,咱们先看一个应用openssl命令来创立证书的例子。 创立证书之前,首先须要创立证书依赖的公钥和私钥,x.509证书能够反对多种公钥私钥算法,比方RSA, DSA, ECDSA, ed25519等。 这里咱们抉择应用RSA算法,生成密钥对如下: openssl genrsa -des3 -out ca.key 1024Generating RSA private key, 1024 bit long modulus...............++++++.............................................++++++e is 65537 (0x10001)Enter pass phrase for ca.key:Verifying - Enter pass phrase for ca.key:输出pass, 咱们能够失去ca.key,这是一个RSA PRIVATE KEY。 接下来就能够用这个ca.key来创立证书。 openssl req -new -x509 -days 20 -key ca.key -out ca.crtEnter pass phrase for ca.key:You are about to be asked to enter information that will be incorporatedinto your certificate request.What you are about to enter is what is called a Distinguished Name or a DN.There are quite a few fields but you can leave some blankFor some fields there will be a default value,If you enter '.', the field will be left blank.-----Country Name (2 letter code) []:SHState or Province Name (full name) []:SHLocality Name (eg, city) []:SHOrganization Name (eg, company) []:HWOrganizational Unit Name (eg, section) []:HWCommon Name (eg, fully qualified host name) []:caserverEmail Address []:flydean@163.com能够看到,在密钥的根底上,证书还须要提供例如Country Name,Province Name,Organization Name等额定信息。 ...

June 22, 2022 · 3 min · jiezi

关于密码学:密码学入门

原创:打码日记(微信公众号ID:codelogs),欢送分享,转载请保留出处。简介 在信息安全畛域,个别会遇到"窃听"、"篡改"、"假装"、"否定"这些威逼,而明码学家们提供了相应的密码学算法来解决这些问题,如下: 窃听:攻击者能够在网络上安置了一个路由器,侦听所有通过的数据包,这样数据就被泄密了,密码学提供了对称明码与公钥明码算法对数据加密,保障机密性。篡改:攻击者对通过的数据包进行批改,使得接管方获取到谬误的信息,密码学提供了单向散列函数生成“数据指纹”,保障数据完整性。假装:攻击者伪装成发送方来发送数据,使得接管方获取到虚伪的信息,密码学提供音讯认证码生成"认证码",保证数据起源的正确性。否定:发送方自身是攻击者,发送了歹意申请后,谎称本人没有发此申请,密码学提供了数字签名算法,使其不可否认。像对称加密、公钥加密、音讯散列、音讯认证码、数字签名这些算法,也被称为明码学家的工具箱。 加密分类现如今的加密算法,次要分为两大类,一类是对称加密,而另一类是非对称加密,而对称加密在实现形式上又能够分为两类,一类是分组加密算法,另一类是流加密算法。 分组加密分组加密(block cipher),每次加密或解密数据中特定长度的一小块,前一块解决完了再解决下一块,直到所有数据都解决完,这里的“一块”就称为分组,而一个分组的bit数就是分组长度。 常见的分组加密算法有DES与AES,比方DES的分组长度是64bit,而AES的分组长度能够是128bit或192bit或256bit,因为DES或AES的算法过程比较复杂,暂不介绍,但咱们有必要理解一下它们常常应用的根底运算XOR。 XOR加密在位运算中,个别有AND(与)、OR(或)、NOT(非)运算,然而还有一种位运算也十分罕用,即XOR(异或),它的运算规定如下: 简略来说,就是两边雷同后果是0,两边不同后果是1,所以才称为“异或”嘛! 而如果将XOR运算利用在多bit的数据上,咱们会发现XOR有十分好的自反个性,如下: 数据A与B异或之后,变成了一个新数据而当咱们将新数据再与B异或运算后,发现它又变成了A! 从而,咱们惊奇的发现,如果把B看成是密钥的话,XOR能够用来实现加密、解密算法,而加密、解密过程,都只须要数据与密钥做XOR运算即可。Linux命令做XOR加密$ pip install xortool# 明文文件,内容是hello$ echo -n hello > plain.txt$ xxd plain.txt00000000: 6865 6c6c 6f hello# 用xor算法加密,明码为pass,生成encrypt.bin$ cat plain.txt | xortool-xor -n -r pwd -f - > encrypt.bin$ xxd encrypt.bin00000000: 1812 081c 18 .....# 用XOR算法解密,可还原成明文$ xortool-xor -n -r pwd -f encrypt.binhello尽管对称加密算法的外围是XOR,但如果加密算法只应用XOR的话,其加密的强度并不够高,很容易被破译或泄密。 上面会以一个故事开展密码学的各种算法概念及用处,背景如下: 男主:小李 ,女主:小红,男配:大李,小李的哥哥,女配:小美,小红的闺蜜 小李与小红是同班同学,业余计算机,小李对小红爱慕已久! 恋爱日记:小李写情书为了表白倾慕之情,小李写了一封情书,写了好长时间,决定发给小红,但又不想被网络上的其他人看到!如下: 因为小李最近学习了XOR加密算法,于是应用XOR加密了情书,如下: cat plain.txt | xortool-xor -n -r pwd -f - > encrypt.bin 能够看到,加密后的数据都是乱码了,成果很好! ...

April 29, 2022 · 4 min · jiezi

关于密码学:FIDO-U2F设备的HID协议

FIDO概述FIDO(Fast IDentity Online)联盟成立于2012年,FIDO联盟通过定义出一套凋谢、可扩大、可协同的技术规范,来扭转现有在线认证形式,缩小认证用户时对明码(password)的依赖。FIDO有两套标准:U2F和UAF。 无明码的UAF(Universal Authentication Framework)用户携带含有UAF的客户设施用户出示一个本地的生物辨认特色或者PIN网站额能够抉择是否保留明码用户抉择一个本地的认证计划(例如按一下指纹、看一下摄像头、对麦克谈话,输出一个PIN等)把他的设施注册到在线服务下来。只须要一次注册,之后用户再须要去认证时,就能够简略的反复一个认证动作即可。用户在进行身份认证时,不在须要输出他们的明码了。UAF也容许组合多种认证计划,比方指纹+PIN。 第二因子的U2F(Universal 2nd Factor)用户携带U2F设施,浏览器反对这个设施用户出示U2F设施网站能够应用简略的明码(比方4个数字的PIN)FIDO U2F认证,国内的文章个别翻译成FIDO两步认证。U2F是在现有的用户名+明码认证的根底之上,减少一个更平安的认证因子用于登录认证。用户能够像以前一样通过用户名和明码登录服务,服务会提醒用户出示一个第二因子设施来进行认证。U2F能够应用简略的明码(比方4个数字的PIN)而不就义安全性。 U2F出示第二因子的模式个别是按一下USB设施上的按键或者放入NFC。 U2F HID协定UAF先放一边,U2F的工作流程比较简单,具体的能够看FIDO联盟官网。上面次要说下U2F HID协定。 首先要明确一下U2FHID协定不是U2F的应用层协定,是形容U2F的音讯如何通过HID传输的底层协定,U2F应用层的协定在U2F Raw Message中定义。U2FHID协定能够反对在大多数平台上间接应用而不须要装置驱动,能够反对多利用并发拜访设施。 并发和通道U2FHID设施解决多客户端,比方多个利用通过HID栈拜访单个资源,每个客户端都能够和U2FHID设施通过一个逻辑通道(logical channel)进行通信,每个客户端都应用一个惟一的32bit通道ID来判断用处。通道ID由U2F设施来进行调配,确保惟一。产生通道ID的算法由U2FHID设施的厂商标准定义,FIDO的U2FHID协定中不进行定义。 通道ID 0是保留的,0xFFFFFFFF也是保留给播送命令的。 音讯和包构造包(Packets)分为两类,初始化包(initialization packets)和附加包(continuation packets)。就像initialization packets这个名字一样,每个应用层音讯的第一包都是initialization packet,也是一个事物的开始。如果整个应用层音讯不能通过一个包下发,就须要一个或者多个continuation packet来发送了,直到把所有音讯发完。 一个应用层音讯从主机发送到设施叫做申请(request),从设施返回给主机的叫做响应(response)。申请和响应音讯是雷同的构造,一个事勿从一个申请的initialization packet开始,截止于一个响应的最初一个包。包的长度永远是固定的大小,一个包中的有的字节并没有被应用到,没有应用的字节须要设置为0。 初始化包的定义 偏移长度名称形容04CID通道ID41CMD命令ID51BCNTH发送数据长度的高位61BCNTL发送数据长度的低位7(s-7)DATA发送数据(s等于包的固定长度)附加包的定义 偏移长度名称形容04CID通道ID41SEQ包的程序,0x00..0x7F5(s-5)DATA发送数据(s等于包的固定长度)如果一个应用层音讯长度小于等于s-7,那么一个包就能够发完,如果一个比拟大的应用层音讯,须要拆分成一个或者多个附加包,第一个附加包的SEQ为0,每次附加包的SEQ值减少1,最大到0xFF。 USB全速设施的包长度的最大值是64字节,这样最大的一个应用层音讯能够发送的长度就是,64-7+128*(64-5)=7609字节。 上面是一个初始化包和附加包的C的定义 typedef struct { uint32_t cid; // Channel identifier union { uint8_t type; // Frame type - b7 defines type struct { uint8_t cmd; // Command - b7 set uint8_t bcnth; // Message byte count - high part uint8_t bcntl; // Message byte count - low part uint8_t data[HID_RPT_SIZE - 7]; // Data payload } init; struct { uint8_t seq; // Sequence number - b7 cleared uint8_t data[HID_RPT_SIZE - 5]; // Data payload } cont; };} U2FHID_FRAME;同步搞USBKEY就不能不说到同步,U2FHID也一样,一个事务永远分为三个阶段:音讯从主机发送到设施,设施解决音讯,响应从设施返回到主机,U2FHID的事务必须是原子的,一个事务一旦开始,不能被其余利用中断。 ...

March 18, 2022 · 3 min · jiezi

关于密码学:FIDO-U2F设备的NFC协议

原本打算写U2F Raw Message的内容的,然而发现FIDO联盟在2015年5月份公布的最新的U2F标准中,减少了NFC协定,所以先写下NFC的协定吧。 FIDO U2F NFC的协定其实非常简单,就是定义了一下FIDO U2F的AID和APDU的标准。 1.协定简介FIDO客户端和认证设施之间通过NFC进行通信,过程如下: (1)客户端发送抉择applet指令 (2)认证设施返回胜利 (3)客户端发送操作指令(注册、认证) (5)认证设施返回响应数据或者谬误 2.封包的问题U2F NFC协定不须要对音讯做任何额定的封包操作(比方USB HID协定,须要对音讯进行封包一样)。音讯只须要依照文档U2F Raw Message中的定义间接发送到认证设施即可。 3.APDU的长度局部响应数据可能比拟长,一条短APDU不能传完,所以U2F 认证设施必须按上面的规定应答: 如果申请指令是扩大长度,认证设施的应答必须应用扩大APDU格局如果申请指令不是扩大长度,认证设施的应答必须应用ISO7816-4 APDU链,比方: 4.Applet抉择FIDO客户端通过NFC与认证设施进行认证/注册操作,每次都须要从抉择applet指令开始。抉择之后的指令就参考U2F Raw Message中的定义即可。 FIDI U2F的AID由RID+AC+AX组成 域值RID0xA000000647AC0x2FAX0x0001所以通过FIDO U2F AID来抉择applet的指令是: 00 A4 04 00 08 A0000006472F0001 FIDO认证设施对抉择applet的命令胜利的响应为版本信息“U2F_V2”,抉择applet胜利的响应为: 0x5532465F56329000 实现抉择applet,剩下的就参考U2F应用层的指令进行即可,下一篇真的写U2F Raw Message了。

March 18, 2022 · 1 min · jiezi

关于密码学:密码学-15-JS-RSA算法

1. JAVA实现var JSEncryptExports = {};var navigator = {};var window = global;(function (exports) { function BigInteger(a, b, c) { null != a && ("number" == typeof a ? this.fromNumber(a, b, c) : null == b && "string" != typeof a ? this.fromString(a, 256) : this.fromString(a, b)) } function nbi() { return new BigInteger(null) } function am1(a, b, c, d, e, f) { for (; --f >= 0;) { var g = b * this[a++] + c[d] + e; e = Math.floor(g / 67108864), c[d++] = 67108863 & g } return e } function am2(a, b, c, d, e, f) { for (var g = 32767 & b, h = b >> 15; --f >= 0;) { var i = 32767 & this[a], j = this[a++] >> 15, k = h * i + j * g; i = g * i + ((32767 & k) << 15) + c[d] + (1073741823 & e), e = (i >>> 30) + (k >>> 15) + h * j + (e >>> 30), c[d++] = 1073741823 & i } return e } function am3(a, b, c, d, e, f) { for (var g = 16383 & b, h = b >> 14; --f >= 0;) { var i = 16383 & this[a], j = this[a++] >> 14, k = h * i + j * g; i = g * i + ((16383 & k) << 14) + c[d] + e, e = (i >> 28) + (k >> 14) + h * j, c[d++] = 268435455 & i } return e } function int2char(a) { return BI_RM.charAt(a) } function intAt(a, b) { var c = BI_RC[a.charCodeAt(b)]; return null == c ? -1 : c } function bnpCopyTo(a) { for (var b = this.t - 1; b >= 0; --b) a[b] = this[b]; a.t = this.t, a.s = this.s } function bnpFromInt(a) { this.t = 1, this.s = 0 > a ? -1 : 0, a > 0 ? this[0] = a : -1 > a ? this[0] = a + DV : this.t = 0 } function nbv(a) { var b = nbi(); return b.fromInt(a), b } function bnpFromString(a, b) { var c; if (16 == b) c = 4; else if (8 == b) c = 3; else if (256 == b) c = 8; else if (2 == b) c = 1; else if (32 == b) c = 5; else { if (4 != b) return void this.fromRadix(a, b); c = 2 } this.t = 0, this.s = 0; for (var d = a.length, e = !1, f = 0; --d >= 0;) { var g = 8 == c ? 255 & a[d] : intAt(a, d); 0 > g ? "-" == a.charAt(d) && (e = !0) : (e = !1, 0 == f ? this[this.t++] = g : f + c > this.DB ? (this[this.t - 1] |= (g & (1 << this.DB - f) - 1) << f, this[this.t++] = g >> this.DB - f) : this[this.t - 1] |= g << f, f += c, f >= this.DB && (f -= this.DB)) } 8 == c && 0 != (128 & a[0]) && (this.s = -1, f > 0 && (this[this.t - 1] |= (1 << this.DB - f) - 1 << f)), this.clamp(), e && BigInteger.ZERO.subTo(this, this) } function bnpClamp() { for (var a = this.s & this.DM; this.t > 0 && this[this.t - 1] == a;) --this.t } function bnToString(a) { if (this.s < 0) return "-" + this.negate().toString(a); var b; if (16 == a) b = 4; else if (8 == a) b = 3; else if (2 == a) b = 1; else if (32 == a) b = 5; else { if (4 != a) return this.toRadix(a); b = 2 } var c, d = (1 << b) - 1, e = !1, f = "", g = this.t, h = this.DB - g * this.DB % b; if (g-- > 0) for (h < this.DB && (c = this[g] >> h) > 0 && (e = !0, f = int2char(c)); g >= 0;) b > h ? (c = (this[g] & (1 << h) - 1) << b - h, c |= this[--g] >> (h += this.DB - b)) : (c = this[g] >> (h -= b) & d, 0 >= h && (h += this.DB, --g)), c > 0 && (e = !0), e && (f += int2char(c)); return e ? f : "0" } function bnNegate() { var a = nbi(); return BigInteger.ZERO.subTo(this, a), a } function bnAbs() { return this.s < 0 ? this.negate() : this } function bnCompareTo(a) { var b = this.s - a.s; if (0 != b) return b; var c = this.t; if (b = c - a.t, 0 != b) return this.s < 0 ? -b : b; for (; --c >= 0;) if (0 != (b = this[c] - a[c])) return b; return 0 } function nbits(a) { var b, c = 1; return 0 != (b = a >>> 16) && (a = b, c += 16), 0 != (b = a >> 8) && (a = b, c += 8), 0 != (b = a >> 4) && (a = b, c += 4), 0 != (b = a >> 2) && (a = b, c += 2), 0 != (b = a >> 1) && (a = b, c += 1), c } function bnBitLength() { return this.t <= 0 ? 0 : this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ this.s & this.DM) } function bnpDLShiftTo(a, b) { var c; for (c = this.t - 1; c >= 0; --c) b[c + a] = this[c]; for (c = a - 1; c >= 0; --c) b[c] = 0; b.t = this.t + a, b.s = this.s } function bnpDRShiftTo(a, b) { for (var c = a; c < this.t; ++c) b[c - a] = this[c]; b.t = Math.max(this.t - a, 0), b.s = this.s } function bnpLShiftTo(a, b) { var c, d = a % this.DB, e = this.DB - d, f = (1 << e) - 1, g = Math.floor(a / this.DB), h = this.s << d & this.DM; for (c = this.t - 1; c >= 0; --c) b[c + g + 1] = this[c] >> e | h, h = (this[c] & f) << d; for (c = g - 1; c >= 0; --c) b[c] = 0; b[g] = h, b.t = this.t + g + 1, b.s = this.s, b.clamp() } function bnpRShiftTo(a, b) { b.s = this.s; var c = Math.floor(a / this.DB); if (c >= this.t) return void (b.t = 0); var d = a % this.DB, e = this.DB - d, f = (1 << d) - 1; b[0] = this[c] >> d; for (var g = c + 1; g < this.t; ++g) b[g - c - 1] |= (this[g] & f) << e, b[g - c] = this[g] >> d; d > 0 && (b[this.t - c - 1] |= (this.s & f) << e), b.t = this.t - c, b.clamp() } function bnpSubTo(a, b) { for (var c = 0, d = 0, e = Math.min(a.t, this.t); e > c;) d += this[c] - a[c], b[c++] = d & this.DM, d >>= this.DB; if (a.t < this.t) { for (d -= a.s; c < this.t;) d += this[c], b[c++] = d & this.DM, d >>= this.DB; d += this.s } else { for (d += this.s; c < a.t;) d -= a[c], b[c++] = d & this.DM, d >>= this.DB; d -= a.s } b.s = 0 > d ? -1 : 0, -1 > d ? b[c++] = this.DV + d : d > 0 && (b[c++] = d), b.t = c, b.clamp() } function bnpMultiplyTo(a, b) { var c = this.abs(), d = a.abs(), e = c.t; for (b.t = e + d.t; --e >= 0;) b[e] = 0; for (e = 0; e < d.t; ++e) b[e + c.t] = c.am(0, d[e], b, e, 0, c.t); b.s = 0, b.clamp(), this.s != a.s && BigInteger.ZERO.subTo(b, b) } function bnpSquareTo(a) { for (var b = this.abs(), c = a.t = 2 * b.t; --c >= 0;) a[c] = 0; for (c = 0; c < b.t - 1; ++c) { var d = b.am(c, b[c], a, 2 * c, 0, 1); (a[c + b.t] += b.am(c + 1, 2 * b[c], a, 2 * c + 1, d, b.t - c - 1)) >= b.DV && (a[c + b.t] -= b.DV, a[c + b.t + 1] = 1) } a.t > 0 && (a[a.t - 1] += b.am(c, b[c], a, 2 * c, 0, 1)), a.s = 0, a.clamp() } function bnpDivRemTo(a, b, c) { var d = a.abs(); if (!(d.t <= 0)) { var e = this.abs(); if (e.t < d.t) return null != b && b.fromInt(0), void (null != c && this.copyTo(c)); null == c && (c = nbi()); var f = nbi(), g = this.s, h = a.s, i = this.DB - nbits(d[d.t - 1]); i > 0 ? (d.lShiftTo(i, f), e.lShiftTo(i, c)) : (d.copyTo(f), e.copyTo(c)); var j = f.t, k = f[j - 1]; if (0 != k) { var l = k * (1 << this.F1) + (j > 1 ? f[j - 2] >> this.F2 : 0), m = this.FV / l, n = (1 << this.F1) / l, o = 1 << this.F2, p = c.t, q = p - j, r = null == b ? nbi() : b; for (f.dlShiftTo(q, r), c.compareTo(r) >= 0 && (c[c.t++] = 1, c.subTo(r, c)), BigInteger.ONE.dlShiftTo(j, r), r.subTo(f, f); f.t < j;) f[f.t++] = 0; for (; --q >= 0;) { var s = c[--p] == k ? this.DM : Math.floor(c[p] * m + (c[p - 1] + o) * n); if ((c[p] += f.am(0, s, c, q, 0, j)) < s) for (f.dlShiftTo(q, r), c.subTo(r, c); c[p] < --s;) c.subTo(r, c) } null != b && (c.drShiftTo(j, b), g != h && BigInteger.ZERO.subTo(b, b)), c.t = j, c.clamp(), i > 0 && c.rShiftTo(i, c), 0 > g && BigInteger.ZERO.subTo(c, c) } } } function bnMod(a) { var b = nbi(); return this.abs().divRemTo(a, null, b), this.s < 0 && b.compareTo(BigInteger.ZERO) > 0 && a.subTo(b, b), b } function Classic(a) { this.m = a } function cConvert(a) { return a.s < 0 || a.compareTo(this.m) >= 0 ? a.mod(this.m) : a } function cRevert(a) { return a } function cReduce(a) { a.divRemTo(this.m, null, a) } function cMulTo(a, b, c) { a.multiplyTo(b, c), this.reduce(c) } function cSqrTo(a, b) { a.squareTo(b), this.reduce(b) } function bnpInvDigit() { if (this.t < 1) return 0; var a = this[0]; if (0 == (1 & a)) return 0; var b = 3 & a; return b = b * (2 - (15 & a) * b) & 15, b = b * (2 - (255 & a) * b) & 255, b = b * (2 - ((65535 & a) * b & 65535)) & 65535, b = b * (2 - a * b % this.DV) % this.DV, b > 0 ? this.DV - b : -b } function Montgomery(a) { this.m = a, this.mp = a.invDigit(), this.mpl = 32767 & this.mp, this.mph = this.mp >> 15, this.um = (1 << a.DB - 15) - 1, this.mt2 = 2 * a.t } function montConvert(a) { var b = nbi(); return a.abs().dlShiftTo(this.m.t, b), b.divRemTo(this.m, null, b), a.s < 0 && b.compareTo(BigInteger.ZERO) > 0 && this.m.subTo(b, b), b } function montRevert(a) { var b = nbi(); return a.copyTo(b), this.reduce(b), b } function montReduce(a) { for (; a.t <= this.mt2;) a[a.t++] = 0; for (var b = 0; b < this.m.t; ++b) { var c = 32767 & a[b], d = c * this.mpl + ((c * this.mph + (a[b] >> 15) * this.mpl & this.um) << 15) & a.DM; for (c = b + this.m.t, a[c] += this.m.am(0, d, a, b, 0, this.m.t); a[c] >= a.DV;) a[c] -= a.DV, a[++c]++ } a.clamp(), a.drShiftTo(this.m.t, a), a.compareTo(this.m) >= 0 && a.subTo(this.m, a) } function montSqrTo(a, b) { a.squareTo(b), this.reduce(b) } function montMulTo(a, b, c) { a.multiplyTo(b, c), this.reduce(c) } function bnpIsEven() { return 0 == (this.t > 0 ? 1 & this[0] : this.s) } function bnpExp(a, b) { if (a > 4294967295 || 1 > a) return BigInteger.ONE; var c = nbi(), d = nbi(), e = b.convert(this), f = nbits(a) - 1; for (e.copyTo(c); --f >= 0;) if (b.sqrTo(c, d), (a & 1 << f) > 0) b.mulTo(d, e, c); else { var g = c; c = d, d = g } return b.revert(c) } function bnModPowInt(a, b) { var c; return c = 256 > a || b.isEven() ? new Classic(b) : new Montgomery(b), this.exp(a, c) } function bnClone() { var a = nbi(); return this.copyTo(a), a } function bnIntValue() { if (this.s < 0) { if (1 == this.t) return this[0] - this.DV; if (0 == this.t) return -1 } else { if (1 == this.t) return this[0]; if (0 == this.t) return 0 } return (this[1] & (1 << 32 - this.DB) - 1) << this.DB | this[0] } function bnByteValue() { return 0 == this.t ? this.s : this[0] << 24 >> 24 } function bnShortValue() { return 0 == this.t ? this.s : this[0] << 16 >> 16 } function bnpChunkSize(a) { return Math.floor(Math.LN2 * this.DB / Math.log(a)) } function bnSigNum() { return this.s < 0 ? -1 : this.t <= 0 || 1 == this.t && this[0] <= 0 ? 0 : 1 } function bnpToRadix(a) { if (null == a && (a = 10), 0 == this.signum() || 2 > a || a > 36) return "0"; var b = this.chunkSize(a), c = Math.pow(a, b), d = nbv(c), e = nbi(), f = nbi(), g = ""; for (this.divRemTo(d, e, f); e.signum() > 0;) g = (c + f.intValue()).toString(a).substr(1) + g, e.divRemTo(d, e, f); return f.intValue().toString(a) + g } function bnpFromRadix(a, b) { this.fromInt(0), null == b && (b = 10); for (var c = this.chunkSize(b), d = Math.pow(b, c), e = !1, f = 0, g = 0, h = 0; h < a.length; ++h) { var i = intAt(a, h); 0 > i ? "-" == a.charAt(h) && 0 == this.signum() && (e = !0) : (g = b * g + i, ++f >= c && (this.dMultiply(d), this.dAddOffset(g, 0), f = 0, g = 0)) } f > 0 && (this.dMultiply(Math.pow(b, f)), this.dAddOffset(g, 0)), e && BigInteger.ZERO.subTo(this, this) } function bnpFromNumber(a, b, c) { if ("number" == typeof b) if (2 > a) this.fromInt(1); else for (this.fromNumber(a, c), this.testBit(a - 1) || this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this), this.isEven() && this.dAddOffset(1, 0); !this.isProbablePrime(b);) this.dAddOffset(2, 0), this.bitLength() > a && this.subTo(BigInteger.ONE.shiftLeft(a - 1), this); else { var d = new Array, e = 7 & a; d.length = (a >> 3) + 1, b.nextBytes(d), e > 0 ? d[0] &= (1 << e) - 1 : d[0] = 0, this.fromString(d, 256) } } function bnToByteArray() { var a = this.t, b = new Array; b[0] = this.s; var c, d = this.DB - a * this.DB % 8, e = 0; if (a-- > 0) for (d < this.DB && (c = this[a] >> d) != (this.s & this.DM) >> d && (b[e++] = c | this.s << this.DB - d); a >= 0;) 8 > d ? (c = (this[a] & (1 << d) - 1) << 8 - d, c |= this[--a] >> (d += this.DB - 8)) : (c = this[a] >> (d -= 8) & 255, 0 >= d && (d += this.DB, --a)), 0 != (128 & c) && (c |= -256), 0 == e && (128 & this.s) != (128 & c) && ++e, (e > 0 || c != this.s) && (b[e++] = c); return b } function bnEquals(a) { return 0 == this.compareTo(a) } function bnMin(a) { return this.compareTo(a) < 0 ? this : a } function bnMax(a) { return this.compareTo(a) > 0 ? this : a } function bnpBitwiseTo(a, b, c) { var d, e, f = Math.min(a.t, this.t); for (d = 0; f > d; ++d) c[d] = b(this[d], a[d]); if (a.t < this.t) { for (e = a.s & this.DM, d = f; d < this.t; ++d) c[d] = b(this[d], e); c.t = this.t } else { for (e = this.s & this.DM, d = f; d < a.t; ++d) c[d] = b(e, a[d]); c.t = a.t } c.s = b(this.s, a.s), c.clamp() } function op_and(a, b) { return a & b } function bnAnd(a) { var b = nbi(); return this.bitwiseTo(a, op_and, b), b } function op_or(a, b) { return a | b } function bnOr(a) { var b = nbi(); return this.bitwiseTo(a, op_or, b), b } function op_xor(a, b) { return a ^ b } function bnXor(a) { var b = nbi(); return this.bitwiseTo(a, op_xor, b), b } function op_andnot(a, b) { return a & ~b } function bnAndNot(a) { var b = nbi(); return this.bitwiseTo(a, op_andnot, b), b } function bnNot() { for (var a = nbi(), b = 0; b < this.t; ++b) a[b] = this.DM & ~this[b]; return a.t = this.t, a.s = ~this.s, a } function bnShiftLeft(a) { var b = nbi(); return 0 > a ? this.rShiftTo(-a, b) : this.lShiftTo(a, b), b } function bnShiftRight(a) { var b = nbi(); return 0 > a ? this.lShiftTo(-a, b) : this.rShiftTo(a, b), b } function lbit(a) { if (0 == a) return -1; var b = 0; return 0 == (65535 & a) && (a >>= 16, b += 16), 0 == (255 & a) && (a >>= 8, b += 8), 0 == (15 & a) && (a >>= 4, b += 4), 0 == (3 & a) && (a >>= 2, b += 2), 0 == (1 & a) && ++b, b } function bnGetLowestSetBit() { for (var a = 0; a < this.t; ++a) if (0 != this[a]) return a * this.DB + lbit(this[a]); return this.s < 0 ? this.t * this.DB : -1 } function cbit(a) { for (var b = 0; 0 != a;) a &= a - 1, ++b; return b } function bnBitCount() { for (var a = 0, b = this.s & this.DM, c = 0; c < this.t; ++c) a += cbit(this[c] ^ b); return a } function bnTestBit(a) { var b = Math.floor(a / this.DB); return b >= this.t ? 0 != this.s : 0 != (this[b] & 1 << a % this.DB) } function bnpChangeBit(a, b) { var c = BigInteger.ONE.shiftLeft(a); return this.bitwiseTo(c, b, c), c } function bnSetBit(a) { return this.changeBit(a, op_or) } function bnClearBit(a) { return this.changeBit(a, op_andnot) } function bnFlipBit(a) { return this.changeBit(a, op_xor) } function bnpAddTo(a, b) { for (var c = 0, d = 0, e = Math.min(a.t, this.t); e > c;) d += this[c] + a[c], b[c++] = d & this.DM, d >>= this.DB; if (a.t < this.t) { for (d += a.s; c < this.t;) d += this[c], b[c++] = d & this.DM, d >>= this.DB; d += this.s } else { for (d += this.s; c < a.t;) d += a[c], b[c++] = d & this.DM, d >>= this.DB; d += a.s } b.s = 0 > d ? -1 : 0, d > 0 ? b[c++] = d : -1 > d && (b[c++] = this.DV + d), b.t = c, b.clamp() } function bnAdd(a) { var b = nbi(); return this.addTo(a, b), b } function bnSubtract(a) { var b = nbi(); return this.subTo(a, b), b } function bnMultiply(a) { var b = nbi(); return this.multiplyTo(a, b), b } function bnSquare() { var a = nbi(); return this.squareTo(a), a } function bnDivide(a) { var b = nbi(); return this.divRemTo(a, b, null), b } function bnRemainder(a) { var b = nbi(); return this.divRemTo(a, null, b), b } function bnDivideAndRemainder(a) { var b = nbi(), c = nbi(); return this.divRemTo(a, b, c), new Array(b, c) } function bnpDMultiply(a) { this[this.t] = this.am(0, a - 1, this, 0, 0, this.t), ++this.t, this.clamp() } function bnpDAddOffset(a, b) { if (0 != a) { for (; this.t <= b;) this[this.t++] = 0; for (this[b] += a; this[b] >= this.DV;) this[b] -= this.DV, ++b >= this.t && (this[this.t++] = 0), ++this[b] } } function NullExp() { } function nNop(a) { return a } function nMulTo(a, b, c) { a.multiplyTo(b, c) } function nSqrTo(a, b) { a.squareTo(b) } function bnPow(a) { return this.exp(a, new NullExp) } function bnpMultiplyLowerTo(a, b, c) { var d = Math.min(this.t + a.t, b); for (c.s = 0, c.t = d; d > 0;) c[--d] = 0; var e; for (e = c.t - this.t; e > d; ++d) c[d + this.t] = this.am(0, a[d], c, d, 0, this.t); for (e = Math.min(a.t, b); e > d; ++d) this.am(0, a[d], c, d, 0, b - d); c.clamp() } function bnpMultiplyUpperTo(a, b, c) { --b; var d = c.t = this.t + a.t - b; for (c.s = 0; --d >= 0;) c[d] = 0; for (d = Math.max(b - this.t, 0); d < a.t; ++d) c[this.t + d - b] = this.am(b - d, a[d], c, 0, 0, this.t + d - b); c.clamp(), c.drShiftTo(1, c) } function Barrett(a) { this.r2 = nbi(), this.q3 = nbi(), BigInteger.ONE.dlShiftTo(2 * a.t, this.r2), this.mu = this.r2.divide(a), this.m = a } function barrettConvert(a) { if (a.s < 0 || a.t > 2 * this.m.t) return a.mod(this.m); if (a.compareTo(this.m) < 0) return a; var b = nbi(); return a.copyTo(b), this.reduce(b), b } function barrettRevert(a) { return a } function barrettReduce(a) { for (a.drShiftTo(this.m.t - 1, this.r2), a.t > this.m.t + 1 && (a.t = this.m.t + 1, a.clamp()), this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3), this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); a.compareTo(this.r2) < 0;) a.dAddOffset(1, this.m.t + 1); for (a.subTo(this.r2, a); a.compareTo(this.m) >= 0;) a.subTo(this.m, a) } function barrettSqrTo(a, b) { a.squareTo(b), this.reduce(b) } function barrettMulTo(a, b, c) { a.multiplyTo(b, c), this.reduce(c) } function bnModPow(a, b) { var c, d, e = a.bitLength(), f = nbv(1); if (0 >= e) return f; c = 18 > e ? 1 : 48 > e ? 3 : 144 > e ? 4 : 768 > e ? 5 : 6, d = 8 > e ? new Classic(b) : b.isEven() ? new Barrett(b) : new Montgomery(b); var g = new Array, h = 3, i = c - 1, j = (1 << c) - 1; if (g[1] = d.convert(this), c > 1) { var k = nbi(); for (d.sqrTo(g[1], k); j >= h;) g[h] = nbi(), d.mulTo(k, g[h - 2], g[h]), h += 2 } var l, m, n = a.t - 1, o = !0, p = nbi(); for (e = nbits(a[n]) - 1; n >= 0;) { for (e >= i ? l = a[n] >> e - i & j : (l = (a[n] & (1 << e + 1) - 1) << i - e, n > 0 && (l |= a[n - 1] >> this.DB + e - i)), h = c; 0 == (1 & l);) l >>= 1, --h; if ((e -= h) < 0 && (e += this.DB, --n), o) g[l].copyTo(f), o = !1; else { for (; h > 1;) d.sqrTo(f, p), d.sqrTo(p, f), h -= 2; h > 0 ? d.sqrTo(f, p) : (m = f, f = p, p = m), d.mulTo(p, g[l], f) } for (; n >= 0 && 0 == (a[n] & 1 << e);) d.sqrTo(f, p), m = f, f = p, p = m, --e < 0 && (e = this.DB - 1, --n) } return d.revert(f) } function bnGCD(a) { var b = this.s < 0 ? this.negate() : this.clone(), c = a.s < 0 ? a.negate() : a.clone(); if (b.compareTo(c) < 0) { var d = b; b = c, c = d } var e = b.getLowestSetBit(), f = c.getLowestSetBit(); if (0 > f) return b; for (f > e && (f = e), f > 0 && (b.rShiftTo(f, b), c.rShiftTo(f, c)); b.signum() > 0;) (e = b.getLowestSetBit()) > 0 && b.rShiftTo(e, b), (e = c.getLowestSetBit()) > 0 && c.rShiftTo(e, c), b.compareTo(c) >= 0 ? (b.subTo(c, b), b.rShiftTo(1, b)) : (c.subTo(b, c), c.rShiftTo(1, c)); return f > 0 && c.lShiftTo(f, c), c } function bnpModInt(a) { if (0 >= a) return 0; var b = this.DV % a, c = this.s < 0 ? a - 1 : 0; if (this.t > 0) if (0 == b) c = this[0] % a; else for (var d = this.t - 1; d >= 0; --d) c = (b * c + this[d]) % a; return c } function bnModInverse(a) { var b = a.isEven(); if (this.isEven() && b || 0 == a.signum()) return BigInteger.ZERO; for (var c = a.clone(), d = this.clone(), e = nbv(1), f = nbv(0), g = nbv(0), h = nbv(1); 0 != c.signum();) { for (; c.isEven();) c.rShiftTo(1, c), b ? (e.isEven() && f.isEven() || (e.addTo(this, e), f.subTo(a, f)), e.rShiftTo(1, e)) : f.isEven() || f.subTo(a, f), f.rShiftTo(1, f); for (; d.isEven();) d.rShiftTo(1, d), b ? (g.isEven() && h.isEven() || (g.addTo(this, g), h.subTo(a, h)), g.rShiftTo(1, g)) : h.isEven() || h.subTo(a, h), h.rShiftTo(1, h); c.compareTo(d) >= 0 ? (c.subTo(d, c), b && e.subTo(g, e), f.subTo(h, f)) : (d.subTo(c, d), b && g.subTo(e, g), h.subTo(f, h)) } return 0 != d.compareTo(BigInteger.ONE) ? BigInteger.ZERO : h.compareTo(a) >= 0 ? h.subtract(a) : h.signum() < 0 ? (h.addTo(a, h), h.signum() < 0 ? h.add(a) : h) : h } function bnIsProbablePrime(a) { var b, c = this.abs(); if (1 == c.t && c[0] <= lowprimes[lowprimes.length - 1]) { for (b = 0; b < lowprimes.length; ++b) if (c[0] == lowprimes[b]) return !0; return !1 } if (c.isEven()) return !1; for (b = 1; b < lowprimes.length;) { for (var d = lowprimes[b], e = b + 1; e < lowprimes.length && lplim > d;) d *= lowprimes[e++]; for (d = c.modInt(d); e > b;) if (d % lowprimes[b++] == 0) return !1 } return c.millerRabin(a) } function bnpMillerRabin(a) { var b = this.subtract(BigInteger.ONE), c = b.getLowestSetBit(); if (0 >= c) return !1; var d = b.shiftRight(c); a = a + 1 >> 1, a > lowprimes.length && (a = lowprimes.length); for (var e = nbi(), f = 0; a > f; ++f) { e.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]); var g = e.modPow(d, this); if (0 != g.compareTo(BigInteger.ONE) && 0 != g.compareTo(b)) { for (var h = 1; h++ < c && 0 != g.compareTo(b);) if (g = g.modPowInt(2, this), 0 == g.compareTo(BigInteger.ONE)) return !1; if (0 != g.compareTo(b)) return !1 } } return !0 } function Arcfour() { this.i = 0, this.j = 0, this.S = new Array } function ARC4init(a) { var b, c, d; for (b = 0; 256 > b; ++b) this.S[b] = b; for (c = 0, b = 0; 256 > b; ++b) c = c + this.S[b] + a[b % a.length] & 255, d = this.S[b], this.S[b] = this.S[c], this.S[c] = d; this.i = 0, this.j = 0 } function ARC4next() { var a; return this.i = this.i + 1 & 255, this.j = this.j + this.S[this.i] & 255, a = this.S[this.i], this.S[this.i] = this.S[this.j], this.S[this.j] = a, this.S[a + this.S[this.i] & 255] } function prng_newstate() { return new Arcfour } function rng_get_byte() { if (null == rng_state) { for (rng_state = prng_newstate(); rng_psize > rng_pptr;) { var a = Math.floor(65536 * Math.random()); rng_pool[rng_pptr++] = 255 & a } for (rng_state.init(rng_pool), rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) rng_pool[rng_pptr] = 0; rng_pptr = 0 } return rng_state.next() } function rng_get_bytes(a) { var b; for (b = 0; b < a.length; ++b) a[b] = rng_get_byte() } function SecureRandom() { } function parseBigInt(a, b) { return new BigInteger(a, b) } function linebrk(a, b) { for (var c = "", d = 0; d + b < a.length;) c += a.substring(d, d + b) + "\n", d += b; return c + a.substring(d, a.length) } function byte2Hex(a) { return 16 > a ? "0" + a.toString(16) : a.toString(16) } // 100 111 110 103 102 101 110 103 function pkcs1pad2(a, b) { if (b < a.length + 11) return console.error("Message too long for RSA"), null; for (var c = new Array, d = a.length - 1; d >= 0 && b > 0;) { var e = a.charCodeAt(d--); 128 > e ? c[--b] = e : e > 127 && 2048 > e ? (c[--b] = 63 & e | 128, c[--b] = e >> 6 | 192) : (c[--b] = 63 & e | 128, c[--b] = e >> 6 & 63 | 128, c[--b] = e >> 12 | 224) } c[--b] = 0; for (var f = new SecureRandom, g = new Array; b > 2;) { for (g[0] = 0; 0 == g[0];) f.nextBytes(g); c[--b] = g[0] } return c[--b] = 2, c[--b] = 0, new BigInteger(c) } function nopad2(a, b) { for (var c = new Array, d = a.length - 1; d >= 0 && b > 0;) { var e = a.charCodeAt(d--); c[--b] = e; } c[--b] = 0; while (--b > 0){ c[--b] = 0 } return new BigInteger(c) } function RSAKey() { this.n = null, this.e = 0, this.d = null, this.p = null, this.q = null, this.dmp1 = null, this.dmq1 = null, this.coeff = null } function RSASetPublic(a, b) { null != a && null != b && a.length > 0 && b.length > 0 ? (this.n = parseBigInt(a, 16), this.e = parseInt(b, 16)) : console.error("Invalid RSA public key") } function RSADoPublic(a) { return a.modPowInt(this.e, this.n) } function RSAEncrypt(a) { // var b = pkcs1pad2(a, this.n.bitLength() + 7 >> 3); var b = nopad2(a, this.n.bitLength() + 7 >> 3); if (null == b) return null; var c = this.doPublic(b); if (null == c) return null; var d = c.toString(16); return 0 == (1 & d.length) ? d : "0" + d } function pkcs1unpad2(a, b) { for (var c = a.toByteArray(), d = 0; d < c.length && 0 == c[d];) ++d; if (c.length - d != b - 1 || 2 != c[d]) return null; for (++d; 0 != c[d];) if (++d >= c.length) return null; for (var e = ""; ++d < c.length;) { var f = 255 & c[d]; 128 > f ? e += String.fromCharCode(f) : f > 191 && 224 > f ? (e += String.fromCharCode((31 & f) << 6 | 63 & c[d + 1]), ++d) : (e += String.fromCharCode((15 & f) << 12 | (63 & c[d + 1]) << 6 | 63 & c[d + 2]), d += 2) } return e } function RSASetPrivate(a, b, c) { null != a && null != b && a.length > 0 && b.length > 0 ? (this.n = parseBigInt(a, 16), this.e = parseInt(b, 16), this.d = parseBigInt(c, 16)) : console.error("Invalid RSA private key") } function RSASetPrivateEx(a, b, c, d, e, f, g, h) { null != a && null != b && a.length > 0 && b.length > 0 ? (this.n = parseBigInt(a, 16), this.e = parseInt(b, 16), this.d = parseBigInt(c, 16), this.p = parseBigInt(d, 16), this.q = parseBigInt(e, 16), this.dmp1 = parseBigInt(f, 16), this.dmq1 = parseBigInt(g, 16), this.coeff = parseBigInt(h, 16)) : console.error("Invalid RSA private key") } function RSAGenerate(a, b) { var c = new SecureRandom, d = a >> 1; this.e = parseInt(b, 16); for (var e = new BigInteger(b, 16); ;) { for (; this.p = new BigInteger(a - d, 1, c), 0 != this.p.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE) || !this.p.isProbablePrime(10);) ; for (; this.q = new BigInteger(d, 1, c), 0 != this.q.subtract(BigInteger.ONE).gcd(e).compareTo(BigInteger.ONE) || !this.q.isProbablePrime(10);) ; if (this.p.compareTo(this.q) <= 0) { var f = this.p; this.p = this.q, this.q = f } var g = this.p.subtract(BigInteger.ONE), h = this.q.subtract(BigInteger.ONE), i = g.multiply(h); if (0 == i.gcd(e).compareTo(BigInteger.ONE)) { this.n = this.p.multiply(this.q), this.d = e.modInverse(i), this.dmp1 = this.d.mod(g), this.dmq1 = this.d.mod(h), this.coeff = this.q.modInverse(this.p); break } } } function RSADoPrivate(a) { if (null == this.p || null == this.q) return a.modPow(this.d, this.n); for (var b = a.mod(this.p).modPow(this.dmp1, this.p), c = a.mod(this.q).modPow(this.dmq1, this.q); b.compareTo(c) < 0;) b = b.add(this.p); return b.subtract(c).multiply(this.coeff).mod(this.p).multiply(this.q).add(c) } function RSADecrypt(a) { var b = parseBigInt(a, 16), c = this.doPrivate(b); return null == c ? null : pkcs1unpad2(c, this.n.bitLength() + 7 >> 3) } function hex2b64(a) { var b, c, d = ""; for (b = 0; b + 3 <= a.length; b += 3) c = parseInt(a.substring(b, b + 3), 16), d += b64map.charAt(c >> 6) + b64map.charAt(63 & c); for (b + 1 == a.length ? (c = parseInt(a.substring(b, b + 1), 16), d += b64map.charAt(c << 2)) : b + 2 == a.length && (c = parseInt(a.substring(b, b + 2), 16), d += b64map.charAt(c >> 2) + b64map.charAt((3 & c) << 4)); (3 & d.length) > 0;) d += b64pad; return d } function b64tohex(a) { var b, c, d = "", e = 0; for (b = 0; b < a.length && a.charAt(b) != b64pad; ++b) v = b64map.indexOf(a.charAt(b)), v < 0 || (0 == e ? (d += int2char(v >> 2), c = 3 & v, e = 1) : 1 == e ? (d += int2char(c << 2 | v >> 4), c = 15 & v, e = 2) : 2 == e ? (d += int2char(c), d += int2char(v >> 2), c = 3 & v, e = 3) : (d += int2char(c << 2 | v >> 4), d += int2char(15 & v), e = 0)); return 1 == e && (d += int2char(c << 2)), d } function b64toBA(a) { var b, c = b64tohex(a), d = new Array; for (b = 0; 2 * b < c.length; ++b) d[b] = parseInt(c.substring(2 * b, 2 * b + 2), 16); return d } var dbits, canary = 0xdeadbeefcafe, j_lm = 15715070 == (16777215 & canary); j_lm && "Microsoft Internet Explorer" == navigator.appName ? (BigInteger.prototype.am = am2, dbits = 30) : j_lm && "Netscape" != navigator.appName ? (BigInteger.prototype.am = am1, dbits = 26) : (BigInteger.prototype.am = am3, dbits = 28), BigInteger.prototype.DB = dbits, BigInteger.prototype.DM = (1 << dbits) - 1, BigInteger.prototype.DV = 1 << dbits; var BI_FP = 52; BigInteger.prototype.FV = Math.pow(2, BI_FP), BigInteger.prototype.F1 = BI_FP - dbits, BigInteger.prototype.F2 = 2 * dbits - BI_FP; var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz", BI_RC = new Array, rr, vv; for (rr = "0".charCodeAt(0), vv = 0; 9 >= vv; ++vv) BI_RC[rr++] = vv; for (rr = "a".charCodeAt(0), vv = 10; 36 > vv; ++vv) BI_RC[rr++] = vv; for (rr = "A".charCodeAt(0), vv = 10; 36 > vv; ++vv) BI_RC[rr++] = vv; Classic.prototype.convert = cConvert, Classic.prototype.revert = cRevert, Classic.prototype.reduce = cReduce, Classic.prototype.mulTo = cMulTo, Classic.prototype.sqrTo = cSqrTo, Montgomery.prototype.convert = montConvert, Montgomery.prototype.revert = montRevert, Montgomery.prototype.reduce = montReduce, Montgomery.prototype.mulTo = montMulTo, Montgomery.prototype.sqrTo = montSqrTo, BigInteger.prototype.copyTo = bnpCopyTo, BigInteger.prototype.fromInt = bnpFromInt, BigInteger.prototype.fromString = bnpFromString, BigInteger.prototype.clamp = bnpClamp, BigInteger.prototype.dlShiftTo = bnpDLShiftTo, BigInteger.prototype.drShiftTo = bnpDRShiftTo, BigInteger.prototype.lShiftTo = bnpLShiftTo, BigInteger.prototype.rShiftTo = bnpRShiftTo, BigInteger.prototype.subTo = bnpSubTo, BigInteger.prototype.multiplyTo = bnpMultiplyTo, BigInteger.prototype.squareTo = bnpSquareTo, BigInteger.prototype.divRemTo = bnpDivRemTo, BigInteger.prototype.invDigit = bnpInvDigit, BigInteger.prototype.isEven = bnpIsEven, BigInteger.prototype.exp = bnpExp, BigInteger.prototype.toString = bnToString, BigInteger.prototype.negate = bnNegate, BigInteger.prototype.abs = bnAbs, BigInteger.prototype.compareTo = bnCompareTo, BigInteger.prototype.bitLength = bnBitLength, BigInteger.prototype.mod = bnMod, BigInteger.prototype.modPowInt = bnModPowInt, BigInteger.ZERO = nbv(0), BigInteger.ONE = nbv(1), NullExp.prototype.convert = nNop, NullExp.prototype.revert = nNop, NullExp.prototype.mulTo = nMulTo, NullExp.prototype.sqrTo = nSqrTo, Barrett.prototype.convert = barrettConvert, Barrett.prototype.revert = barrettRevert, Barrett.prototype.reduce = barrettReduce, Barrett.prototype.mulTo = barrettMulTo, Barrett.prototype.sqrTo = barrettSqrTo; var lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997], lplim = (1 << 26) / lowprimes[lowprimes.length - 1]; BigInteger.prototype.chunkSize = bnpChunkSize, BigInteger.prototype.toRadix = bnpToRadix, BigInteger.prototype.fromRadix = bnpFromRadix, BigInteger.prototype.fromNumber = bnpFromNumber, BigInteger.prototype.bitwiseTo = bnpBitwiseTo, BigInteger.prototype.changeBit = bnpChangeBit, BigInteger.prototype.addTo = bnpAddTo, BigInteger.prototype.dMultiply = bnpDMultiply, BigInteger.prototype.dAddOffset = bnpDAddOffset, BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo, BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo, BigInteger.prototype.modInt = bnpModInt, BigInteger.prototype.millerRabin = bnpMillerRabin, BigInteger.prototype.clone = bnClone, BigInteger.prototype.intValue = bnIntValue, BigInteger.prototype.byteValue = bnByteValue, BigInteger.prototype.shortValue = bnShortValue, BigInteger.prototype.signum = bnSigNum, BigInteger.prototype.toByteArray = bnToByteArray, BigInteger.prototype.equals = bnEquals, BigInteger.prototype.min = bnMin, BigInteger.prototype.max = bnMax, BigInteger.prototype.and = bnAnd, BigInteger.prototype.or = bnOr, BigInteger.prototype.xor = bnXor, BigInteger.prototype.andNot = bnAndNot, BigInteger.prototype.not = bnNot, BigInteger.prototype.shiftLeft = bnShiftLeft, BigInteger.prototype.shiftRight = bnShiftRight, BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit, BigInteger.prototype.bitCount = bnBitCount, BigInteger.prototype.testBit = bnTestBit, BigInteger.prototype.setBit = bnSetBit, BigInteger.prototype.clearBit = bnClearBit, BigInteger.prototype.flipBit = bnFlipBit, BigInteger.prototype.add = bnAdd, BigInteger.prototype.subtract = bnSubtract, BigInteger.prototype.multiply = bnMultiply, BigInteger.prototype.divide = bnDivide, BigInteger.prototype.remainder = bnRemainder, BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder, BigInteger.prototype.modPow = bnModPow, BigInteger.prototype.modInverse = bnModInverse, BigInteger.prototype.pow = bnPow, BigInteger.prototype.gcd = bnGCD, BigInteger.prototype.isProbablePrime = bnIsProbablePrime, BigInteger.prototype.square = bnSquare, Arcfour.prototype.init = ARC4init, Arcfour.prototype.next = ARC4next; var rng_psize = 256, rng_state, rng_pool, rng_pptr; if (null == rng_pool) { rng_pool = new Array, rng_pptr = 0; var t; if (window.crypto && window.crypto.getRandomValues) { var z = new Uint32Array(256); for (window.crypto.getRandomValues(z), t = 0; t < z.length; ++t) rng_pool[rng_pptr++] = 255 & z[t] } var onMouseMoveListener = function (a) { if (this.count = this.count || 0, this.count >= 256 || rng_pptr >= rng_psize) return void (window.removeEventListener ? window.removeEventListener("mousemove", onMouseMoveListener) : window.detachEvent && window.detachEvent("onmousemove", onMouseMoveListener)); this.count += 1; var b = a.x + a.y; rng_pool[rng_pptr++] = 255 & b }; window.addEventListener ? window.addEventListener("mousemove", onMouseMoveListener) : window.attachEvent && window.attachEvent("onmousemove", onMouseMoveListener) } SecureRandom.prototype.nextBytes = rng_get_bytes, RSAKey.prototype.doPublic = RSADoPublic, RSAKey.prototype.setPublic = RSASetPublic, RSAKey.prototype.encrypt = RSAEncrypt, RSAKey.prototype.doPrivate = RSADoPrivate, RSAKey.prototype.setPrivate = RSASetPrivate, RSAKey.prototype.setPrivateEx = RSASetPrivateEx, RSAKey.prototype.generate = RSAGenerate, RSAKey.prototype.decrypt = RSADecrypt, function () { var a = function (a, b, c) { var d = new SecureRandom, e = a >> 1; this.e = parseInt(b, 16); var f = new BigInteger(b, 16), g = this, h = function () { var b = function () { if (g.p.compareTo(g.q) <= 0) { var a = g.p; g.p = g.q, g.q = a } var b = g.p.subtract(BigInteger.ONE), d = g.q.subtract(BigInteger.ONE), e = b.multiply(d); 0 == e.gcd(f).compareTo(BigInteger.ONE) ? (g.n = g.p.multiply(g.q), g.d = f.modInverse(e), g.dmp1 = g.d.mod(b), g.dmq1 = g.d.mod(d), g.coeff = g.q.modInverse(g.p), setTimeout(function () { c() }, 0)) : setTimeout(h, 0) }, i = function () { g.q = nbi(), g.q.fromNumberAsync(e, 1, d, function () { g.q.subtract(BigInteger.ONE).gcda(f, function (a) { 0 == a.compareTo(BigInteger.ONE) && g.q.isProbablePrime(10) ? setTimeout(b, 0) : setTimeout(i, 0) }) }) }, j = function () { g.p = nbi(), g.p.fromNumberAsync(a - e, 1, d, function () { g.p.subtract(BigInteger.ONE).gcda(f, function (a) { 0 == a.compareTo(BigInteger.ONE) && g.p.isProbablePrime(10) ? setTimeout(i, 0) : setTimeout(j, 0) }) }) }; setTimeout(j, 0) }; setTimeout(h, 0) }; RSAKey.prototype.generateAsync = a; var b = function (a, b) { var c = this.s < 0 ? this.negate() : this.clone(), d = a.s < 0 ? a.negate() : a.clone(); if (c.compareTo(d) < 0) { var e = c; c = d, d = e } var f = c.getLowestSetBit(), g = d.getLowestSetBit(); if (0 > g) return void b(c); g > f && (g = f), g > 0 && (c.rShiftTo(g, c), d.rShiftTo(g, d)); var h = function () { (f = c.getLowestSetBit()) > 0 && c.rShiftTo(f, c), (f = d.getLowestSetBit()) > 0 && d.rShiftTo(f, d), c.compareTo(d) >= 0 ? (c.subTo(d, c), c.rShiftTo(1, c)) : (d.subTo(c, d), d.rShiftTo(1, d)), c.signum() > 0 ? setTimeout(h, 0) : (g > 0 && d.lShiftTo(g, d), setTimeout(function () { b(d) }, 0)) }; setTimeout(h, 10) }; BigInteger.prototype.gcda = b; var c = function (a, b, c, d) { if ("number" == typeof b) if (2 > a) this.fromInt(1); else { this.fromNumber(a, c), this.testBit(a - 1) || this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this), this.isEven() && this.dAddOffset(1, 0); var e = this, f = function () { e.dAddOffset(2, 0), e.bitLength() > a && e.subTo(BigInteger.ONE.shiftLeft(a - 1), e), e.isProbablePrime(b) ? setTimeout(function () { d() }, 0) : setTimeout(f, 0) }; setTimeout(f, 0) } else { var g = new Array, h = 7 & a; g.length = (a >> 3) + 1, b.nextBytes(g), h > 0 ? g[0] &= (1 << h) - 1 : g[0] = 0, this.fromString(g, 256) } }; BigInteger.prototype.fromNumberAsync = c }(); var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", b64pad = "=", JSX = JSX || {}; JSX.env = JSX.env || {}; var L = JSX, OP = Object.prototype, FUNCTION_TOSTRING = "[object Function]", ADD = ["toString", "valueOf"]; JSX.env.parseUA = function (a) { var b, c = function (a) { var b = 0; return parseFloat(a.replace(/\./g, function () { return 1 == b++ ? "" : "." })) }, d = navigator, e = { ie: 0, opera: 0, gecko: 0, webkit: 0, chrome: 0, mobile: null, air: 0, ipad: 0, iphone: 0, ipod: 0, ios: null, android: 0, webos: 0, caja: d && d.cajaVersion, secure: !1, os: null }, f = a || navigator && navigator.userAgent, g = window && window.location, h = g && g.href; return e.secure = h && 0 === h.toLowerCase().indexOf("https"), f && (/windows|win32/i.test(f) ? e.os = "windows" : /macintosh/i.test(f) ? e.os = "macintosh" : /rhino/i.test(f) && (e.os = "rhino"), /KHTML/.test(f) && (e.webkit = 1), b = f.match(/AppleWebKit\/([^\s]*)/), b && b[1] && (e.webkit = c(b[1]), / Mobile\//.test(f) ? (e.mobile = "Apple", b = f.match(/OS ([^\s]*)/), b && b[1] && (b = c(b[1].replace("_", "."))), e.ios = b, e.ipad = e.ipod = e.iphone = 0, b = f.match(/iPad|iPod|iPhone/), b && b[0] && (e[b[0].toLowerCase()] = e.ios)) : (b = f.match(/NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/), b && (e.mobile = b[0]), /webOS/.test(f) && (e.mobile = "WebOS", b = f.match(/webOS\/([^\s]*);/), b && b[1] && (e.webos = c(b[1]))), / Android/.test(f) && (e.mobile = "Android", b = f.match(/Android ([^\s]*);/), b && b[1] && (e.android = c(b[1])))), b = f.match(/Chrome\/([^\s]*)/), b && b[1] ? e.chrome = c(b[1]) : (b = f.match(/AdobeAIR\/([^\s]*)/), b && (e.air = b[0]))), e.webkit || (b = f.match(/Opera[\s\/]([^\s]*)/), b && b[1] ? (e.opera = c(b[1]), b = f.match(/Version\/([^\s]*)/), b && b[1] && (e.opera = c(b[1])), b = f.match(/Opera Mini[^;]*/), b && (e.mobile = b[0])) : (b = f.match(/MSIE\s([^;]*)/), b && b[1] ? e.ie = c(b[1]) : (b = f.match(/Gecko\/([^\s]*)/), b && (e.gecko = 1, b = f.match(/rv:([^\s\)]*)/), b && b[1] && (e.gecko = c(b[1]))))))), e }, JSX.env.ua = JSX.env.parseUA(), JSX.isFunction = function (a) { return "function" == typeof a || OP.toString.apply(a) === FUNCTION_TOSTRING }, JSX._IEEnumFix = JSX.env.ua.ie ? function (a, b) { var c, d, e; for (c = 0; c < ADD.length; c += 1) d = ADD[c], e = b[d], L.isFunction(e) && e != OP[d] && (a[d] = e) } : function () { }, JSX.extend = function (a, b, c) { if (!b || !a) throw new Error("extend failed, please check that all dependencies are included."); var d, e = function () { }; if (e.prototype = b.prototype, a.prototype = new e, a.prototype.constructor = a, a.superclass = b.prototype, b.prototype.constructor == OP.constructor && (b.prototype.constructor = b), c) { for (d in c) L.hasOwnProperty(c, d) && (a.prototype[d] = c[d]); L._IEEnumFix(a.prototype, c) } }, "undefined" != typeof KJUR && KJUR || (KJUR = {}), "undefined" != typeof KJUR.asn1 && KJUR.asn1 || (KJUR.asn1 = {}), KJUR.asn1.ASN1Util = new function () { this.integerToByteHex = function (a) { var b = a.toString(16); return b.length % 2 == 1 && (b = "0" + b), b }, this.bigIntToMinTwosComplementsHex = function (a) { var b = a.toString(16); if ("-" != b.substr(0, 1)) b.length % 2 == 1 ? b = "0" + b : b.match(/^[0-7]/) || (b = "00" + b); else { var c = b.substr(1), d = c.length; d % 2 == 1 ? d += 1 : b.match(/^[0-7]/) || (d += 2); for (var e = "", f = 0; d > f; f++) e += "f"; var g = new BigInteger(e, 16), h = g.xor(a).add(BigInteger.ONE); b = h.toString(16).replace(/^-/, "") } return b }, this.getPEMStringFromHex = function (a, b) { var c = CryptoJS.enc.Hex.parse(a), d = CryptoJS.enc.Base64.stringify(c), e = d.replace(/(.{64})/g, "$1\r\n"); return e = e.replace(/\r\n$/, ""), "-----BEGIN " + b + "-----\r\n" + e + "\r\n-----END " + b + "-----\r\n" } }, KJUR.asn1.ASN1Object = function () { var a = ""; this.getLengthHexFromValue = function () { if ("undefined" == typeof this.hV || null == this.hV) throw"this.hV is null or undefined."; if (this.hV.length % 2 == 1) throw"value hex must be even length: n=" + a.length + ",v=" + this.hV; var b = this.hV.length / 2, c = b.toString(16); if (c.length % 2 == 1 && (c = "0" + c), 128 > b) return c; var d = c.length / 2; if (d > 15) throw"ASN.1 length too long to represent by 8x: n = " + b.toString(16); var e = 128 + d; return e.toString(16) + c }, this.getEncodedHex = function () { return (null == this.hTLV || this.isModified) && (this.hV = this.getFreshValueHex(), this.hL = this.getLengthHexFromValue(), this.hTLV = this.hT + this.hL + this.hV, this.isModified = !1), this.hTLV }, this.getValueHex = function () { return this.getEncodedHex(), this.hV }, this.getFreshValueHex = function () { return "" } }, KJUR.asn1.DERAbstractString = function (a) { KJUR.asn1.DERAbstractString.superclass.constructor.call(this); this.getString = function () { return this.s }, this.setString = function (a) { this.hTLV = null, this.isModified = !0, this.s = a, this.hV = stohex(this.s) }, this.setStringHex = function (a) { this.hTLV = null, this.isModified = !0, this.s = null, this.hV = a }, this.getFreshValueHex = function () { return this.hV }, "undefined" != typeof a && ("undefined" != typeof a.str ? this.setString(a.str) : "undefined" != typeof a.hex && this.setStringHex(a.hex)) }, JSX.extend(KJUR.asn1.DERAbstractString, KJUR.asn1.ASN1Object), KJUR.asn1.DERAbstractTime = function () { KJUR.asn1.DERAbstractTime.superclass.constructor.call(this); this.localDateToUTC = function (a) { utc = a.getTime() + 6e4 * a.getTimezoneOffset(); var b = new Date(utc); return b }, this.formatDate = function (a, b) { var c = this.zeroPadding, d = this.localDateToUTC(a), e = String(d.getFullYear()); "utc" == b && (e = e.substr(2, 2)); var f = c(String(d.getMonth() + 1), 2), g = c(String(d.getDate()), 2), h = c(String(d.getHours()), 2), i = c(String(d.getMinutes()), 2), j = c(String(d.getSeconds()), 2); return e + f + g + h + i + j + "Z" }, this.zeroPadding = function (a, b) { return a.length >= b ? a : new Array(b - a.length + 1).join("0") + a }, this.getString = function () { return this.s }, this.setString = function (a) { this.hTLV = null, this.isModified = !0, this.s = a, this.hV = stohex(this.s) }, this.setByDateValue = function (a, b, c, d, e, f) { var g = new Date(Date.UTC(a, b - 1, c, d, e, f, 0)); this.setByDate(g) }, this.getFreshValueHex = function () { return this.hV } }, JSX.extend(KJUR.asn1.DERAbstractTime, KJUR.asn1.ASN1Object), KJUR.asn1.DERAbstractStructured = function (a) { KJUR.asn1.DERAbstractString.superclass.constructor.call(this); this.setByASN1ObjectArray = function (a) { this.hTLV = null, this.isModified = !0, this.asn1Array = a }, this.appendASN1Object = function (a) { this.hTLV = null, this.isModified = !0, this.asn1Array.push(a) }, this.asn1Array = new Array, "undefined" != typeof a && "undefined" != typeof a.array && (this.asn1Array = a.array) }, JSX.extend(KJUR.asn1.DERAbstractStructured, KJUR.asn1.ASN1Object), KJUR.asn1.DERBoolean = function () { KJUR.asn1.DERBoolean.superclass.constructor.call(this), this.hT = "01", this.hTLV = "0101ff" }, JSX.extend(KJUR.asn1.DERBoolean, KJUR.asn1.ASN1Object), KJUR.asn1.DERInteger = function (a) { KJUR.asn1.DERInteger.superclass.constructor.call(this), this.hT = "02", this.setByBigInteger = function (a) { this.hTLV = null, this.isModified = !0, this.hV = KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(a) }, this.setByInteger = function (a) { var b = new BigInteger(String(a), 10); this.setByBigInteger(b) }, this.setValueHex = function (a) { this.hV = a }, this.getFreshValueHex = function () { return this.hV }, "undefined" != typeof a && ("undefined" != typeof a.bigint ? this.setByBigInteger(a.bigint) : "undefined" != typeof a["int"] ? this.setByInteger(a["int"]) : "undefined" != typeof a.hex && this.setValueHex(a.hex)) }, JSX.extend(KJUR.asn1.DERInteger, KJUR.asn1.ASN1Object), KJUR.asn1.DERBitString = function (a) { KJUR.asn1.DERBitString.superclass.constructor.call(this), this.hT = "03", this.setHexValueIncludingUnusedBits = function (a) { this.hTLV = null, this.isModified = !0, this.hV = a }, this.setUnusedBitsAndHexValue = function (a, b) { if (0 > a || a > 7) throw"unused bits shall be from 0 to 7: u = " + a; var c = "0" + a; this.hTLV = null, this.isModified = !0, this.hV = c + b }, this.setByBinaryString = function (a) { a = a.replace(/0+$/, ""); var b = 8 - a.length % 8; 8 == b && (b = 0); for (var c = 0; b >= c; c++) a += "0"; for (var d = "", c = 0; c < a.length - 1; c += 8) { var e = a.substr(c, 8), f = parseInt(e, 2).toString(16); 1 == f.length && (f = "0" + f), d += f } this.hTLV = null, this.isModified = !0, this.hV = "0" + b + d }, this.setByBooleanArray = function (a) { for (var b = "", c = 0; c < a.length; c++) b += 1 == a[c] ? "1" : "0"; this.setByBinaryString(b) }, this.newFalseArray = function (a) { for (var b = new Array(a), c = 0; a > c; c++) b[c] = !1; return b }, this.getFreshValueHex = function () { return this.hV }, "undefined" != typeof a && ("undefined" != typeof a.hex ? this.setHexValueIncludingUnusedBits(a.hex) : "undefined" != typeof a.bin ? this.setByBinaryString(a.bin) : "undefined" != typeof a.array && this.setByBooleanArray(a.array)) }, JSX.extend(KJUR.asn1.DERBitString, KJUR.asn1.ASN1Object), KJUR.asn1.DEROctetString = function (a) { KJUR.asn1.DEROctetString.superclass.constructor.call(this, a), this.hT = "04" }, JSX.extend(KJUR.asn1.DEROctetString, KJUR.asn1.DERAbstractString), KJUR.asn1.DERNull = function () { KJUR.asn1.DERNull.superclass.constructor.call(this), this.hT = "05", this.hTLV = "0500" }, JSX.extend(KJUR.asn1.DERNull, KJUR.asn1.ASN1Object), KJUR.asn1.DERObjectIdentifier = function (a) { var b = function (a) { var b = a.toString(16); return 1 == b.length && (b = "0" + b), b }, c = function (a) { var c = "", d = new BigInteger(a, 10), e = d.toString(2), f = 7 - e.length % 7; 7 == f && (f = 0); for (var g = "", h = 0; f > h; h++) g += "0"; e = g + e; for (var h = 0; h < e.length - 1; h += 7) { var i = e.substr(h, 7); h != e.length - 7 && (i = "1" + i), c += b(parseInt(i, 2)) } return c }; KJUR.asn1.DERObjectIdentifier.superclass.constructor.call(this), this.hT = "06", this.setValueHex = function (a) { this.hTLV = null, this.isModified = !0, this.s = null, this.hV = a }, this.setValueOidString = function (a) { if (!a.match(/^[0-9.]+$/)) throw"malformed oid string: " + a; var d = "", e = a.split("."), f = 40 * parseInt(e[0]) + parseInt(e[1]); d += b(f), e.splice(0, 2); for (var g = 0; g < e.length; g++) d += c(e[g]); this.hTLV = null, this.isModified = !0, this.s = null, this.hV = d }, this.setValueName = function (a) { if ("undefined" == typeof KJUR.asn1.x509.OID.name2oidList[a]) throw"DERObjectIdentifier oidName undefined: " + a; var b = KJUR.asn1.x509.OID.name2oidList[a]; this.setValueOidString(b) }, this.getFreshValueHex = function () { return this.hV }, "undefined" != typeof a && ("undefined" != typeof a.oid ? this.setValueOidString(a.oid) : "undefined" != typeof a.hex ? this.setValueHex(a.hex) : "undefined" != typeof a.name && this.setValueName(a.name)) }, JSX.extend(KJUR.asn1.DERObjectIdentifier, KJUR.asn1.ASN1Object), KJUR.asn1.DERUTF8String = function (a) { KJUR.asn1.DERUTF8String.superclass.constructor.call(this, a), this.hT = "0c" }, JSX.extend(KJUR.asn1.DERUTF8String, KJUR.asn1.DERAbstractString), KJUR.asn1.DERNumericString = function (a) { KJUR.asn1.DERNumericString.superclass.constructor.call(this, a), this.hT = "12" }, JSX.extend(KJUR.asn1.DERNumericString, KJUR.asn1.DERAbstractString), KJUR.asn1.DERPrintableString = function (a) { KJUR.asn1.DERPrintableString.superclass.constructor.call(this, a), this.hT = "13" }, JSX.extend(KJUR.asn1.DERPrintableString, KJUR.asn1.DERAbstractString), KJUR.asn1.DERTeletexString = function (a) { KJUR.asn1.DERTeletexString.superclass.constructor.call(this, a), this.hT = "14" }, JSX.extend(KJUR.asn1.DERTeletexString, KJUR.asn1.DERAbstractString), KJUR.asn1.DERIA5String = function (a) { KJUR.asn1.DERIA5String.superclass.constructor.call(this, a), this.hT = "16" }, JSX.extend(KJUR.asn1.DERIA5String, KJUR.asn1.DERAbstractString), KJUR.asn1.DERUTCTime = function (a) { KJUR.asn1.DERUTCTime.superclass.constructor.call(this, a), this.hT = "17", this.setByDate = function (a) { this.hTLV = null, this.isModified = !0, this.date = a, this.s = this.formatDate(this.date, "utc"), this.hV = stohex(this.s) }, "undefined" != typeof a && ("undefined" != typeof a.str ? this.setString(a.str) : "undefined" != typeof a.hex ? this.setStringHex(a.hex) : "undefined" != typeof a.date && this.setByDate(a.date)) }, JSX.extend(KJUR.asn1.DERUTCTime, KJUR.asn1.DERAbstractTime), KJUR.asn1.DERGeneralizedTime = function (a) { KJUR.asn1.DERGeneralizedTime.superclass.constructor.call(this, a), this.hT = "18", this.setByDate = function (a) { this.hTLV = null, this.isModified = !0, this.date = a, this.s = this.formatDate(this.date, "gen"), this.hV = stohex(this.s) }, "undefined" != typeof a && ("undefined" != typeof a.str ? this.setString(a.str) : "undefined" != typeof a.hex ? this.setStringHex(a.hex) : "undefined" != typeof a.date && this.setByDate(a.date)) }, JSX.extend(KJUR.asn1.DERGeneralizedTime, KJUR.asn1.DERAbstractTime), KJUR.asn1.DERSequence = function (a) { KJUR.asn1.DERSequence.superclass.constructor.call(this, a), this.hT = "30", this.getFreshValueHex = function () { for (var a = "", b = 0; b < this.asn1Array.length; b++) { var c = this.asn1Array[b]; a += c.getEncodedHex() } return this.hV = a, this.hV } }, JSX.extend(KJUR.asn1.DERSequence, KJUR.asn1.DERAbstractStructured), KJUR.asn1.DERSet = function (a) { KJUR.asn1.DERSet.superclass.constructor.call(this, a), this.hT = "31", this.getFreshValueHex = function () { for (var a = new Array, b = 0; b < this.asn1Array.length; b++) { var c = this.asn1Array[b]; a.push(c.getEncodedHex()) } return a.sort(), this.hV = a.join(""), this.hV } }, JSX.extend(KJUR.asn1.DERSet, KJUR.asn1.DERAbstractStructured), KJUR.asn1.DERTaggedObject = function (a) { KJUR.asn1.DERTaggedObject.superclass.constructor.call(this), this.hT = "a0", this.hV = "", this.isExplicit = !0, this.asn1Object = null, this.setASN1Object = function (a, b, c) { this.hT = b, this.isExplicit = a, this.asn1Object = c, this.isExplicit ? (this.hV = this.asn1Object.getEncodedHex(), this.hTLV = null, this.isModified = !0) : (this.hV = null, this.hTLV = c.getEncodedHex(), this.hTLV = this.hTLV.replace(/^../, b), this.isModified = !1) }, this.getFreshValueHex = function () { return this.hV }, "undefined" != typeof a && ("undefined" != typeof a.tag && (this.hT = a.tag), "undefined" != typeof a.explicit && (this.isExplicit = a.explicit), "undefined" != typeof a.obj && (this.asn1Object = a.obj, this.setASN1Object(this.isExplicit, this.hT, this.asn1Object))) }, JSX.extend(KJUR.asn1.DERTaggedObject, KJUR.asn1.ASN1Object), function (a) { "use strict"; var b, c = {}; c.decode = function (c) { var d; if (b === a) { var e = "0123456789ABCDEF", f = " \f\n\r  \u2028\u2029"; for (b = [], d = 0; 16 > d; ++d) b[e.charAt(d)] = d; for (e = e.toLowerCase(), d = 10; 16 > d; ++d) b[e.charAt(d)] = d; for (d = 0; d < f.length; ++d) b[f.charAt(d)] = -1 } var g = [], h = 0, i = 0; for (d = 0; d < c.length; ++d) { var j = c.charAt(d); if ("=" == j) break; if (j = b[j], -1 != j) { if (j === a) throw"Illegal character at offset " + d; h |= j, ++i >= 2 ? (g[g.length] = h, h = 0, i = 0) : h <<= 4 } } if (i) throw"Hex encoding incomplete: 4 bits missing"; return g }, window.Hex = c }(), function (a) { "use strict"; var b, c = {}; c.decode = function (c) { var d; if (b === a) { var e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", f = "= \f\n\r  \u2028\u2029"; for (b = [], d = 0; 64 > d; ++d) b[e.charAt(d)] = d; for (d = 0; d < f.length; ++d) b[f.charAt(d)] = -1 } var g = [], h = 0, i = 0; for (d = 0; d < c.length; ++d) { var j = c.charAt(d); if ("=" == j) break; if (j = b[j], -1 != j) { if (j === a) throw"Illegal character at offset " + d; h |= j, ++i >= 4 ? (g[g.length] = h >> 16, g[g.length] = h >> 8 & 255, g[g.length] = 255 & h, h = 0, i = 0) : h <<= 6 } } switch (i) { case 1: throw"Base64 encoding incomplete: at least 2 bits missing"; case 2: g[g.length] = h >> 10; break; case 3: g[g.length] = h >> 16, g[g.length] = h >> 8 & 255 } return g }, c.re = /-----BEGIN [^-]+-----([A-Za-z0-9+\/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+\/=\s]+)====/, c.unarmor = function (a) { var b = c.re.exec(a); if (b) if (b[1]) a = b[1]; else { if (!b[2]) throw"RegExp out of sync"; a = b[2] } return c.decode(a) }, window.Base64 = c }(), function (a) { "use strict"; function b(a, c) { a instanceof b ? (this.enc = a.enc, this.pos = a.pos) : (this.enc = a, this.pos = c) } function c(a, b, c, d, e) { this.stream = a, this.header = b, this.length = c, this.tag = d, this.sub = e } var d = 100, e = "‿", f = { tag: function (a, b) { var c = document.createElement(a); return c.className = b, c }, text: function (a) { return document.createTextNode(a) } }; b.prototype.get = function (b) { if (b === a && (b = this.pos++), b >= this.enc.length) throw"Requesting byte offset " + b + " on a stream of length " + this.enc.length; return this.enc[b] }, b.prototype.hexDigits = "0123456789ABCDEF", b.prototype.hexByte = function (a) { return this.hexDigits.charAt(a >> 4 & 15) + this.hexDigits.charAt(15 & a) }, b.prototype.hexDump = function (a, b, c) { for (var d = "", e = a; b > e; ++e) if (d += this.hexByte(this.get(e)), c !== !0) switch (15 & e) { case 7: d += " "; break; case 15: d += "\n"; break; default: d += " " } return d }, b.prototype.parseStringISO = function (a, b) { for (var c = "", d = a; b > d; ++d) c += String.fromCharCode(this.get(d)); return c }, b.prototype.parseStringUTF = function (a, b) { for (var c = "", d = a; b > d;) { var e = this.get(d++); c += String.fromCharCode(128 > e ? e : e > 191 && 224 > e ? (31 & e) << 6 | 63 & this.get(d++) : (15 & e) << 12 | (63 & this.get(d++)) << 6 | 63 & this.get(d++)) } return c }, b.prototype.parseStringBMP = function (a, b) { for (var c = "", d = a; b > d; d += 2) { var e = this.get(d), f = this.get(d + 1); c += String.fromCharCode((e << 8) + f) } return c }, b.prototype.reTime = /^((?:1[89]|2\d)?\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/, b.prototype.parseTime = function (a, b) { var c = this.parseStringISO(a, b), d = this.reTime.exec(c); return d ? (c = d[1] + "-" + d[2] + "-" + d[3] + " " + d[4], d[5] && (c += ":" + d[5], d[6] && (c += ":" + d[6], d[7] && (c += "." + d[7]))), d[8] && (c += " UTC", "Z" != d[8] && (c += d[8], d[9] && (c += ":" + d[9]))), c) : "Unrecognized time: " + c }, b.prototype.parseInteger = function (a, b) { var c = b - a; if (c > 4) { c <<= 3; var d = this.get(a); if (0 === d) c -= 8; else for (; 128 > d;) d <<= 1, --c; return "(" + c + " bit)" } for (var e = 0, f = a; b > f; ++f) e = e << 8 | this.get(f); return e }, b.prototype.parseBitString = function (a, b) { var c = this.get(a), d = (b - a - 1 << 3) - c, e = "(" + d + " bit)"; if (20 >= d) { var f = c; e += " "; for (var g = b - 1; g > a; --g) { for (var h = this.get(g), i = f; 8 > i; ++i) e += h >> i & 1 ? "1" : "0"; f = 0 } } return e }, b.prototype.parseOctetString = function (a, b) { var c = b - a, f = "(" + c + " byte) "; c > d && (b = a + d); for (var g = a; b > g; ++g) f += this.hexByte(this.get(g)); return c > d && (f += e), f }, b.prototype.parseOID = function (a, b) { for (var c = "", d = 0, e = 0, f = a; b > f; ++f) { var g = this.get(f); if (d = d << 7 | 127 & g, e += 7, !(128 & g)) { if ("" === c) { var h = 80 > d ? 40 > d ? 0 : 1 : 2; c = h + "." + (d - 40 * h) } else c += "." + (e >= 31 ? "bigint" : d); d = e = 0 } } return c }, c.prototype.typeName = function () { if (this.tag === a) return "unknown"; var b = this.tag >> 6, c = (this.tag >> 5 & 1, 31 & this.tag); switch (b) { case 0: switch (c) { case 0: return "EOC"; case 1: return "BOOLEAN"; case 2: return "INTEGER"; case 3: return "BIT_STRING"; case 4: return "OCTET_STRING"; case 5: return "NULL"; case 6: return "OBJECT_IDENTIFIER"; case 7: return "ObjectDescriptor"; case 8: return "EXTERNAL"; case 9: return "REAL"; case 10: return "ENUMERATED"; case 11: return "EMBEDDED_PDV"; case 12: return "UTF8String"; case 16: return "SEQUENCE"; case 17: return "SET"; case 18: return "NumericString"; case 19: return "PrintableString"; case 20: return "TeletexString"; case 21: return "VideotexString"; case 22: return "IA5String"; case 23: return "UTCTime"; case 24: return "GeneralizedTime"; case 25: return "GraphicString"; case 26: return "VisibleString"; case 27: return "GeneralString"; case 28: return "UniversalString"; case 30: return "BMPString"; default: return "Universal_" + c.toString(16) } case 1: return "Application_" + c.toString(16); case 2: return "[" + c + "]"; case 3: return "Private_" + c.toString(16) } }, c.prototype.reSeemsASCII = /^[ -~]+$/, c.prototype.content = function () { if (this.tag === a) return null; var b = this.tag >> 6, c = 31 & this.tag, f = this.posContent(), g = Math.abs(this.length); if (0 !== b) { if (null !== this.sub) return "(" + this.sub.length + " elem)"; var h = this.stream.parseStringISO(f, f + Math.min(g, d)); return this.reSeemsASCII.test(h) ? h.substring(0, 2 * d) + (h.length > 2 * d ? e : "") : this.stream.parseOctetString(f, f + g) } switch (c) { case 1: return 0 === this.stream.get(f) ? "false" : "true"; case 2: return this.stream.parseInteger(f, f + g); case 3: return this.sub ? "(" + this.sub.length + " elem)" : this.stream.parseBitString(f, f + g); case 4: return this.sub ? "(" + this.sub.length + " elem)" : this.stream.parseOctetString(f, f + g); case 6: return this.stream.parseOID(f, f + g); case 16: case 17: return "(" + this.sub.length + " elem)"; case 12: return this.stream.parseStringUTF(f, f + g); case 18: case 19: case 20: case 21: case 22: case 26: return this.stream.parseStringISO(f, f + g); case 30: return this.stream.parseStringBMP(f, f + g); case 23: case 24: return this.stream.parseTime(f, f + g) } return null }, c.prototype.toString = function () { return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + (null === this.sub ? "null" : this.sub.length) + "]" }, c.prototype.print = function (b) { if (b === a && (b = ""), document.writeln(b + this), null !== this.sub) { b += " "; for (var c = 0, d = this.sub.length; d > c; ++c) this.sub[c].print(b) } }, c.prototype.toPrettyString = function (b) { b === a && (b = ""); var c = b + this.typeName() + " @" + this.stream.pos; if (this.length >= 0 && (c += "+"), c += this.length, 32 & this.tag ? c += " (constructed)" : 3 != this.tag && 4 != this.tag || null === this.sub || (c += " (encapsulates)"), c += "\n", null !== this.sub) { b += " "; for (var d = 0, e = this.sub.length; e > d; ++d) c += this.sub[d].toPrettyString(b) } return c }, c.prototype.toDOM = function () { var a = f.tag("div", "node"); a.asn1 = this; var b = f.tag("div", "head"), c = this.typeName().replace(/_/g, " "); b.innerHTML = c; var d = this.content(); if (null !== d) { d = String(d).replace(/</g, "&lt;"); var e = f.tag("span", "preview"); e.appendChild(f.text(d)), b.appendChild(e) } a.appendChild(b), this.node = a, this.head = b; var g = f.tag("div", "value"); if (c = "Offset: " + this.stream.pos + "<br/>", c += "Length: " + this.header + "+", c += this.length >= 0 ? this.length : -this.length + " (undefined)", 32 & this.tag ? c += "<br/>(constructed)" : 3 != this.tag && 4 != this.tag || null === this.sub || (c += "<br/>(encapsulates)"), null !== d && (c += "<br/>Value:<br/><b>" + d + "</b>", "object" == typeof oids && 6 == this.tag)) { var h = oids[d]; h && (h.d && (c += "<br/>" + h.d), h.c && (c += "<br/>" + h.c), h.w && (c += "<br/>(warning!)")) } g.innerHTML = c, a.appendChild(g); var i = f.tag("div", "sub"); if (null !== this.sub) for (var j = 0, k = this.sub.length; k > j; ++j) i.appendChild(this.sub[j].toDOM()); return a.appendChild(i), b.onclick = function () { a.className = "node collapsed" == a.className ? "node" : "node collapsed" }, a }, c.prototype.posStart = function () { return this.stream.pos }, c.prototype.posContent = function () { return this.stream.pos + this.header }, c.prototype.posEnd = function () { return this.stream.pos + this.header + Math.abs(this.length) }, c.prototype.fakeHover = function (a) { this.node.className += " hover", a && (this.head.className += " hover") }, c.prototype.fakeOut = function (a) { var b = / ?hover/; this.node.className = this.node.className.replace(b, ""), a && (this.head.className = this.head.className.replace(b, "")) }, c.prototype.toHexDOM_sub = function (a, b, c, d, e) { if (!(d >= e)) { var g = f.tag("span", b); g.appendChild(f.text(c.hexDump(d, e))), a.appendChild(g) } }, c.prototype.toHexDOM = function (b) { var c = f.tag("span", "hex"); if (b === a && (b = c), this.head.hexNode = c, this.head.onmouseover = function () { this.hexNode.className = "hexCurrent" }, this.head.onmouseout = function () { this.hexNode.className = "hex" }, c.asn1 = this, c.onmouseover = function () { var a = !b.selected; a && (b.selected = this.asn1, this.className = "hexCurrent"), this.asn1.fakeHover(a) }, c.onmouseout = function () { var a = b.selected == this.asn1; this.asn1.fakeOut(a), a && (b.selected = null, this.className = "hex") }, this.toHexDOM_sub(c, "tag", this.stream, this.posStart(), this.posStart() + 1), this.toHexDOM_sub(c, this.length >= 0 ? "dlen" : "ulen", this.stream, this.posStart() + 1, this.posContent()), null === this.sub) c.appendChild(f.text(this.stream.hexDump(this.posContent(), this.posEnd()))); else if (this.sub.length > 0) { var d = this.sub[0], e = this.sub[this.sub.length - 1]; this.toHexDOM_sub(c, "intro", this.stream, this.posContent(), d.posStart()); for (var g = 0, h = this.sub.length; h > g; ++g) c.appendChild(this.sub[g].toHexDOM(b)); this.toHexDOM_sub(c, "outro", this.stream, e.posEnd(), this.posEnd()) } return c }, c.prototype.toHexString = function () { return this.stream.hexDump(this.posStart(), this.posEnd(), !0) }, c.decodeLength = function (a) { var b = a.get(), c = 127 & b; if (c == b) return c; if (c > 3) throw"Length over 24 bits not supported at position " + (a.pos - 1); if (0 === c) return -1; b = 0; for (var d = 0; c > d; ++d) b = b << 8 | a.get(); return b }, c.hasContent = function (a, d, e) { if (32 & a) return !0; if (3 > a || a > 4) return !1; var f = new b(e); 3 == a && f.get(); var g = f.get(); if (g >> 6 & 1) return !1; try { var h = c.decodeLength(f); return f.pos - e.pos + h == d } catch (i) { return !1 } }, c.decode = function (a) { a instanceof b || (a = new b(a, 0)); var d = new b(a), e = a.get(), f = c.decodeLength(a), g = a.pos - d.pos, h = null; if (c.hasContent(e, f, a)) { var i = a.pos; if (3 == e && a.get(), h = [], f >= 0) { for (var j = i + f; a.pos < j;) h[h.length] = c.decode(a); if (a.pos != j) throw"Content size is not correct for container starting at offset " + i } else try { for (; ;) { var k = c.decode(a); if (0 === k.tag) break; h[h.length] = k } f = i - a.pos } catch (l) { throw"Exception while decoding undefined length content: " + l } } else a.pos += f; return new c(d, g, f, e, h) }, c.test = function () { for (var a = [{value: [39], expected: 39}, {value: [129, 201], expected: 201}, { value: [131, 254, 220, 186], expected: 16702650 }], d = 0, e = a.length; e > d; ++d) { var f = new b(a[d].value, 0), g = c.decodeLength(f); g != a[d].expected && document.write("In test[" + d + "] expected " + a[d].expected + " got " + g + "\n") } }, window.ASN1 = c }(), ASN1.prototype.getHexStringValue = function () { var a = this.toHexString(), b = 2 * this.header, c = 2 * this.length; return a.substr(b, c) }, RSAKey.prototype.parseKey = function (a) { try { var b = 0, c = 0, d = /^\s*(?:[0-9A-Fa-f][0-9A-Fa-f]\s*)+$/, e = d.test(a) ? Hex.decode(a) : Base64.unarmor(a), f = ASN1.decode(e); if (3 === f.sub.length && (f = f.sub[2].sub[0]), 9 === f.sub.length) { b = f.sub[1].getHexStringValue(), this.n = parseBigInt(b, 16), c = f.sub[2].getHexStringValue(), this.e = parseInt(c, 16); var g = f.sub[3].getHexStringValue(); this.d = parseBigInt(g, 16); var h = f.sub[4].getHexStringValue(); this.p = parseBigInt(h, 16); var i = f.sub[5].getHexStringValue(); this.q = parseBigInt(i, 16); var j = f.sub[6].getHexStringValue(); this.dmp1 = parseBigInt(j, 16); var k = f.sub[7].getHexStringValue(); this.dmq1 = parseBigInt(k, 16); var l = f.sub[8].getHexStringValue(); this.coeff = parseBigInt(l, 16) } else { if (2 !== f.sub.length) return !1; var m = f.sub[1], n = m.sub[0]; b = n.sub[0].getHexStringValue(), this.n = parseBigInt(b, 16), c = n.sub[1].getHexStringValue(), this.e = parseInt(c, 16) } return !0 } catch (o) { return !1 } }, RSAKey.prototype.getPrivateBaseKey = function () { var a = {array: [new KJUR.asn1.DERInteger({"int": 0}), new KJUR.asn1.DERInteger({bigint: this.n}), new KJUR.asn1.DERInteger({"int": this.e}), new KJUR.asn1.DERInteger({bigint: this.d}), new KJUR.asn1.DERInteger({bigint: this.p}), new KJUR.asn1.DERInteger({bigint: this.q}), new KJUR.asn1.DERInteger({bigint: this.dmp1}), new KJUR.asn1.DERInteger({bigint: this.dmq1}), new KJUR.asn1.DERInteger({bigint: this.coeff})]}, b = new KJUR.asn1.DERSequence(a); return b.getEncodedHex() }, RSAKey.prototype.getPrivateBaseKeyB64 = function () { return hex2b64(this.getPrivateBaseKey()) }, RSAKey.prototype.getPublicBaseKey = function () { var a = {array: [new KJUR.asn1.DERObjectIdentifier({oid: "1.2.840.113549.1.1.1"}), new KJUR.asn1.DERNull]}, b = new KJUR.asn1.DERSequence(a); a = {array: [new KJUR.asn1.DERInteger({bigint: this.n}), new KJUR.asn1.DERInteger({"int": this.e})]}; var c = new KJUR.asn1.DERSequence(a); a = {hex: "00" + c.getEncodedHex()}; var d = new KJUR.asn1.DERBitString(a); a = {array: [b, d]}; var e = new KJUR.asn1.DERSequence(a); return e.getEncodedHex() }, RSAKey.prototype.getPublicBaseKeyB64 = function () { return hex2b64(this.getPublicBaseKey()) }, RSAKey.prototype.wordwrap = function (a, b) { if (b = b || 64, !a) return a; var c = "(.{1," + b + "})( +|$\n?)|(.{1," + b + "})"; return a.match(RegExp(c, "g")).join("\n") }, RSAKey.prototype.getPrivateKey = function () { var a = "-----BEGIN RSA PRIVATE KEY-----\n"; return a += this.wordwrap(this.getPrivateBaseKeyB64()) + "\n", a += "-----END RSA PRIVATE KEY-----" }, RSAKey.prototype.getPublicKey = function () { var a = "-----BEGIN PUBLIC KEY-----\n"; return a += this.wordwrap(this.getPublicBaseKeyB64()) + "\n", a += "-----END PUBLIC KEY-----" }, RSAKey.prototype.hasPublicKeyProperty = function (a) { return a = a || {}, a.hasOwnProperty("n") && a.hasOwnProperty("e") }, RSAKey.prototype.hasPrivateKeyProperty = function (a) { return a = a || {}, a.hasOwnProperty("n") && a.hasOwnProperty("e") && a.hasOwnProperty("d") && a.hasOwnProperty("p") && a.hasOwnProperty("q") && a.hasOwnProperty("dmp1") && a.hasOwnProperty("dmq1") && a.hasOwnProperty("coeff") }, RSAKey.prototype.parsePropertiesFrom = function (a) { this.n = a.n, this.e = a.e, a.hasOwnProperty("d") && (this.d = a.d, this.p = a.p, this.q = a.q, this.dmp1 = a.dmp1, this.dmq1 = a.dmq1, this.coeff = a.coeff) }; var JSEncryptRSAKey = function (a) { RSAKey.call(this), a && ("string" == typeof a ? this.parseKey(a) : (this.hasPrivateKeyProperty(a) || this.hasPublicKeyProperty(a)) && this.parsePropertiesFrom(a)) }; JSEncryptRSAKey.prototype = new RSAKey, JSEncryptRSAKey.prototype.constructor = JSEncryptRSAKey; var JSEncrypt = function (a) { a = a || {}, this.default_key_size = parseInt(a.default_key_size) || 1024, this.default_public_exponent = a.default_public_exponent || "010001", this.log = a.log || !1, this.key = null }; JSEncrypt.prototype.setKey = function (a) { this.log && this.key && console.warn("A key was already set, overriding existing."), this.key = new JSEncryptRSAKey(a) }, JSEncrypt.prototype.setPrivateKey = function (a) { this.setKey(a) }, JSEncrypt.prototype.setPublicKey = function (a) { this.setKey(a) }, JSEncrypt.prototype.decrypt = function (a) { try { return this.getKey().decrypt(b64tohex(a)) } catch (b) { return !1 } }, JSEncrypt.prototype.encrypt = function (a) { try { return hex2b64(this.getKey().encrypt(a)) } catch (b) { return !1 } }, JSEncrypt.prototype.getKey = function (a) { if (!this.key) { if (this.key = new JSEncryptRSAKey, a && "[object Function]" === {}.toString.call(a)) return void this.key.generateAsync(this.default_key_size, this.default_public_exponent, a); this.key.generate(this.default_key_size, this.default_public_exponent) } return this.key }, JSEncrypt.prototype.getPrivateKey = function () { return this.getKey().getPrivateKey() }, JSEncrypt.prototype.getPrivateKeyB64 = function () { return this.getKey().getPrivateBaseKeyB64() }, JSEncrypt.prototype.getPublicKey = function () { return this.getKey().getPublicKey() }, JSEncrypt.prototype.getPublicKeyB64 = function () { return this.getKey().getPublicBaseKeyB64() }; exports.JSEncrypt = JSEncrypt;})(JSEncryptExports);var JSEncrypt = JSEncryptExports.JSEncrypt;2. 调用示例function getEncrypt(password, publickey){ var jsEncrypt = new JSEncrypt(); jsEncrypt.setPublicKey(publickey); return jsEncrypt.encrypt(password);}function RSA_encryptjs(public_key,password){ var RSA_encryptor = new JSEncrypt() RSA_encryptor.setPublicKey(public_key); return RSA_encryptor.encrypt(password)}var publicKeyBase64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCDxUolobESaYbT+6AX8Jj7XmvO" +"7oi8n9D2JArwvs1CWMUu8Rp2XOH8EqwVltkpzuu2Jfl4MbftyEXzORaCb78AqZTZ" +"1e6HNyKtYvKSNWxlewg/skrXV8v5uzqBQ9gxhFnxQUm4YJ2Cntd8Y5mTIXsF0zuf" +"gKe4b0upFHDkBOWR+wIDAQAB";console.log(RSA_encryptjs(publicKeyBase64,"hengdin.com" ));

January 5, 2022 · 56 min · jiezi

关于密码学:密码学-14-CryptoJS

1. 为什么抉择应用JavaScript来复现算法JS实现的算法,能够很不便地被任何语言调用 2. CryptoJS中音讯摘要算法的应用CryptoJS.MD5(message);CryptoJS.HmacMD5(message, key);CryptoJS.SHA1(message);CryptoJS.HmacSHA1(message, key);CryptoJS.SHA256(message);CryptoJS.HmacSHA256(message, key);CryptoJS.SHA512(message);CryptoJS.HmacSHA512(message, key);CryptoJS.SHA3('xiaojianbang', {outputLength: 256})3. 音讯摘要算法的其余调用模式SHA256var hasher = CryptoJS.algo.SHA256.create();hasher.reset();hasher.update('message');hasher.update(wordArray);var hash = hasher.finalize();var hash = hasher.finalize('message');var hash = hasher.finalize(wordArray);HmacSHA256var hmacHasher = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);hmacHasher.reset();hmacHasher.update('message');hmacHasher.update(wordArray);var hmac = hmacHasher.finalize();var hmac = hmacHasher.finalize('message');var hmac = hmacHasher.finalize(wordArray);4. CryptoJS(字符串解析)// -------------------------------------- string转wordArray console.log("-----------------------------------------------");var md5_str= "hengdin.com";var utf8_str= md5_str;var hex_str = "68656e6764696e2e636f6d";var base64_str = "aGVuZ2Rpbi5jb20==";var utf8_wordarray = CryptoJS.enc.Utf8.parse(utf8_str);var hex_wordarray= CryptoJS.enc.Hex.parse(hex_str);var base64_wordarray = CryptoJS.enc.Base64.parse(base64_str);console.log(utf8_wordarray);console.log(hex_wordarray);console.log(base64_wordarray);// { words: [ 1751477863, 1684631086, 1668246784 ], sigBytes: 11 }// 解析-------------------------------------- wordArray转stringconsole.log("-----------------------1111------------------------");// 形式一:// 默认应用hex 编码 console.log(utf8_wordarray+'');console.log(hex_wordarray+'');console.log(base64_wordarray+'');// 形式二:console.log(utf8_wordarray.toString(CryptoJS.enc.Utf8));console.log(hex_wordarray.toString(CryptoJS.enc.Hex));console.log(base64_wordarray.toString(CryptoJS.enc.Base64));// 形式三:console.log(CryptoJS.enc.Utf8.stringify(utf8_wordarray));console.log(CryptoJS.enc.Hex.stringify(hex_wordarray));console.log(CryptoJS.enc.Base64.stringify(base64_wordarray));// Hex编码转Base64编码 :先转成 wordarray 再转成 base64 strvar hex_str = "68656e6764696e2e636f6d"; var wordArray__ = CryptoJS.enc.Hex.parse(hex_str);var base_str__= CryptoJS.enc.Base64.stringify(wordArray__);console.log(base_str__);// 加密的参数 能够是String 类型 ,也能够是wordarray 类型 string类型的数据,将应用默认的Utf8.parse来解析var str = "hendi";var str_wordarray = CryptoJS.enc.Utf8.parse(str);console.log(CryptoJS.MD5(str).toString());console.log(CryptoJS.MD5(str_wordarray).toString());

January 2, 2022 · 1 min · jiezi

关于密码学:密码学-13-数字签名算法

简介:对指标先进行音讯摘要算法,失去的后果再进行RSA加密。 特点:这种状况下的RSA往往是NoPadding 模式。保障每次的加密后果统一。 代码实现 public static String getSignature(String data) throws Exception { PrivateKey privateKey = RSABase64.genPrivatekey(); Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(data.getBytes(StandardCharsets.UTF_8)); byte[] res= signature.sign(); ByteString of = ByteString.of(res); return of.base64(); }; public static boolean verifySignature (String data, String sign) throws Exception { PublicKey publicKey = RSABase64.genPublickey(); Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(data.getBytes(StandardCharsets.UTF_8)); return signature.verify(ByteString.decodeBase64(sign).toByteArray()); };

December 31, 2021 · 1 min · jiezi

关于密码学:密码学-12-AES和RSA-的结合

多种加密算法的常见联合套路随机生成AES密钥AESKeyAESKey密钥用于AES加密数据,失去数据密文cipherText应用RSA对AESKey加密,失去密钥密文cipherKey提交密钥密文cipherKey和数据密文cipherText给服务器 JAVA实现 // 客户端实现String AESkey = genAESkey();String plain_Text = "qianniuwei";String AES_Ciper_Text = AES_T.encryptAES(AESkey,plain_Text);String AESkey_en_by_RSA = RSABase64_T.encryptBase64(AESkey);Log.d("hengdi","AESkey_en_by_RSA:"+AESkey_en_by_RSA);// 服务端解密过程 -------------String AESkey_ = decryptBase64(AESkey_en_by_RSA);Log.d("hendi","AES_T key:" + AESkey_);String origin_plain_Text = AES_T.decryptAES(AESkey_,AES_Ciper_Text);Log.d("hengdi","origin_plain_Text:"+origin_plain_Text);

December 29, 2021 · 1 min · jiezi

关于密码学:密码学-10-非对称加密算法

典型算法:RSA 须要生成一个密钥对,蕴含公钥和私钥,密钥不是轻易写的密钥对生成 http://web.chacuo.net/netrsak... 留神:加密算法的秘钥对,自身是 byte 数组的字节流,这儿所显示的,只是字节流进行base64编码的后果,实在的字节流是咱们看到的秘钥进行base64解码后的后果。 公钥加密的数据,私钥能力解密私钥加密的数据,公钥能力解密个别公钥是公开的,私钥窃密,私钥蕴含公钥,从公钥无奈推导出私钥加密解决平安,然而性能极差,单次加密长度有限度RSA算法既可用于加密解密,也可用于数据签名

December 26, 2021 · 1 min · jiezi

关于密码学:密码学-08-DESede

1. java实现蕴含了三种生成加密key的形式 public static String encryptDESede(String plaintext) throws Exception { // 初始化秘钥 和加密算法 秘钥必须大于8位// ------------------------ 1 应用 DESedeKeySpec 来生成 ------------------------------ DESedeKeySpec desedekey = new DESedeKeySpec("123456781234567812345678".getBytes(StandardCharsets.UTF_8)); SecretKeyFactory key = SecretKeyFactory.getInstance("DESede"); SecretKey keySpec = key.generateSecret(desedekey); // -----------------------2 间接应用字节 来生成 key ---------------------------------// byte[] keySpec_bytes= new byte[]{1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8};// SecretKeySpec keySpec = new SecretKeySpec(keySpec_bytes,"DESede"); // ------------------------3 应用 字符串转byte数组 来生成 -----------------------------// SecretKeySpec keySpec = new SecretKeySpec("123456781234567812345678".getBytes(StandardCharsets.UTF_8),"DESede"); IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes(StandardCharsets.UTF_8)); // 失去Cipher的实例 026fcbe02f76529d0a5bb3904aa6efdc Am/L4C92Up0KW7OQSqbv3A== Cipher desede = Cipher.getInstance("DESede/CBC/PKCS5Padding"); // 对Cipher 实例进行初始化, desede.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec); // update并不会每次都减少要加密的字符串的长度,并不牢靠。现实中的状态是 xiaojianbang +hengdi 解密发现只有banghengdi// des.update("xiaojianbang".getBytes(StandardCharsets.UTF_8)); // 加密 由字符串 失去的byte[] byte[] res = desede.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); Log.d("hengdi", "DESede byte test" + Arrays.toString(res)); // 将 byte[] 实例化为 ByteString 对象byte test ByteString bty = ByteString.of(res); // hex 编码 String str_hex = bty.hex(); // base64 编码 String str_base64 = bty.base64(); return str_hex + " || " + str_base64; } public static String decryptDESede(String cipherText) throws Exception { // 将加密后base64编码的字符串 解码,并还原成 byte[] byte[] cipherTextBytes = ByteString.decodeBase64(cipherText).toByteArray(); SecretKeySpec keySpec = new SecretKeySpec("123456781234567812345678".getBytes(StandardCharsets.UTF_8), "DES"); IvParameterSpec ivParameterSpec = new IvParameterSpec("12345678".getBytes(StandardCharsets.UTF_8)); Cipher desede = Cipher.getInstance("DESede/CBC/PKCS5Padding");// des.init(Cipher.DECRYPT_MODE, keySpec); desede.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec); byte[] res = desede.doFinal(cipherTextBytes); Log.d("hengdi", "DESede decryptDESede byte test" + Arrays.toString(res)); return new String(res); }2. 特点:算法秘钥长度秘钥长度默认值工作模式填充模式备注DESede(TripleDES,3DES)112,168168ECB,CBC,PCBC,CTR,CTS,CFB,CFB8至CFB128,OFB,OFB8至OFB128NOPadding,PKCS5Padding,ISO10126PaddingJAVA6实现DESede(TripleDES,3DES)128,192同上同上PKCS7Padding,ISO10126d2Padding,X932Padding,ISO7816d4Padding,ZeroBytePaddingBouncy CastleECB模式和CBC模式的区别对称加密算法里,应用NOPadding,加密的明文必须等于分组长度倍数,否则报错如果应用PKCS5Padding,会对加密的明文填充1字节-1个分组的长度没有指明加密模式和填充形式,示意应用默认的DESede/ECB/PKCS5Padding加密后的字节数组能够编码成Hex、Base64DESede算法明文按64位进行分组加密,能够调用cipher.getBlockSize()来获取要复现一个对称加密算法,须要失去明文、key、iv、mode、padding明文、key、iv须要留神解析形式,而且不肯定是字符串模式如果加密模式是ECB,则不须要加iv,加了的话会报错如果明文中有两个分组的内容雷同,ECB会失去齐全一样的密文,CBC不会加密算法的后果通常与明文等长或者更长,如果变短了,那可能是gzip、protobuf

December 16, 2021 · 1 min · jiezi

关于密码学:密码学-07-DES

1. 简介算法秘钥长度秘钥长度默认值工作模式填充形式备注DES5656ECB,CBC,PCBC,CTR,CTS,CFB,CFB8至CFB128,OFB,OFB8至OFB128NoPadding,PKCS5Padding,ISO101126Paddingjava6实现DES5656ECB,CBC,PCBC,CTR,CTS,CFB,CFB8至CFB128,OFB,OFB8至OFB128NoPadding,PKCS5Padding,ISO101126Paddingjava6实现2. java实现public static String encryptDES(String plaintext) throws Exception { // 初始化秘钥 和加密算法 秘钥必须大于8位 SecretKeySpec keySpec = new SecretKeySpec("12345678".getBytes(StandardCharsets.UTF_8),"DES"); // 失去Cipher的实例 Cipher des = Cipher.getInstance("DES/ECB/PKCS5Padding"); // 对Cipher 实例进行初始化, des.init(Cipher.ENCRYPT_MODE,keySpec); // update并不会每次都减少要加密的字符串的长度,并不牢靠。现实中的状态是 xiaojianbang +hengdi 解密发现只有banghengdi des.update("xiaojianbang".getBytes(StandardCharsets.UTF_8)); // 加密 由字符串 失去的byte[] byte[] res=des.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); // 将 byte[] 实例化为 ByteString 对象 ByteString bty= ByteString.of(res); // hex 编码 String str_hex= bty.hex(); // base64 编码 String str_base64 = bty.base64(); return str_hex + " || " + str_base64; }解密: ...

December 14, 2021 · 1 min · jiezi

关于密码学:密码学-06-对称加密算法

加密/解密的过程可逆的算法,叫做加密算法加密/解密应用雷同的密钥,叫做对称加密算法对称加密算法的密钥能够随机给,然而有位数要求对称加密算法的输出数据没有长度要求,加密速度快各算法的密钥长度RC4 密钥长度1-256字节DES 密钥长度8字节3DES/DESede/TripleDES 密钥长度24字节AES 密钥长度16、24、32字节依据密钥长度不同AES又分为AES-128、AES-192、AES-256对称加密分类a) 序列加密/流加密: 以字节流的形式,顺次加密(解密)明文(密文)中的每一个字节RC4 b) 分组加密: 将明文音讯分组(每组有多个字节),逐组进行加密DES、3DES、AES

December 14, 2021 · 1 min · jiezi

关于密码学:密码学-05-MAC算法

MAC系列算法 1. 特点MAC算法与MD和SHA的区别是多了一个密钥,密钥能够随机给 2. java实现public static String getMAC(String plaintext) throws NoSuchAlgorithmException, InvalidKeyException { // 生成秘钥 SecretKeySpec skp1 = new SecretKeySpec("12345678".getBytes(StandardCharsets.UTF_8),"HmacSHA1"); // 应用 字符串 a123456789 中索引 1 开始,计算8位为秘钥 SecretKeySpec skp2 = new SecretKeySpec("a123456789".getBytes(StandardCharsets.UTF_8),1,8,"HmacSHA1"); // 通过 getEncoded 失去秘钥 String key = new String(skp1.getEncoded()); // 通过 getAlgorithm 失去加密算法 String algorithm = skp1.getAlgorithm(); // 生成 Mac 算法实例 Mac mac = Mac.getInstance(algorithm); // 初始化 mac 对象 mac.init(skp1); String saltstr = "saltstr"; // 加盐操作 mac.update(saltstr.getBytes(StandardCharsets.UTF_8)); // 最初失去加密后的byte列表 byte[] bty = mac.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); // 生成 ByteString 对象 ByteString res = ByteString.of(bty); // 调用hex 和base64 办法 失去编码后的值 String resHex = res.hex(); String resBase64 = res.base64(); return resHex+"||"+resBase64; }3. 加密后的字节数组能够编码成Hex、Base644. 没有任何输出,也能计算hash值

December 13, 2021 · 1 min · jiezi

关于密码学:密码学-04-MD5

1. MD5的Java实现 public static String getmd5(String plaintext) throws NoSuchAlgorithmException { // 生成md5 对象 MessageDigest md5 = MessageDigest.getInstance("MD5"); // 更新 其实相当于减少这个字符串 md5.update("lihai".getBytes(StandardCharsets.UTF_8)); // 清空之前字符串 也就是之后的字符串才算无效 的字符串 md5.reset(); md5.update("hehe_dalao".getBytes(StandardCharsets.UTF_8), 3, 3); // 调用digest 生成 byte 数组 byte[] digest = md5.digest(plaintext.getBytes(StandardCharsets.UTF_8)); // 生成数组的 String 模式 String bytes = Arrays.toString(digest); // 将数组转为 ByteString 对象 ByteString res = ByteString.of(digest); // 别离生成 hex和base64的编码 String res_hex = res.hex(); String res_base64 = res.base64(); System.out.println("横笛,bytes:" + bytes); System.out.println("横笛,res_hex:" + res_hex); System.out.println("横笛,res_base64:" + res_base64); // 将hex编码后的string 解码,并生成byte 数组 byte[] bytes1 = ByteString.decodeHex(res_hex).toByteArray(); // 对于md5生成的数组和 hex解码后的数组 是统一的 String string1 = Arrays.toString(bytes1); String string2 = Arrays.toString(digest); System.out.println(string1); System.out.println(string2); return bytes + "||" + res_hex + "|| " + res_base64; }加密后的字节数组能够编码成Hex、Base64没有任何输出,也能计算hash值碰到加salt的MD5,能够间接输出空的值,失去后果去CMD5查问一下,有可能就失去salt其实相当于是 :在md5解决字字符串加密之前,定义了一个字符串, 调用了update办法,参数为这个字符串,也就是相当于 自定义的字符串会和自身要加密的字符串拼接,并加密。 ...

December 12, 2021 · 1 min · jiezi

关于密码学:密码学-03-消息摘要算法

1. 算法特点a) 音讯摘要算法/单向散列函数/哈希函数b) 不同长度的输出,产生固定长度的输入c) 散列后的密文不可逆d) 散列后的后果惟一e) 哈希碰撞f) 个别用于校验数据完整性、签名sign因为密文不可逆,所以服务端也无奈解密想要验证,就须要跟前端一样的形式去从新签名一遍签名算法个别会把源数据和签名后的值一起提交到服务端要保障在签名时候的数据和提交下来的源数据统一 2. 常见算法MD5、SHA1、SHA256、SHA512、HmacMD5、HmacSHA1、HmacSHA256、HmacSHA512RIPEMD160、HmacRIPEMD160、PBKDF2、EvpKDF

December 12, 2021 · 1 min · jiezi

关于密码学:密码学基础编码方式消息摘要算法加密算法总结

字节码转文本的编码方式在计算机中,无论是内存、磁盘、网络传输,波及到的数据都是以二进制格局来存储或传输的。 每一个二进制位(bit)只能是 0 或 1。二进制位不会独自存在,而是以 8 个二进制位组成 1 个字节(byte)的形式存在,即 1 byte = 8 bit。 字节码无奈间接转为可打印的文本字符,有时想通过文本形式配置、存储、传输一段二进制字节码,比方配置文件、HTML/XML、URL、e-mail 注释、HTTP Header 等仅反对文本的场景下,就须要将二进制字节码转为文本字符串。 二进制字节码转文本字符有很多种形式,最简略的形式是间接用 0 和 1 来示意。然而这样的话,8 个 0/1 字符能力示意 1 个字节,长度太长很不不便。 上面介绍两种更加紧凑的形式:HEX 编码和 Base64 编码。 HEX 编码HEX 是 16 进制的编码方式,所以又称为 Base16。 如果把一个字节中的二进制数值转为十六进制,应用 0-9 和 a-e(疏忽大小写)这 16 个字符,那每个字符就能够示意 4 个二进制位(因为 2 的 4 次方等于 16),那么仅须要两个可打印字符就能够示意一个字节。 Java 中应用 HEX 编码(依赖 Apache Commons Codec): String str = "相对论";byte[] bytes = str.getBytes("UTF-8");// Hex 编码String encodeString = Hex.encodeHexString(bytes);System.out.println(encodeString); // 输入:e79bb8e5afb9e8aeba// Hex 解码byte[] decodeBytes = Hex.decodeHex(encodeString);System.out.println(new String(decodeBytes, "UTF-8")); // 输入:相对论HEX 编码应用场景十分多。上面介绍几种常见的应用场景: ...

December 2, 2021 · 6 min · jiezi

关于密码学:密码学系列之加密货币中的scrypt算法

简介为了抵挡明码破解,科学家们想出了很多种办法,比方对明码进行混同加盐操作,对明码进行模式变换和组合。然而这些算法逐步被一些特制的ASIC处理器战胜,这些ASIC处理器不做别的,就是专门来破解你的明码或者进行hash运算。 最有名的当然是比特币了,它应用的是为人诟病的POW算法,谁的算力高,谁就能够挖矿,这样就导致了大量无意义的矿机的产生,这些矿机什么都不能干,就算是用来算hash值。后果节约了大量的电力。 普通人更是别想退出这个只有巨头能力领有的赛道,如果你想用一个一般的PC机来挖矿,那么我预计你挖到矿的几率可能跟被陨石砸中差不多。 为了抵挡这种CPU为主的明码加密形式,科学家们创造了很多其余的算法,比方须要占用大量内存的算法,因为内存不像CPU能够疯狂提速,所以限度了很多暴力破解的场景,明天要将的scrypt算法就是其中一种,该算法被利用到很多新的加密货币挖矿体系中,用以示意他们挖矿程序的公平性。 scrypt算法scrypt是一种明码衍生算法,它是由Colin Percival创立的。应用scrypt算法来生成衍生key,须要用到大量的内存。scrypt算法在2016年作为RFC 7914规范公布。 明码衍生算法次要作用就是依据初始化的主明码来生成系列的衍生明码。这种算法次要是为了抵挡暴力破解的攻打。通过减少明码生成的复杂度,同时也减少了暴力破解的难度。 然而和下面提到的起因一样,之前的password-based KDF,比方PBKDF2尽管进步了明码生成的遍历次数,然而它应用了很少的内存空间。所以很容易被简略的ASIC机器破解。scrypt算法就是为了解决这样的问题呈现的。 scrypt算法详解scrypt算法会生成十分大的伪随机数序列,这个随机数序列会被用在后续的key生成过程中,所以一般来说须要一个RAM来进行存储。这就是scrypt算法须要大内存的起因。 接下咱们详细分析一下scrypt算法,规范的Scrypt算法须要输出8个参数,如下所示: Passphrase: 要被hash的输出明码Salt: 对密码保护的盐,避免彩虹表攻打CostFactor (N): CPU/memory cost 参数,必须是2的指数(比方: 1024)BlockSizeFactor (r): blocksize 参数ParallelizationFactor (p): 并行参数DesiredKeyLen (dkLen): 输入的衍生的key的长度hLen: hash函数的输入长度MFlen: Mix函数的输入长度这个函数的输入就是DerivedKey。 首先咱们须要生成一个expensiveSalt。首先失去blockSize: blockSize = 128*BlockSizeFactor 而后应用PBKDF2生成p个blockSize,将这p个block组合成一个数组: [B0...Bp−1] = PBKDF2HMAC-SHA256(Passphrase, Salt, 1, blockSize*ParallelizationFactor)应用ROMix对失去的block进行混合: for i ← 0 to p-1 do Bi ← ROMix(Bi, CostFactor)将B组合成新的expensiveSalt: expensiveSalt ← B0∥B1∥B2∥ ... ∥Bp-1接下来应用PBKDF2和新的salt生成最终的衍生key: return PBKDF2HMAC-SHA256(Passphrase, expensiveSalt, 1, DesiredKeyLen);上面是ROMix函数的伪代码: Function ROMix(Block, Iterations) Create Iterations copies of X X ← Block for i ← 0 to Iterations−1 do Vi ← X X ← BlockMix(X) for i ← 0 to Iterations−1 do j ← Integerify(X) mod Iterations X ← BlockMix(X xor Vj) return X其中BlockMix的伪代码如下: ...

October 14, 2021 · 1 min · jiezi

关于密码学:密码学系列之1Password的加密基础PBKDF2

简介1password是一个十分优良的明码管理软件,有了它你能够轻松对你的明码进行治理,从而不必再思考明码泄露的问题,据1password官网介绍,它的底层应用的是PBKDF2算法对明码进行加密。 那么PBKDF2是何方神圣呢?它有什么长处能够让1password得以青眼呢?一起来看看吧。 PBKDF2和PBKDF1PBKDF的全称是Password-Based Key Derivation Function,简略的说,PBKDF就是一个明码衍生的工具。既然有PBKDF2那么就必定有PBKDF1,那么他们两个的区别是什么呢? PBKDF2是PKCS系列的规范之一,具体来说他是PKCS#5的2.0版本,同样被作为RFC 2898公布。它是PBKDF1的替代品,为什么会代替PBKDF1呢?那是因为PBKDF1只能生成160bits长度的key,在计算机性能疾速倒退的明天,曾经不可能满足咱们的加密须要了。所以被PBKDF2替换了。 在2017年公布的RFC 8018(PKCS #5 v2.1)中,是倡议是用PBKDF2作为明码hashing的规范。 PBKDF2和PBKDF1次要是用来避免明码暴力破解的,所以在设计中退出了对算力的主动调整,从而抵挡暴力破解的可能性。 PBKDF2的工作流程PBKDF2实际上就是将伪散列函数PRF(pseudorandom function)利用到输出的明码、salt中,生成一个散列值,而后将这个散列值作为一个加密key,利用到后续的加密过程中,以此类推,将这个过程反复很屡次,从而减少了明码破解的难度,这个过程也被称为是明码增强。 咱们看一个规范的PBKDF2工作的流程图: 从图中能够看到,初始的明码跟salt通过PRF的操作生成了一个key,而后这个key作为下一次加密的输出和明码再次通过PRF操作,生成了后续的key,这样反复很屡次,生成的key再做异或操作,生成了最终的T,而后把这些最终生成的T合并,生成最终的明码。 依据2000年的倡议,一般来说这个遍历次数要达到1000次以上,才算是平安的。当然这个次数也会随着CPU计算能力的增强发生变化。这个次数能够依据安全性的要求自行调整。 有了遍历之后,为什么还须要加上salt呢?加上salt是为了避免对明码进行彩虹表攻打。也就是说攻击者不能预选计算好特定明码的hash值,因为不能提前预测,所以安全性得以进步。规范salt的长度举荐是64bits,美国国家标准与技术研究所举荐的salt长度是128 bits。 详解PBKDF2的key生成流程下面一大节,咱们以一种通俗易懂的形式通知大家,PBKDF2到底是怎么工作的。一般来说,理解到这一层也就够了,然而如果你想更加深刻,理解PBKDF2的key生成的底层原理,那么还请关注这一大节。 咱们下面介绍了PBKDF2是一个生成衍生key的函数,作为一个函数,那么就有输出和输入,咱们先看下PBKDF2的定义: DK = PBKDF2(PRF, Password, Salt, c, dkLen)PBKDF2有5个函数,咱们看下各个参数代表什么意思: PRF 是一个伪随机散列函数,咱们能够依据须要对其进行替换,比方替换成为HMAC函数。Password 是主明码用来生成衍生key。Salt是一个bits序列,用来对明码加盐。c 是循环的次数。dkLen 是生成的key要求的bits长度。DK是最初生成的衍生key。在上一节中,咱们能够看到其实最初的衍生key是由好几局部组成的,上图中的每一个T都代表着衍生key的一部分,最初将这些T合并起来就失去了最终的衍生key,其公式如下: DK = T1 + T2 + ⋯ + Tdklen/hlenTi = F(Password, Salt, c, i)下面的F是c次遍历的异或链。其公式如下: F(Password, Salt, c, i) = U1 ^ U2 ^ ⋯ ^ Uc其中: U1 = PRF(Password, Salt + INT_32_BE(i))U2 = PRF(Password, U1)⋮Uc = PRF(Password, Uc−1)HMAC明码碰撞如果PBKDF2的PRF应用的是HMAC的话,那么将会发送一些很有意思的问题。对于HMAC来说,如果明码的长度大于HMAC能够承受的范畴,那么该明码会首先被做一次hash运算,而后hash过后的字符串会被作为HMAC的输出。 ...

September 28, 2021 · 1 min · jiezi

关于密码学:密码学系列之Argon2加密算法详解

简介Argon2是一个密钥推导函数,在2015年7月被选为明码哈希大赛的冠军,它由卢森堡大学的Alex Biryukov、Daniel Dinu和Dmitry Khovratovich设计,Argon2的实现通常是以Creative Commons CC0许可(即公共畛域)或Apache License 2.0公布,并提供了三个相干版本,别离是Argon2d,Argon2i和Argon2id。 本文将会讨论一下Argon2的原理和应用。 密钥推导函数key derivation function在密码学中,密钥推导函数(KDF)是一种密码学哈希函数,它应用伪随机函数从一个机密值(如主密钥、明码或口令)中推导出一个或多个密钥。 KDF可用于将密钥拉伸成更长的密钥,或取得所需格局的密钥,例如将Diffie-Hellman密钥替换的后果转换为用于AES的对称密钥。 Password Hashing Competition密码学尽管是钻研明码的,然而其加密算法是越公开越好,只有公开能力去检视该算法的好坏,只有通过大家的彻底钻研,才可能让该算法得以在业界应用和流传。 最闻名的明码算法大赛必定是由NIST在2001年为了指定规范的AES算法举办的大赛,该大赛的目标寻找最新的加密算法来代替老的DES算法。在这次大赛中,涌现了许多优良的算法,包含CAST-256, CRYPTON, DEAL, DFC, E2, FROG, HPC, LOKI97, MAGENTA, MARS, RC6, Rijndael, SAFER+, Serpent, 和 Twofish等。最终Rijndael算法被选为最终的AES算法实现。 同样的PHC也是一个这样的算法较量,和NIST举办的算法较量不同的是,这是一个非官方的,由明码学家们组织的较量。它是在由Jean-Philippe Aumasson于2012年秋季发动。 2013年第一季度,公布了征集意见书的告诉,到2014年3月31日截止日期,共收到24份意见书。2014年12月,确定了9个入围名单。2015年7月,发表Argon2为优胜者。 Argon2算法Argon2 的设计很简略,旨在实现最高的内存填充率和对多个计算单元的无效利用,同时还能提供对 tradeoff attacks 的进攻(通过利用处理器的缓存和内存)。 Argon2有三个变种。Argon2i、Argon2d和Argon2id。Argon2d速度更快,并且应用数据依赖的内存拜访形式,这使得它对GPU破解攻打有很强的抵抗力,适宜没有side-channel timing attacks威逼的利用(例如加密货币)。 Argon2i则应用数据无关的内存拜访,这对于明码哈希和基于明码的密钥推导算法来说是首选,其特点是速度较慢,因为它在内存上运行了更多的解决逻辑,以避免 tradeoff attacks 。 Argon2id是Argon2i和Argon2d的混合体,采纳数据依赖型和数据独立型内存拜访相结合的形式,从而能够同时抵挡side-channel timing attacks和GPU破解攻打的能力。 Argon2的输出参数Argon2有两类输出参数,别离是primary inputs和secondary inputs。 primary inputs包含要加密的音讯P和nonce S,别离代表password和salt。 P的长度是0到232-1字节,S的长度是8到232-1字节(如果是做明码hash,举荐16字节)。 之所以叫做primary inputs,是因为这两个参数是必须输出的。 剩下的参数叫做secondary inputs,他们包含: 并行水平p,示意同时能够有多少独立的计算链同时运行,取值是1到224-1。Tag长度 , 长度从4到232-1字节。‘内存大小 m, 单位是兆,值取 8p到232-1。迭代器的个数t,晋升运行速度。取值1到232-1。版本号v,一个字节,取值0x13。安全值 K , 长度是0到232-1字节。附加数据 X,长度是0到232-1字节。Argon2的类型,0代表Argon2d,1代表Argon2i,2代表Argon2id。这些输出能够用上面的代码来示意: ...

September 20, 2021 · 4 min · jiezi

关于密码学:密码学系列之bcrypt加密算法详解

简介明天要给大家介绍的一种加密算法叫做bcrypt, bcrypt是由Niels Provos和David Mazières设计的明码哈希函数,他是基于Blowfish明码而来的,并于1999年在USENIX上提出。 除了加盐来抵挡rainbow table 攻打之外,bcrypt的一个十分重要的特色就是自适应性,能够保障加密的速度在一个特定的范畴内,即便计算机的运算能力十分高,能够通过减少迭代次数的形式,使得加密速度变慢,从而能够抵挡暴力搜寻攻打。 bcrypt函数是OpenBSD和其余零碎包含一些Linux发行版(如SUSE Linux)的默认明码哈希算法。 bcrypt的工作原理咱们先回顾一下Blowfish的加密原理。 blowfish首先须要生成用于加密应用的K数组和S-box, blowfish在生成最终的K数组和S-box须要消耗肯定的工夫,每个新的密钥都须要进行大略4 KB文本的预处理,和其余分组明码算法相比,这个会很慢。然而一旦生成结束,或者说密钥不变的状况下,blowfish还是很疾速的一种分组加密办法。 那么慢有没有益处呢? 当然有,因为对于一个失常利用来说,是不会常常更换密钥的。所以预处理只会生成一次。在前面应用的时候就会很快了。 而对于歹意攻击者来说,每次尝试新的密钥都须要进行漫长的预处理,所以对攻击者来说要破解blowfish算法是十分不划算的。所以blowfish是能够抵挡字典攻打的。 Provos和Mazières利用了这一点,并将其进一步倒退。他们为Blowfish开发了一种新的密钥设置算法,将由此产生的明码称为 "Eksblowfish"("expensive key schedule Blowfish")。这是对Blowfish的改良算法,在bcrypt的初始密钥设置中,salt 和 password 都被用来设置子密钥。而后通过一轮轮的规范Blowfish算法,通过交替应用salt 和 password作为key,每一轮都依赖上一轮子密钥的状态。尽管从实践上来说,bcrypt算法的强度并不比blowfish更好,然而因为在bcrpyt中重置key的轮数是能够配置的,所以能够通过减少轮数来更好的抵挡暴力攻打。 bcrypt算法实现简略点说bcrypt算法就是对字符串OrpheanBeholderScryDoubt 进行64次blowfish加密失去的后果。有敌人会问了,bcrypt不是用来对明码进行加密的吗?怎么加密的是一个字符串? 别急,bcrpyt是将明码作为对该字符串加密的因子,同样也失去了加密的成果。咱们看下bcrypt的根本算法实现: Function bcrypt Input: cost: Number (4..31) log2(Iterations). e.g. 12 ==> 212 = 4,096 iterations salt: array of Bytes (16 bytes) random salt password: array of Bytes (1..72 bytes) UTF-8 encoded password Output: hash: array of Bytes (24 bytes) //Initialize Blowfish state with expensive key setup algorithm //P: array of 18 subkeys (UInt32[18]) //S: Four substitution boxes (S-boxes), S0...S3. Each S-box is 1,024 bytes (UInt32[256]) P, S <- EksBlowfishSetup(cost, salt, password) //Repeatedly encrypt the text "OrpheanBeholderScryDoubt" 64 times ctext <- "OrpheanBeholderScryDoubt" //24 bytes ==> three 64-bit blocks repeat (64) ctext <- EncryptECB(P, S, ctext) //encrypt using standard Blowfish in ECB mode //24-byte ctext is resulting password hash return Concatenate(cost, salt, ctext)上述函数bcrypt 有3个输出和1个输入。 ...

September 16, 2021 · 3 min · jiezi

关于密码学:密码学系列之海绵函数sponge-function

简介海绵函数sponge function是密码学中应用的一种函数,它接管肯定长度的输出,而后输入肯定长度的输入,两头蕴含了无限个外部状态。 因为海绵函数的弱小性能,所以能够用来建模和实现许多明码原语,包含明码散列,音讯身份验证码,生成掩码,流明码,伪随机数生成器等。 本文将会解说海绵函数的构造。 海绵函数的构造咱们先看一个海绵函数的结构图: 这个函数被分成了两局部,右边局部叫做排汇局部,左边局部叫做输入局部,一吸一出,像是海绵一样,所以叫做海绵函数。 P示意的是输出的字符串,Z示意的时候输入字符串。 一个海绵函数由三局部组成,别离是state, 函数f和填充函数pad。 state就是上图的r+c局部,r被称为Bitrate, c被称为Capacity。 P被分成n份,每一份都会跟Bitrate进行异或操作,如果P的长度不是Bitrate的整数倍,那么须要应用Pad函数进行填充。 每一轮,Bitrate跟P进行异或操作的后果作为最新的Bitrate, 而后生成新的state,而后这个state又被f(state)来替换。 其中函数 f 是 从n个{0,1} 到n个{0,1}的映射。 就这样一轮一轮进行上来,直到所有的P都参加了运算。 输入局部是将最终生成的state进行f运算,每次运算都取Bitrate局部作为输入,从而失去最终的输入。 海绵函数的利用因为海绵函数的优良的个性,所以被用在很多方面。比方SHA-3的实现算法Keccak就是应用的海绵函数。 通过替换f和多轮置换,海绵函数能够生成十分平安的明码算法,所以失去了宽泛的应用。 本文已收录于 http://www.flydean.com/36-sponge-function/ 最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现! 欢送关注我的公众号:「程序那些事」,懂技术,更懂你!

September 14, 2021 · 1 min · jiezi

关于密码学:密码学系列之Merkle–Damgård结构和长度延展攻击

简介Merkle–Damgård构造简称为MD构造,次要用在hash算法中抵挡碰撞攻打。这个构造是一些优良的hash算法,比方MD5,SHA-1和SHA-2的根底。明天给大家解说一下这个MD构造和对他进行的长度延展攻打。 MD构造MD构造是Ralph Merkle在1979年的博士论文中形容的。因为Ralph Merkle 和 Ivan Damgård 别离证实了这个构造的合理性,所以这个构造被称为Merkle–Damgård构造。 接下来,咱们看下MD构造是怎么工作的。 MD构造首先对输出音讯进行填充,让音讯变成固定长度的整数倍(比方512或者1024)。这是因为压缩算法是不能对任意长度的音讯进行解决的,所以在解决之前必须进行填充。 通常来说,咱们会应用恒定的数据,比如说0来填充整个音讯块。 举个例子,如果咱们的音讯是“HashInput”,压缩块的大小是8字节(64位),那么咱们的音讯将会被分成两个块,前面一个块应用0来填充,将会失去:“HashInpu t0000000”。 然而这样做往往是不够的,因为通常对于压缩函数来说,会删除掉最初面的额定的0,所以导致填充和不填充最初计算出来的hash值是一样的。 为防止这种状况,必须更改填充常量数据的第一位。因为常量填充通常由零组成,因而第一个填充位将强制更改为“ 1”。 也就是“HashInpu t1000000”。 咱们还能够对填充进行进一步的加强,比方应用一个额定的block来填充音讯的长度。 然而额定的应用一个block往往有点节约,一个更加节约空间的做法就是,如果填充到最初一个block的0中有住够的空间的话,那么能够音讯的长度放在那里。 填充好block之后,接下来就能够对音讯进行压缩了,咱们看下一下MD的流程图: 音讯被分成了很多个block,最开始的初始化向量和第一个block进行f操作,失去了的后果再和第二个block进行操作,如此循环进行,最终失去了最初的后果。 长度延展攻打在密码学中长度延展攻打就是指攻击者通过已知的hash(message1)和message1的长度,从而可能晓得hash(message1‖message2)的值。其中‖ 示意的是连接符。并且攻击性并须要晓得message1到底是什么。 上一节咱们讲到的MD构造,是将音讯分成一个一个的block,前一个block 运算进去的值会跟下一个block再次进行运算,这种构造能够很不便的进行长度延展攻打。前提是咱们须要晓得原音讯的长度。 咱们举个例子,假如咱们有上面的申请: Original Data: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggoOriginal Signature: 6d5f807e23db210bc254a28be2d6759a0f5f5d99下面的例子是给编号为1的用户发送鸡蛋馅的华夫饼,并附带了音讯签名,以保障音讯的正确性。这里音讯签名应用的MAC算法。 如果歹意攻击者想把waffle的值从eggo批改成为liege。 那么新的数据将会是这样的: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo&waffle=liege为了对该新音讯进行签名,通常,攻击者须要晓得该音讯签名应用的密钥,并通过生成新的MAC来生成新的签名。然而,通过长度扩大攻打,能够将哈希(下面给出的签名)作为输出,并在原始申请已中断的中央持续进行hash输入,只有晓得原始申请的长度即可。 如果思考到padding(音讯填充)的影响的话,咱们还须要复原原始音讯的填充内容,而后在复原过后的内容之后再增加咱们的攻打代码: New Data: count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo\x80\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x02\x28&waffle=liege这样咱们就能够失去新的MAC值: New Signature: 0e41270260895979317fff3898ab85668953aaa2Wide pipe为了防止长度延展攻打,咱们能够对MD构造进行一些变形。 先看一下Wide Pipe构造: wide pipe和MD的流程基本上是统一的,不同的是生成的两头长期的加密后的音讯长度是最终生成音讯长度的两倍。 这也就是为什么上图中会有两个初始向量IV1 和 IV2。如果最终的后果长度是n的话,那么在两头生成的后果的长度就是2n。咱们须要在最初的final 这一步中,将2n长度的数据缩减为n长度的数据。 SHA-512/224 和 SHA-512/256 只是简略的抛弃掉一半数据。 Fast wide pipe还有一种比wide pipe更快的算法叫做fast wide pipe: 和wide pipe不同的是,它的次要思维是将前一个链接值的一半转发给XOR,而后将其与压缩函数的输入进行XOR。 ...

July 23, 2021 · 1 min · jiezi

关于密码学:密码学系列之memorybound函数

简介memory-bound函数能够称为内存受限函数,它是指实现给定计算问题的工夫次要取决于保留工作数据所需的内存量。和之绝对应的就是计算受限compute-bound的函数,在计算受限的函数中,计算所须要的计算步骤是其决定因素。 本文将会介绍一下内存受限函数和它跟内存函数的关系。 内存函数内存函数和内存受限函数看名字如同很相似,其实他们的作用是不同的,咱们先来看下内存函数。 在学习算法中,有一个非常简单好用的算法叫做递归算法,相熟递归算法的敌人可能都晓得,递归算法尽管好用,然而有个毛病就是会反复计算递归过程中的函数,比如说递归中最经典的斐波拉赫数列: Recursive_Fibonacci (n) { if (n == 0) return 0 if (n == 1) return 1 return Recursive_Fibonacci (n-1) + Recursive_Fibonacci (n-2) }很简略,然而咱们来思考下计算过程,F(3)=F(2)+F(1), 而F(4)=F(3)+F(2),在这个过程中须要计算2次F(2)的值。如果计算的N值够大的话,反复计算的值还会更多。 一个解决办法就是将之前计算过的后果应用内存存起来,这种办法就叫做内存函数: Fibonacci (n) { for i = 0 to n-1 results[i] = -1 // -1 means undefined return Fibonacci_Results (results, n); } Fibonacci_Results (results, n) { if (results[n] != -1) // If it has been solved before, return results[n] // look it up. if (n == 0) val = 0 else if (n == 1) val = 1 else val = Fibonacci_Results(results, n-2 ) + Fibonacci_Results(results, n-1) results[n] = val // Save this result for re-use. return val }尽管间接递归的逻辑很简略,写起来也很不便,然而它的工夫复杂度会更高。 ...

July 14, 2021 · 1 min · jiezi

关于密码学:密码学系列之twofish对称密钥分组算法

简介之前的文章咱们讲到blowfish算法因为每次加密的块比拟小只有64bits,所以不倡议应用blowfish加密超过4G的文件。同时因为加密块小还会导致生日攻打等。所以才有了blowfish的继承者twofish。 明天咱们一起来揭秘一下twofish这个加密算法。 twofish的起源twofish和blowfish一样,也是一种对称加密算法。不同的是twofish的块大小是128bits,而它的密钥长度是256bits。 AES置信大家都很相熟了,AES的全称是The Advanced Encryption Standard,它是由美国国家标准技术研究院(NIST)批准的规范的对称分组明码技术。 AES是从1997年到2000年公开选拔进去的。次要是为了替换DES而创立的。因为DES只有56位密钥,容易受到暴力攻打。 NIST在1997年1月2日发表,他们心愿抉择DES的继任者,即AES。 与DES一样,它也应是“一种可能在二十世纪之前很好地爱护政府敏感信息的,未公开的,公开的加密算法。” 然而,NIST并没有简略地公布一个继任者,而是进行公开的提拔,候选者须要提供相干的材料,证实其比DES优良。 这种开放式征集立刻引起了人们的浓厚兴趣。 在随后的几个月外面,NIST收到了来自多个国家的15个提案。他们是CAST-256,CRYPTON,DEAL,DFC,E2,FROG,HPC,LOKI97,MAGENTA,MARS,RC6,Rijndael,SAFER +,Serpent和Twofish。 在随后的评估中,明码学家对这些候选的算法进行了包含安全性,性能和无限环境运行等因素进行了评估,最终在1999年8月发表了5个最终入围的算法:MARS ,RC6,Rijndael,Serpent和Twofish。 最终在2000年10月2日,NIST发表选中Rijndael作为最终的AES算法。并于2001年11月26日作为正式的AES规范。 twofish尽管没有作为最初的AES规范,然而可能跻身5强,也是很厉害了。 twofish的性能在2000年的时候,对于大多数平台来说,twofish在128-bit keys的体现要比Rijndael 要慢,这也是为什么Rijndael会入选为AES规范的起因。然而在256-bit keys的体现要好于Rijndael 。 然而随着Rijndael 作为AES的规范,越来越多的硬件都基于AES做了优化,最初导致twofish和Rijndael 的差距越来越大。 twofish和Blowfish一样也是收费的。twofish的原理twofish是由blowfish演变来的。咱们先看下twofish的工作图: twofish和DES一样,也是应用的Feistel structure。 首先将128bits的明文分成4局部,而后别离和K0,K1,K2,K3进行异或操作,生成4个后果,咱们称他们为A1,A2,A3,A4。 虚线括起来的局部是F函数。 A1,A2作为F的输出生成的后果和A3进行异或操作,而后右移一位,和A4左移一位的后果进行异或操作,而后替换左右局部的地位。 最初一轮的输入不进行替换间接与四个扩大密钥字进行异或而失去密文C。 咱们再来看看F函数。 F是64位数据上与密钥相干的置换函数,它有三个参数,R1,R2两个输出,还有一个r示意的子项的轮数。 R1和R2先通过S-box的变换,而后乘以MDS矩阵,而后再进行PHT变换,最初和子密钥进行异或操作。 本文已收录于 http://www.flydean.com/twofish/ 最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

June 30, 2021 · 1 min · jiezi

关于密码学:密码学系列之blowfish对称密钥分组算法

简介Blowfish是由Bruce Schneier在1993年创造的对称密钥分组加密算法,相似的DES和AES都是分组加密算法,Blowfish是用来代替DES算法呈现的,并且Blowfish是没有商用限度的,任何人都能够自在应用。 比照而言,尽管AES也是一种明码强度很高的对称明码算法,然而如果须要商用的话要向NIST领取受权费用。 blowfish详解blowfish和DES一样,应用的是feistel明码来进行分组加密。blowfish的分组块大小是64bits,可变密钥长度能够从32bits到448bits不等。 blowfish须要进行16轮的feistel加密操作,咱们先从下图大抵感受一下blowfish算法的加密流程: 大略的流程就是将P(原始数据)分成左右两局部,先拿右边的局部和Kr 做异或操作,得出的后果调用F函数,最初将F函数的输入后果和右半局部进行异或操作。 调换左右局部的地位,持续进行这样的操作,总共进行16轮就失去了最终的加密后果。 大家能够看到整个加密过程中最重要的两个变量就是Kr 和 F函数。 接下来咱们会具体进行解说。 密钥数组和S-box密钥数组从图上咱们能够看到,Kr 的范畴是从K1 到 K18 。总共有18个密钥组成的数组。 每个密钥的长度是32位。 咱们来看一下密钥数组是怎么生成的。 首先咱们应用随机数来对密钥数组进行初始化。怎么能力生成一个足够随机的32位数字呢? 一个很罕用的办法就是应用常量的小数局部,将其转换成为16净值,如下所示: K1 = 0x76a301d3 K2 = 0xbc452aef ... K18 = 0xd7acc4a5 还记得blowfish的可变密钥的长度吗?是从32bits到448bits,也就是从1到14个32位的数字。咱们用Pi来示意,那么就是从P1到P14总共14个可变密钥。 接下来咱们须要应用K和P进行操作,从而生成最终的K数组。 咱们应用K1和P1进行异或操作,K2和P2进行异或操作,始终到K14和P14。 因为P只有14个值,而K有18个值,所以接下来咱们须要重复使用P的值,也就是K15和P1进行异或,K16和P2进行异或,直到K18和P4。 将异或之后的值作为新的K数组的值。 当初咱们取得了一个新的K数组。 留神,这个K数组并不是最终的数组,咱们接下来看。S-box在生成最终的P数组之前,咱们还要介绍一个概念叫做S-box。 在密码学中,s-box的全称是substitution-box,也就是一个替换盒子,能够将输出替换成不同的输入。 S-box 接管 n个bits的输出,而后将其转换成m个bits的输入。 这里n和m能够是不等的。 咱们看一下DES中S-box的例子: 下面的S-box将6-bits的输出转换成为4-bits的输入。 S-box能够是固定的,也能够是动静的。比方,在DES中S-box就是动态的,而在Blowfish和Twofish中S-box就是动静生成的。 Blowfish算法中的F函数须要用到4个S-box,F函数的输出是32bits,首先将32bits分成4份,也就是4个8bits。 S-box的作用就是将8bits转换成为32bits。 咱们再具体看一下F函数的工作流程: S-box生成的值会进行相加,而后进行异或操作。最终失去最终的32bits。 S-box的初始值也能够跟K数组一样,应用常量的小数局部来初始化。 生成最终的K数组在下面两节,咱们生成了初始化的K数组和S-box。 blowfish认为这样还不够平安,不够随机。 咱们还须要进行一些操作来生成最终的K数组。 首先咱们取一个全为0的64bits,而后K数组和S-box,利用blowfish算法,生成一个64bits。 将这个64bits分成两局部,别离作为新的K1 和 K2。 而后将这个64bits作为输出,再次调用blowfish算法,作为新的K3 和 K4。 顺次类推,最终生成所有K数组中的元素。 4个S-box的数组也依照下面的流程来进行生成。从而失去最终的S-box。 blowfish有了最终的K数组和S-box,咱们就能够真正的对要加密的文件进行加密操作了。 用个伪代码来示意整个流程: uint32_t P[18];uint32_t S[4][256];uint32_t f (uint32_t x) { uint32_t h = S[0][x >> 24] + S[1][x >> 16 & 0xff]; return ( h ^ S[2][x >> 8 & 0xff] ) + S[3][x & 0xff];}void encrypt (uint32_t & L, uint32_t & R) { for (int i=0 ; i<16 ; i += 2) { L ^= P[i]; R ^= f(L); R ^= P[i+1]; L ^= f(R); } L ^= P[16]; R ^= P[17]; swap (L, R);}void decrypt (uint32_t & L, uint32_t & R) { for (int i=16 ; i > 0 ; i -= 2) { L ^= P[i+1]; R ^= f(L); R ^= P[i]; L ^= f(R); } L ^= P[1]; R ^= P[0]; swap (L, R);} // ... // initializing the P-array and S-boxes with values derived from pi; omitted in the example // ...{ for (int i=0 ; i<18 ; ++i) P[i] ^= key[i % keylen]; uint32_t L = 0, R = 0; for (int i=0 ; i<18 ; i+=2) { encrypt (L, R); P[i] = L; P[i+1] = R; } for (int i=0 ; i<4 ; ++i) for (int j=0 ; j<256; j+=2) { encrypt (L, R); S[i][j] = L; S[i][j+1] = R; }}blowfish的利用从下面的流程能够看出,blowfish在生成最终的K数组和S-box须要消耗肯定的工夫,然而一旦生成结束,或者说密钥不变的状况下,blowfish还是很疾速的一种分组加密办法。 ...

June 21, 2021 · 2 min · jiezi

关于密码学:密码学系列之feistel-cipher

简介feistel cipher也叫做Luby–Rackoff分组明码,是用来构建分组加密算法的对称构造。它是由德籍明码学家Horst Feistel在IBM工作的时候创造的。feistel cipher也被称为Feistel网络。 很多分组加密算法都是在feistel cipher的根底上倒退起来的,比方十分有名的DES算法。 在feistel cipher中,加密和解密的操作十分类似,通常须要进行多轮加密和解密操作。 Feistel网络的原理Feistel网络中会用到一个round function也叫做轮函数,这个函数接管两个输出参数,别离是分组数据(原始数据的一半)和子key,而后生成和分组数据同样长度的数据。 而后应用上一轮生成的数据和原始数据的另一半进行XOR异或操作,作为下一轮轮函数的输出。 就这样一轮一轮进行上来最初生成加密过后的数据。 解密的流程和加密的流程是相似的,只不过把加密的操作反过来。 Feistel网络的轮数能够任意减少。不管多少轮都能够失常解密。 解密与轮函数f无关,轮函数f也不须要有逆函数。轮函数能够设计得足够复制。 加密和解密能够应用完全相同的构造来实现。从下面咱们讲到的能够看到,加密和解密其实是没有什么区别的。 Feistel网络的例子咱们用一个图的形式来介绍一下Feistel的工作流程: 上图中F示意的就是round function也就是轮函数。 K0,K1,K2...,Kn示意的是子key,别离作为各轮的输出。 原始数据被分成了左右两边相等的局部,(L0,R0) 每一轮都会进行上面的操作: Li+1 = RiRi+1 = Li XOR F(Ri,Ki)最初的加密出的后果就是(Ri+1,Li+1) 解密的过程是加密过程的逆序,每一轮解密都会进行上面的操作: Ri = Li+1Li = Ri+1 XOR F(Li+1,Ki)最终失去咱们的原始数据(R0,L0) Feistel网络的实践钻研Michael Luby 和 Charles Rackoff 证实了如果轮函数是应用Ki为种子的明码平安的伪随机函数,那么通过三轮操作之后,生成的分组明码就曾经是伪随机排列了。通过四轮操作能够生成“强”伪随机排列。 什么是伪随机数呢? 考虑一下如果在计算机中生成随机数,因为计算机中的数据是由0和1组成的,所有的数据都是确定的,要么是0要么是1,所以计算机程序并不能生成真正的随机数。 如果要让计算机来生成随机数,通常的做法就是将输出通过肯定的算法函数进行计算,从而失去解决过后的数字。 如果这个算法函数是确定的,也就是说同样的输出能够失去同样的输入,那么这个数就不是随机产生的,这个数就被称为伪随机数。 伪随机数是用确定性的算法计算出来自[0,1]均匀分布的随机数序列。并不真正的随机,但具备相似于随机数的统计特色,如平均性、独立性等。 因为Luby和Rackoff的钻研十分重要,所以Feistel明码也称为Luby–Rackoff明码。 Feistel网络的拓展除了咱们之前介绍过的DES之外,很多算法都用到了Feistel网络结构,比方Blowfish,Twofish等等。 因为Feistel网络的对称性质和简略的操作,使得通过硬件的形式来实现Feistel网络变得非常简单,所以Feistel网络的利用十分的宽泛。 本文已收录于 http://www.flydean.com/feistel-cipher/ 最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现! 欢送关注我的公众号:「程序那些事」,懂技术,更懂你!

June 16, 2021 · 1 min · jiezi

关于密码学:密码学系列之生日攻击

简介生日攻打其实是一个概率论的问题,也就是说一个看起来很难产生的事件,事实上它产生的概率却很大。这种主观上和事实上的概率差距,让随机攻打胜利的几率变的更高,这样的攻打就叫做生日攻打。 生日问题的由来生日问题也叫做生日悖论,它是这样这样形容的。 如果随机抉择n集体,那么这个n集体中有两个人的生日雷同的概率是多少。如果要想概率是100%,那么只须要抉择367集体就够了。因为只有366个生日日期(包含2月29日)。 如果想要概率达到99.9% ,那么只须要70集体就够了。50%的概率只须要23集体。 对于当初的幼儿园小朋友来说,一个班上差不多有30人,那么将会有大于50%的几率,班上有两个人的生日是一样的。 听起来是不是很神奇?跟咱们第一映像中的基数是不是要少很多。 咱们看一张概率图: 在理论利用中,能够利用生日问题中的概率模型,从而缩小碰撞攻打的复杂度,或者来评估一个hash函数中可能呈现碰撞攻打的几率。 怎么计算呢? 如果P(A) 是生日雷同的概率,那么P(A) = 1 - P(A<super>’</super>) ,其中P(A<super>’</super>)是生日不同的概率。 一个人生日不同的概率是365/365,两个人生日不同的概率就是365/365 * 364/365 ,顺次类推。 咱们能够失去23集体生日不同的概率大略就是 0.492703。 也就是说23集体中有两个人生日雷同的概率能够大于50%。 再看一张表来个更加直观的形容: 生日问题的衍生生日问题的取值范畴是在一年的365天之内,也就是说生日只可能有365种可能性。 咱们将这个问题扩大一下到个别的状况,假如有一个函数f,它的输入范畴是H,那么咱们的攻打就是找到两个不同的x,y,让f(x)=f(y)。 这时候,咱们能够称x和y产生了碰撞。 依据概率论的公式,咱们想要达到50%的几率,那么须要尝试的次数是: 如果以bits位来示意可能计算出的后果的话,咱们能够参考上面的概率表: 生日攻打的利用生日攻打个别利用在数字签名中。一般来说为了对机密消息进行签名,因为加密的限度,如果音讯很大的状况下,不可能对所有的音讯进行签名,通常会对音讯计算hash值,而后对这个hash值进行签名。 比方有人想做一个欺诈性的合同,那么会在原合同的根底上进行批改,一直的进行尝试,从而找到一个批改后的合同,让合同和之前合同的hash是一样的,从而导致两者的签名也是一样的。 怎么抵挡这种攻打呢?依据咱们生日攻打的公式,当然是将签名计划应用的哈希函数的输入长度抉择得足够大,以使生日攻打在计算上变得不可行。 本文已收录于 http://www.flydean.com/birthday-attack/ 最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现! 欢送关注我的公众号:「程序那些事」,懂技术,更懂你!

June 9, 2021 · 1 min · jiezi

关于密码学:模型和Shamir秘密共享机制

模型平安多方计算的平安显然是在有攻击者状况下的平安。在不同情景下,实现平安的难度也不同。最极其的例子是一个平安多方计算协定的所有参与者都是歹意参与者,那么这个协定的安全性就很难保障了。要实现平安,首先应该针对不同的状况建设不同的模型,而后针对这些模型进行钻研。 首先假如有攻击者(Adversary),攻击者能够通过各种伎俩收购或者管制(Corrupt)局部参与者,而参与者一旦被收购或者管制,该参与者的所有通信历史信息和本地信息都会被攻击者把握。攻击者能够理论了解成黑客,他通过黑客伎俩入侵到了参与者的计算机中,获得了参与者计算机的控制权,因而能够把握所有该参与者把握的信息。攻击者也能够了解成竞争公司的人,通过金钱来贿赂参与者,以此获得信息。 那么显然,攻击者可能最大收购的参与者人数,很大水平上影响了协定是否平安。(t,n)门限攻击者构造是指参与者总数是n,攻击者最多可能收购t个参与者。对于攻击者构造,常常会说是图片的攻击者构造指攻击者收购的参与者汇合中的人数小于参与者总人数的 1/2,即 < 1/2;图片的攻击者构造指攻击者收购的参与者汇合中的人数小于参与者总人数的 < 1/3。 攻击者模型分为半诚恳攻击者模型和歹意攻击者模型。在半诚恳攻击者模型下,被攻击者收购的参与者恪守协定,不会在协定执行中途退出,也会诚恳地发送本人的计算结果,不会篡改协定计算结果。然而被收购的参与者的所有信息,包含历史通信信息、计算结果等都会被攻击者得悉。在歹意攻击者模型下,被攻击者收购的参与者不会再诚恳地恪守协定,可能会篡改协定计算结果,其发送给其余参与者的信息有可能是虚伪和伪造的。 攻击者的能力还能够依据其计算能力进行划分,在计算意义下平安的模型中,攻击者的计算能力是概率多项式工夫的,意味着攻击者无奈解决常见的艰难问题,即便计算出来,所破费的工夫也曾经超过了信息的有效期,取得的信息曾经是过期的信息。另一种模型为信息论意义下平安的模型,在这种模型下,攻击者的计算能力是有限的。 门限机制和Shamir机密共享设 t 和 n 为两个正整数,且 t≤n。n个须要共享机密的参与者汇合为 = {1,… , },一个(t,n)门限机密共享体制是指:假如1,… ,要共享同一个机密s, 将s称为主机密,有一个机密管理中心0来负责对s进行治理和调配。机密管理中心0把握有机密调配算法和机密重构算法,这两个算法均满足重构要求和安全性要求。 机密管理中心0首先通过将主机密s输出机密调配算法,生成n个值,别离为1,… ,,称1,… ,为子机密。而后机密管理中心0别离将机密调配算法产生的子机密1,… ,通过0与之间的平安通信信道机密地传送给参与者,参与者不得向任何人泄露本人所收到的子机密。 门限值t指的是任意大于或等于t个参与者,将各自把握的子机密进行共享,任意的一个参与者在取得其余−1个参与者所把握的子机密后,都可独立地通过机密重构算法复原出主机密s。而即便有任意的−个参与者失落了各自所把握的子机密,剩下的 t 个参与者仍旧能够通过将各自把握的子机密与其余参与者共享,再应用机密重构算法来重构出主机密s。安全性要求是指任意攻击者通过收购等伎俩获取了少于 t个的子机密,或者任意少于 t 个参与者串通都无奈复原出主机密 s,也无奈失去主机密 s 的信息。 Shamir于1979年,基于多项式插值算法设计了Shamir(t,n)门限机密共享体制,它的机密调配算法如下: 首先假如为q元无限域,q是素数且>。图片是参与者汇合,P共享主机密,∈,机密管理中心0按如下所述的步骤对主机密进行调配,为了可读性起见,以下公式均略去了模q操作: 参与者0机密的在无限域中随机选取−1个元素,记为图片,并取以为变元的多项式() 对于1≤≤ ,0机密计算=() 对于1≤≤,0通过平安信道机密地将(, )调配给 Shamir(t,n)门限共享体制的机密重构能够应用艰深的解方程法,即t个方程能够确定t个未知数,而这t个未知数即为包含主机密在内的多项式()的各项系数。如参与者1,… ,把握了子机密(1),…,(),解方程: 即可求解出系数。另一种形式是应用多项式插值法进行重构主机密。假如这 t 个子秘钥别离为( ,) ,其中=(),=1,…, 且 ≠ 时 ≠ 。参与者1,… ,独特计算 显然,ℎ()是一个−1次的多项式,且因为≠时≠,每个加式的分母均不为零,因而对于=1,…,,=ℎ()=() 。又依据多项式的性质,如果存在两个最高次均为−1次的多项式,这两个多项式在个互不雷同的点所取的值均雷同,那么这两个多项式雷同。即ℎ()=(),进而参与者计算ℎ(0)=(0)=,即可复原主机密。 对于无限域上n-1次的多项式,设为(),存在无限域上的n个元,记为1,…,,使得: 称(1,…,)为重组向量(recombinationn vector),因为证实过程较为繁琐,因而具体证实过程写在最初。 对于矩阵M:设机密调配多项式为(),参与者把握的子机密为(),因为存在重组向量(1,…,),因而有: 若要计算重组向量,可通过计算矩阵的逆矩阵图片来计算重组向量(1,…,): 在取得重组向量后,可构建基于Shamir门限体制的平安多方计算协定。首先假如={1,…, }是参与者汇合,把握输出(1≤≤),须要独特计算的函数为(1,…, )。在无限域上的ℎ(+1,)门限体制次要流程为: 输出阶段,每个参与者将本人的输出,利用ℎ(+1, )门限机密共享体制,机密选取最高 t 次的随机多项式(),满足(0)= 。而后将()发送给参与者。 ...

June 5, 2021 · 1 min · jiezi

关于密码学:安全多方计算的历史和应用

姚期智院士于1982年通过 “百万富翁问题”提出了平安单方计算问题,“百万富翁问题”即两个百万富翁如何在没有第三者参加的状况下,比拟二者间谁更加富裕: 平安单方计算可被艰深的解释为:有两个人Alice和Bob,Alice把握数a,Bob把握数b,如何在Alice、Bob不通知对方数a、b的具体值状况下,独特利用数a和b进行计算。 姚期智院士在提出“百万富翁问题”的同时,给出了三种解决办法,并探讨了在机密投票(Secret Vote)、不经意协商(Oblivious Negotiation)、隐衷查问数据(Private Querying of Database)的利用。 之后Goldreich在1987年对平安多方计算(Secure Multi-Party Computation)进行了探讨,提出了能够计算任意函数的计算意义下平安的平安多方计算协定。 Goldreich 还从实践上证实了能够通过通用电路(Universal Circuit)估值来实现所有的平安多方计算协定。其后于1988年,Goldreich 对平安多方计算进行了总结和安全性定义。 之后在1989年,Beaver等人钻研了信息论平安模型下的平安多方科学计算问题,提出了能够实现信息论平安的,复杂程度为常数轮的平安多方算数运算协定。 平安多方计算兼具实践钻研和理论利用价值,在电子投票、隐衷爱护的数据挖掘、机器学习、区块链、生物数据比拟、云计算等畛域有着宽泛的利用前景。 现实生活中的投票选举通过对立采纳空白选派、投票箱、有公信力的计票人以及全程录像直播等形式来确保偏心公正。而在电子投票畛域,投票人在家投票时,家中的计算机可能已被感化病毒,投票后果可能被歹意获取篡改等,因而电子投票系统必须保障投票人晓得本人的投票信息是否被正确提交,是否被歹意攻击者篡改,同时要爱护投票人的投票信息不被除了计票人外的其他人获取。平安多方计算为这种分布式环境下如何进行爱护隐衷信息和确保后果正确性的问题提供了良好解决方案。 Cramer等人基于ElGamal门限加密技术和零常识证实提出了首个多选一电子投票计划,之后Damgard等人基于Pailier同态加密技术提出了多选多的电子投票计划。在1992年,A.Fujioka等应用盲签名技术提出了驰名的FOO电子投票协定。 数据挖掘作为一个十分无效的数据分析工具,能够发现数据中隐含的法则,对迷信和政策钻研、商务决策等方面有着重要利用。然而被开掘的数据中往往都有着大量敏感性的信息,因而必须收到爱护,在隐衷爱护下进行数据挖掘。在多方状况下进行数据挖掘时,参与者往往不违心共享数据,只违心共享数据挖掘的后果,这种状况在迷信和医学钻研等方面十分常见,如各个医疗机构的病人信息是敏感信息,不会违心走漏。利用平安多方计算能够在爱护各方数据信息不被泄露的同时多方合作实现数据挖掘。 机器学习已被利用到各个领域,引发了大量改革,如图像和语音辨认、异样检测等。而在机器学习想要获得好的成果,须要大量数据进行模型训练。训练数据的隐衷爱护同样是问题。在多个机构单干进行模型训练时,数据分布在不同参与者处,平安多方计算能够在爱护敏感数据的隐衷性的同时让各个机构胜利进行模型训练。 总之,当各个参与者处于分布式环境下,又有数据隐衷爱护的要求时,就非常适宜利用平安多方。

June 5, 2021 · 1 min · jiezi

关于密码学:密码学系列之碰撞抵御和碰撞攻击collision-attack

简介hash是密码学和平时的程序中常常会用到的一个性能,如果hash算法设计的不好,会产生hash碰撞,甚至产生碰撞攻打。 明天和大家具体探讨一下碰撞攻打。 什么是碰撞攻打所谓碰撞攻打指的是对于同一个hash函数来说,两个不同的input通过hash计算失去了同样的hash值。用公式来说就是: hash(m1) = hash(m2)这个攻打有什么作用呢? 举个例子,通常咱们通过网络下载应用程序或者软件,除了下载链接之外,还会提供一个MD5的校验码。这个校验码就是用来校验下载的软件是不是官网提供的软件。 MD5算法也是一种hash算法,如果歹意用户能够结构一个和原始软件一样MD5的软件的话,就很可能施行碰撞攻打。 还有一种状况用在数字签名中。在数字签名中,因为效率的起因,如果文章特地大的状况下,通常会先取文章的hash值,而后对这个hash进行签名。 所以这外面有两个能够被攻打的中央,一个就是hash碰撞,一个就是签名算法。 举个例子,比如说师妃暄给徐子陵写了一封信A,说是凌晨的时候在竹林有事件相告,然而没有间接交给徐子陵而是给了他的好兄弟寇仲,寇仲思考到夜晚太危险了,不想让他的好兄弟冒险,于是伪造了这封信A,结构了和原来的信A同样hash值的信B,并附带了师妃暄的签名。 徐子陵收到了信B和签名,通过验证发现的确是师妃暄写的,于是就没有去赴约。 碰撞攻打取决于hash算法的强度,像是MD5和SHA-1这些hash算法曾经被证实是不平安的,能够在很快的工夫内被攻破。 抉择前缀抵触攻打除了后面传统的碰撞攻打之外,还有一种叫做Chosen-prefix collision attack抉择前缀抵触攻打。 攻击者能够抉择两个不同的前缀p1和p2,而后附在不同的字符串m1,m2后面,那么有: hash(p1 ∥ m1) = hash(p2 ∥ m2) 其中 ∥ 示意连接符咱们看一个在SHA-1中由盖坦.勒伦(Gatan Leurent)和托马.佩林(Thomas Peyrin)发现的一个攻打的例子,这是两个别离带有前缀99040d047fe81780012000和99030d047fe81780011800的例子。 两个音讯内容能够从上面下载: messageA: sha-mbles.github.io/messageA messageB:sha-mbles.github.io/messageB 咱们能够看下音讯的截图: 这两个音讯通过sha1sum运算,能够失去雷同的hash值。 sha1sum messageA : 8ac60ba76f1999a1ab70223f225aefdc78d4ddc0 sha1sum messageB: 8ac60ba76f1999a1ab70223f225aefdc78d4ddc0 java中的hash攻打java中有一个常常会用到的类叫做hashMap,在JDK7之前,HashMap在存储数据的时候如果遇到了hash抵触,则会将数据以链表的模式插入到这个hash节点的最初。 这样会有什么毛病呢? 那么就是如果有歹意攻击者,始终向hashMap中插入同样hash值的key对象,那么hashMap实际上就会进化成为一个链表。 这样会大大影响hashMap的查问效率。如果数据特地大的话,可能就会导致DDOS攻打。 这个问题的根本原因就是java中hashMap中的hash计算太过简略,很容易就可能找到雷同hash值的key。 实际上在2011年tomcat还公布了一个对于这个问题的破绽解决方案。 尽管这是java的问题,然而最初的锅还是由tomcat来背。tomcat的做法就是限度maxPostSize,从最大的20M改成了10K,这样能够无效的缩小申请中的item大小。 当然,在JDK8中,原来的链表构造曾经被改成了红黑树结构,置信也是为了防止这种DDOS hash攻打的计划。 原像攻打Preimage attack和碰撞攻打相似的还有一个攻打叫做原像攻打。 原像攻打的抵挡须要满足两个条件,第一个条件是给定一个hash值y,很难找到一个x,使得hash(x)=y。 第二个条件就是给定一个x,很难找到一个y,使得hash(x) = hash(y)。 很显著,碰撞攻打的抵挡肯定满足第二个条件,然而不肯定满足第一个条件。 本文已收录于 http://www.flydean.com/collision-attack/ 最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现! 欢送关注我的公众号:「程序那些事」,懂技术,更懂你!

June 3, 2021 · 1 min · jiezi

关于密码学:密码技术国密证书及go语言生成自签国密证书

go语言生成自签证书文件(SM2) package mainimport ( "crypto/rand" "crypto/x509/pkix" "encoding/pem" "github.com/tjfoc/gmsm/sm2" "github.com/tjfoc/gmsm/x509" "math/big" "net" "os" "time")func GenerateCertKeySM2(host, commonName string, alternateIPs []net.IP, alternateDNS []string){ priv, err := sm2.GenerateKey(rand.Reader) if err != nil { panic(err) } max := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, _ := rand.Int(rand.Reader, max) template := x509.Certificate{ SerialNumber: serialNumber, Issuer: pkix.Name{}, Subject: pkix.Name{ CommonName: commonName, Organization: []string{"Test"}, Country: []string{"CN"}, ExtraNames: []pkix.AttributeTypeAndValue{ { Type: []int{2, 5, 4, 42}, Value: "Gopher", }, { Type: []int{2, 5, 4, 6}, Value: "NL", }, }, }, NotBefore: time.Now(), NotAfter: time.Now().Add(time.Hour * 24 * 365), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, IsCA: true, SignatureAlgorithm: x509.SM2WithSM3, } if ip := net.ParseIP(host); ip != nil { template.IPAddresses = append(template.IPAddresses, ip) }else { template.DNSNames = append(template.DNSNames, host) } template.IPAddresses = append(template.IPAddresses, alternateIPs...) template.DNSNames = append(template.DNSNames, alternateDNS...) publicKey := priv.Public().(*sm2.PublicKey) certificateToPem, err := x509.CreateCertificateToPem(&template, &template, publicKey, priv) if err != nil { panic(err) } file, err := os.Create("caSM2.crt") if err != nil { panic(err) } defer file.Close() file.Write(certificateToPem) privateKey, err := x509.MarshalSm2PrivateKey(priv, []byte("jjjjjj")) if err != nil { panic(err) } block := pem.Block{ Type: "SM2 PRIVATE KEY", Headers: nil, Bytes: privateKey, } file, err = os.Create("caSM2.key") if err != nil { panic(err) } defer file.Close() err = pem.Encode(file, &block) if err != nil { panic(err) }}func main(){ ip := []byte("127.0.0.1") alternateDNS := []string{"localhost"} GenerateCertKeySM2("192.168.0.1", "www.example.com", []net.IP{net.ParseIP(string(ip))}, alternateDNS)}查看证书信息,用该库生成的证书Signature Algorithm字段解析进去的值与template中设置的不统一,已在GitHub提交了issue ...

May 28, 2021 · 2 min · jiezi

关于密码学:密码学系列之SAFER

简介分组明码是一个十分优良的加密构造,很多罕用加的加密算法应用的都是分组算法,比方DES。SAFER示意的也是一种分组明码算法。一起来看看吧。 SAFER简介SAFER的全称是Secure And Fast Encryption Routine,在密码学中,SAFER次要是由James Massey(IDEA的设计师之一)代表Cylink公司设计的一组分组明码。 SAFER次要有四种类型别离是:SAFER K, SAFER SK ,SAFER+ 和SAFER++ 。 其中SAFER K 和 SAFER SK 是比拟晚期的设计,共享雷同的加密函数,然而轮次和密钥调度是不一样的。 前面的SAFER+ 和SAFER++ 是作为AES算法的候选算法提交给NESSIE的。 SAFER系列中的所有算法都没有专利限度,能够收费应用。 SAFER K 和 SAFER SK第一个SAFER明码是由Massey在1993年公布的SAFER K-64,具备64位块大小。 “K-64”示意64位的密钥大小。 因为64位的块太小了,不适宜加密大的数据,所以第二年,Massey公布了反对128位的变体,叫做:SAFER K-128。 然而,Lars Knudsen和Sean Murphy发现这个版本存在一些问题,于是将密钥调度依照Knudsen的倡议从新设计。这些变种算法别离被命名为SAFER SK-64和SAFER SK-128。 其中 “SK”代表"Strengthened Key schedule"也就是强化过的工夫调度。 除此之外,还有一种40位块大小的变种算法SAFER SK-40。 咱们应用一个图来看下SAFER K算法的根本流程: 从最下面开始,是明文的输出阶段,每个明文都被分成8块。 接着就是密钥混合阶段,在这个阶段,明文别离和子密钥进行加法模256或者XOR运算。 而后是替换阶段,将上一阶段生成的后果通过两个相同的S盒映射成为新的数据。 这两个S盒别离是由45x和log45这两个函数派生进去的。有想理解S盒的敌人能够参考 “密码学系列之:blowfish对称密钥分组算法” 一文。 而后是第二次的密钥混合阶段。 在第二次密钥混合阶段之后,会应用pseudo-Hadamard transform (PHT)来进行扩散。 这四个阶段组成了一轮加密。 SAFER+ 和 SAFER++SAFER+ 和 SAFER++ 是对原有的SAFER加密算法的改良。他们是由亚美尼亚明码学家Gurgen Khachatrian(亚美尼亚大学)和Melsik Kuregian与Massey独特设计的。 SAFER + 是在1998年提出的,然而是以AES的候选算法提交的,它的块大小是128位。蓝牙的密钥是基于SAFER +的自定义算法来实现的(称为E21和E22), ...

May 28, 2021 · 1 min · jiezi

关于密码学:密码技术国密SM2椭圆曲线公钥密码算法及Go语言应用

SM2椭圆曲线公钥明码算法SM2算法和RSA算法都是公钥明码算法,SM2算法是一种更先进平安的算法,SM2是国家明码局与2010年12月17日公布的椭圆曲线公钥明码算法,在咱们国家商用明码体系中被用来替换RSA算法。 SM2加解密 package mainimport ( "crypto/rand" "github.com/tjfoc/gmsm/sm2" "github.com/tjfoc/gmsm/x509" "os")func GerenateSM2Key(){ //1.生成sm2密钥对 privateKey, err := sm2.GenerateKey(rand.Reader) if err != nil { panic(err) } //2.通过x509将私钥反序列化并进行pem编码 privateKeyToPem, err := x509.WritePrivateKeyToPem(privateKey, nil) if err != nil { panic(err) } //3.将私钥写入磁盘文件 file, err := os.Create("sm2Private.pem") if err != nil { panic(err) } defer file.Close() _, err = file.Write(privateKeyToPem) if err != nil { panic(err) } //4.进行SM2公钥断言 publicKey := privateKey.Public().(*sm2.PublicKey) //5.将公钥通过x509序列化并进行pem编码 publicKeyToPem, err := x509.WritePublicKeyToPem(publicKey) if err != nil { panic(err) } //6.将公钥写入磁盘文件 file, err = os.Create("sm2Public.pem") if err != nil { panic(err) } defer file.Close() _, err = file.Write(publicKeyToPem) if err != nil { panic(err) }}//加密func EncryptSM2(plainText []byte, pubFileName string) []byte { //1.关上公钥文件读取公钥 file, err := os.Open(pubFileName) if err != nil { panic(err) } defer file.Close() fileInfo, err := file.Stat() if err != nil { panic(err) } buf := make([]byte, fileInfo.Size()) _, err = file.Read(buf) if err != nil { panic(err) } //2.将pem格局公钥解码并反序列化 publicKeyFromPem, err := x509.ReadPublicKeyFromPem(buf) if err != nil { panic(err) } //3.加密 cipherText, err := publicKeyFromPem.EncryptAsn1(plainText, rand.Reader) if err != nil { panic(err) } return cipherText}//解密func DecryptSM2(cipherText []byte, priFileName string) []byte { //1.关上私钥问价读取私钥 file, err := os.Open(priFileName) if err != nil { panic(err) } defer file.Close() fileInfo, err := file.Stat() if err != nil { panic(err) } buf := make([]byte, fileInfo.Size()) _, err = file.Read(buf) if err != nil { panic(err) } //2.将pem格局私钥文件解码并反序列话 privateKeyFromPem, err := x509.ReadPrivateKeyFromPem(buf, nil) if err != nil { panic(err) } //3.解密 planiText, err := privateKeyFromPem.DecryptAsn1(cipherText) if err != nil { panic(err) } return planiText}func main(){ GerenateSM2Key() src := []byte("这是应用SM2椭圆曲线算法进行数据加解密测试") cipherText := EncryptSM2(src, "sm2Public.pem") plainText := DecryptSM2(cipherText, "sm2Private.pem") flag := bytes.Equal(plainText, src) fmt.Println("解密是否胜利:", flag)}SM2签名验签 ...

May 27, 2021 · 3 min · jiezi

关于密码学:密码技术国密SM4分组密码算法及Go语言应用

SM4对称加密算法SM4是我国采纳的一种分组明码规范,有国家明码局与2012年3月21日公布,秘钥长度和分组长度为128位。 go语言中利用 简略版本:其函数接口中已实现分组明码底层接口的调用 func sm4Sample(){ src := []byte("这是对称加密SM4的CBC模式加解密测试") key := []byte("1q2w3e4r5t6y7u8i") cipherText, err := sm4.Sm4Cbc(key, src, true) if err != nil { panic(err) } plainText, err := sm4.Sm4Cbc(key, cipherText, false) if err != nil { panic(err) } flag := bytes.Equal(src, plainText) fmt.Println("SM4疾速实现加解密,数据填充规范为pksc7,是否解密胜利:", flag)}简单版本:本人实现数据填充和分组明码底层接口的调用 package mainimport ( "crypto/cipher" "github.com/tjfoc/gmsm/sm4")//明文数据填充func paddingLastGroup(plainText []byte, blockSize int) []byte { //1.计算最初一个分组中明文后须要填充的字节数 padNum := blockSize - len(plainText)%blockSize //2.将字节数转换为byte类型 char := []byte{byte(padNum)} //3.创立切片并初始化 newPlain := bytes.Repeat(char, padNum) //4.将填充数据追加到原始数据后 newText := append(plainText, newPlain...) return newText}//去掉明文前面的填充数据func unpaddingLastGroup(plainText []byte) []byte { //1.拿到切片中的最初一个字节 length := len(plainText) lastChar := plainText[length-1] //2.将最初一个数据转换为整数 number := int(lastChar) return plainText[:length-number]}func sm4Encrypt (plainText, key []byte) []byte { block, err := sm4.NewCipher(key) if err != nil { panic(err) } paddData := paddingLastGroup(plainText, block.BlockSize()) iv := []byte("12345678qwertyui") blokMode := cipher.NewCBCEncrypter(block, iv) cipherText := make([]byte, len(paddData)) blokMode.CryptBlocks(cipherText, paddData) return cipherText}func sm4Dectypt(cipherText, key []byte) []byte { block, err := sm4.NewCipher(key) if err != nil { panic(err) } iv := []byte("12345678qwertyui") blockMode := cipher.NewCBCDecrypter(block, iv) blockMode.CryptBlocks(cipherText, cipherText) plainText := unpaddingLastGroup(cipherText) return plainText}func main(){ src := []byte("这是对称加密SM4的CBC模式加解密测试") key := []byte("1q2w3e4r5t6y7u8i") cipherText := sm4Encrypt(src, key) plainText := sm4Dectypt(cipherText, key) flag := bytes.Equal(src, plainText) fmt.Println("解密是否胜利:", flag)}

May 27, 2021 · 1 min · jiezi

关于密码学:密码技术国密SM3哈希算法及Go语言应用

SM3杂凑算法sm3是我国国产的哈希算法,是一种明码散列函数规范,有国家明码管理局与2010年12月17日公布,该算法次要用于数字签名及验证,音讯认证码生成及验证,随机数生成等,算法公开,其效率与sha256相当。 go语言利用 package mainimport ( "fmt" "github.com/tjfoc/gmsm/sm3")func main(){ src := []byte("sm3是我国国产的哈希算法") hash := sm3.New() hash.Write(src) hashed := hash.Sum(nil) fmt.Printf("哈希后果为:%x", hashed)}运行后果: 哈希后果为:3b366d29964b5543be7aa7cc064f9eeef9481baaa656c8bd3a88b431a8fb6f6c

May 27, 2021 · 1 min · jiezi

关于密码学:密码学系列之memoryhard函数

简介Memory hard function简称为MHF,在密码学中,内存艰难函数(MHF)是一个须要破费大量内存来实现的函数。MHF次要被用在工作量证实中。因为须要破费大量的内存,所以MHF也会被用在明码Hash中,能够避免歹意破解。 和MHF相识的还有一个MBF(memory-bound function ),叫做内存束缚函数,它是通过内存提早来减慢计算速度,从而产生运算老本。 为什么须要MHF咱们晓得应用程序的执行须要两个局部,一个局部是CPU,用来提供计算能力,一个局部就是内存,用来提供存储能力。 以比特币而言,比特币的挖矿其实是重复的计算SHA-2的函数,当其后果足够小的时候,挖矿就胜利了。然而对于传统的CPU来说,当工作是反复计算同一个固定函数的时候,效率会非常低。于是矿工们创造了特定了利用集成电路(ASIC)也就是矿机,来大大的进步这种计算效率。从此挖矿就只把握在矿工或者矿池手里了,因为普通人基本竞争不过他们。 因为SHA-2只是依赖于CPU的,CPU够好,或者应用ASIC针对算法进行优化,就能够超过其他人,取得劣势位置。 而随之带来的就是算力无意义的节约和用电量的激增。这实际上并不是咱们想要的。所以须要一种新的算法来扭转这个场面。 咱们留神到,程序除了CPU之外,还须要应用到内存,而内存绝对CPU相比是更加稀缺的资源。举个例子,如果计算一个函数须要1G空间,对于普通人而言,一个8核16G的电脑能够同时计算16个函数。如果你想放慢运算,那么就须要进步内存空间,并且速度的晋升也不会太显著,所以如果应用内存作为计算的限度,则能够大大减少歹意的运算,从而让加密解密变得绝对偏心。 所以,咱们须要MHF。 Memory hard的评估办法那么怎么样才叫做Memory hard呢?咱们能够从3个方面去掂量,第一个方面就是累计内存复杂度,简称为CMC。在并行模型中,CMC通过将每一步的所有输出相加来掂量内存的难度。 另一个办法就是应用工夫和内存的乘积来掂量。还有一个办法就是计算内存总线上内存带宽的耗费,这种类型的函数也叫做bandwidth-hard functions(BHF)。 MHF的品种依据MHF的评估形式,MHF能够分为两个类型,别离是数据依赖型(dMHF)和数据独立型(iMHF)。 数据依赖型(dMHF)指的是前面计算的数据须要依赖于之前的数据,然而到底须要哪些音讯是不确定的。 数据独立型(iMHF)是指前面计算依赖的数据是确定的。 dMHFs的例子是scrypt和Argon2d。iMHFs的例子是Argon2i和catena。 因为MHF的内存个性,所以非常适合用来做明码哈希函数。 因为dMHF是数据依赖型的,所以它比iMHF在密码学上具备更强的memory-hard个性。然而dMHF有一个问题,就是容易受到缓存时序等侧通道攻打。因为这个起因,人们偏向于iMHFs来作为明码加密的算法。 MHF的密码学意义咱们晓得MHF次要用来进行明码加密的,次要是为了抵挡ASIC(利用集成电路)的破解。下面咱们提到了3种掂量形式,这里咱们应用工夫和内存的乘积来示意。 失常来说,咱们给定明码P和盐S,通过Hash函数H来生成后果Tag。 然而对于破解者来说,他们失去的是Tag和S,心愿通过各种逆向形式来取得P,如下所示: 在明码哈希的状况下,咱们假如明码创建者为每个明码调配肯定的执行工夫(如1秒)和肯定数量的CPU核(如4核)。而后他应用最大的内存量M对明码进行哈希。 那么对于明码破解者来说,他们应用ASIC来破解,假如须要用到的内存区域是A,运行ASIC的工夫T由最长计算链的长度和ASIC内存提早决定。我心愿使得AT的乘积最大。从而达到防破解的意义。 通常来说ASIC机子的内存必定要比一般内存M要小,假如A=aM, 这里 a < 1 。 依据工夫衡量原理,内存应用的少了,天然相应的计算工夫要变长,假如须要计算C(a)次,那么相应的计算工夫要缩短到D(a)倍。 咱们能够失去上面的最大化公式: 如果上式中,当a趋近于0的时候, D(a) > 1/a。 也就是说只有应用的内存变小,那么内存和工夫的乘积就要比之前的要大,对于这样的函数,咱们就叫做memory-hard函数。 memory-hard在MHF中的利用思考MHF中的memory-hard的利用,须要在计算明码hash之前,通过内存筹备一些初始数据,这些初始化的工作就是memory-hard的实质。 能够将内存数组B[i]的初始化简略概括为上面的步骤: 初始值: 对于内存数组中从1到t的index j,咱们通过上面的形式来初始化: 其中G是压缩函数,是index函数。 依据的不同,咱们能够将MHF分成两种类型,一种是数据独立类型,也就是说的值不依赖于输出的明码P和盐S,那么整个的内存数组B值能够在失去明码和盐之前来构建,并且能够借助于并行运算性能,同时进行计算。 假如一个运算核G占用的内存是总内存的beta倍,那么这一种状况下工夫和内存的乘积就是: 如果的值依赖于输出的明码P和盐S,那么我称这种状况叫做数据独立型。这种状况下,不能进行并行计算,如果最终计算的次数是一个均匀深度为D的树,那么这种状况下工夫和内存的乘积能够示意为: 下面就是MHF的密码学意义。 本文已收录于 http://www.flydean.com/memory-hard/ 最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现! 欢送关注我的公众号:「程序那些事」,懂技术,更懂你!

May 26, 2021 · 1 min · jiezi

关于密码学:密码技术椭圆曲线EDCSA数字签名及Go语言应用

1.生成秘钥对并写入磁盘文件1.应用ecdsa生成秘钥对 2.将私钥写入磁盘 应用x509进行反序列化将失去的切片字符串放到pem.Block构造体中应用pem编码3.将公钥写入磁盘 从私钥中失去公钥应用x509进行序列化将失去的切片字符串放入pem.Block构造体中应用pem编码2.应用私钥进行数字签名1.关上私钥文件,将内容读出来 2.应用pem进行数据解码 3.应用x509对数据还原 4.对原始数据进行哈希运算 5.进行数字签名func Sign(rand io.Reader, priv PrivateKey, hash []byte) (r, s big.Int, err error)6.返回值为指针,因而须要将该地址指向内存中的数据进行序列话 3.应用公钥验证数字签名1.关上公钥文件,读出数据 2.应用pem进行解码 3.应用x509进行公钥数据还原func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) 4.因为上一步返回的是一个接口类型,因而须要进行类型断言,将接口类型转换为公钥 5.对原始数据进行哈希运算 6.验签 4.go中利用package mainimport ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/x509" "encoding/pem" "os" "crypto/sha256" "math/big")func GenerateEcdsaKey () { //1.应用ecdsa生成秘钥对 privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) if err != nil { panic(err) } //2.将私钥写入磁盘 //* 应用x509进行反序列化 ecPrivateKey, err := x509.MarshalECPrivateKey(privateKey) if err != nil { panic(err) } //* 将失去的切片字符串放到pem.Block构造体中 block := pem.Block{ Type: "ecdsa private key", Headers: nil, Bytes: ecPrivateKey, } //* 应用pem编码 file, err := os.Create("ecPrivate.pem") if err != nil { panic(err) } defer file.Close() err = pem.Encode(file, &block) if err != nil { panic(err) } //3.将公钥写入磁盘 //* 从私钥中失去公钥 publicKey := privateKey.PublicKey //* 应用x509进行序列化 ecPublicKey, err := x509.MarshalPKIXPublicKey(&publicKey) if err != nil { panic(err) } //* 将失去的切片字符串放入pem.Block构造体中 block = pem.Block{ Type: "ecdsa public key", Headers: nil, Bytes: ecPublicKey, } //* 应用pem编码 file, err = os.Create("ecPublic.pem") if err != nil { panic(err) } defer file.Close() pem.Encode(file, &block)}//签名func SignECDSA (plainText []byte, priFileName string) (rText, sText []byte) { //1.关上私钥文件,将内容读出来 file, err := os.Open(priFileName) if err != nil { panic(err) } defer file.Close() fileInfo, err := file.Stat() if err != nil { panic(err) } buf := make([]byte, fileInfo.Size()) _, err = file.Read(buf) if err != nil { panic(err) } //2.应用pem进行数据解码 block, _ := pem.Decode(buf) //3.应用x509对数据还原 privateKey, err := x509.ParseECPrivateKey(block.Bytes) if err != nil { panic(err) } //4.对原始数据进行哈希运算 hashText := sha256.Sum256(plainText) //5.进行数字签名 var r, s *big.Int //留神这里 r, s, err = ecdsa.Sign(rand.Reader, privateKey, hashText[:]) if err != nil { panic(err) } //6.返回值为指针,因而须要将该地址指向内存中的数据进行序列话 rText, err = r.MarshalText() if err != nil { panic(err) } sText, err = s.MarshalText() if err != nil { panic(err) } return rText,sText}//验签func VerifyECDSA (plainText, rText, sText []byte, pubFileName string) bool { //1.关上公钥文件,读出数据 file, err := os.Open(pubFileName) if err != nil { panic(err) } defer file.Close() fileInfo, err := file.Stat() if err != nil { panic(err) } buf := make([]byte, fileInfo.Size()) _, err = file.Read(buf) if err != nil { panic(err) } //2.应用pem进行解码 block, _ := pem.Decode(buf) //3.应用x509进行公钥数据还原 pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { panic(err) } //4.因为上一步返回的是一个接口类型,因而须要进行类型断言,将接口类型转换为公钥 publicKey := pubInterface.(*ecdsa.PublicKey) //5.对原始数据进行哈希运算 hashText := sha256.Sum256(plainText) //6.验签 var r, s big.Int r.UnmarshalText(rText) s.UnmarshalText(sText) res := ecdsa.Verify(publicKey, hashText[:], &r, &s) return res}

May 25, 2021 · 2 min · jiezi

关于密码学:密码技术非对称加密算法及Go语言应用

非对称加密算法1.对称加密的弊病秘钥散发艰难能够通过非对称加密实现秘钥的散发 Alice和Bob通信,Alice给Bob发送数据,应用对称加密的流程 1.Bob生成一个非对称的密钥对, 2.Bob将公钥发送给Alice 3.Alice生成一个用于对称加密的秘钥 4.Alice应用Bob的公钥就对对称加密的秘钥进行加密,并发送给Bob 5.Bob应用私钥对数据解密,失去对称加密的私钥 通信的单方应用写好的秘钥进行对称加密对数据加密 场景剖析1.通信流程,信息加密(A写数据给B,信息只容许B读)A: 公钥B:私钥 2.登录认证(客户端要登录,连贯服务器,向服务器申请集体数据)客户端:私钥服务端:公钥 3.数字签名(表明信息没有受到篡改,的确是信息拥有者收回来的,附在信息原文的前面)发送端:私钥承受端:公钥 4.网银U盾集体:私钥银行:公钥. 总结: 数据对谁更重要,谁就拿私钥 2. 非对称加密的秘钥不存在秘钥散发的艰难问题3.生成RSA的密钥对概念x509证书标准、pem、base64 pem是一种源自窃密加强邮件协定的编码标准,可进行数据加密base64也是一种编码标准,过程可逆无论原始数据是什么,将原始数据应用64个字符来代替(a-z、A-Z、0-9、/、+) ASN.1形象语法标记 PKCS1规范 密钥对生成流程私钥生成 1.应用rsa中的GenerateKey办法生成私钥func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err error) rand.Reader -> import "crypto/rand"bits 1024的整数倍-倡议2.通过x509规范将失去的rsa私钥序列化为ASN.1的DER编码字符串func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte 3.将私钥字符串设置到pem格局块中初始化一个pem.Block块 4.通过pem将设置好的数据进行编码,并写入磁盘文件func Encode(out io.Writer, b *Block) errorout - 筹备一个文件指针 公钥生成 1.从失去的私钥对象中将公钥信息取出 type PrivateKey struct { PublicKey // 公钥 D *big.Int // 公有的指数 Primes []*big.Int // N的素因子,至多有两个 // 蕴含事后计算好的值,可在某些状况下减速私钥的操作 Precomputed PrecomputedValues}2.通过x509规范将失去的rsa公钥序列化为字符串 func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)3.将公钥字符串设置到pem格局块中 ...

May 23, 2021 · 2 min · jiezi

关于密码学:密码技术分组密码的加密模式

分组明码的加密模式1. 按位异或第一步须要将数据转换为二进制2. ECB - Electronic Code Book,电子密码本模式ECB模式是简略的加密模式,明文数据被分成固定大小的块,并且每个块被独自加密,每个块的加密和解密都是独立的,且应用雷同的办法进行加密,所以能够进行并行计算,这种模式下有一个块被破解了,用雷同的办法就能够破解其余块失去明文数据。安全性比拟差,实用于数据较少的情景,加密前须要把明文数据填充到块大小的整数倍 go接口中不提供该模式3.04 总结: 特点:简略,效率高,密文有法则,容易被破解最初一个明文分组必须要填充不须要初始化向量3. CBC - Cipher Block Chaining, 明码块链CBC模式中每个分组要先和前一个分组加密后的数据进行异或运算,而后再进行加密,第一个数据块进行加密之前须要应用初始化向量IV进行异或运算。CBC模式是一种最罕用的加密模式,它次要毛病是加密是间断的,不能并行处理,并且与ECB一样音讯块必须填充到块大小的整数倍 总结: 特点:密文没有法则,常常应用的加密形式最初一个明文分组须要填充须要一个初始化向量,并且加解密应用的初始化向量必须雷同4. CFB - Cipher FeedBack,密文反馈模式CFB模式中,前一个分组的密文加密后和以后分组的明文进行异或运算生成以后分组的密文,第一步对初始化向量进行加密,并且该模式不须要进行数据填充,不须要填充的还有前面说到的OFB模式和CTR模式 总结: 特点:密文没有法则,明文分组是和数据流进行按位异或,最终生成密文须要一个初始化向量,并且加解密应用的初始化向量必须雷同不须要填充5. OFB - Output FeedBack,输入反馈模式在OFB模式中,明码算法的输入会反馈到明码算法的输出中,即上一个明码算法的输入是以后分组明码算法的输出。OFB模式并不通过明码算法对明文间接进行加密,而是通过明文分组和明码算法的输入进行异或来产生密文分组的,在这一点上OFB和CFB模式十分类似 总结: 特点:密文没有法则,明文分组是和数据流进行按位异或,最终生成密文须要一个初始化向量,并且加解密应用的初始化向量必须雷同不须要填充6. CTR - CountTeR,计数器模式CTR模式是一种通过将逐次累加的计数器进行加密来生成密钥流的流明码,最终的密文分组是通过将计数器加密失去的比特序列,与明文分组进行异或而失去的 总结: 特点:密文没有法则,明文分组是和数据流进行按位异或,最终生成密文不须要初始化向量不须要填充

May 23, 2021 · 1 min · jiezi

关于密码学:密码技术对称加密算法及Go语言应用

对称加密算法DESData Encryption Standard (数据加密规范)是1977年美国联邦信息处理规范(FIPS)中所采纳的一种对称明码。DES始终以来被美国以及其余国家的政府和银行等广泛应用,然而,随着计算机的提高,当初DES曾经可能本暴力破解,强度大不如以前了。 RSA公司举办过破译DES密钥的较量(DESChallenge),较量的后果为: 1977年较量中用了96天破解1998年第一次较量用了41天破解1998年第二次较量用了56小时1999年第三次较量中只用了22小时15分钟就被破解了因为DES的密文能够在短时间内被破解,因而除了它来解密以前的密文外,当初咱们不应该应用该算法了,不平安。 DES的加密解密 DES时一种将64比特的明文加密成64比特的密文的对称明码算法,它的密钥长度是56比特,只管从规格上来说,DES的密钥长度是64比特,但因为每隔7比特会设置一个用于谬误查看的比特,因而本质上其密钥长度是56比特。 总结: 当初应用DES形式加密,数据还平安吗? 不平安,曾经被破解是不是分组明码? 是,先对数据进行分组,而后再加密或解密DES的分组长度? 8byte==64bitDES 的秘钥长度? 56bit秘钥长度+8bit谬误查看标记位=64bit==8byte3DESTriple-DES,三重DES--应用DES三次加密 总结: 3DES平安吗? 平安,然而效率低算法形容? 进行了三次DES加密是不是分组算法? 是3DES分组长度? 8byte3DES秘钥长度? 24byte,在算法外部会被均匀分成3份,目标是兼容DES3DES加密过程? 秘钥1->加密->,秘钥2->解密,秘钥3->加密3DES解密过程? 秘钥1->解密->,秘钥2->加密,秘钥3->解密DES-CBC模式加解密Go实现 package mainimport ( "crypto/cipher" "crypto/des")//明文数据填充func paddingLastGroup(plainText []byte, blockSize int) []byte { //1.计算最初一个分组中明文后须要填充的字节数 padNum := blockSize - len(plainText)%blockSize //2.将字节数转换为byte类型 char := []byte{byte(padNum)} //3.创立切片并初始化 newPlain := bytes.Repeat(char, padNum) //4.将填充数据追加到原始数据后 newText := append(plainText, newPlain...) return newText}//去掉明文前面的填充数据func unpaddingLastGroup(plainText []byte) []byte { //1.拿到切片中的最初一个字节 length := len(plainText) lastChar := plainText[length-1] //2.将最初一个数据转换为整数 number := int(lastChar) return plainText[:length-number]}//des加密func desEncrypt(plainText, key []byte) []byte { //1.建设一个底层应用的des明码接口 block, err := des.NewCipher(key) if err != nil { panic(err) } //2.填充明文数据(这里必须填充,不论原始明文是否能被块长度整除) groupData := paddingLastGroup(plainText, block.BlockSize()) //3.抉择加密模式 iv := []byte("12345678") blockMode := cipher.NewCBCEncrypter(block, iv) //4.加密 cipherText := make([]byte, len(groupData)) blockMode.CryptBlocks(cipherText, groupData) //blockMode.CryptBlocks(groupData, groupData) //这样也能够,官网文档中阐明传入传出参数可指向同一地址 return cipherText}//des解密func desDecrypt(cipherText, key []byte) []byte { //1.创立一个des底层明码接口 block, err := des.NewCipher(key) if err != nil { panic(err) } //2.抉择解密模式 iv := []byte("12345678") //初始化向量必须和加密时的一样 blockMode := cipher.NewCBCDecrypter(block, iv) //3.解密 padText := make([]byte, len(cipherText)) blockMode.CryptBlocks(padText, cipherText) //4.去填充数据 plainText := unpaddingLastGroup(padText) return plainText}func main(){ fmt.Println("des 加解密") key := []byte("1q2w3e4r") src := []byte("DES --Data Encryption Standard (数据加密规范)是1977年美国联邦信息处理规范(FIPS)中所采纳的一种对称明码。DES始终以来被美国以及其余国家的政府和银行等广泛应用,然而,随着计算机的提高,当初DES曾经可能本暴力破解,强度大不如以前了。因为DES的密文能够在短时间内被破解,因而除了它来解密以前的密文外,当初咱们不应该应用该算法了,不平安。") cipherText := desEncrypt(src, key) plainText := desDecrypt(cipherText, key) fmt.Println("解密后的数据为:", string(plainText))}AESAdvanced Encryption Standard(高级加密规范),AES是取代DES的一种对称明码算法,底层算法为Rijndael,该底层算法是有比利时明码学家设计的分组明码算法。 ...

May 23, 2021 · 2 min · jiezi

关于密码学:密码学系列之NIST和SHA算法

简介SHA算法大家应该都很相熟了,它是一个用来计算hash的算法,目前的SHA算法有SHA1,SHA2和SHA3种。这三种算法都是由美国NIST制订的。 NIST的全称是美国国家标准与技术研究所,次要来制订各种规范。 本文将会解说下NIST和SHA各种算法的关系。 SHA1在密码学中,SHA-1(Secure Hash Algorithm 1)是一种加密哈希函数,它承受一个输出,并产生一个160位(20字节)的哈希值,称为信息摘要。 咱们先看下SHA1的加密流程图: <img src="https://img-blog.csdnimg.cn/20210403200253300.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_25,color_8F8F8F,t_70" style="zoom:67%;" /> 下面的A,B,C,D,E都是32bits的state。 F是一个非线性函数。 <<< 示意额是左移操作,红色的加号示意的是加法而后对232取模。 SHA1算法很简略,在2005年之后,SHA1被认为是不平安的,截至2010年,许多组织都倡议更换SHA-1。 NIST在2011年正式废止了SHA-1的应用,并在2013年不容许将其用于数字签名。 所有次要的网络浏览器厂商在2017年都进行承受SHA-1 SSL证书。 SHA2SHA-2(Secure Hash Algorithm 2)也是由美国国家安全局(NSA)设计的一组加密哈希函数,于2001年首次颁布,它们采纳Merkle-Damgård构造。 SHA-2和SHA-1相比,包含了重大的变动。SHA-2系列蕴含六个哈希函数,别离是SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。 咱们看下SHA2的算法流程: <img src="https://img-blog.csdnimg.cn/20210403220300178.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_25,color_8F8F8F,t_70" style="zoom:67%;" /> 咱们看下这几个函数示意什么意思: SHA32006年,NIST组织了NIST哈希函数比赛,以创立一个新的哈希规范SHA-3。SHA-3并不是要取代SHA-2,因为目前还没有证实对SHA-2的重大攻打。然而因为MD5、SHA-0和SHA-1的胜利攻打,NIST认为须要一种可代替的、不同的加密哈希,这就是SHA-3。 在这个较量中,最终Keccak算法胜出,被选为SHA3的规范。 SHA3算法是基于海绵构造的,咱们看下海绵构造的工作原理: 这个函数被分成了两局部,右边局部叫做排汇局部,左边局部叫做输入局部,一吸一出,像是海绵一样,所以叫做海绵函数。 P示意的是输出的字符串,Z示意的时候输入字符串。 一个海绵函数由三局部组成,别离是state, 函数f和填充函数pad。 state就是上图的r+c局部,r被称为Bitrate, c被称为Capacity。 P被分成n份,每一份都会跟Bitrate进行异或操作,如果P的长度不是Bitrate的整数倍,那么须要应用Pad函数进行填充。 每一轮,Bitrate跟P进行异或操作的后果作为最新的Bitrate, 而后生成新的state,而后这个state又被f(state)来替换。 其中函数 f 是 从n个{0,1} 到n个{0,1}的映射。 就这样一轮一轮进行上来,直到所有的P都参加了运算。 输入局部是将最终生成的state进行f运算,每次运算都取Bitrate局部作为输入,从而失去最终的输入。 本文已收录于http://www.flydean.com/sha1-2-3/> 最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现! > 欢送关注我的公众号:「程序那些事」,懂技术,更懂你!

May 10, 2021 · 1 min · jiezi

关于密码学:密码学之对称加密

1.密码学简介2.对称加密3.分组加密模式4.非对称加密5.单向散列函数6.音讯认证码7.数字签名8.证书9.SSL/TLS本文将介绍比特序列运算中的异或运算,同时简略介绍DES、3DES、AES等对称加密算法,最初给出对应的Golang加密代码。 源代码 比特序列密码首先咱们要明确二个概念,一个是计算机编码,咱们都晓得计算机操作的对象并不是文字图像,而是有0和1排列组成的比特序列;一个是异或运算(XOR), 二个不同的比特位异或运算后果是1,不同的比特位异或运算后果是0。 假如咱们将01001100这个比特序列成为A,将10101010这个比特序列成为B,那么A与B的异或运算后果就如下所示: 因为二个雷同的数进行异或运算的后果是0,因而如果将A与B异或的后果再与B进行异或估算,则后果就会变回A。 可能你曾经发现了,下面的计算和加密、解密的步骤十分类似。 将明文A与秘钥B进行加密运算,失去密文A⊕B将密文A⊕B用秘钥B进行解密,失去明文A实际上,次要抉择一个适合的B,仅仅应用异或运算就能够实现一个高强度的明码。 DESDES(Data EncryptionStandard) 是1977年美国联邦信息处理规范(FIPS)中所采纳的一种对称明码(FIPS46.3)。DES始终以来被美国以及其余国家的政府和银行等宽泛应用。然而,随着计算机的提高,当初DES曾经可能被暴力破解,强度大不如前了。RSA公司举办过破解DES密钥的较量(DESChallenge),咱们能够看一看RSA公司官网颁布的比赛结果: 1997年的DES Challenge1中用了96天破译密钥1998年的DES ChallengeIl-I中用了41天破译密钥1998年的DES ChallengeII-2中用了56小时破译密钥1999年的DES ChallengeIll中只用了22小时15分钟破译密钥因为DES的密文能够在短时间内被破译,因而除了用它来解密以前的密文以外,当初咱们不应该再应用DES了。 加密和解密DES是一种将64比特的明文加密成64比特的密文的对称明码算法, 它的密钥长度是56比特。只管<font color="red">从规格上来说,DES的密钥长度是64比特,但因为每隔7比特会设置一个用于谬误查看的比特,因而本质上其密钥长度是56比特</font>。 <font color="red">DES是以64比特的明文(比特序列)为一个单位来进行加密的</font>,这个64比特的单位称为分组。一般来说,以分组为单位进行解决的明码算法称为分组明码(blockcipher),DES就是分组明码的一种。 DES每次只能加密64比特的数据,如果要加密的明文比拟长,就须要对DES加密进行迭代(重复),而迭代的具体形式就称为模式(mode)(后续文章具体解说分组明码的模式)。 大B -> bit 小b -> byte 秘钥长度(56bit + 8bit)/8 = 8byte DES的加密与解密 - 图例 Go中对DES的操作加解密实现思路加密 - CBC分组模式 创立并返回一个应用DES算法的cipher.Block接口 秘钥长度为64bit, 即 64/8 = 8字节(byte)对最初一个明文分组进行数据填充 DES是以64比特的明文(比特序列)为一个单位来进行加密的最初一组不够64bit, 则须要进行数据填充( 参考第三章)创立一个明码分组为链接模式的, 底层应用DES加密的BlockMode接口加密间断的数据块解密 创立并返回一个应用DES算法的cipher.Block接口创立一个明码分组为链接模式的, 底层应用DES解密的BlockMode接口数据块解密去掉最初一组的填充数据加解密的代码实现在Go中应用DES须要导入的包: import ( "crypto/des" "crypto/cipher" "fmt" "bytes")DES加密代码: // src -> 要加密的明文// key -> 秘钥, 大小为: 8bytefunc DesEncrypt_CBC(src, key []byte) []byte{ // 1. 创立并返回一个应用DES算法的cipher.Block接口 block, err := des.NewCipher(key) // 2. 判断是否创立胜利 if err != nil{ panic(err) } // 3. 对最初一个明文分组进行数据填充 src = PKCS5Padding(src, block.BlockSize()) // 4. 创立一个明码分组为链接模式的, 底层应用DES加密的BlockMode接口 // 参数iv的长度, 必须等于b的块尺寸 tmp := []byte("helloAAA") blackMode := cipher.NewCBCEncrypter(block, tmp) // 5. 加密间断的数据块 dst := make([]byte, len(src)) blackMode.CryptBlocks(dst, src) fmt.Println("加密之后的数据: ", dst) // 6. 将加密数据返回 return dst}DES解密代码 ...

August 28, 2020 · 5 min · jiezi

关于密码学:密码学入门之密码

最近在研读《图解明码技术》这本书,将有一系列的密码学学习笔记,波及到明码的相干概念、对称加密、非对称加密、单向散列函数、音讯认证码、数字签名、数字证书等内容,同时波及到代码局部也会应用Golang进行展现,感兴趣的能够关注更新哦。好了,明天先讲讲密码学中的一些概念性的问题。 1.密码学简介2.对称加密3.分组加密模式4.非对称加密5.单向散列函数6.音讯认证码7.数字签名8.证书9.SSL/TLS发送者、接收者和窃听者请设想一个Alice向Bob发送电子邮件的场景。在这个场景中,收回邮件的Alice称为 发送者(sender),而收到邮件的Bob则称为 接收者(receiver)。当某个人向另一个人发送信息时,收回信息的人称为发送者,而收到信息的人称为接收者。另外,被发送的信息有时也统称为 音讯(message)。 邮件是通过互联网从Alice的计算机发送到Bob的计算机的。在发送邮件时,邮件会通过许多台计算机和通信设施进行直达,在这个过程中,就存在被歹意窃听者(eavesdropper)偷看到的可能性 窃听者Eve并不一定是人类,有可能是装置在通信设施上的某种窃~听~器,也可能是装置在邮件软件和邮件服务器上的某些程序。只管邮件内容本来应该只有发送者和接收者两个人晓得,但如果不采取相应的对策,就存在被第三方晓得的危险。 加密和解密那么如何避免窃听者的窃听呢?Alice不想让他人看到邮件的内容,于是她决定将邮件进行加密(encrypt)后再发送进来。 加密之前的音讯称为明文(plaintext),加密之后的音讯称为密文(cipher-text)。 明文加密之后就会变成看不懂的密文 Bob收到了来自Alice的加密邮件,但作为接收者的Bob也是无奈间接浏览密文的,于是Bob须要对密文进行解密(decrypt)之后再浏览。解密就是将密文复原成明文的过程。密文解密之后就变成了原来的明文 将音讯加密后发送的话,即便音讯被窃听,窃听者失去的也只是密文,而无奈得悉加密前的明文内容将音讯加密后发送, 窃听者只能失去密文 在上述场景中,Alice将邮件进行加密,而Bob则进行解密,这样做的目标,是为了不让窃听者Eve读取邮件的内容Alice和Bob通过使用明码(cryptography)技术,保障了邮件的机密性(confidentiality)。 秘钥明码算法用于解决简单问题的步骤,通常称为算法(algorithm)。从明文生成密文的步骤,也就是加密的步骤,称为“加密算法",而解密的步骤则称为“解密算法"。加密、解密的算法合在一起统称为明码算法。秘钥明码算法中须要密钥(key)。事实世界中的“钥'',是像 这样的形态奥妙而简单的小金属片。然而,明码算法中的密钥,则是像203554728568477650354673080689430768这样的一串十分大的数字。加密、解密与秘钥 无论是在加密时还是在解密时,都须要晓得密钥。正如保险柜的钥匙能够爱护保险柜中寄存的贵重物品一样,明码中的密钥能够爱护你的重要数据。即便保险箱再坚硬,如果钥匙被盗, 外面的贵重物品也会被盗。同样地咱们也必须留神不要让明码的密钥被别人窃取。 凯撒明码恺撒明码(Caesar cipher)是一种相传尤利乌斯·恺撒曾应用过的明码。恺撒于公元前100年左右诞生于古罗马,是一位驰名的军事统帅。<font color="red">恺撤明码是通过将明文中所应用的字母表依照肯定的字数“平移”来进行加密的</font>。比方在日语(例如平假名)或者汉语(例如汉语拼音)或者英文字母表中都能够用同样的思路来实现恺撒明码。 为了解说不便,咱们用小写字母(a,b,c,…)来表小明文,用大写字母(A,B,C,...)来示意密文。 当初咱们将字母表平移3个字母,于是,明文中的a在加密后就变成了与其相隔3个字母的D,以此类推。b变成E,c变成F,d变成G......v变成Y,w变成Z,而x则会回到字母表的结尾而变成A,相应地,y变成B,z变成C。通过下图咱们能够很容易地了解“平移"的具体工作形式。 凯撒明码的加密这里,咱们假如要窃密的信息为monkey d luffy这个男孩的名字。咱们暂且不论这个名字到底代表一位实在的男性,还是只是一种暗号,只思考将它在窃密的状态下发送给接收者。此时,明文蕴含下列12个字母:monkeydluffy, 接下来咱们对明文中的字母逐个加密: m ---> P o ---> R n ---> Q k ---> N e ---> H y ---> B d ---> G l ---> O u ---> X f ---> I f ---> I y ---> B这样,明文 monkey d luffy 就被转换成了密文PRQNHB G OXIIB,monkey d luffy这个词咱们可能看懂,但PRQNHB G OXIIB就看不懂了。 ...

August 26, 2020 · 1 min · jiezi

关于密码学:密码学与攻击理论引申iOS-App签名机制

常见加密算法总结1. 平安散列算法Secure Hash Algorithm,常见的算法包含了 MD5、SHA1、HMAC 等。 将任意长度的二进制值映射为较短的固定长度的二进制值,这个短的二进制值称为哈希值,这个算法具备不可逆、碰撞低等个性。同时该类算法能够用作数字签名,用来证实某个信息的确是由某个人收回的,同时能够保障信息没有被批改。实际上,简略来说,这种算法有两个个性:A) 不同的输出肯定得出不同的 hash 值;B) 无奈从 hash 值倒推出原来的输出。 2. 对称加密symmetric-key encryption,其中常见的算法包含了 AES、DES、3DES 、RC4等。对称加密指的是能够应用同一个密钥对内容进行加密和解密,相比非对称加密,它的特点是加/解密速度快,并且加密的内容长度简直没有限度。3. 非对称加密asymmetric/public-key encryption,常见的加密算法有 RSA、DSA、ECC 等。非对称加密有两个密钥,别离为公钥和私钥,其中公钥公开给所有人,私钥永远只能本人晓得。应用公钥加密的信息只能应用私钥解密,应用私钥加密只能应用公钥解密。前者用来传输须要窃密的信息,因为全世界只有晓得对应私钥的人才能够解密;后者用来作数字签名,因为公钥对所有人公开的,能够用来确认这个信息是否是从私钥的拥有者收回的。 平安散列算法MD5信息摘要 MD5 Message-Digest Algorithm,一种被宽泛应用的明码散列函数,能够产生出一个128位(16字节)的散列值(hash value),用于确保信息传输残缺统一。MD5由美国明码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。将数据(如一段文字)运算变为另一固定长度值,是散列算法的根底原理。1996年后被证实存在弱点,能够被加以破解,对于须要高度安全性的数据,专家个别倡议改用其余算法,如SHA-2。2004年,证实MD5算法无奈避免碰撞(collision),因而不适用于安全性认证,如SSL公开密钥认证或是数字签名等用处。#include <CommonCrypto/CommonCrypto.h>@implementation NSData (Add)- (NSString *)md5String { unsigned char result[CC_MD5_DIGEST_LENGTH]; CC_MD5(self.bytes, (CC_LONG)self.length, result); NSMutableString *hash = [NSMutableString string]; for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { [hash appendFormat:@"%02x", result[i]]; } return hash;}@endSHA家族平安散列算法(英语:Secure Hash Algorithm,缩写为SHA)是一个明码散列函数家族,是FIPS所认证的平安散列算法。 能计算出一个数字音讯所对应到的,长度固定的字符串(又称音讯摘要)的算法。且若输出的音讯不同,它们对应到不同字符串的机率很高。 SHA家族的算法,由美国国家安全局(NSA)所设计,并由美国国家标准与技术研究院(NIST)公布,是美国的政府规范,其别离是:SHA-0:1993年公布,过后称做平安散列规范(Secure Hash Standard),公布之后很快就被NSA撤回,是SHA-1的前身。SHA-1:1995年公布,SHA-1在许多平安协定中广为应用,包含TLS和SSL、PGP、SSH、S/MIME和IPsec,曾被视为是MD5(更早之前被广为应用的散列函数)的后继者。但SHA-1的安全性在2000年当前曾经不被大多数的加密场景所承受。 2017年荷兰密码学钻研小组CWI和Google正式发表攻破了SHA-1。 ...

August 25, 2020 · 11 min · jiezi

AES-cbc-ctr

采用vs2010 + Crypto++(C++)配置Crypto++ //0->0x30(48) 1->0x31(49) //将字符串转换为对应16进制数(一般是两位)string strToHex(string str){ stringstream ss; ss << hex << setfill('0'); for(int i=0;i<str.size();i++){ // setw(2) 占两位,setfill('0') 空位填充 0 ss << setw(2) << (int)(unsigned char)str[i]; } return ss.str();}//0x30->0 0x31->1//将16进制数(一般两位为位为一个分割)转换为对应字符串string hexToStr(string hex){ string str; for(string::size_type i = 0; i < hex.length(); i += 2) { string tempStr = hex.substr(i, 2); // string to int unsigned char ch = (unsigned char)stoi(tempStr, nullptr, 16); str.push_back(ch); } return str;}PKCS5 是按 8 字节分组对数据进行填充的AES 实际按 16 字节大小分组(PKCS5)PKCS5 为 PKCS7 的一个子集h<0x07><0x07><0x07><0x07><0x07><0x07><0x07> 7he<0x06><0x06><0x06><0x06><0x06><0x06> 6hel<0x05><0x05><0x05><0x05><0x05> 5hell<0x04><0x04><0x04><0x04> 4hello<0x03><0x03><0x03> 3hello <0x02><0x02> 2hello w<0x01> 1hello wo<0x08><0x08><0x08><0x08><0x08><0x08><0x08><0x08> 8 ...

June 30, 2020 · 4 min · jiezi

区块链发展史

区块链技术并不是一项凭空出世的神奇技术,而是站在前人几百年的研究基础之上,将多学科进行融合发展而成的一项技术。为了让大家能够明白区块链技术,到底是对哪些传统技术的融合和发展,秘猿科技区块链小课堂给大家带来了通俗易懂的第二篇文章,诉说区块链的发展历程。秘猿科技区块链小课堂第 2 期 区块链发展史Cypherpunk 发展和成果众所周知,区块链发源于比特币这个项目,也可以说比特币是区块链的第一个应用。但其实在比特币诞生之前,就已经有很多东西在酝酿之中了,而这其中就包含区块链所依赖的最关键的两个技术,一个是分布式系统,一个是密码学。(所以有人称区块链是分布式数据库,更准确一点说是采用了密码学加密的分布式数据库) 密码学是一个很古老的学问,发展到上世纪 70 年代的时候出现了一些质变,包括民用的对称加密算法,非对称签名算法,还有密钥交换算法。这给互联网的隐私传输加密创造了条件。其中一个非常重要的变化,是非对称对抗成为可能。而这也直接导致了 Cypherpunk 的诞生。 在 1997 年亚当·贝克(Adam Back)创建了“Hashcash”匿名交易系统。就其本质而言,它其实是一种反垃圾邮件机制,通过增加发送电子邮件的时间和计算能力,从而使发送垃圾邮件的成本提高:发件人必须证明他们已经花费了算力在电子邮件标题中创建“邮票”(这是比特币中工作量证明 PoW 的雏形) 1998年,戴伟(Wei Dai)发布了B-Money提案,并推出了两种维护交易数据的方法。在提案中,对记录数据进行监管的用户组表现诚实的话,就会获得激励。为此,他们不仅需要把自己的钱存入到一个特殊账户中,如果他们表现的不诚实,就会损失这笔钱。这种方法被称为“权益证明(Proof of stake)”,用户特定组(或主节点)如果试图处理任何欺诈性交易,那么将会失去自己所有的资金。 2004年,哈尔·芬尼(Hal Finney)借鉴亚当·贝克的 Hashcash 原则,创造了可重复使用的工作量证明(Proof of Work)。2005年,尼克·萨博(Nick Szabo)发布了 Bitgold 提案,该提案的理念正是建立在哈尔·芬妮和其他加密项目的基础之上的。 分布式系统的发展和成果在 80 年代的时候,分布式系统的研究已经开展了一段时间,在这些人中有两类人,一类人是比较实际的,他们研究的是数据库的技术,那时候已经有分布式的数据库了,他们研究的是怎么样把这个分布式的数据库做得更加稳定和可靠;而另一类人则偏重于理论,他们会研究一些在实践中基本碰不到的问题,比如 Leslie Lamport 在 1982 年提出的拜占庭将军问题。 到了 2000 年以后,在这两个领域都已经有了很多研究成果。正是在这样的背景下,出现了区块链这样一个技术。2008 年 10 月,中本聪向 metzdowd.com 的“密码朋克”邮件列表中发布了论文《比特币:P2P电子现金系统》。这篇论文直接引用了戴伟的 B-Money 和亚当·贝克 Hashcash,同时还解决了早期开发者所面临的许多问题,比如双重支付。至此,比特币登上了加密货币的历史舞台,区块链技术也应运而生。

June 4, 2019 · 1 min · jiezi

加密那些事儿

本文旨在帮助大家分清各种加密方式以及用途原理说明,具体的加密算法分析不在本文的主要探讨之内!知识路线graph LR对称加密 --> 非对称加密非对称加密 --> 哈希算法哈希算法 --> 数字签名引入背景:不论是前端还是后端开发中,数字签名、信息加密是经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、oauth 等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的 签名加密算法 来达到业务目标。 漫画:https://cloud.tencent.com/dev... 早在古罗马时期,加密算法就被应用于战争当中。 在大规模的战争中,部队之间常常需要信使往来,传递重要的军事情报。 传送情报过程中,容易遭到中间人攻击,怎样防止这种情况的发生呢? 古罗马人想出了一种非常朴素的加密方法,被称为凯撒密码。加密的原理就像下图这样: 数据加密过程:在对称加密算法中,数据发送方 将 明文 (原始数据) 和 加密密钥 一起经过特殊 加密处理,生成复杂的 加密密文 进行发送。数据解密过程:数据接收方 收到密文后,若想读取原数据,则需要使用 加密使用的密钥 及相同算法的 逆算法 对加密的密文进行解密,才能使其恢复成 可读明文常见的加密算法介绍常见的加密算法可以大致分为:对称加密算法、非对称加密算法、摘要算法。接下来主要围绕这三种算法进行介绍。对称加密算法(Symmetric-key algorithm)常见的 对称加密 算法主要有 DES、3DES、AES 等原理 讲解常见的集中算法AES、DES、3DES、Blowfish、IDEA、RC4、RC5、RC6常见对称加密算法的原理DES (Data Encryption Standard) 数字加密算法是1977年美国联邦信息处理标准(FIPS)中所采用的一种对称密码。DES一直以来被美国以及其它国家的政府和银行等广泛使用。DES运算速度快、资源消耗较少,但是随着计算机计算能力的增强,DES已经能够在短时间内暴力破解,安全性较低。RSA公司在20世纪末举办过的破译DES密钥的比赛数据显示,到1999年破译密钥只需要22小时15分钟。鉴于DES已经能够在短时间内被破解,现在除了破解之前的密文,已不再推荐使用。3DES(Triple Data Encryption Algorithm) 由于DES已经能够在短时间内被破解,为了增加DES的强度,将DES重复3次的用来替代DES的分组密码3DES被开发出来,也称为TDEA(Triple Data Encryption Algorithm)。 但是,3DES处理速度不高,除了在一些重视向下兼容性的环境中,很少有新的用途,也逐渐被AES所取代。 AES (Advanced Encryption Standard) 高级加密标准是取代DES标准的一种对称加密算法的新标准,最终在2000年从众多候选对称密码算法中选出了Rijndael作为AES。被选为AES的密码算法必须满足一定的条件,比如,算法没有弱点、加密以及密钥准备的速度要够快、实现容易、能够在各平台上有效工,同时,还必须无条件地免费供全世界使用。可以说,被选为AES的算法近乎“完美”。AES加解密机制较复杂,综合运用了逐字节替换、平移行、混合列、与轮密钥进行XOR等,其优点在运算速度快、资源消耗少,且安全性高。前面我们简单介绍了DES、3DES和AES三种对称密码,DES已经能够被暴力破解,3DES也逐渐被AES取代。鉴于AES在其选定过程中经过了全世界密码专家的严谨验证,一般来说,我们在使用的时候应尽量使用AES。 优点与缺点优点:对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。缺点:秘钥的管理和分发非常困难,不够安全。在数据传送前,发送方和接收方必须商定好秘钥,然后双方都必须要保存好秘钥,如果一方的秘钥被泄露,那么加密信息也就不安全了。另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的唯一秘钥,这会使得收、发双方所拥有的钥匙数量巨大,密钥管理成为双方的负担。用途与场景通信过程中的加密数据库存储的敏感信息加密【一般用于保存用户手机号、身份证等敏感但能解密的信息 】思考 在对称加密中,我们应该如何将密钥安全地发送给接收者? 非对称加密算法非对称加密算法,又称为 公开密钥加密算法。它需要两个密钥,一个称为 公开密钥 (public key),即 公钥,另一个称为 私有密钥 (private key),即 私钥。 ...

June 1, 2019 · 2 min · jiezi

现代密码学与应用-L01

现代密码学与应用应用, 云盘加密存储, 如何在数据库中快速查询(用户上传新的明文, 查询已有密文数据) Whycore part in information security信息安全, 系统安全 X.800 安全标准 Data Confidentiality, 保密性, 防止未授权的访问, 不可以被第三方看见 Data Integrity, 完整性, 消息认证, 防篡改, 校验, hash Authentication, 实体认证, 身份认证, B 想从 A 得到消息, 要确保消息确实是 A 发送的, 比如登录的时候发送手机短信验证用户(且5分钟内有效), msg + 时间戳, 对发送方的约束 Non-Repudiation, 不可抵赖性, 常用于金融纠纷, 对发送方的约束 Access Control, 访问控制, 开机需要登录 Availability, 可用性, 服务器奔溃了, 就算有账号密码也登录不了, 就是可用性出了问题 三个角色 Alice --- Bob | Eve Where聊天 登录注册时候的验证码, 可以当盐 数据库一条记录: 用户名 + hash + salt ...

April 30, 2019 · 1 min · jiezi

Golang 实现凯撒密码

一.凯撒密码加密代码思路基本思路:设置明文 和 位移步长(秘钥)将明文转成小写,准备 明文字节切片 与 密文切片循环将每个明文字符 按照 位移步长 做位移,存入密文切片返回密文导入包import ( “fmt” “strings” // 包含字符串操作相关方法)凯撒密码加密代码//一、凯撒密码加密func caesarEn(strRaw string, step byte) string { //1.明文 转成 小写 str_raw := strings.ToLower(strRaw) //2.位移步长 step_move := step //3.将字符串 转为 明文字符切片 str_slice_src := []byte(str_raw) fmt.Println(“明文字符切片:”, str_slice_src) //4. 创建 密文字符切片 str_slice_dst := str_slice_src //5.循环明文切片 for i := 0; i < len(str_slice_src); i++ { //6.如果当前循环的明文字符 在位移 范围内,则直接 加上 位移步长 存入 密文字符切片 if str_slice_src[i] < 123-step_move { str_slice_dst[i] = str_slice_src[i] + step_move } else { //7.如果 明文字符 超出 范围,则 位移后 步长再减去 26 str_slice_dst[i] = str_slice_src[i] + step_move - 26 } } //8.输出结果 fmt.Println(“加密结果为:”, step_move, str_slice_dst, string(str_slice_dst)) return string(str_slice_dst)}二.凯撒密码解密代码思路基本思路:设置密文 和 位移步长准备 密文字符切片 与 明文字符切片循环将每个 密文字符 按照位移步长 做位移,存入明文切片返回明文凯撒密码解密代码//二、凯撒密码解密func caesarDe(strCipher string, step byte) string { //1.明文 转成 小写 str_cipher := strings.ToLower(strCipher) //2.位移步长 step_move := step //3.将字符串 转为 明文字符切片 str_slice_src := []byte(str_cipher) fmt.Println(“密文字符切片:”, str_slice_src) //4. 创建 密文字符切片 str_slice_dst := str_slice_src //5.循环明文切片 for i := 0; i < len(str_slice_src); i++ { //6.如果当前循环的明文字符 在位移 范围内,则直接 加上 位移步长 存入 密文字符切片 if str_slice_src[i] >= 97+step_move { str_slice_dst[i] = str_slice_src[i] - step_move } else { //7.如果 明文字符 超出 范围,则 位移后 步长再减去 26 str_slice_dst[i] = str_slice_src[i] + 26 - step_move } } //8.输出结果 fmt.Println(“解密结果为:”, step_move, str_slice_dst, string(str_slice_dst)) return string(str_slice_dst)} ...

February 15, 2019 · 1 min · jiezi

非对称加密算法--RSA加密原理及运用

密码学是在编码与破译的斗争实践中逐步发展起来的,并随着先进科学技术的应用,已成为一门综合性的尖端技术科学。密码学发展史在说RSA加密算法之前, 先说下密码学的发展史。其实密码学的诞生,就是为了运用在战场,在公元前,战争之中出现了秘密书信。在中国历史上最早的加密算法的记载出自于周朝兵书《六韬.龙韬》中的《阴符》和《阴书》。在遥远的西方,在希罗多德(Herodotus)的《历史》中记载了公元前五世纪,希腊城邦和波斯帝国的战争中,广泛使用了移位法进行加密处理战争通讯信息。相传凯撒大帝为了防止敌人窃取信息,就使用加密的方式传递信息。那么当时的加密方式非常的简单,就是对二十几个罗马字母建立一张对照表,将明文对应成为密文。那么这种方式其实持续了很久。甚至在二战时期,日本的电报加密就是采用的这种原始加密方式。早期的密码学一直没有什么改进,几乎都是根据经验慢慢发展的。直到20世纪中叶,由香农发表的《秘密体制的通信理论》一文,标志着加密算法的重心转移往应用数学上的转移。于是,逐渐衍生出了当今重要的三类加密算法:非对称加密、对称加密以及哈希算法(HASH严格说不是加密算法,但由于其不可逆性,已成为加密算法中的一个重要构成部分)。1976年以前,所有的加密方法都是同一种模式:加密和解密使用同样规则(简称"密钥"),这被称为"对称加密算法",使用相同的密钥,两次连续的对等加密运算后会回复原始文字,也有很大的安全隐患。1976年,两位美国计算机学家Whitfield Diffie 和 Martin Hellman,提出了一种崭新构思,可以在不直接传递密钥的情况下,完成解密。这被称为"Diffie-Hellman密钥交换算法"。也正是因为这个算法的产生,人类终于可以实现非对称加密了:A给B发送信息B要先生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。A获取B的公钥,然后用它对信息加密。B得到加密后的信息,用私钥解密。理论上如果公钥加密的信息只有私钥解得开,那么只要私钥不泄漏,通信就是安全的。1977年,三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字命名,叫做RSA算法。从那时直到现在,RSA算法一直是最广为使用的"非对称加密算法"。毫不夸张地说,只要有计算机网络的地方,就有RSA算法。这种算法非常可靠,密钥越长,它就越难破解。根据已经披露的文献,目前被破解的最长RSA密钥是232个十进制位,也就是768个二进制位,因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全,当然量子计算机除外。RSA算法的原理下面进入正题,解释RSA算法的原理,其实RSA算法并不难,只需要一点数论知识就可以理解。素数:又称质数,指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。互质,又称互素。若N个整数的最大公因子是1,则称这N个整数互质。模运算即求余运算。“模”是“Mod”的音译。和模运算紧密相关的一个概念是“同余”。数学上,当两个整数除以同一个正整数,若得相同余数,则二整数同余。欧拉函数任意给定正整数n,请问在小于等于n的正整数之中,有多少个与n构成互质关系?(比如,在1到8之中,有多少个数与8构成互质关系?)计算这个值的方法就叫做欧拉函数,以(n)表示。计算8的欧拉函数,和8互质的 1、2、3、4、5、6、7、8(8) = 4如果n是质数的某一个次方,即 n = p^k (p为质数,k为大于等于1的整数),则(n) = (p^k) = p^k - p^(k-1)。也就是(8) = (2^3) =2^3 - 2^2 = 8 -4 = 4计算7的欧拉函数,和7互质的 1、2、3、4、5、6、7(7) = 6如果n是质数,则 (n)=n-1 。因为质数与小于它的每一个数,都构成互质关系。比如5与1、2、3、4都构成互质关系。计算56的欧拉函数(56) = (8) (7) = 4 6 = 24如果n可以分解成两个互质的整数之积,即 n = p k ,则(n) = (p k) = (p1)(p2)欧拉定理:如果两个正整数m和n互质,那么m的(n)次方减去1,可以被n整除。费马小定理:欧拉定理的特殊情况,如果两个正整数m和n互质,而且n为质数!那么(n)结果就是n-1。模反元素还剩下最后一个概念,模反元素:如果两个正整数e和x互质,那么一定可以找到整数d,使得 ed-1 被x整除,或者说ed被x除的余数是1。那么d就是e相对于x的模反元素。等式转换根据欧拉定理由于1^k ≡ 1,等号左右两边都来个k次方由于1 m ≡ m,等号左右两边都乘上m根据模反元素,因为ed 一定是x的倍数加1。所以如下:通过多次的等式转换。终于可以将这两个等式进行合并了!如下:这个等式成立有一个前提!就是关于模反元素的,就是当整数e和(n)互质!一定有一个整数d是e相对于(n)的模反元素。我们可以测试一下。m取值为4n取值为15(n)取值为8e 如果取值为3d 可以为 11、19…(模反元素很明显不止一个,其实就是解二元一次方程)如果你测试了,那么你可以改变m的值试一下,其实这个等式不需要m和n 互质。只要m小于n 等式依然成立。这里需要注意的是,我们可以看做 m 通过一系列运算得到结果仍然是 m。这一系列运算中,分别出现了多个参数n、(n)、e还有d。m 的 e乘上d 次方为加密运算,得到结果 cc 模以 n 为解密运算,得到结果 m这似乎可以用于加密和解密。但这样,加密的结果会非常大。明文数据将非常小(虽然RSA用于加密的数据也很小,但是没这么大悬殊),真正的RSA要更加强大,那么RSA是怎么演变来的呢??早期很多数学家也停留在了这一步!直到1967年迪菲赫尔曼密钥交换打破了僵局!迪菲赫尔曼密钥交换这个密钥交换当时轰动了整个数学界!而且对人类密码学的发展非常重要,因为这个伟大的算法能够拆分刚才的等式。当非对称加密算法没有出现以前,人类都是用的对称加密。所以密钥的传递,就必须要非常小心。迪菲赫尔曼密钥交换 就是解决了密钥传递的保密性,我们来看一下假设一个传递密钥的场景。算法就是用3 的次方去模以17。 三个角色服务器 随机数 15这个15只有服务器才知道。通过算法得到结果 6 因为 3的15次方 mod 17 = 6 。然后将结果 6 公开发送出去,拿到客户端的 12 ,然后用12^15 mod 17 得到结果10(10就是交换得到的密钥)客户端 随机数13客户端用3 的 13次方 mod 17 = 12 然后将得到的结果12公布出去。拿到服务器的 6 ,然后用6^13 mod 17 得到结果10(10就是交换得到的密钥)第三者第三者只能拿到6 和 12 ,因为没有私密数据13、15,所以它没法得到结果10。为什么 6的13次方会和12的15次方得到一样的结果呢?因为这就是规律,我们可以用小一点的数字测试一下3^3 mod 17 = 10和10 ^ 2 mod 17 ; 3 ^ 2 mod 17 = 9和9^3 mod 17结果都是15。迪菲赫尔曼密钥交换最核心的地方就在于这个规律RSA的诞生现在我们知道了m^e % n = c是加密,c^d % n = m是解密,m就是原始数据,c是密文,公钥是n和e,私钥是n和d,所以只有n和e是公开的。加密时我们也要知道(n)的值,最简单的方式是用两个质数之积得到,别人想破解RSA也要知道(n)的值,只能对n进行因数分解,那么我们不想m被破解,n的值就要非常大,就是我们之前说的,长度一般为1024个二进制位,这样就很安全了。但是据说量子计算机(用于科研,尚未普及)可以破解,理论上量子计算机的运行速度无穷快,大家可以了解一下。以上就是RSA的数学原理检验RSA加密算法我们用终端命令演示下这个加密、解密过程。假设m = 12(随便取值,只要比n小就OK),n = 15(还是随机取一个值),(n) = 8,e = 3(只要和(n)互质就可以),d = 19(3d - 1 = 8,d也可以为3,11等等,也就是d = (8k + 1)/3 )终端分别以m=12,7输入结果OpenSSL进行RSA的命令运行Mac可以直接使用OpenSSL,首先进入相应文件夹生成公私钥// 生成RSA私钥,文件名为private.pem,长度为1024bitopenssl genrsa -out private.pem 1024// 从私钥中提取公钥openssl rsa -in private.pem -pubout -out publick.pem// 查看刚刚生成好的私钥cat private.pem// 查看刚刚生成好的公钥cat publick.pem我们可以看到base64编码,明显私钥二进制很大,公钥就小了很多。这时候我们的文件夹内已经多了刚刚生成好的公私钥文件了// 将私钥转换为明文openssl rsa -in private.pem -text -out private.txt里面就是P1、P2还有KEY等信息。对文件进行加密、解密// 编辑文件message内容为hello Vincent!!!// 刚刚的public.pem写成了publick.pem(哎。。。) $ vi message.txt $ cat message.txt hello Vincent!!!// 通过公钥加密数据时,使用encrypt对文件进行加密 $ openssl rsautl -encrypt -in message.txt -inkey publick.pem -pubin -out enc.txt// 此时查看该文件内容为乱码 $ cat enc.txtj��E]a��d�kUE�&< ��I��V/��pL[����O�+�-�M��K��&⪅O��2���o34�:�$���6��C�L��,b�‘M�S�k�0���A��3%�[I���1�����ps"%// 通过私钥解密数据 $ openssl rsautl -decrypt -in enc.txt -inkey private.pem -out dec.txt// 已成功解密,正确显示文件内容 $ cat dec.txt hello Vincent!!!// 通过私钥加密数据时,要使用sign对文件进行重签名$ openssl rsautl -sign -in message.txt -inkey private.pem -out enc.bin// 此时查看该文件内容同样为乱码$ cat enc.bin{���Ew�3�1E��,8-OA2�Is�:���:�@MU���� �i1B���#��6���m�D(�t#/��� ��������>(�>�^@�C��3�MQ�O%// 通过公钥解密数据$ openssl rsautl -verify -in enc.bin -inkey publick.pem -pubin -out dec.bin// 已成功解密,正确显示文件内容$ cat dec.bin hello Vincent!!!RSA用途及特点到这里,大家都知道RSA通过数学算法来加密和解密,效率比较低,所以一般RSA的主战场是加密比较小的数据,比如对大数据进行对称加密,再用RSA给对称加密的KEY进行加密,或者加密Hash值,也就是数字签名。关于RSA数字签名后面再慢慢阐述。该文章为记录本人的学习路程,希望能够帮助大家,也欢迎大家点赞留言交流!!!https://www.jianshu.com/p/ad3… ...

January 4, 2019 · 2 min · jiezi