目前,vuelidate 2.x暂时还没发布正式版本,而如果需要适配Vue 3.x,则需要自己编译,编译过程如下:
智障儿童欢乐多
目前,vuelidate 2.x暂时还没发布正式版本,而如果需要适配Vue 3.x,则需要自己编译,编译过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
$ brew install npm $ brew install yarn $ npm install bili -g $ git clone https://github.com/vuelidate/vuelidate.git # 如果需要编译umd版本,目前需要使用下面的仓库 # git clone https://github.com/wangqiang1588/vuelidate.git $ cd vuelidate $ cd packages $ cd vuelidate $ npm run build # 当前目录下的dist/index.umd.js即是目标文件,拷贝出来即可 $ cd .. $ cd validators $ npm run build # 当前目录下的dist/index.umd.js即是目标文件,拷贝出来即可 |
最近在macOS Big Sur(11.4)编译Hummer
1 2 3 4 5 6 7 8 9 10 |
$ git clone https://github.com/didi/Hummer.git # 出问题的提交版本 ece6d899da93e83d58ecbe68659d34cafaed8c38 # git checkout ece6d899da93e83d58ecbe68659d34cafaed8c38 $ cd Hummer $ cd android $ bash gradlew clean build |
结果报错如下:
1 2 3 |
* What went wrong: A problem occurred configuring project ':hummer-core'. > ABIs [arm64-v8a] are not supported for platform. Supported ABIs are [armeabi-v7a, x86]. |
原因为默认引入的构建工具版本如下:
1 |
classpath 'com.android.tools.build:gradle:3.4.1' |
解决方法为升级到:
1 |
classpath 'com.android.tools.build:gradle:3.6.4' |
注意: 目前测试发现,如果升级到
1 |
classpath 'com.android.tools.build:gradle:4.2.1' |
不能成功编译。原因为module.gradle
的配置没有被正确解析。
在使用WordPress写文章的时候,如果使用H5及以下字体,英文字母会被强制转换成大写,导致布局非常难看。
解决方法是通过覆盖默认的 text-transform属性,如下:
1 |
<h5 style="text-transform: none;">1.JavaScriptCore</h5> |
在一般的移动端开发场景中,每次更新应用功能都是通过 Native 语言开发并通过应用市场版本分发来实现的。但是市场瞬息万变,Native 语言在开发效率上存在一定不足,并且从APP版本更新
到应用市场审核发布
再到用户下载更新
,总会存在一定的时间差,这样就导致新的功能无法及时覆盖全量用户。
为了解决这个问题,开发者们一般会在项目里引入一门脚本语言,提速APP的研发流程。在移动端应用比较广泛的脚本语言有 Lua和JavaScript,前者在游戏领域用的比较多,后者在应用领域用的比较多。本篇文章主要是想探讨一下移动双端(iOS
& Android
)的JavaScript引擎选型。由于个人水平有限,文章总会有遗漏和不足的地方,还请各位大佬多多指教。
JavaScript作为世界上最热门的脚本语言,有着非常多的引擎实现:有Apple御用的 JavaScriptCore,有性能最强劲的V8,还有最近热度很高的QuickJS......如何从这些JS引擎里选出最适合的?我个人认为要有几个考量:
比较麻烦的是,上面的几个点都不是互相独立的,比如说开启JIT的V8引擎,性能肯定是最好的,但它引擎体积就很大,内存占用也很高;在包体积上很占优势的QuickJS,由于没有JIT加持,和有JIT的引擎比起来平均会有5-10倍的性能差距。
下面我会综合刚刚提到的几个点,并选择了JavaScriptCore,V8,Hermes 和 QuickJS这4个JSVM,说说它们的优点和特点,再谈谈他们的不足。
我们在使用Qussar QScrollArea等控件的时候,很多情况下,必须给出一个固定的高度,否则控件的高度会变成 0,导致无法显示出来。如何动态适配View高度呢? 参考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
<template> <q-page> . . . <q-scroll-area> <q-resize-obeserver @resize="updateHeights" /> <q-scroll-area> </q-page> </template> <script> . . . export default { . . . methods: { . . . updateHeights(size) { // { // width: 20 // width of container (in px) // height: 50 // height of container (in px) // } //重新计算整个区域的高度,使得滚动区域占满整个父控件的高度 const sz = this.measureElWrapSize(this.$el); this.$el.style.height = sz.height + 'px'; }, /** * 字符串对象是否不为空 */ isNotEmpty: function (s) { return (!this.isEmpty(s)); }, /** * 字符串对象是否为空 */ isEmpty: function (s) { return (this.isNull(s) || ('' === s)); }, /** * 对象是否为空 */ isNull: function (o) { return ((undefined === o) || (null === o)); }, /** * 对象是否不为空 */ isNotNull: function (o) { return (!this.isNull(o)); }, // 计算对象适配父控件大小,从底层开始向上搜索,直到搜索到受到限制的位置为止,然后反向计算应该得到的宽高 measureElWrapSize: function (el) { const hls = []; //到第一次出现高度限制的父窗口的数组 const wls = []; //到第一次出现宽度限制的父窗口的数组 // 向上搜索,直到父控件出现高度限制,否则继续向上搜索 let p = el.offsetParent; let maxH = window.innerHeight; while (this.isNotNull(p)) { const h = p.style.height; if (this.isNotEmpty(h)) { maxH = Math.min(parseInt(h), maxH); break; } hls[hls.length] = p; p = p.offsetParent; } // 逐层遍历父亲控件,计算剩余高度 let i = 0; for (; i < hls.length; i++) { const e = hls[i]; maxH = maxH - e.offsetTop; } //需要计算自身的顶部偏移 maxH = maxH - el.offsetTop; if (maxH < 0) { maxH = 0; } // 向上搜索,直到父控件出现宽度限制,否则继续向上搜索 let maxW = window.innerWidth; p = el.offsetParent; while (this.isNotNull(p)) { const w = p.style.width; if (this.isNotEmpty(w)) { maxW = Math.min(parseInt(w), maxW); break; } wls[wls.length] = p; p = p.offsetParent; } // 逐层遍历父亲控件,计算剩余宽度 i = 0; for (; i < wls.length; i++) { const e = wls[i]; maxW = maxW - e.offsetLeft; } //需要计算自身的左侧偏移 maxW = maxW - el.offsetLeft; if (maxW < 0) { maxW = 0; } return {width: maxW, height: maxH}; }, }, } |
或者类似下面的情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<div id="q-app"> <div class="q-pa-md"> <q-layout class="shadow-2 rounded-borders" container ref="mainView" view="hHh Lpr lff"> . . . <q-resize-obeserver @resize="updateHeights" /> </q-layout> </div> </div> </template> <script> . . . export default { . . . methods: { . . . updateHeights(size) { // { // width: 20 // width of container (in px) // height: 50 // height of container (in px) // } const offset = this.$refs.mainView.$el.offsetTop; this.$refs.mainView.$el.style.height = (window.innerHeight - offset) + 'px'; } }, } |
我们开发WEB代码的时候,经常回遇到各种高度的计算. 因为总是忘记几者之间得区别,每次都要现查,这次通过这篇文章彻底搞明白这几个长度的区别。
Maven依赖:
1 2 3 4 5 |
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.54</version> </dependency> |
Java实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
import java.math.BigInteger; import java.security.SecureRandom; import java.util.Arrays; import org.bouncycastle.crypto.DerivationFunction; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.ShortenedDigest; import org.bouncycastle.crypto.generators.KDF1BytesGenerator; import org.bouncycastle.crypto.params.ISO18033KDFParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; /** * <B>说 明<B/>:SM2的非对称加解密工具类,椭圆曲线方程为:y^2=x^3+ax+b 使用Fp-256 */ public class SM2Util { /** 素数p */ private static final BigInteger p = new BigInteger("FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFF", 16); /** 系数a */ private static final BigInteger a = new BigInteger("FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "00000000" + "FFFFFFFF" + "FFFFFFFC", 16); /** 系数b */ private static final BigInteger b = new BigInteger("28E9FA9E" + "9D9F5E34" + "4D5A9E4B" + "CF6509A7" + "F39789F5" + "15AB8F92" + "DDBCBD41" + "4D940E93", 16); /** 坐标x */ private static final BigInteger xg = new BigInteger("32C4AE2C" + "1F198119" + "5F990446" + "6A39C994" + "8FE30BBF" + "F2660BE1" + "715A4589" + "334C74C7", 16); /** 坐标y */ private static final BigInteger yg = new BigInteger("BC3736A2" + "F4F6779C" + "59BDCEE3" + "6B692153" + "D0A9877C" + "C62A4740" + "02DF32E5" + "2139F0A0", 16); /** 基点G, G=(xg,yg),其介记为n */ private static final BigInteger n = new BigInteger("FFFFFFFE" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "7203DF6B" + "21C6052B" + "53BBF409" + "39D54123", 16); private static SecureRandom random = new SecureRandom(); private ECCurve.Fp curve; private ECPoint G; public static String printHexString(byte[] b) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < b.length; i++) { String hex = Integer.toHexString(b[i] & 0xFF); if (hex.length() == 1) { builder.append('0'+hex); hex = '0' + hex; } // System.out.print(hex.toUpperCase()); System.out.print(hex.toUpperCase()); builder.append(hex); } System.out.println(); return builder.toString(); } public BigInteger random(BigInteger max) { BigInteger r = new BigInteger(256, random); // int count = 1; while (r.compareTo(max) >= 0) { r = new BigInteger(128, random); // count++; } // System.out.println("count: " + count); return r; } private boolean allZero(byte[] buffer) { for (int i = 0; i < buffer.length; i++) { if (buffer[i] != 0) return false; } return true; } /** * 加密 * @param input 待加密消息M * @param publicKey 公钥 * @return byte[] 加密后的字节数组 */ public byte[] encrypt(String input, ECPoint publicKey) { System.out.println("publicKey is: "+publicKey); byte[] inputBuffer = input.getBytes(); printHexString(inputBuffer); /* 1 产生随机数k,k属于[1, n-1] */ BigInteger k = random(n); System.out.print("k: "); printHexString(k.toByteArray()); /* 2 计算椭圆曲线点C1 = [k]G = (x1, y1) */ ECPoint C1 = G.multiply(k); byte[] C1Buffer = C1.getEncoded(false); System.out.print("C1: "); printHexString(C1Buffer); // 3 计算椭圆曲线点 S = [h]Pb * curve没有指定余因子,h为空 // BigInteger h = curve.getCofactor(); System.out.print("h: "); // printHexString(h.toByteArray()); if (publicKey != null) { ECPoint // result = publicKey.multiply(h); if (!result.isInfinity()) { // System.out.println("pass"); } else { // System.err.println("计算椭圆曲线点 S = [h]Pb失败"); return null; } } /* 4 计算 [k]PB = (x2, y2) */ ECPoint kpb = publicKey.multiply(k).normalize(); /* 5 计算 t = KDF(x2||y2, klen) */ byte[] kpbBytes = kpb.getEncoded(false); DerivationFunction kdf = new KDF1BytesGenerator(new ShortenedDigest( new SHA256Digest(), 20)); byte[] t = new byte[inputBuffer.length]; kdf.init(new ISO18033KDFParameters(kpbBytes)); kdf.generateBytes(t, 0, t.length); if (allZero(t)) { System.err.println("all zero"); } /* 6 计算C2=M^t */ byte[] C2 = new byte[inputBuffer.length]; for (int i = 0; i < inputBuffer.length; i++) { C2[i] = (byte) (inputBuffer[i] ^ t[i]); } /* 7 计算C3 = Hash(x2 || M || y2) */ byte[] C3 = calculateHash(kpb.getXCoord().toBigInteger(), inputBuffer, kpb.getYCoord().toBigInteger()); /* 8 输出密文 C=C1 || C2 || C3 */ byte[] encryptResult = new byte[C1Buffer.length + C2.length + C3.length]; System.arraycopy(C1Buffer, 0, encryptResult, 0, C1Buffer.length); System.arraycopy(C2, 0, encryptResult, C1Buffer.length, C2.length); System.arraycopy(C3, 0, encryptResult, C1Buffer.length + C2.length, C3.length); System.out.print("密文: "); printHexString(encryptResult); return encryptResult; } public void decrypt(byte[] encryptData, BigInteger privateKey) { System.out.println("privateKey is: "+privateKey); System.out.println("encryptData length: " + encryptData.length); byte[] C1Byte = new byte[65]; System.arraycopy(encryptData, 0, C1Byte, 0, C1Byte.length); ECPoint C1 = curve.decodePoint(C1Byte).normalize(); /* 计算[dB]C1 = (x2, y2) */ ECPoint dBC1 = C1.multiply(privateKey).normalize(); /* 计算t = KDF(x2 || y2, klen) */ byte[] dBC1Bytes = dBC1.getEncoded(false); DerivationFunction kdf = new KDF1BytesGenerator(new ShortenedDigest( new SHA256Digest(), 20)); int klen = encryptData.length - 65 - 20; System.out.println("klen = " + klen); byte[] t = new byte[klen]; kdf.init(new ISO18033KDFParameters(dBC1Bytes)); kdf.generateBytes(t, 0, t.length); if (allZero(t)) { System.err.println("all zero"); } /* 5 计算M'=C2^t */ byte[] M = new byte[klen]; for (int i = 0; i < M.length; i++) { M[i] = (byte) (encryptData[C1Byte.length + i] ^ t[i]); } /* 6 计算 u = Hash(x2 || M' || y2) 判断 u == C3是否成立 */ byte[] C3 = new byte[20]; System.arraycopy(encryptData, encryptData.length - 20, C3, 0, 20); byte[] u = calculateHash(dBC1.getXCoord().toBigInteger(), M, dBC1 .getYCoord().toBigInteger()); if (Arrays.equals(u, C3)) { System.out.println("解密成功"); System.out.println("M' = " + new String(M)); } else { System.out.print("u = "); printHexString(u); System.out.print("C3 = "); printHexString(C3); System.err.println("解密验证失败"); } } private byte[] calculateHash(BigInteger x2, byte[] M, BigInteger y2) { ShortenedDigest digest = new ShortenedDigest(new SHA256Digest(), 20); byte[] buf = x2.toByteArray(); digest.update(buf, 0, buf.length); digest.update(M, 0, M.length); buf = y2.toByteArray(); digest.update(buf, 0, buf.length); buf = new byte[20]; digest.doFinal(buf, 0); return buf; } private boolean between(BigInteger param, BigInteger min, BigInteger max) { if (param.compareTo(min) >= 0 && param.compareTo(max) < 0) { return true; } else { return false; } } /** * 公钥校验 * @param publicKey 公钥 * @return boolean true或false */ private boolean checkPublicKey(ECPoint publicKey) { if (!publicKey.isInfinity()) { BigInteger x = publicKey.getXCoord().toBigInteger(); BigInteger y = publicKey.getYCoord().toBigInteger(); if (between(x, new BigInteger("0"), p) && between(y, new BigInteger("0"), p)) { BigInteger xResult = x.pow(3).add(a.multiply(x)).add(b).mod(p); System.out.println("xResult: " + xResult.toString()); BigInteger yResult = y.pow(2).mod(p); System.out.println("yResult: " + yResult.toString()); if (yResult.equals(xResult) && publicKey.multiply(n).isInfinity()) { return true; } } return false; } else { return false; } } /** * 获得公私钥对 * @return */ public SM2KeyPair generateKeyPair() { BigInteger d = random(n.subtract(new BigInteger("1"))); SM2KeyPair keyPair = new SM2KeyPair(G.multiply(d).normalize(), d); if (checkPublicKey(keyPair.getPublicKey())) { System.out.println("generate key successfully"); return keyPair; } else { System.err.println("generate key failed"); return null; } } public SM2Util() { curve = new ECCurve.Fp(p, // q a, // a b); // b G = curve.createPoint(xg, yg); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import java.math.BigInteger; import org.bouncycastle.math.ec.ECPoint; /** * <B>说 明<B/>:SM2公私钥实体类 */ public class SM2KeyPair { /** 公钥 */ private ECPoint publicKey; /** 私钥 */ private BigInteger privateKey; SM2KeyPair(ECPoint publicKey, BigInteger privateKey) { this.publicKey = publicKey; this.privateKey = privateKey; } public ECPoint getPublicKey() { return publicKey; } public BigInteger getPrivateKey() { return privateKey; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import java.util.Arrays; /** * <B>说 明<B/>:SM2非对称加解密工具类测试 */ public class SM2UtilTest { /** 元消息串 */ private static String M = "哈哈哈,&*&…………&、、//\\!@#$%^&*()物品woyebuzhidaowozijiqiaodesha!@#$%^&*())))))ooooooooppppppppppppppppppplllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkffffffffffffffffffffffffffffffffffffff"; public static void main(String[] args) { SM2Util sm2 = new SM2Util(); SM2KeyPair keyPair = sm2.generateKeyPair(); byte[] data = sm2.encrypt(M,keyPair.getPublicKey()); System.out.println("data is:"+Arrays.toString(data)); sm2.decrypt(data, keyPair.getPrivateKey());//71017045908707391874054405929626258767106914144911649587813342322113806533034 } } |
1 2 3 4 5 |
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk16</artifactId> <version>1.46</version> </dependency> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.math.ec.ECPoint; import java.math.BigInteger; public class Cipher { private int ct; private ECPoint p2; private SM3Digest sm3keybase; private SM3Digest sm3c3; private byte[] key; private byte keyOff; public Cipher() { this.ct = 1; this.key = new byte[32]; this.keyOff = 0; } private void Reset() { this.sm3keybase = new SM3Digest(); this.sm3c3 = new SM3Digest(); byte[] p = Util.byteConvert32Bytes(p2.getX().toBigInteger()); this.sm3keybase.update(p, 0, p.length); this.sm3c3.update(p, 0, p.length); p = Util.byteConvert32Bytes(p2.getY().toBigInteger()); this.sm3keybase.update(p, 0, p.length); this.ct = 1; NextKey(); } private void NextKey() { SM3Digest sm3keycur = new SM3Digest(this.sm3keybase); sm3keycur.update((byte) (ct >> 24 & 0xff)); sm3keycur.update((byte) (ct >> 16 & 0xff)); sm3keycur.update((byte) (ct >> 8 & 0xff)); sm3keycur.update((byte) (ct & 0xff)); sm3keycur.doFinal(key, 0); this.keyOff = 0; this.ct++; } public ECPoint Init_enc(SM2 sm2, ECPoint userKey) { AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair(); ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate(); ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic(); BigInteger k = ecpriv.getD(); ECPoint c1 = ecpub.getQ(); this.p2 = userKey.multiply(k); Reset(); return c1; } public void Encrypt(byte[] data) { this.sm3c3.update(data, 0, data.length); for (int i = 0; i < data.length; i++) { if (keyOff == key.length) { NextKey(); } data[i] ^= key[keyOff++]; } } public void Init_dec(BigInteger userD, ECPoint c1) { this.p2 = c1.multiply(userD); Reset(); } public void Decrypt(byte[] data) { for (int i = 0; i < data.length; i++) { if (keyOff == key.length) { NextKey(); } data[i] ^= key[keyOff++]; } this.sm3c3.update(data, 0, data.length); } public void doFinal(byte[] c3) { byte[] p = Util.byteConvert32Bytes(p2.getY().toBigInteger()); this.sm3c3.update(p, 0, p.length); this.sm3c3.doFinal(c3, 0); Reset(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
public class SM3 { public static final byte[] iv = {0x73, (byte) 0x80, 0x16, 0x6f, 0x49, 0x14, (byte) 0xb2, (byte) 0xb9, 0x17, 0x24, 0x42, (byte) 0xd7, (byte) 0xda, (byte) 0x8a, 0x06, 0x00, (byte) 0xa9, 0x6f, 0x30, (byte) 0xbc, (byte) 0x16, 0x31, 0x38, (byte) 0xaa, (byte) 0xe3, (byte) 0x8d, (byte) 0xee, 0x4d, (byte) 0xb0, (byte) 0xfb, 0x0e, 0x4e}; public static int[] Tj = new int[64]; static { for (int i = 0; i < 16; i++) { Tj[i] = 0x79cc4519; } for (int i = 16; i < 64; i++) { Tj[i] = 0x7a879d8a; } } public static byte[] CF(byte[] V, byte[] B) { int[] v, b; v = convert(V); b = convert(B); return convert(CF(v, b)); } private static int[] convert(byte[] arr) { int[] out = new int[arr.length / 4]; byte[] tmp = new byte[4]; for (int i = 0; i < arr.length; i += 4) { System.arraycopy(arr, i, tmp, 0, 4); out[i / 4] = bigEndianByteToInt(tmp); } return out; } private static byte[] convert(int[] arr) { byte[] out = new byte[arr.length * 4]; byte[] tmp = null; for (int i = 0; i < arr.length; i++) { tmp = bigEndianIntToByte(arr[i]); System.arraycopy(tmp, 0, out, i * 4, 4); } return out; } public static int[] CF(int[] V, int[] B) { int a, b, c, d, e, f, g, h; int ss1, ss2, tt1, tt2; a = V[0]; b = V[1]; c = V[2]; d = V[3]; e = V[4]; f = V[5]; g = V[6]; h = V[7]; int[][] arr = expand(B); int[] w = arr[0]; int[] w1 = arr[1]; for (int j = 0; j < 64; j++) { ss1 = (bitCycleLeft(a, 12) + e + bitCycleLeft(Tj[j], j)); ss1 = bitCycleLeft(ss1, 7); ss2 = ss1 ^ bitCycleLeft(a, 12); tt1 = FFj(a, b, c, j) + d + ss2 + w1[j]; tt2 = GGj(e, f, g, j) + h + ss1 + w[j]; d = c; c = bitCycleLeft(b, 9); b = a; a = tt1; h = g; g = bitCycleLeft(f, 19); f = e; e = P0(tt2); /*System.out.print(j+" "); System.out.print(Integer.toHexString(a)+" "); System.out.print(Integer.toHexString(b)+" "); System.out.print(Integer.toHexString(c)+" "); System.out.print(Integer.toHexString(d)+" "); System.out.print(Integer.toHexString(e)+" "); System.out.print(Integer.toHexString(f)+" "); System.out.print(Integer.toHexString(g)+" "); System.out.print(Integer.toHexString(h)+" "); System.out.println("");*/ } // System.out.println(""); int[] out = new int[8]; out[0] = a ^ V[0]; out[1] = b ^ V[1]; out[2] = c ^ V[2]; out[3] = d ^ V[3]; out[4] = e ^ V[4]; out[5] = f ^ V[5]; out[6] = g ^ V[6]; out[7] = h ^ V[7]; return out; } private static int[][] expand(int[] B) { int[] W = new int[68]; int[] W1 = new int[64]; for (int i = 0; i < B.length; i++) { W[i] = B[i]; } for (int i = 16; i < 68; i++) { W[i] = P1(W[i - 16] ^ W[i - 9] ^ bitCycleLeft(W[i - 3], 15)) ^ bitCycleLeft(W[i - 13], 7) ^ W[i - 6]; } for (int i = 0; i < 64; i++) { W1[i] = W[i] ^ W[i + 4]; } int[][] arr = new int[][]{W, W1}; return arr; } private static byte[] bigEndianIntToByte(int num) { return back(Util.intToBytes(num)); } private static int bigEndianByteToInt(byte[] bytes) { return Util.byteToInt(back(bytes)); } private static int FFj(int X, int Y, int Z, int j) { if (j >= 0 && j <= 15) { return FF1j(X, Y, Z); } else { return FF2j(X, Y, Z); } } private static int GGj(int X, int Y, int Z, int j) { if (j >= 0 && j <= 15) { return GG1j(X, Y, Z); } else { return GG2j(X, Y, Z); } } // 逻辑位运算函数 private static int FF1j(int X, int Y, int Z) { int tmp = X ^ Y ^ Z; return tmp; } private static int FF2j(int X, int Y, int Z) { int tmp = ((X & Y) | (X & Z) | (Y & Z)); return tmp; } private static int GG1j(int X, int Y, int Z) { int tmp = X ^ Y ^ Z; return tmp; } private static int GG2j(int X, int Y, int Z) { int tmp = (X & Y) | (~X & Z); return tmp; } private static int P0(int X) { int y = rotateLeft(X, 9); y = bitCycleLeft(X, 9); int z = rotateLeft(X, 17); z = bitCycleLeft(X, 17); int t = X ^ y ^ z; return t; } private static int P1(int X) { int t = X ^ bitCycleLeft(X, 15) ^ bitCycleLeft(X, 23); return t; } /** * 对最后一个分组字节数据padding * * @param in * @param bLen 分组个数 * @return */ public static byte[] padding(byte[] in, int bLen) { int k = 448 - (8 * in.length + 1) % 512; if (k < 0) { k = 960 - (8 * in.length + 1) % 512; } k += 1; byte[] padd = new byte[k / 8]; padd[0] = (byte) 0x80; long n = in.length * 8 + bLen * 512; byte[] out = new byte[in.length + k / 8 + 64 / 8]; int pos = 0; System.arraycopy(in, 0, out, 0, in.length); pos += in.length; System.arraycopy(padd, 0, out, pos, padd.length); pos += padd.length; byte[] tmp = back(Util.longToBytes(n)); System.arraycopy(tmp, 0, out, pos, tmp.length); return out; } /** * 字节数组逆序 * * @param in * @return */ private static byte[] back(byte[] in) { byte[] out = new byte[in.length]; for (int i = 0; i < out.length; i++) { out[i] = in[out.length - i - 1]; } return out; } public static int rotateLeft(int x, int n) { return (x << n) | (x >> (32 - n)); } private static int bitCycleLeft(int n, int bitLen) { bitLen %= 32; byte[] tmp = bigEndianIntToByte(n); int byteLen = bitLen / 8; int len = bitLen % 8; if (byteLen > 0) { tmp = byteCycleLeft(tmp, byteLen); } if (len > 0) { tmp = bitSmall8CycleLeft(tmp, len); } return bigEndianByteToInt(tmp); } private static byte[] bitSmall8CycleLeft(byte[] in, int len) { byte[] tmp = new byte[in.length]; int t1, t2, t3; for (int i = 0; i < tmp.length; i++) { t1 = (byte) ((in[i] & 0x000000ff) << len); t2 = (byte) ((in[(i + 1) % tmp.length] & 0x000000ff) >> (8 - len)); t3 = (byte) (t1 | t2); tmp[i] = (byte) t3; } return tmp; } private static byte[] byteCycleLeft(byte[] in, int byteLen) { byte[] tmp = new byte[in.length]; System.arraycopy(in, byteLen, tmp, 0, in.length - byteLen); System.arraycopy(in, 0, tmp, in.length - byteLen, byteLen); return tmp; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
import org.bouncycastle.util.encoders.Hex; public class SM3Digest { /** * SM3值的长度 */ private static final int BYTE_LENGTH = 32; /** * SM3分组长度 */ private static final int BLOCK_LENGTH = 64; /** * 缓冲区长度 */ private static final int BUFFER_LENGTH = BLOCK_LENGTH * 1; /** * 缓冲区 */ private byte[] xBuf = new byte[BUFFER_LENGTH]; /** * 缓冲区偏移量 */ private int xBufOff; /** * 初始向量 */ private byte[] V = SM3.iv.clone(); private int cntBlock = 0; public SM3Digest() { } public SM3Digest(SM3Digest t) { System.arraycopy(t.xBuf, 0, this.xBuf, 0, t.xBuf.length); this.xBufOff = t.xBufOff; System.arraycopy(t.V, 0, this.V, 0, t.V.length); } public static void main(String[] args) { byte[] md = new byte[32]; byte[] msg1 = "ererfeiisgod".getBytes(); SM3Digest sm3 = new SM3Digest(); sm3.update(msg1, 0, msg1.length); sm3.doFinal(md, 0); String s = new String(Hex.encode(md)); System.out.println(s.toUpperCase()); } /** * SM3结果输出 * * @param out 保存SM3结构的缓冲区 * @param outOff 缓冲区偏移量 * @return */ public int doFinal(byte[] out, int outOff) { byte[] tmp = doFinal(); System.arraycopy(tmp, 0, out, 0, tmp.length); return BYTE_LENGTH; } public void reset() { xBufOff = 0; cntBlock = 0; V = SM3.iv.clone(); } /** * 明文输入 * * @param in 明文输入缓冲区 * @param inOff 缓冲区偏移量 * @param len 明文长度 */ public void update(byte[] in, int inOff, int len) { int partLen = BUFFER_LENGTH - xBufOff; int inputLen = len; int dPos = inOff; if (partLen < inputLen) { System.arraycopy(in, dPos, xBuf, xBufOff, partLen); inputLen -= partLen; dPos += partLen; doUpdate(); while (inputLen > BUFFER_LENGTH) { System.arraycopy(in, dPos, xBuf, 0, BUFFER_LENGTH); inputLen -= BUFFER_LENGTH; dPos += BUFFER_LENGTH; doUpdate(); } } System.arraycopy(in, dPos, xBuf, xBufOff, inputLen); xBufOff += inputLen; } private void doUpdate() { byte[] B = new byte[BLOCK_LENGTH]; for (int i = 0; i < BUFFER_LENGTH; i += BLOCK_LENGTH) { System.arraycopy(xBuf, i, B, 0, B.length); doHash(B); } xBufOff = 0; } private void doHash(byte[] B) { byte[] tmp = SM3.CF(V, B); System.arraycopy(tmp, 0, V, 0, V.length); cntBlock++; } private byte[] doFinal() { byte[] B = new byte[BLOCK_LENGTH]; byte[] buffer = new byte[xBufOff]; System.arraycopy(xBuf, 0, buffer, 0, buffer.length); byte[] tmp = SM3.padding(buffer, cntBlock); for (int i = 0; i < tmp.length; i += BLOCK_LENGTH) { System.arraycopy(tmp, i, B, 0, B.length); doHash(B); } return V; } public void update(byte in) { byte[] buffer = new byte[]{in}; update(buffer, 0, 1); } public int getDigestSize() { return BYTE_LENGTH; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyGenerationParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECFieldElement.Fp; import org.bouncycastle.math.ec.ECPoint; import java.math.BigInteger; import java.security.SecureRandom; public class SM2 { //测试参数 // public static final String[] ecc_param = { // "8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", // "787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", // "63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", // "8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", // "421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", // "0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2" // }; //正式参数 public static String[] ecc_param = { "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0" }; public final BigInteger ecc_p; public final BigInteger ecc_a; public final BigInteger ecc_b; public final BigInteger ecc_n; public final BigInteger ecc_gx; public final BigInteger ecc_gy; public final ECCurve ecc_curve; public final ECPoint ecc_point_g; public final ECDomainParameters ecc_bc_spec; public final ECKeyPairGenerator ecc_key_pair_generator; public final ECFieldElement ecc_gx_fieldelement; public final ECFieldElement ecc_gy_fieldelement; public SM2() { this.ecc_p = new BigInteger(ecc_param[0], 16); this.ecc_a = new BigInteger(ecc_param[1], 16); this.ecc_b = new BigInteger(ecc_param[2], 16); this.ecc_n = new BigInteger(ecc_param[3], 16); this.ecc_gx = new BigInteger(ecc_param[4], 16); this.ecc_gy = new BigInteger(ecc_param[5], 16); this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx); this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy); this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b); this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement); this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n); ECKeyGenerationParameters ecc_ecgenparam; ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom()); this.ecc_key_pair_generator = new ECKeyPairGenerator(); this.ecc_key_pair_generator.init(ecc_ecgenparam); } public static SM2 Instance() { return new SM2(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.math.ec.ECPoint; import java.io.IOException; import java.math.BigInteger; public class SM2Utils { //生成随机秘钥对 public static void generateKeyPair() { SM2 sm2 = SM2.Instance(); AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair(); ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate(); ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic(); BigInteger privateKey = ecpriv.getD(); ECPoint publicKey = ecpub.getQ(); System.out.println("公钥: " + Util.byteToHex(publicKey.getEncoded())); System.out.println("私钥: " + Util.byteToHex(privateKey.toByteArray())); } //数据加密 public static String encrypt(byte[] publicKey, byte[] data) throws IOException { if (publicKey == null || publicKey.length == 0) { return null; } if (data == null || data.length == 0) { return null; } byte[] source = new byte[data.length]; System.arraycopy(data, 0, source, 0, data.length); Cipher cipher = new Cipher(); SM2 sm2 = SM2.Instance(); ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey); ECPoint c1 = cipher.Init_enc(sm2, userKey); cipher.Encrypt(source); byte[] c3 = new byte[32]; cipher.doFinal(c3); // System.out.println("C1 " + Util.byteToHex(c1.getEncoded())); // System.out.println("C2 " + Util.byteToHex(source)); // System.out.println("C3 " + Util.byteToHex(c3)); //C1 C2 C3拼装成加密字串 return Util.byteToHex(c1.getEncoded()) + Util.byteToHex(source) + Util.byteToHex(c3); } //数据解密 //c3InFront 硬件为了方便加解密数据,把定长的C3放在变长的C2前面,由默认的C1C2C3调整成C1C3C2的格式 public static byte[] decrypt(byte[] privateKey, byte[] encryptedData, boolean c3InFront) throws IOException { if (privateKey == null || privateKey.length == 0) { return null; } if (encryptedData == null || encryptedData.length == 0) { return null; } //加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2 String data = Util.byteToHex(encryptedData); /***分解加密字串 * (C1 = C1标志位2位 + C1实体部分128位 = 130) * (C3 = C3实体部分64位 = 64) * (C2 = encryptedData.length * 2 - C1长度 - C2长度) */ byte[] c1Bytes = Util.hexToByte(data.substring(0, 130)); byte[] c2; byte[] c3; if (c3InFront) { c3 = Util.hexToByte(data.substring(130, 64)); c2 = Util.hexToByte(data.substring(130 + 64)); } else { /***C1 || C2 || C3 的意思就是拼在一起,而不是做什么或运算 * * 根据国密推荐的SM2椭圆曲线公钥密码算法,首先产生随机数计算出曲线点C1, * 2个32byte的BIGNUM大数,即为SM2加密结果的第1部分(C1)。 * 第2部分则是真正的密文,是对明文的加密结果,长度和明文一样(C2)。 * 第3部分是杂凑值,用来效验数据(C3)。按国密推荐的256位椭圆曲线, * 明文加密结果比原长度会大97byte(C1使用EC_POINT_point2oct转换)。 * */ int c2Len = encryptedData.length - 97; c2 = Util.hexToByte(data.substring(130, 130 + 2 * c2Len)); c3 = Util.hexToByte(data.substring(130 + 2 * c2Len, 194 + 2 * c2Len)); } SM2 sm2 = SM2.Instance(); BigInteger userD = new BigInteger(1, privateKey); //通过C1实体字节来生成ECPoint ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes); Cipher cipher = new Cipher(); cipher.Init_dec(userD, c1); cipher.Decrypt(c2); cipher.doFinal(c3); //返回解密结果 return c2; } public static void main(String[] args) throws Exception { //生成密钥对 generateKeyPair(); String plainText = "ererfeiisgod"; byte[] sourceData = plainText.getBytes(); //下面的秘钥可以使用generateKeyPair()生成的秘钥内容 // 国密规范正式私钥 String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94"; // 国密规范正式公钥 String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A"; System.out.println("加密: "); String cipherText = SM2Utils.encrypt(Util.hexToByte(pubk), sourceData); System.out.println(cipherText); System.out.println("解密: "); plainText = new String(SM2Utils.decrypt(Util.hexToByte(prik), Util.hexToByte(cipherText), false)); System.out.println(plainText); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 |
import java.math.BigInteger; public class Util { /** * 用于建立十六进制字符的输出的小写字符数组 */ private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /** * 用于建立十六进制字符的输出的大写字符数组 */ private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; /** * 整形转换成网络传输的字节流(字节数组)型数据 * * @param num 一个整型数据 * @return 4个字节的自己数组 */ public static byte[] intToBytes(int num) { byte[] bytes = new byte[4]; bytes[0] = (byte) (0xff & (num >> 0)); bytes[1] = (byte) (0xff & (num >> 8)); bytes[2] = (byte) (0xff & (num >> 16)); bytes[3] = (byte) (0xff & (num >> 24)); return bytes; } /** * 四个字节的字节数据转换成一个整形数据 * * @param bytes 4个字节的字节数组 * @return 一个整型数据 */ public static int byteToInt(byte[] bytes) { int num = 0; int temp; temp = (0x000000ff & (bytes[0])) << 0; num = num | temp; temp = (0x000000ff & (bytes[1])) << 8; num = num | temp; temp = (0x000000ff & (bytes[2])) << 16; num = num | temp; temp = (0x000000ff & (bytes[3])) << 24; num = num | temp; return num; } /** * 长整形转换成网络传输的字节流(字节数组)型数据 * * @param num 一个长整型数据 * @return 4个字节的自己数组 */ public static byte[] longToBytes(long num) { byte[] bytes = new byte[8]; for (int i = 0; i < 8; i++) { bytes[i] = (byte) (0xff & (num >> (i * 8))); } return bytes; } /** * 大数字转换字节流(字节数组)型数据 * * @param n * @return */ public static byte[] byteConvert32Bytes(BigInteger n) { byte[] tmpd = null; if (n == null) { return null; } if (n.toByteArray().length == 33) { tmpd = new byte[32]; System.arraycopy(n.toByteArray(), 1, tmpd, 0, 32); } else if (n.toByteArray().length == 32) { tmpd = n.toByteArray(); } else { tmpd = new byte[32]; for (int i = 0; i < 32 - n.toByteArray().length; i++) { tmpd[i] = 0; } System.arraycopy(n.toByteArray(), 0, tmpd, 32 - n.toByteArray().length, n.toByteArray().length); } return tmpd; } /** * 换字节流(字节数组)型数据转大数字 * * @param b * @return */ public static BigInteger byteConvertInteger(byte[] b) { if (b[0] < 0) { byte[] temp = new byte[b.length + 1]; temp[0] = 0; System.arraycopy(b, 0, temp, 1, b.length); return new BigInteger(temp); } return new BigInteger(b); } /** * 根据字节数组获得值(十六进制数字) * * @param bytes * @return */ public static String getHexString(byte[] bytes) { return getHexString(bytes, true); } /** * 根据字节数组获得值(十六进制数字) * * @param bytes * @param upperCase * @return */ public static String getHexString(byte[] bytes, boolean upperCase) { String ret = ""; for (int i = 0; i < bytes.length; i++) { ret += Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1); } return upperCase ? ret.toUpperCase() : ret; } /** * 打印十六进制字符串 * * @param bytes */ public static void printHexString(byte[] bytes) { for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(bytes[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } System.out.print("0x" + hex.toUpperCase() + ","); } System.out.println(); } /** * Convert hex string to byte[] * * @param hexString the hex string * @return byte[] */ public static byte[] hexStringToBytes(String hexString) { if (hexString == null || hexString.equals("")) { return null; } hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return d; } /** * Convert char to byte * * @param c char * @return byte */ public static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @return 十六进制char[] */ public static char[] encodeHex(byte[] data) { return encodeHex(data, true); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式 * @return 十六进制char[] */ public static char[] encodeHex(byte[] data, boolean toLowerCase) { return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); } /** * 将字节数组转换为十六进制字符数组 * * @param data byte[] * @param toDigits 用于控制输出的char[] * @return 十六进制char[] */ protected static char[] encodeHex(byte[] data, char[] toDigits) { int l = data.length; 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 out; } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @return 十六进制String */ public static String encodeHexString(byte[] data) { return encodeHexString(data, true); } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @param toLowerCase <code>true</code> 传换成小写格式 , <code>false</code> 传换成大写格式 * @return 十六进制String */ public static String encodeHexString(byte[] data, boolean toLowerCase) { return encodeHexString(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); } /** * 将字节数组转换为十六进制字符串 * * @param data byte[] * @param toDigits 用于控制输出的char[] * @return 十六进制String */ protected static String encodeHexString(byte[] data, char[] toDigits) { return new String(encodeHex(data, toDigits)); } /** * 将十六进制字符数组转换为字节数组 * * @param data 十六进制char[] * @return byte[] * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常 */ public static byte[] decodeHex(char[] data) { int len = data.length; if ((len & 0x01) != 0) { throw new RuntimeException("Odd number of characters."); } byte[] out = new byte[len >> 1]; // two characters form the hex value. for (int i = 0, j = 0; j < len; i++) { int f = toDigit(data[j], j) << 4; j++; f = f | toDigit(data[j], j); j++; out[i] = (byte) (f & 0xFF); } return out; } /** * 将十六进制字符转换成一个整数 * * @param ch 十六进制char * @param index 十六进制字符在字符数组中的位置 * @return 一个整数 * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 */ protected static int toDigit(char ch, int index) { int digit = Character.digit(ch, 16); if (digit == -1) { throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); } return digit; } /** * 数字字符串转ASCII码字符串 * * @param content 字符串 * @return ASCII字符串 */ public static String StringToAsciiString(String content) { String result = ""; int max = content.length(); for (int i = 0; i < max; i++) { char c = content.charAt(i); String b = Integer.toHexString(c); result = result + b; } return result; } /** * 十六进制转字符串 * * @param hexString 十六进制字符串 * @param encodeType 编码类型4:Unicode,2:普通编码 * @return 字符串 */ public static String hexStringToString(String hexString, int encodeType) { String result = ""; int max = hexString.length() / encodeType; for (int i = 0; i < max; i++) { char c = (char) hexStringToAlgorism(hexString .substring(i * encodeType, (i + 1) * encodeType)); result += c; } return result; } /** * 十六进制字符串装十进制 * * @param hex 十六进制字符串 * @return 十进制数值 */ public static int hexStringToAlgorism(String hex) { hex = hex.toUpperCase(); int max = hex.length(); int result = 0; for (int i = max; i > 0; i--) { char c = hex.charAt(i - 1); int algorism = 0; if (c >= '0' && c <= '9') { algorism = c - '0'; } else { algorism = c - 55; } result += Math.pow(16, max - i) * algorism; } return result; } /** * 十六转二进制 * * @param hex 十六进制字符串 * @return 二进制字符串 */ public static String hexStringToBinary(String hex) { hex = hex.toUpperCase(); String result = ""; int max = hex.length(); for (int i = 0; i < max; i++) { char c = hex.charAt(i); switch (c) { case '0': result += "0000"; break; case '1': result += "0001"; break; case '2': result += "0010"; break; case '3': result += "0011"; break; case '4': result += "0100"; break; case '5': result += "0101"; break; case '6': result += "0110"; break; case '7': result += "0111"; break; case '8': result += "1000"; break; case '9': result += "1001"; break; case 'A': result += "1010"; break; case 'B': result += "1011"; break; case 'C': result += "1100"; break; case 'D': result += "1101"; break; case 'E': result += "1110"; break; case 'F': result += "1111"; break; } } return result; } /** * ASCII码字符串转数字字符串 * * @param content ASCII字符串 * @return 字符串 */ public static String AsciiStringToString(String content) { String result = ""; int length = content.length() / 2; for (int i = 0; i < length; i++) { String c = content.substring(i * 2, i * 2 + 2); int a = hexStringToAlgorism(c); char b = (char) a; String d = String.valueOf(b); result += d; } return result; } /** * 将十进制转换为指定长度的十六进制字符串 * * @param algorism int 十进制数字 * @param maxLength int 转换后的十六进制字符串长度 * @return String 转换后的十六进制字符串 */ public static String algorismToHexString(int algorism, int maxLength) { String result = ""; result = Integer.toHexString(algorism); if (result.length() % 2 == 1) { result = "0" + result; } return patchHexString(result.toUpperCase(), maxLength); } /** * 字节数组转为普通字符串(ASCII对应的字符) * * @param bytearray byte[] * @return String */ public static String byteToString(byte[] bytearray) { String result = ""; char temp; int length = bytearray.length; for (int i = 0; i < length; i++) { temp = (char) bytearray[i]; result += temp; } return result; } /** * 二进制字符串转十进制 * * @param binary 二进制字符串 * @return 十进制数值 */ public static int binaryToAlgorism(String binary) { int max = binary.length(); int result = 0; for (int i = max; i > 0; i--) { char c = binary.charAt(i - 1); int algorism = c - '0'; result += Math.pow(2, max - i) * algorism; } return result; } /** * 十进制转换为十六进制字符串 * * @param algorism int 十进制的数字 * @return String 对应的十六进制字符串 */ public static String algorismToHEXString(int algorism) { String result = ""; result = Integer.toHexString(algorism); if (result.length() % 2 == 1) { result = "0" + result; } result = result.toUpperCase(); return result; } /** * HEX字符串前补0,主要用于长度位数不足。 * * @param str String 需要补充长度的十六进制字符串 * @param maxLength int 补充后十六进制字符串的长度 * @return 补充结果 */ static public String patchHexString(String str, int maxLength) { String temp = ""; for (int i = 0; i < maxLength - str.length(); i++) { temp = "0" + temp; } str = (temp + str).substring(0, maxLength); return str; } /** * 将一个字符串转换为int * * @param s String 要转换的字符串 * @param defaultInt int 如果出现异常,默认返回的数字 * @param radix int 要转换的字符串是什么进制的,如16 8 10. * @return int 转换后的数字 */ public static int parseToInt(String s, int defaultInt, int radix) { int i = 0; try { i = Integer.parseInt(s, radix); } catch (NumberFormatException ex) { i = defaultInt; } return i; } /** * 将一个十进制形式的数字字符串转换为int * * @param s String 要转换的字符串 * @param defaultInt int 如果出现异常,默认返回的数字 * @return int 转换后的数字 */ public static int parseToInt(String s, int defaultInt) { int i = 0; try { i = Integer.parseInt(s); } catch (NumberFormatException ex) { i = defaultInt; } return i; } /** * 十六进制串转化为byte数组 * * @return the array of byte */ public static byte[] hexToByte(String hex) throws IllegalArgumentException { if (hex.length() % 2 != 0) { throw new IllegalArgumentException(); } char[] arr = hex.toCharArray(); byte[] b = new byte[hex.length() / 2]; for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) { String swap = "" + arr[i++] + arr[i]; int byteint = Integer.parseInt(swap, 16) & 0xFF; b[j] = new Integer(byteint).byteValue(); } return b; } /** * 字节数组转换为十六进制字符串 * * @param b byte[] 需要转换的字节数组 * @return String 十六进制字符串 */ public static String byteToHex(byte[] b) { if (b == null) { throw new IllegalArgumentException( "Argument b ( byte array ) is null! "); } String hs = ""; String stmp = ""; for (int n = 0; n < b.length; n++) { stmp = Integer.toHexString(b[n] & 0xff); if (stmp.length() == 1) { hs = hs + "0" + stmp; } else { hs = hs + stmp; } } return hs.toUpperCase(); } public static byte[] subByte(byte[] input, int startIndex, int length) { byte[] bt = new byte[length]; for (int i = 0; i < length; i++) { bt[i] = input[i + startIndex]; } return bt; } } |
注意:
我们可以利用 密文,长度和明文一样(C2)这个原理,来跟踪现实中的调试问题,我们在没办法解密用户输入数据的内容的情况下,可以知道用户输入内容的长度,也能辅助我们解决很多调试问题。
上述的代码还可参考 Java—bouncycastle支持国密SM2的公钥加密算法
正常情况下Android项目需要的jar包,我们可以通过放置到项目对应的libs目录下即可。但是如果我们只希望某些特定libs/jar/aar只在执行单元测试时引入,那么应该怎么处理呢?
在Android Studio 4.1.x及以上版本,build tools 4.1.x及以上版本的情况下(较低的版本可能不支持这些配置项),我们可以通过使用 androidTestImplementation fileTree的方式进行引入(同理 常规单元测试使用 testImplementation fileTree),如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
apply plugin: 'android' dependencies { implementation project(':android-sdk') // The libs folder is included in the apk of the real app Implementation fileTree(dir: 'libs', include: '*.jar') // The tests-libs folder is included only for tests androidTestImplementation fileTree(dir: 'libs-tests', include: '*.jar') } android { compileSdkVersion 19 buildToolsVersion "20.0.0" compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } sourceSets { main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } // Move the tests to tests/java, tests/res, etc... androidTest.setRoot('tests') // Note - to run the tests from command line: // $ gradle clean connectedCheck build // (requires gradle 1.10) // Move the build types to build-types/<type> // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ... // This moves them out of them default location under src/<type>/... which would // conflict with src/ being used by the main source set. // Adding new build types or product flavors should be accompanied // by a similar customization. debug.setRoot('build-types/debug') release.setRoot('build-types/release') } } |
之前参考网上的各种方法,均为达到期望的效果,于是到Thymeleaf 官网逛了下,找到官网的例子来实现了:
1 2 |
以fragment方式分离公有css和js, 以replace+参数的方式传入每个页面单独的css和js. |
直接上栗子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
公有css(存放在templates/common/htmlHead.html中): <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head th:fragment="common_header(title,links)"> <!-- Common styles and scripts --> <title th:replace="${title}">The awesome application</title> <meta charset="utf-8"></meta> <meta http-equiv="X-UA-Compatible" content="IE=edge"></meta> <meta name="renderer" content="webkit" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"></meta> <meta name="description" content=""></meta> <meta name="author" content=""></meta> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"></meta> <link rel="icon" href="/img/icon.ico" type="image/x-icon" /> <link rel="shortcut icon" href="/favicon.ico" /> <link th:href="@{/webjars/bootstrap/3.3.7/dist/css/bootstrap.css}" rel="stylesheet"> <link href="css/common/top_header.css" rel="stylesheet"> <link href="css/common/bottom_footer.css" rel="stylesheet"> <!--<link th:href="${myCss}" rel="stylesheet">--> <link th:href="@{/webjars/cropper/2.3.4/dist/cropper.css}" rel="stylesheet"> <link href="css/common/rspMsg.css" rel="stylesheet"> <link href="css/common/common.css" rel="stylesheet"> <!--/* Per-page placeholder for additional links */--> <th:block th:replace="${links}" /> </head> |
一般页面调用公有CSS,并使用自己的CSS:
1 2 3 4 5 6 7 |
<!DOCTYPE html> <html lang="zh-CN"> <head th:replace="common/htmlHead :: common_header(~{::title},~{::link})"> <title>设置</title> <link rel="stylesheet" href="css/index.css"> </head> </html> |
公有js的提取方式是一样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!DOCTYPE html> <html lang="zh-CN"> <div th:fragment="common_js(scripts)"> <script th:src="@{/webjars/vue/2.3.4/dist/vue.js}"></script> <script th:src="@{/webjars/vue-resource/1.3.1/dist/vue-resource.js}"></script> <script th:src="@{/webjars/jquery/3.2.1/dist/jquery.js}"></script> <script th:src="@{/webjars/tether/1.4.0/js/tether.js}"></script> <script th:src="@{/webjars/bootstrap/3.3.7/dist/js/bootstrap.js}"></script> <script th:src="@{/webjars/cropper/2.3.4/dist/cropper.js}"></script> <script src="js/common/top_header.js"></script> <script src="js/common/common.js"></script> <!--/* Per-page placeholder for additional js */--> <th:block th:replace="${scripts}" /> </div> </html> |
公有JS的使用包一层DIV即可:
1 2 3 4 5 6 7 8 9 10 11 |
...... <body> ...... <!--使用公有js--> <div th:replace="common/htmlJS::common_js(~{::script})"> <!--每个页面自己的js--> <script src="js/index.js"></script> </div> </body> </html> |
测试结果可用,具体解释请参考Thymeleaf官方文档
注意,上述的配置在引入的是,会多出来一个DIV标签,如果想去掉这个标签,可以使用如下方式声明以及引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<!DOCTYPE html> <html lang="zh-CN"> <th:block th:fragment="common_js(scripts)"> <script th:src="@{/webjars/vue/2.3.4/dist/vue.js}"></script> <script th:src="@{/webjars/vue-resource/1.3.1/dist/vue-resource.js}"></script> <script th:src="@{/webjars/jquery/3.2.1/dist/jquery.js}"></script> <script th:src="@{/webjars/tether/1.4.0/js/tether.js}"></script> <script th:src="@{/webjars/bootstrap/3.3.7/dist/js/bootstrap.js}"></script> <script th:src="@{/webjars/cropper/2.3.4/dist/cropper.js}"></script> <script src="js/common/top_header.js"></script> <script src="js/common/common.js"></script> <!--/* Per-page placeholder for additional js */--> <th:block th:replace="${scripts}" /> </th:block> </html> |
公有JS的使用包一层th:block即可:
1 2 3 4 5 6 7 8 9 10 11 |
...... <body> ...... <!--使用公有js--> <th:block th:replace="common/htmlJS::common_js(~{::script})"> <!--每个页面自己的js--> <script src="js/index.js"></script> </th:block> </body> </html> |
另外如果使用的SpringBoot版本是1.5.4,默认的thymeleaf不是3.0版本,上面的测试需要thymeleaf 3.0版本才可以,需要修改下pom.xml文件,添加以下配置即可:
1 2 3 4 5 6 7 8 |
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!-- set thymeleaf version --> <thymeleaf.version>3.0.0.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.0.0</thymeleaf-layout-dialect.version> </properties> |
另外如果使用的SpringBoot版本是2.5.0,则不需要任何修改,直接可以生效。
最近需要 Python 实现 AES 加解密操作,在目前的 macOS Big Sur (11.4) 上使用 PyCryptodome 或 PyCrypto 都会报错:
1 |
ImportError: No module named 'Crypto' |
经过测试,我们可以使用 cryptography 来实现这个功能,如下:
1 2 3 4 5 6 7 8 9 10 |
>>> import os >>> from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes >>> key = os.urandom(32) >>> iv = os.urandom(16) >>> cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) >>> encryptor = cipher.encryptor() >>> ct = encryptor.update(b"a secret message") + encryptor.finalize() >>> decryptor = cipher.decryptor() >>> decryptor.update(ct) + decryptor.finalize() b'a secret message' |
参考 AES 算法实现代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 |
#!/usr/bin/python # # aes.py: implements AES - Advanced Encryption Standard # from the SlowAES project, http://code.google.com/p/slowaes/ # # Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ), # Alex Martelli ( http://www.aleax.it ) # # Ported from C code written by Laurent Haan ( http://www.progressive-coding.com ) # # Licensed under the Apache License, Version 2.0 # http://www.apache.org/licenses/ # import os import sys import math class AES(object): '''AES funtions for a single block ''' # Very annoying code: all is for an object, but no state is kept! # Should just be plain functions in a AES modlule. # valid key sizes keySize = dict(SIZE_128=16, SIZE_192=24, SIZE_256=32) # Rijndael S-box sbox = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16] # Rijndael Inverted S-box rsbox = [0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb , 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb , 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e , 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 , 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 , 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 , 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 , 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b , 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 , 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e , 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b , 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 , 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f , 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef , 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 , 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d] def getSBoxValue(self,num): """Retrieves a given S-Box Value""" return self.sbox[num] def getSBoxInvert(self,num): """Retrieves a given Inverted S-Box Value""" return self.rsbox[num] def rotate(self, word): """ Rijndael's key schedule rotate operation. Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d Word is an char list of size 4 (32 bits overall). """ return word[1:] + word[:1] # Rijndael Rcon Rcon = [0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb ] def getRconValue(self, num): """Retrieves a given Rcon Value""" return self.Rcon[num] def core(self, word, iteration): """Key schedule core.""" # rotate the 32-bit word 8 bits to the left word = self.rotate(word) # apply S-Box substitution on all 4 parts of the 32-bit word for i in range(4): word[i] = self.getSBoxValue(word[i]) # XOR the output of the rcon operation with i to the first part # (leftmost) only word[0] = word[0] ^ self.getRconValue(iteration) return word def expandKey(self, key, size, expandedKeySize): """Rijndael's key expansion. Expands an 128,192,256 key into an 176,208,240 bytes key expandedKey is a char list of large enough size, key is the non-expanded key. """ # current expanded keySize, in bytes currentSize = 0 rconIteration = 1 expandedKey = [0] * expandedKeySize # set the 16, 24, 32 bytes of the expanded key to the input key for j in range(size): expandedKey[j] = key[j] currentSize += size while currentSize < expandedKeySize: # assign the previous 4 bytes to the temporary value t t = expandedKey[currentSize-4:currentSize] # every 16,24,32 bytes we apply the core schedule to t # and increment rconIteration afterwards if currentSize % size == 0: t = self.core(t, rconIteration) rconIteration += 1 # For 256-bit keys, we add an extra sbox to the calculation if size == self.keySize["SIZE_256"] and ((currentSize % size) == 16): for l in range(4): t[l] = self.getSBoxValue(t[l]) # We XOR t with the four-byte block 16,24,32 bytes before the new # expanded key. This becomes the next four bytes in the expanded # key. for m in range(4): expandedKey[currentSize] = expandedKey[currentSize - size] ^ \ t[m] currentSize += 1 return expandedKey def addRoundKey(self, state, roundKey): """Adds (XORs) the round key to the state.""" for i in range(16): state[i] ^= roundKey[i] return state def createRoundKey(self, expandedKey, roundKeyPointer): """Create a round key. Creates a round key from the given expanded key and the position within the expanded key. """ roundKey = [0] * 16 for i in range(4): for j in range(4): roundKey[j*4+i] = expandedKey[roundKeyPointer + i*4 + j] return roundKey def galois_multiplication(self, a, b): """Galois multiplication of 8 bit characters a and b.""" p = 0 for counter in range(8): if b & 1: p ^= a hi_bit_set = a & 0x80 a <<= 1 # keep a 8 bit a &= 0xFF if hi_bit_set: a ^= 0x1b b >>= 1 return p # # substitute all the values from the state with the value in the SBox # using the state value as index for the SBox # def subBytes(self, state, isInv): if isInv: getter = self.getSBoxInvert else: getter = self.getSBoxValue for i in range(16): state[i] = getter(state[i]) return state # iterate over the 4 rows and call shiftRow() with that row def shiftRows(self, state, isInv): for i in range(4): state = self.shiftRow(state, i*4, i, isInv) return state # each iteration shifts the row to the left by 1 def shiftRow(self, state, statePointer, nbr, isInv): for i in range(nbr): if isInv: state[statePointer:statePointer+4] = \ state[statePointer+3:statePointer+4] + \ state[statePointer:statePointer+3] else: state[statePointer:statePointer+4] = \ state[statePointer+1:statePointer+4] + \ state[statePointer:statePointer+1] return state # galois multiplication of the 4x4 matrix def mixColumns(self, state, isInv): # iterate over the 4 columns for i in range(4): # construct one column by slicing over the 4 rows column = state[i:i+16:4] # apply the mixColumn on one column column = self.mixColumn(column, isInv) # put the values back into the state state[i:i+16:4] = column return state # galois multiplication of 1 column of the 4x4 matrix def mixColumn(self, column, isInv): if isInv: mult = [14, 9, 13, 11] else: mult = [2, 1, 1, 3] cpy = list(column) g = self.galois_multiplication column[0] = g(cpy[0], mult[0]) ^ g(cpy[3], mult[1]) ^ \ g(cpy[2], mult[2]) ^ g(cpy[1], mult[3]) column[1] = g(cpy[1], mult[0]) ^ g(cpy[0], mult[1]) ^ \ g(cpy[3], mult[2]) ^ g(cpy[2], mult[3]) column[2] = g(cpy[2], mult[0]) ^ g(cpy[1], mult[1]) ^ \ g(cpy[0], mult[2]) ^ g(cpy[3], mult[3]) column[3] = g(cpy[3], mult[0]) ^ g(cpy[2], mult[1]) ^ \ g(cpy[1], mult[2]) ^ g(cpy[0], mult[3]) return column # applies the 4 operations of the forward round in sequence def aes_round(self, state, roundKey): state = self.subBytes(state, False) state = self.shiftRows(state, False) state = self.mixColumns(state, False) state = self.addRoundKey(state, roundKey) return state # applies the 4 operations of the inverse round in sequence def aes_invRound(self, state, roundKey): state = self.shiftRows(state, True) state = self.subBytes(state, True) state = self.addRoundKey(state, roundKey) state = self.mixColumns(state, True) return state # Perform the initial operations, the standard round, and the final # operations of the forward aes, creating a round key for each round def aes_main(self, state, expandedKey, nbrRounds): state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0)) i = 1 while i < nbrRounds: state = self.aes_round(state, self.createRoundKey(expandedKey, 16*i)) i += 1 state = self.subBytes(state, False) state = self.shiftRows(state, False) state = self.addRoundKey(state, self.createRoundKey(expandedKey, 16*nbrRounds)) return state # Perform the initial operations, the standard round, and the final # operations of the inverse aes, creating a round key for each round def aes_invMain(self, state, expandedKey, nbrRounds): state = self.addRoundKey(state, self.createRoundKey(expandedKey, 16*nbrRounds)) i = nbrRounds - 1 while i > 0: state = self.aes_invRound(state, self.createRoundKey(expandedKey, 16*i)) i -= 1 state = self.shiftRows(state, True) state = self.subBytes(state, True) state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0)) return state # encrypts a 128 bit input block against the given key of size specified def encrypt(self, iput, key, size): output = [0] * 16 # the number of rounds nbrRounds = 0 # the 128 bit block to encode block = [0] * 16 # set the number of rounds if size == self.keySize["SIZE_128"]: nbrRounds = 10 elif size == self.keySize["SIZE_192"]: nbrRounds = 12 elif size == self.keySize["SIZE_256"]: nbrRounds = 14 else: return None # the expanded keySize expandedKeySize = 16*(nbrRounds+1) # Set the block values, for the block: # a0,0 a0,1 a0,2 a0,3 # a1,0 a1,1 a1,2 a1,3 # a2,0 a2,1 a2,2 a2,3 # a3,0 a3,1 a3,2 a3,3 # the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 # # iterate over the columns for i in range(4): # iterate over the rows for j in range(4): block[(i+(j*4))] = iput[(i*4)+j] # expand the key into an 176, 208, 240 bytes key # the expanded key expandedKey = self.expandKey(key, size, expandedKeySize) # encrypt the block using the expandedKey block = self.aes_main(block, expandedKey, nbrRounds) # unmap the block again into the output for k in range(4): # iterate over the rows for l in range(4): output[(k*4)+l] = block[(k+(l*4))] return output # decrypts a 128 bit input block against the given key of size specified def decrypt(self, iput, key, size): output = [0] * 16 # the number of rounds nbrRounds = 0 # the 128 bit block to decode block = [0] * 16 # set the number of rounds if size == self.keySize["SIZE_128"]: nbrRounds = 10 elif size == self.keySize["SIZE_192"]: nbrRounds = 12 elif size == self.keySize["SIZE_256"]: nbrRounds = 14 else: return None # the expanded keySize expandedKeySize = 16*(nbrRounds+1) # Set the block values, for the block: # a0,0 a0,1 a0,2 a0,3 # a1,0 a1,1 a1,2 a1,3 # a2,0 a2,1 a2,2 a2,3 # a3,0 a3,1 a3,2 a3,3 # the mapping order is a0,0 a1,0 a2,0 a3,0 a0,1 a1,1 ... a2,3 a3,3 # iterate over the columns for i in range(4): # iterate over the rows for j in range(4): block[(i+(j*4))] = iput[(i*4)+j] # expand the key into an 176, 208, 240 bytes key expandedKey = self.expandKey(key, size, expandedKeySize) # decrypt the block using the expandedKey block = self.aes_invMain(block, expandedKey, nbrRounds) # unmap the block again into the output for k in range(4): # iterate over the rows for l in range(4): output[(k*4)+l] = block[(k+(l*4))] return output class AESModeOfOperation(object): '''Handles AES with plaintext consistingof multiple blocks. Choice of block encoding modes: OFT, CFB, CBC ''' # Very annoying code: all is for an object, but no state is kept! # Should just be plain functions in an AES_BlockMode module. aes = AES() # structure of supported modes of operation modeOfOperation = dict(OFB=0, CFB=1, CBC=2) # converts a 16 character string into a number array def convertString(self, string, start, end, mode): if end - start > 16: end = start + 16 if mode == self.modeOfOperation["CBC"]: ar = [0] * 16 else: ar = [] i = start j = 0 while len(ar) < end - start: ar.append(0) while i < end: ar[j] = ord(string[i]) j += 1 i += 1 return ar # Mode of Operation Encryption # stringIn - Input String # mode - mode of type modeOfOperation # hexKey - a hex key of the bit length size # size - the bit length of the key # hexIV - the 128 bit hex Initilization Vector def encrypt(self, stringIn, mode, key, size, IV): if len(key) % size: return None if len(IV) % 16: return None # the AES input/output plaintext = [] iput = [0] * 16 output = [] ciphertext = [0] * 16 # the output cipher string cipherOut = [] # char firstRound firstRound = True if stringIn != None: for j in range(int(math.ceil(float(len(stringIn))/16))): start = j*16 end = j*16+16 if end > len(stringIn): end = len(stringIn) plaintext = self.convertString(stringIn, start, end, mode) # print 'PT@%s:%s' % (j, plaintext) if mode == self.modeOfOperation["CFB"]: if firstRound: output = self.aes.encrypt(IV, key, size) firstRound = False else: output = self.aes.encrypt(iput, key, size) for i in range(16): if len(plaintext)-1 < i: ciphertext[i] = 0 ^ output[i] elif len(output)-1 < i: ciphertext[i] = plaintext[i] ^ 0 elif len(plaintext)-1 < i and len(output) < i: ciphertext[i] = 0 ^ 0 else: ciphertext[i] = plaintext[i] ^ output[i] for k in range(end-start): cipherOut.append(ciphertext[k]) iput = ciphertext elif mode == self.modeOfOperation["OFB"]: if firstRound: output = self.aes.encrypt(IV, key, size) firstRound = False else: output = self.aes.encrypt(iput, key, size) for i in range(16): if len(plaintext)-1 < i: ciphertext[i] = 0 ^ output[i] elif len(output)-1 < i: ciphertext[i] = plaintext[i] ^ 0 elif len(plaintext)-1 < i and len(output) < i: ciphertext[i] = 0 ^ 0 else: ciphertext[i] = plaintext[i] ^ output[i] for k in range(end-start): cipherOut.append(ciphertext[k]) iput = output elif mode == self.modeOfOperation["CBC"]: for i in range(16): if firstRound: iput[i] = plaintext[i] ^ IV[i] else: iput[i] = plaintext[i] ^ ciphertext[i] # print 'IP@%s:%s' % (j, iput) firstRound = False ciphertext = self.aes.encrypt(iput, key, size) # always 16 bytes because of the padding for CBC for k in range(16): cipherOut.append(ciphertext[k]) return mode, len(stringIn), cipherOut # Mode of Operation Decryption # cipherIn - Encrypted String # originalsize - The unencrypted string length - required for CBC # mode - mode of type modeOfOperation # key - a number array of the bit length size # size - the bit length of the key # IV - the 128 bit number array Initilization Vector def decrypt(self, cipherIn, originalsize, mode, key, size, IV): # cipherIn = unescCtrlChars(cipherIn) if len(key) % size: return None if len(IV) % 16: return None # the AES input/output ciphertext = [] iput = [] output = [] plaintext = [0] * 16 # the output plain text character list chrOut = [] # char firstRound firstRound = True if cipherIn != None: for j in range(int(math.ceil(float(len(cipherIn))/16))): start = j*16 end = j*16+16 if j*16+16 > len(cipherIn): end = len(cipherIn) ciphertext = cipherIn[start:end] if mode == self.modeOfOperation["CFB"]: if firstRound: output = self.aes.encrypt(IV, key, size) firstRound = False else: output = self.aes.encrypt(iput, key, size) for i in range(16): if len(output)-1 < i: plaintext[i] = 0 ^ ciphertext[i] elif len(ciphertext)-1 < i: plaintext[i] = output[i] ^ 0 elif len(output)-1 < i and len(ciphertext) < i: plaintext[i] = 0 ^ 0 else: plaintext[i] = output[i] ^ ciphertext[i] for k in range(end-start): chrOut.append(chr(plaintext[k])) iput = ciphertext elif mode == self.modeOfOperation["OFB"]: if firstRound: output = self.aes.encrypt(IV, key, size) firstRound = False else: output = self.aes.encrypt(iput, key, size) for i in range(16): if len(output)-1 < i: plaintext[i] = 0 ^ ciphertext[i] elif len(ciphertext)-1 < i: plaintext[i] = output[i] ^ 0 elif len(output)-1 < i and len(ciphertext) < i: plaintext[i] = 0 ^ 0 else: plaintext[i] = output[i] ^ ciphertext[i] for k in range(end-start): chrOut.append(chr(plaintext[k])) iput = output elif mode == self.modeOfOperation["CBC"]: output = self.aes.decrypt(ciphertext, key, size) for i in range(16): if firstRound: plaintext[i] = IV[i] ^ output[i] else: plaintext[i] = iput[i] ^ output[i] firstRound = False if originalsize is not None and originalsize < end: for k in range(originalsize-start): chrOut.append(chr(plaintext[k])) else: for k in range(end-start): chrOut.append(chr(plaintext[k])) iput = ciphertext return "".join(chrOut) def append_PKCS7_padding(s): """return s padded to a multiple of 16-bytes by PKCS7 padding""" numpads = 16 - (len(s)%16) return s + numpads*chr(numpads) def strip_PKCS7_padding(s): """return s stripped of PKCS7 padding""" if len(s)%16 or not s: raise ValueError("String of len %d can't be PCKS7-padded" % len(s)) numpads = ord(s[-1]) if numpads > 16: raise ValueError("String ending with %r can't be PCKS7-padded" % s[-1]) return s[:-numpads] def encryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]): """encrypt `data` using `key` `key` should be a string of bytes. returned cipher is a string of bytes prepended with the initialization vector. """ key = map(ord, key) if mode == AESModeOfOperation.modeOfOperation["CBC"]: data = append_PKCS7_padding(data) keysize = len(key) assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize # create a new iv using random data iv = [ord(i) for i in os.urandom(16)] moo = AESModeOfOperation() (mode, length, ciph) = moo.encrypt(data, mode, key, keysize, iv) # With padding, the original length does not need to be known. It's a bad # idea to store the original message length. # prepend the iv. return ''.join(map(chr, iv)) + ''.join(map(chr, ciph)) def decryptData(key, data, mode=AESModeOfOperation.modeOfOperation["CBC"]): """decrypt `data` using `key` `key` should be a string of bytes. `data` should have the initialization vector prepended as a string of ordinal values. """ key = map(ord, key) keysize = len(key) assert keysize in AES.keySize.values(), 'invalid key size: %s' % keysize # iv is first 16 bytes iv = map(ord, data[:16]) data = map(ord, data[16:]) moo = AESModeOfOperation() decr = moo.decrypt(data, None, mode, key, keysize, iv) if mode == AESModeOfOperation.modeOfOperation["CBC"]: decr = strip_PKCS7_padding(decr) return decr def generateRandomKey(keysize): """Generates a key from random data of length `keysize`. The returned key is a string of bytes. """ if keysize not in (16, 24, 32): emsg = 'Invalid keysize, %s. Should be one of (16, 24, 32).' raise ValueError, emsg % keysize return os.urandom(keysize) def testStr(cleartext, keysize=16, modeName = "CBC"): '''Test with random key, choice of mode.''' print 'Random key test', 'Mode:', modeName print 'cleartext:', cleartext key = generateRandomKey(keysize) print 'Key:', [ord(x) for x in key] mode = AESModeOfOperation.modeOfOperation[modeName] cipher = encryptData(key, cleartext, mode) print 'Cipher:', [ord(x) for x in cipher] decr = decryptData(key, cipher, mode) print 'Decrypted:', decr if __name__ == "__main__": moo = AESModeOfOperation() cleartext = "This is a test with several blocks!" cypherkey = [143,194,34,208,145,203,230,143,177,246,97,206,145,92,255,84] iv = [103,35,148,239,76,213,47,118,255,222,123,176,106,134,98,92] mode, orig_len, ciph = moo.encrypt(cleartext, moo.modeOfOperation["CBC"], cypherkey, moo.aes.keySize["SIZE_128"], iv) print 'm=%s, ol=%s (%s), ciph=%s' % (mode, orig_len, len(cleartext), ciph) decr = moo.decrypt(ciph, orig_len, mode, cypherkey, moo.aes.keySize["SIZE_128"], iv) print decr testStr(cleartext, 16, "CBC") |