首页 文章 精选 留言 我的

精选列表

搜索[密码学],共723篇文章
优秀的个人博客,低调大师

密码学常用解码网站

xssee:http://web2hack.org/xsseexssee:http://evilcos.me/lab/xssee程默的博客(DES,3DES,AES,RC,Blowfish,Twofish,Serpent,Gost,Rijndael,Cast,Xtea,RSA):http://tool.chacuo.net/cryptdes在线编码解码(多种并排):http://bianma.911cha.com在线加密解密(多种):http://encode.chahuo.comUnicode转中文:http://www.bejson.com/convert/unicode_chinese栅栏密码 && 凯撒密码 && 摩斯电码:http://heartsnote.com/tools/cipher.htmCaesar cipher(凯撒密码):http://planetcalc.com/1434/Quoted-Printable && ROT13:http://www.mxcz.net/tools/QuotedPrintable.aspxROT5/13/18/47编码转换:http://www.qqxiuzi.cn/bianma/ROT5-13-18-47.phpBase32/16:http://pbaseconverter.com/Base32:https://tools.deamwork.com/crypt/decrypt/base32decode.htmlquipqiup古典密码自动化爆破(词频分析):http://quipqiup.com/index.php词频分析/替换:http://cryptoclub.org/tools/cracksub_topframe.php'+.<>[]' && '!.?'(Brainfuck/Ook!):https://www.splitbrain.org/services/ook'+-.<>[]'(Brainfuck):https://www.nayuki.io/page/brainfuck-interpreter-javascript'+-.<>[]'(Brainfuck):http://esoteric.sange.fi/brainfuck/impl/interp/i.html'()[]!+'JavaScript编码(JSfuck):http://discogscounter.getfreehosting.co.uk/js-noalnum.php用 6 个字符'()[]!+'来编写 JavaScript 程序(JSfuck同上):http://www.jsfuck.com/http://patriciopalladino.com/files/hieroglyphy/摩斯密码翻译器:http://www.jb51.net/tools/morse.htmMorse Code摩尔斯电码:http://rumkin.com/tools/cipher/morse.php字符串编码,解码,转换(长度,反转,进制转换):http://www.5ixuexiwang.com/str/Cisco Type 7 Reverser:http://packetlife.net/toolbox/type7Cisco:http://www.ifm.net.nz/cookbooks/passwordcracker.htmlcmd5 && NTLM && mysql...:http://www.cmd5.comspammimic(字符2一段话):http://www.spammimic.com/js代码在线加密解密:https://tool.lu/js/JScript/VBscript脚本解密(#@~^....^#~@):http://www.dheart.net/decode/index.phpVBScript.Encode解密(tip:Asp encode):http://adophper.com/encode.htmlJScript.Encode脚本加密与解密:http://www.haokuwang.com/jsendecode.htm'+/v+'UTF-7加密:http://web2hack.org/xssee各种无知密码解密:http://www.tools88.comuuencode解码 && xxencode解码(古老的邮件密码):http://web.chacuo.net/charsetuuencodeMIME标准(邮件编码的一种):http://dogmamix.com/MimeHeadersDecoder/Binhex编码(邮件编码的一种,常见于MAC机):http://encoders-decoders.online-domain-tools.com/%u8001%u9525非/u的hex,%u编码,只能编码汉字(xssee):http://web.chacuo.net/charsetescape猪圈密码:http://www.simonsingh.net/The_Black_Chamber/pigpen.htmlppencode(把Perl代码转换成只有英文字母的字符串):http://namazu.org/~takesako/ppencode/demo.htmlrrencode(把ruby代码全部转换成符号):挂了aaencode(JS代码转换成常用的网络表情,也就是我们说的颜文字js加密):http://utf-8.jp/public/aaencode.html'()[]!+' && '$=~[]+"_.\();'jother编码jjencode(JS代码转换成只有符号的字符串):http://web2hack.org/xsseejother(是一种运用于javascript语言中利用少量字符构造精简的匿名函数方法对于字符串进行的编码方式。其中8个少量字符包括:! + ( ) [ ] { }。只用这些字符就能完成对任意字符串的编码):http://tmxk.org/jother/jjencode/aaencode可用xssee && Chrome的Console模式来直接输出解密。Manchester曼彻斯特解密:http://eleif.net/manchester.htmlVigenère维多利亚解密:https://www.guballa.de/vigenere-solverVigenère cipher:http://planetcalc.com/2468/Hill cipher(希尔密码):http://planetcalc.com/3327/Atbash cipher(埃特巴什码):http://planetcalc.com/4647/snow(html隐写):http://fog.misty.com/perry/ccs/snow/snow/snow.htmlSerpent加密解密:http://serpent.online-domain-tools.com/十六进制Hex转文本字符串:http://www.bejson.com/convert/ox2str/Hex2text:http://www.convertstring.com/EncodeDecode/HexDecodeBinary(二进制),ACSII,Hex(十六进制),Decimal(十进制):http://www.binaryhexconverter.com/集合:http://www.qqxiuzi.cn/daohang.htm集合(各种古典密码):http://rumkin.com/tools/cipher/文本加密为汉字("盲文",音符,各种语言,花朵,箭头...):http://www.qqxiuzi.cn/bianma/wenbenjiami.php在线工具集合:http://tomeko.net/online_tools/二维码/条形码:https://online-barcode-reader.inliteresearch.com/生成二维码:http://www.wwei.cn/在线二维码解密:http://jiema.wwei.cn/Image2Base64:http://www.vgot.net/test/image2base64.php与佛论禅:http://www.keyfc.net/bbs/tools/tudoucode.aspx在线分解GIF帧图:http://zh.bloggif.com/gif-extractbejson(杂乱):http://www.bejson.comatool(杂乱):http://www.atool.orgPunch Card:http://www.kloth.net/services/cardpunch.php分解素数(ESA):http://www.factordb.com/index.php文件在线Hash:http://www.atool.org/file_hash.php 您可以考虑给博主来个小小的打赏以资鼓励,您的肯定将是我最大的动力。thx. 微信打赏 支付宝打赏 作 者: Angel_Kitty 出 处:http://www.cnblogs.com/ECJTUACM-873284962/ 关于作者:潜心机器学习以及信息安全的综合研究。如有问题或建议,请多多赐教! 版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。 特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信我 声援博主:如果您觉得文章对您有帮助,可以点击右下角【推荐】推荐一下该博文。您的鼓励是作者坚持原创和持续写作的最大动力!

优秀的个人博客,低调大师

密码学系列之:生日攻击

简介 生日攻击其实是一个概率论的问题,也就是说一个看起来很难发生的事情,事实上它发生的概率却很大。这种主观上和事实上的概率差距,让随机攻击成功的几率变的更高,这样的攻击就叫做生日攻击。 生日问题的由来 生日问题也叫做生日悖论,它是这样这样描述的。 假如随机选择n个人,那么这个n个人中有两个人的生日相同的概率是多少。如果要想概率是100%,那么只需要选择367个人就够了。因为只有366个生日日期(包括2月29日)。 如果想要概率达到99.9% ,那么只需要70个人就够了。50%的概率只需要23个人。 对于现在的幼儿园小朋友来说,一个班上差不多有30人,那么将会有大于50%的几率,班上有两个人的生日是一样的。 听起来是不是很神奇?跟我们第一映像中的基数是不是要少很多。 我们看一张概率图: 在实际应用中,可以应用生日问题中的概率模型,从而减少碰撞攻击的复杂度,或者来评估一个hash函数中可能出现碰撞攻击的几率。 怎么计算呢? 假如P(A) 是生日相同的概率,那么P(A) = 1 – P(A’) ,其中P(A’)是生日不同的概率。 一个人生日不同的概率是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/ 最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现! 欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

优秀的个人博客,低调大师

密码学系列之:内容嗅探

简介 内容嗅探,也被称为媒体类型嗅探或MIME嗅探,是检查一个字节流的内容,试图推断其中数据的文件格式的做法。内容嗅探通常用在媒体类型没有被准确指定的情况,用于补偿元数据信息。 本文将会讲解内容嗅探的常用场景和可能出现的问题。 MIME types MIME的全称是Multipurpose Internet Mail Extensions,多用途互联网邮件扩展。它是一种标准,它表明了文档、文件或各种字节的性质和格式。它是在IETF的RFC 6838中定义的。互联网编号分配机构(IANA)负责定义所有官方的MIME类型。 MIME的结构包含两部分,分别是type和subtype,他们以 / 来进行分割: type/subtype 类型代表数据类型所属的一般类别,如视频或文本。子类型确定MIME类型所代表的指定类型的确切数据种类。例如,对于 MIME 类型的文本,子类型可能是 plain(纯文本)、html(HTML 源代码)或日历(对于 iCalendar/.ics)文件。 每种类型都有它自己的一套可能的子类型, 一个MIME类型必须包含一个类型和一个子类型。 还可以在后面加上额外的参数: type/subtype;parameter=value 例如,对于主类型是text的任何MIME类型,可选的charset参数可以用来指定数据中字符的字符集。如果没有指定字符集,默认为ASCII (US-ASCII),除非被用户代理的设置覆盖。要指定UTF-8文本文件,则使用MIME类型text/plain;charset=UTF-8。 MIME类型不区分大小写,但传统上用小写,但参数值除外,因为参数值的大小写可能有或没有特定的意义。 MIME有两中类型,分别是discrete和multipart。 离散类型是代表单一文件或媒介的类型,如单一文本或音乐文件,或单一视频。 多部分类型是指由多个组件组成的文件,每个组件都有自己独立的MIME类型;或者,指封装在一个事务中一起发送的多个文件。例如,电子邮件中多个附件就是一种多部分MIME类型。 我们看下常见的discrete类型: application, 比如:application/octet-stream,application/pdf,application/pkcs8和application/zip等。 audioList, 比如:audio/mpeg,audio/vorbis。 font, 比如:font/woff,font/ttf和font/otf。 image,比如:image/jpeg,image/png和image/svg+xml。 model, 比如:model/3mf和model/vml。 text,比如:text/plain,text/csv和text/html. video,比如:video/mp4。 常见的Multipart类型如下: message,比如:message/rfc822和message/partial。 multipartList, 比如:multipart/form-data 和multipart/byteranges。 浏览器嗅探 因为浏览器使用MIME类型,而不是文件扩展名来决定如何处理一个URL,所以Web服务器在响应的Content-Type头中发送正确的MIME类型非常重要。如果没有正确配置,浏览器很可能会误解文件的内容,网站将无法正常运行,下载的文件也可能会被错误处理。 为了解决这个问题,或者说是更好的用户体验,很多浏览器会进行MIME内容嗅探,也就是通过解析文件的内容,来猜测MIME类型的格式。 不同的浏览器处理MIME嗅探的方式是不一样的。但是他们都可能会产生严重的安全漏洞,因为有些MIME类型是可执行类型的,恶意攻击者可以通过混淆MIME嗅探算法,从而使攻击者可以进行网站运营者或用户都没有预料到的操作,如跨站脚本攻击。 如果不想浏览器端进行嗅探,可以在服务端的响应中设置X-Content-Type-Options头,比如: X-Content-Type-Options: nosniff 这个头最早是在IE 8中支持的,不过现在所有的浏览器基本都支持这个head类型了。 客户端嗅探 我们通常需要在JS中判断浏览器是否是IE浏览器,然后做响应的处理: var isIEBrowser = false; if (window.ActiveXObject) { isIEBrowser = true; } // Or, shorter: var isIE = (window.ActiveXObject !== undefined); 上面的例子就是非常简单的客户端嗅探,通过判断window是否有ActiveXObject 这个属性来确定这个浏览器是否是IE浏览器。 本文已收录于http://www.flydean.com/content-sniffing/ 最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现! 欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

优秀的个人博客,低调大师

密码学系列之: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个输出。 在输入部分,cost 表示的是轮循的次数,这个我们可以自己指定,轮循次数多加密就慢。 salt 是加密用盐,用来混淆密码使用。 password 就是我们要加密的密码了。 最后的输出是加密后的结果hash。 有了3个输入,我们会调用EksBlowfishSetup函数去初始化18个subkeys和4个1K大小的S-boxes,从而达到最终的P和S。 然后使用P和S对”OrpheanBeholderScryDoubt” 进行64次blowfish运算,最终得到结果。 接下来看下 EksBlowfishSetup方法的算法实现: Function EksBlowfishSetup Input: password: array of Bytes (1..72 bytes) UTF-8 encoded password salt: array of Bytes (16 bytes) random salt cost: Number (4..31) log2(Iterations). e.g. 12 ==> 212 = 4,096 iterations Output: P: array of UInt32 array of 18 per-round subkeys S1..S4: array of UInt32 array of four SBoxes; each SBox is 256 UInt32 (i.e. 1024 KB) //Initialize P (Subkeys), and S (Substitution boxes) with the hex digits of pi P, S <- InitialState() //Permutate P and S based on the password and salt P, S <- ExpandKey(P, S, salt, password) //This is the "Expensive" part of the "Expensive Key Setup". //Otherwise the key setup is identical to Blowfish. repeat (2cost) P, S <- ExpandKey(P, S, 0, password) P, S <- ExpandKey(P, S, 0, salt) return P, S 代码很简单,EksBlowfishSetup 接收上面我们的3个参数,返回最终的包含18个子key的P和4个1k大小的Sbox。 首先初始化,得到最初的P和S。 然后调用ExpandKey,传入salt和password,生成第一轮的P和S。 然后循环2的cost方次,轮流使用password和salt作为参数去生成P和S,最后返回。 最后看一下ExpandKey的实现: Function ExpandKey Input: password: array of Bytes (1..72 bytes) UTF-8 encoded password salt: Byte[16] random salt P: array of UInt32 Array of 18 subkeys S1..S4: UInt32[1024] Four 1 KB SBoxes Output: P: array of UInt32 Array of 18 per-round subkeys S1..S4: UInt32[1024] Four 1 KB SBoxes //Mix password into the P subkeys array for n <- 1 to 18 do Pn <- Pn xor password[32(n-1)..32n-1] //treat the password as cyclic //Treat the 128-bit salt as two 64-bit halves (the Blowfish block size). saltHalf[0] <- salt[0..63] //Lower 64-bits of salt saltHalf[1] <- salt[64..127] //Upper 64-bits of salt //Initialize an 8-byte (64-bit) buffer with all zeros. block <- 0 //Mix internal state into P-boxes for n <- 1 to 9 do //xor 64-bit block with a 64-bit salt half block <- block xor saltHalf[(n-1) mod 2] //each iteration alternating between saltHalf[0], and saltHalf[1] //encrypt block using current key schedule block <- Encrypt(P, S, block) P2n <- block[0..31] //lower 32-bits of block P2n+1 <- block[32..63] //upper 32-bits block //Mix encrypted state into the internal S-boxes of state for i <- 1 to 4 do for n <- 0 to 127 do block <- Encrypt(state, block xor salt[64(n-1)..64n-1]) //as above Si[2n] <- block[0..31] //lower 32-bits Si[2n+1] <- block[32..63] //upper 32-bits return state ExpandKey主要用来生成P和S,算法的生成比较复杂,大家感兴趣的可以详细研究一下。 bcrypt hash的结构 我们可以使用bcrypt来加密密码,最终以bcrypt hash的形式保存到系统中,一个bcrypt hash的格式如下: $2b$[cost]$[22 character salt][31 character hash] 比如: $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy \__/\/ \____________________/\_____________________________/ Alg Cost Salt Hash 上面例子中,$2a$表示的hash算法的唯一标志。这里表示的是bcrypt算法。 10 表示的是代价因子,这里是2的10次方,也就是1024轮。 N9qo8uLOickgx2ZMRZoMye 是16个字节(128bits)的salt经过base64编码得到的22长度的字符。 最后的IjZAgcfl7p92ldGxad68LJZdL17lhWy是24个字节(192bits)的hash,经过bash64的编码得到的31长度的字符。 hash的历史 这种hash格式是遵循的是OpenBSD密码文件中存储密码时使用的Modular Crypt Format格式。最开始的时候格式定义是下面的: $1$: MD5-based crypt (‘md5crypt’) $2$: Blowfish-based crypt (‘bcrypt’) $sha1$: SHA-1-based crypt (‘sha1crypt’) $5$: SHA-256-based crypt (‘sha256crypt’) $6$: SHA-512-based crypt (‘sha512crypt’) 但是最初的规范没有定义如何处理非ASCII字符,也没有定义如何处理null终止符。修订后的规范规定,在hash字符串时: String 必须是UTF-8编码 必须包含null终止符 因为包含了这些改动,所以bcrypt的版本号被修改成了$2a$。 但是在2011年6月,因为PHP对bcypt的实现crypt_blowfish中的一个bug,他们建议系统管理员更新他们现有的密码数据库,用$2x$代替$2a$,以表明这些哈希值是坏的(需要使用旧的算法)。他们还建议让crypt_blowfish对新算法生成的哈希值使用头$2y$。 当然这个改动只限于PHP的crypt_blowfish。 然后在2014年2月,在OpenBSD的bcrypt实现中也发现了一个bug,他们将字符串的长度存储在无符号char中(即8位Byte)。如果密码的长度超过255个字符,就会溢出来。 因为bcrypt是为OpenBSD创建的。所以当他们的库中出现了一个bug时, 他们决定将版本号升级到$2b$。 本文已收录于http://www.flydean.com/37-bcrypt/ 最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现! 欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

优秀的个人博客,低调大师

【Java小工匠聊密码学】--对称加密--概述

1、什么是对称加密 对称加密算法是应用较早的加密算法,技术成熟。在对称加密算法中,数据发信方将明文“原始数据”和“加密密钥“一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。 image.png 2、对称密码特点 (1)加密密钥与解密密钥相同,加密算法与解密算法互逆 (2)算法公开、计算量小、加密速度快、加密效率高。 (3)双方使用相同的密钥,安全性得不到保障。 3、对称密码工作模式 (1)ECB:电子密码本 最基本的加密模式,也就是通常理解的加密,相同的明文将永远加密成相同的密文,无初始向量,容易受到密码本重放攻击,一般情况下很少用。 (2)CBC:密码连接 明文被加密前要与前面的密文进行异或运算后再加密,因此只要选择不同的初始向量,相同的密文加密后会形成不同的密文,这是目前应用最广泛的模式。CBC加密后的密文是上下文相关的,但明文的错误不会传递到后续分组,但如果一个分组丢失,后面的分组将全部作废(同步错误)。 (3)CFB:密文反馈 类似于自同步序列密码,分组加密后,按8位分组将密文和明文进行移位异或后得到输出同时反馈回移位寄存器,优点最小可以按字节进行加解密,也可以是n位的,CFB也是上下文相关的,CFB模式下,明文的一个错误会影响后面的密文(错误扩散)。 (4)OFB:输出反馈 将分组密码作为同步序列密码运行,和CFB相似,不过OFB用的是前一个n位密文输出分组反馈回移位寄存器,OFB没有错误扩散问题。 (5) CTR : 计数器 计算器模式不常见,在CTR模式中, 有一个自增的算子,这个算子用密钥加密之后的输出和明文异或的结果得到密文,相当于一次一密。这种加密方式简单快速,安全可靠,而且可以并行加密,但是在计算器不能维持很长的情况下,密钥只能使用一次。 image.png 4、对称密码填充方式 (1) NoPadding (2) PKCS5Padding (3) ISO 10126 5、常用对称加密算法 (1)DES(Data Encryption Standard):数据加密标准,速度较快,适用于加密大量数据的场合; (2)3DES(Triple DES):是基于DES,对一块数据用三个不同的密钥进行三次加密,强度更高; (3)AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高,目前 AES 标准的一个实现是 Rijndael 算法; 如果读完觉得有收获的话,欢迎点赞、关注、加公众号【小工匠技术圈】 个人公众号,欢迎关注,查阅更多精彩历史! image

优秀的个人博客,低调大师

【Java小工匠聊密码学】--对称加密--DES

1、 DES算法 DES对称加密,对称加密,是一种比较传统的加密方式,其加密运算、解密运算使用的是同样的密钥,信息的发送者和信息的接收者在进行信息的传输与处理时,必须共同持有该密码(称为对称密码),是一对称加密算法。 2、DES算法特点 密钥长度56位+8位校验位,生命周期短。 3、DES算法实现 3.1 DES的JDK实现 (1)工作模式 ECB、CBC、PCBC、CTR、CTS、CFB、CFB8-CFB128、OFB、OFB8-OFB128. (2) 填充模式 NoPadding、PKCS5Padding、ISO10126Padding (3) 代码实现 package lzf.cipher.jdk; import java.nio.charset.Charset; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; /** * @author Java小工匠 */ public class JdkDesUtils { // 获取 DES Key public static byte[] getDesKey() { try { // 1、创建密钥生成器 KeyGenerator keyGenerator = KeyGenerator.getInstance("DES"); keyGenerator.init(56); // 2、产生密钥 SecretKey secretKey = keyGenerator.generateKey(); // 3、获取密钥 byte[] key = secretKey.getEncoded(); return key; } catch (Exception e) { throw new RuntimeException(e); } } // DES 加密 public static byte[] encryptDes(byte[] data, byte[] key) { try { SecretKey secretKey = new SecretKeySpec(key, "DES"); String type = "DES"; // 加工作模式和填充方式 // type= "DES/ECB/PKCS5Padding"; Cipher cipher = Cipher.getInstance(type); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] rsData = cipher.doFinal(data); return rsData; } catch (Exception e) { throw new RuntimeException(e); } } //解密 public static byte[] decryptDes(byte[] data, byte[] key) { try { SecretKey secretKey = new SecretKeySpec(key, "DES"); String type = "DES"; // 加工作模式和填充方式 // type= "DES/ECB/PKCS5Padding"; Cipher cipher = Cipher.getInstance(type); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] rsData = cipher.doFinal(data); return rsData; } catch (Exception e) { throw new RuntimeException(e); } } // 数据转16进制编码 public static String encodeHex(final byte[] data, final boolean toLowerCase) { final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; final char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER; final int l = data.length; final char[] out = new char[l << 1]; // two characters form the hex value. for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } return new String(out); } public static void main(String[] args) { byte[] data = "java小工匠".getBytes(Charset.forName("UTF-8")); byte[] key = getDesKey(); String hexKeyStr = encodeHex(key, true); System.out.println("加密密钥:" + hexKeyStr + ",length=" + hexKeyStr.length()); byte[] encryptData = encryptDes(data, key); System.out.println("加密数据:" + encodeHex(encryptData, true)); byte[] decryptData = decryptDes(encryptData, key); System.out.println("解密数据:" + new String(decryptData)); } } 如果读完觉得有收获的话,欢迎点赞、关注、加公众号【小工匠技术圈】 个人公众号,欢迎关注,查阅更多精彩历史! image

优秀的个人博客,低调大师

【Java小工匠聊密码学】--base58编码

1、Base58编码概述 1.1 什么是Base58编码? Base58是比特币的一种特殊编码方式,主要用于产生比特币钱包地址。相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+"和"/"符号。 1.2 Base58的设计目的? Base58编码,为比特币比特币钱包地址设计的。 (1)避免混淆,在某些字体下,数字0和字母大写O,以及字母大写I和字母小写l会非常相似。 (2)Base64编码中包含"+"和"/",非字母或数字的字符串作为帐号较难被接受。 (3) 在邮件系统中,使用字符和数字的组合,不容易换行。 (4)双击可以选中整个字符串。 1.3 Base58编码表 Base58编码 1.4 Base58编码原理 Base58编码实际上是58进制,和2进制、8进制、16进制是一样的道理,只是用58作为进制的单位了,正好和58个不容易混淆的字符对应。 1.5 Base58Check 二进制数据的传输过程中,为了防止数据传输的错误,保护数据安全,通常会加一个校验码。通过校验码的配合可以发现数据是否被破坏或者是否在发送时输入错误了。Base58Check就是Base58加上校验码,或者可以说是Base58的一种编码形式,在比特币系统中生成钱包地址的时候就使用到了这种编码形式。 2、Base58编码实现 2.1 比特币Base58 比特币中的Base58编码,不是纯正的Base58,包含一部分比特币地址规范,例如前导0的处理。 package lzf.cipher.jdk; import java.util.Arrays; /** * 复制比特币源码,去掉与Base58编码无关功能 * * @author java小工匠 */ public class Base58 { // Bsae58 编码表 public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); private static final char ENCODED_ZERO = ALPHABET[0]; private static final int[] INDEXES = new int[128]; static { Arrays.fill(INDEXES, -1); for (int i = 0; i < ALPHABET.length; i++) { INDEXES[ALPHABET[i]] = i; } } // Base58 编码 public static String encode(byte[] input) { if (input.length == 0) { return ""; } // 统计前导0 int zeros = 0; while (zeros < input.length && input[zeros] == 0) { ++zeros; } // 复制一份进行修改 input = Arrays.copyOf(input, input.length); // 最大编码数据长度 char[] encoded = new char[input.length * 2]; int outputStart = encoded.length; // Base58编码正式开始 for (int inputStart = zeros; inputStart < input.length;) { encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)]; if (input[inputStart] == 0) { ++inputStart; } } // 输出结果中有0,去掉输出结果的前端0 while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) { ++outputStart; } // 处理前导0 while (--zeros >= 0) { encoded[--outputStart] = ENCODED_ZERO; } // 返回Base58 return new String(encoded, outputStart, encoded.length - outputStart); } public static byte[] decode(String input) { if (input.length() == 0) { return new byte[0]; } // 将BASE58编码的ASCII字符转换为BASE58字节序列 byte[] input58 = new byte[input.length()]; for (int i = 0; i < input.length(); ++i) { char c = input.charAt(i); int digit = c < 128 ? INDEXES[c] : -1; if (digit < 0) { String msg = "Invalid characters,c=" + c; throw new RuntimeException(msg); } input58[i] = (byte) digit; } // 统计前导0 int zeros = 0; while (zeros < input58.length && input58[zeros] == 0) { ++zeros; } // Base58 编码转 字节序(256进制)编码 byte[] decoded = new byte[input.length()]; int outputStart = decoded.length; for (int inputStart = zeros; inputStart < input58.length;) { decoded[--outputStart] = divmod(input58, inputStart, 58, 256); if (input58[inputStart] == 0) { ++inputStart; } } // 忽略在计算过程中添加的额外超前零点。 while (outputStart < decoded.length && decoded[outputStart] == 0) { ++outputStart; } // 返回原始的字节数据 return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length); } // 进制转换代码 private static byte divmod(byte[] number, int firstDigit, int base, int divisor) { int remainder = 0; for (int i = firstDigit; i < number.length; i++) { int digit = (int) number[i] & 0xFF; int temp = remainder * base + digit; number[i] = (byte) (temp / divisor); remainder = temp % divisor; } return (byte) remainder; } public static void main(String[] args) { byte[] data = new byte[] {0,0,58,0,0,59}; String dataStr = Base58.encode(data); System.out.println("Base58编码后:"+dataStr); byte[] ndata = Base58.decode(dataStr); for (byte b : ndata) { System.out.println(b); } } } 如果读完觉得有收获的话,欢迎点赞、关注、加公众号【小工匠技术圈】 个人公众号,欢迎关注,查阅更多精彩历史! image

优秀的个人博客,低调大师

【Java小工匠聊密码学】--数字签名-RSA

1、RSA数字签名概述 使用RSA非对称加密技术实现的数字签名。 2、RSA数字签名算法分类 (1)MD (MD2withRSA、MD5withRSA) (2)SHA (SHA1withRSA、SHA256withRSA、SHA384withRSA、SHA512withRSA) 3、RSA数字签名实现 3.1 JDK实现 package lzf.cipher.jdk; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; /** * @author java小工匠 */ public class JdkSignatureRsaUtils { public static final String RSA = "RSA"; public static final String MD5withRSA = "MD5withRSA"; // 初始化密钥对 public static KeyPair initKey() { try { KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA); // 512 -65536 && 64 的倍数 generator.initialize(1024); return generator.generateKeyPair(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } // 获取公钥 public static byte[] getPublicKey(KeyPair keyPair) { byte[] bytes = keyPair.getPublic().getEncoded(); return bytes; } // 获取公钥 public static String getPublicKeyStr(KeyPair keyPair) { byte[] bytes = keyPair.getPublic().getEncoded(); return encodeHex(bytes); } // 获取私钥 public static byte[] getPrivateKey(KeyPair keyPair) { byte[] bytes = keyPair.getPrivate().getEncoded(); return bytes; } // 获取私钥 public static String getPrivateKeyStr(KeyPair keyPair) { byte[] bytes = keyPair.getPrivate().getEncoded(); return encodeHex(bytes); } // 签名 public static byte[] sign(byte[] data, byte[] privateKey, String type) { try { // 还原使用 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA); PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); // 1、实例化Signature Signature signature = Signature.getInstance(type); // 2、初始化Signature signature.initSign(priKey); // 3、更新数据 signature.update(data); // 4、签名 return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } // 验证 public static boolean verify(byte[] data, byte[] publicKey, byte[] sign, String type) { try { X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey); KeyFactory keyFactory = KeyFactory.getInstance(RSA); PublicKey pubKey = keyFactory.generatePublic(keySpec); // 1、实例化Signature Signature signature = Signature.getInstance(type); // 2、初始化Signature signature.initVerify(pubKey); // 3、更新数据 signature.update(data); // 4、签名 return signature.verify(sign); } catch (Exception e) { throw new RuntimeException(e); } } // 数据准16进制编码 public static String encodeHex(final byte[] data) { return encodeHex(data, true); } // 数据转16进制编码 public static String encodeHex(final byte[] data, final boolean toLowerCase) { final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; final char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER; final int l = data.length; final char[] out = new char[l << 1]; // two characters form the hex value. for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } return new String(out); } public static void main(String[] args) { String str = "java小工匠"; byte[] data = str.getBytes(); // 初始化密钥度 KeyPair keyPair = initKey(); byte[] publicKey = getPublicKey(keyPair); byte[] privateKey = getPrivateKey(keyPair); // 签名 String type = MD5withRSA; byte[] sign = sign(str.getBytes(), privateKey, type); // 验证 boolean b = verify(data, publicKey, sign, type); System.out.println("验证:" + b); } }

优秀的个人博客,低调大师

【Java小工匠聊密码学】--消息摘要--RIPEMD算法

1、什么是RIPEMD RIPEMD(RACE Integrity Primitives Evaluation Message Digest,RACE原始完整性校验消息摘要,是Hans Dobbertin等3人在md4,md5的基础上,于1996年提出来的。算法共有4个标准128、160、256和320,其对应输出长度分别为16字节、20字节、32字节和40字节。不过,让人难以致信的是RIPEMD的设计者们根本就没有真正设计256和320位这2种标准,他们只是在128位和160位的基础上,修改了初始参数和s-box来达到输出为256和320位的目的。所以,256位的强度和128相当,而320位的强度和160位相当。RIPEMD建立在md的基础之上,所以,其添加数据的方式和md5完全一样。 2、RIPEMD 应用案例 比特币在生成地址算法中,使用了该算法。 3、RIPEMD算法实现 package lzf.cipher.bc; import java.nio.charset.Charset; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.RIPEMD128Digest; import org.bouncycastle.crypto.digests.RIPEMD160Digest; import org.bouncycastle.crypto.digests.RIPEMD256Digest; import org.bouncycastle.crypto.digests.RIPEMD320Digest; import org.bouncycastle.util.encoders.Hex; /** * @author Java小工匠 */ public class BCRIPEMDUtils { public static String ripemd128(byte[] bytes) { Digest digest = new RIPEMD128Digest(); digest.update(bytes, 0, bytes.length); byte[] rsData = new byte[digest.getDigestSize()]; digest.doFinal(rsData, 0); return Hex.toHexString(rsData); } public static String ripemd160(byte[] bytes) { Digest digest = new RIPEMD160Digest(); digest.update(bytes, 0, bytes.length); byte[] rsData = new byte[digest.getDigestSize()]; digest.doFinal(rsData, 0); return Hex.toHexString(rsData); } public static String ripemd256(byte[] bytes) { Digest digest = new RIPEMD256Digest(); digest.update(bytes, 0, bytes.length); byte[] rsData = new byte[digest.getDigestSize()]; digest.doFinal(rsData, 0); return Hex.toHexString(rsData); } public static String ripemd320(byte[] bytes) { Digest digest = new RIPEMD320Digest(); digest.update(bytes, 0, bytes.length); byte[] rsData = new byte[digest.getDigestSize()]; digest.doFinal(rsData, 0); return Hex.toHexString(rsData); } public static void main(String[] args) { byte[] bytes = "java小工匠".getBytes(Charset.forName("UTF-8")); String ripemd128 = ripemd128(bytes); System.out.println("ripemd128:" + ripemd128 + ",lengh=" + ripemd128.length()); String ripemd160 = ripemd160(bytes); System.out.println("ripemd160:" + ripemd160 + ",lengh=" + ripemd160.length()); String ripemd256 = ripemd256(bytes); System.out.println("ripemd256:" + ripemd256 + ",lengh=" + ripemd256.length()); String ripemd320 = ripemd320(bytes); System.out.println("ripemd320:" + ripemd320 + ",lengh=" + ripemd320.length()); } } 如果读完觉得有收获的话,欢迎点赞、关注、加公众号【小工匠技术圈】 个人公众号,欢迎关注,查阅更多精彩历史! image

优秀的个人博客,低调大师

【Java小工匠聊密码学】--数字签名-DSA

1、DSA数字签名概述 1.1 DSA加密算算法 DSA(Digital Signature Algorithm)是Schnorr和ElGamal签名算法的变种,被美国NIST作为数字签名标准(DigitalSignature Standard)。 DSA(Digital Signature Algorithm,数字签名算法,用作数字签名标准的一部分),它是另一种公开密钥算法,它不能用作加密,只用作数字签名。DSA使用公开密钥,为接受者验证数据的完整性和数据发送者的身份。它也可用于由第三方去确定签名和所签数据的真实性。DSA算法的安全性基于解离散对数的困难性,这类签字标准具有较大的兼容性和适用性,成为网络安全体系的基本构件之一。 2、DSA数字签名算法分类 DSA只能与SHA-1一起使用。 3、DSA数字签名实现 3.1 JDK实现 package lzf.cipher.jdk; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; /** * @author java小工匠 */ public class JdkSignatureDsaUtils { public static final String DSA = "DSA"; public static final String MD5withDSA = "SHAwithDSA"; // 初始化密钥对 public static KeyPair initKey() { try { KeyPairGenerator generator = KeyPairGenerator.getInstance(DSA); // 512 -65536 && 64 的倍数 generator.initialize(1024); return generator.generateKeyPair(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } // 获取公钥 public static byte[] getPublicKey(KeyPair keyPair) { byte[] bytes = keyPair.getPublic().getEncoded(); return bytes; } // 获取公钥 public static String getPublicKeyStr(KeyPair keyPair) { byte[] bytes = keyPair.getPublic().getEncoded(); return encodeHex(bytes); } // 获取私钥 public static byte[] getPrivateKey(KeyPair keyPair) { byte[] bytes = keyPair.getPrivate().getEncoded(); return bytes; } // 获取私钥 public static String getPrivateKeyStr(KeyPair keyPair) { byte[] bytes = keyPair.getPrivate().getEncoded(); return encodeHex(bytes); } // 签名 public static byte[] sign(byte[] data, byte[] privateKey, String type) { try { // 还原使用 PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKey); KeyFactory keyFactory = KeyFactory.getInstance(DSA); PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); // 1、实例化Signature Signature signature = Signature.getInstance(type); // 2、初始化Signature signature.initSign(priKey); // 3、更新数据 signature.update(data); // 4、签名 return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } // 验证 public static boolean verify(byte[] data, byte[] publicKey, byte[] sign, String type) { try { X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey); KeyFactory keyFactory = KeyFactory.getInstance(DSA); PublicKey pubKey = keyFactory.generatePublic(keySpec); // 1、实例化Signature Signature signature = Signature.getInstance(type); // 2、初始化Signature signature.initVerify(pubKey); // 3、更新数据 signature.update(data); // 4、签名 return signature.verify(sign); } catch (Exception e) { throw new RuntimeException(e); } } // 数据准16进制编码 public static String encodeHex(final byte[] data) { return encodeHex(data, true); } // 数据转16进制编码 public static String encodeHex(final byte[] data, final boolean toLowerCase) { final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; final char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER; final int l = data.length; final char[] out = new char[l << 1]; // two characters form the hex value. for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } return new String(out); } public static void main(String[] args) { String str = "java小工匠"; byte[] data = str.getBytes(); // 初始化密钥度 KeyPair keyPair = initKey(); byte[] publicKey = getPublicKey(keyPair); byte[] privateKey = getPrivateKey(keyPair); // 签名 String type = MD5withDSA; byte[] sign = sign(str.getBytes(), privateKey, type); // 验证 boolean b = verify(data, publicKey, sign, type); System.out.println("验证:" + b); } }

优秀的个人博客,低调大师

【Java小工匠聊密码学】--非对称加密--RSA

1、什么是RSA 算法 RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。 对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用RSA加密的信息的可靠性就肯定会极度下降。但找到这样的算法的可能性是非常小的。今天只有短的RSA钥匙才可能被强力方式解破。到目前为止,世界上还没有任何可靠的攻击RSA算法的方式。只要其钥匙的长度足够长,用RSA加密的信息实际上是不能被解破的。 2、RSA算法过程 3、RAS算法实现 3.1、JDK算法实现 package lzf.cipher.jdk; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import javax.crypto.Cipher; /** * @author Java小工匠 */ public class JdkRsaUtils { // 初始化密钥对 public static KeyPair initKey() { try { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); // 512 -65536 && 64 的倍数 generator.initialize(1024); return generator.generateKeyPair(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } // 获取公钥 public static byte[] getPublicKey(KeyPair keyPair) { byte[] bytes = keyPair.getPublic().getEncoded(); return bytes; } // 获取公钥 public static String getPublicKeyStr(KeyPair keyPair) { byte[] bytes = keyPair.getPublic().getEncoded(); return encodeHex(bytes); } // 获取私钥 public static byte[] getPrivateKey(KeyPair keyPair) { byte[] bytes = keyPair.getPrivate().getEncoded(); return bytes; } // 获取私钥 public static String getPrivateKeyStr(KeyPair keyPair) { byte[] bytes = keyPair.getPrivate().getEncoded(); return encodeHex(bytes); } // 加密数据 public static byte[] encryptRsa(byte[] data, byte[] key) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key); RSAPublicKey secretKey = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.PUBLIC_KEY, secretKey); byte[] rs = cipher.doFinal(data); return rs; } catch (Exception e) { throw new RuntimeException(e); } } // 解密数据 public static byte[] decryptRsa(byte[] data, byte[] key) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key); RSAPrivateCrtKey secretKey = (RSAPrivateCrtKey) keyFactory.generatePrivate(keySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.PRIVATE_KEY, secretKey); byte[] rs = cipher.doFinal(data); return rs; } catch (Exception e) { throw new RuntimeException(e); } } // 数据准16进制编码 public static String encodeHex(final byte[] data) { return encodeHex(data, true); } // 数据转16进制编码 public static String encodeHex(final byte[] data, final boolean toLowerCase) { final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; final char[] toDigits = toLowerCase ? DIGITS_LOWER : DIGITS_UPPER; final int l = data.length; final char[] out = new char[l << 1]; // two characters form the hex value. for (int i = 0, j = 0; i < l; i++) { out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; out[j++] = toDigits[0x0F & data[i]]; } return new String(out); } public static void main(String[] args) throws Exception { String str = "java小工匠"; KeyPair keyPair = initKey(); byte[] publicKey = getPublicKey(keyPair); byte[] privateKey = getPrivateKey(keyPair); byte[] secretData = encryptRsa(str.getBytes(), publicKey); System.out.println("RSA加密:" + encodeHex(secretData)); byte[] data = decryptRsa(secretData, privateKey); System.out.println("RSA解密:" + new String(data)); } }

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册