abcdefg送你一张我的ep什么歌(abcdefg送你我的一的歌词)

你和你的朋友决定发明一种神秘的交流方式,没有人能在看到它的时候猜出它的真实含义。首先想到的是拼音,但显然把一句话改成拼音是不够的,于是我们用记谱法把26个英文字母从低音到高音排列起来,得到了一个简单的码本:

用这个码本变换“我们都是好朋友”后,得到这样的结果:

小时候很喜欢玩这个游戏,觉得很有趣。上大学后,有幸听了《计算机密码学》卢凯成教授的讲座,才知道我们小时候玩的游戏不能叫加密。那么到底什么是加密呢?

什么是加密?

将字符串123456转换为base64后,得到MTIzNDU2。有人说是base64加密。

字符串123456通过md5转换成E10DC3949BA59ABB56E057F20F883E。有人说这是md5加密。

严格来说,无论是base64还是md5,甚至更复杂的sha256,都不能称之为加密。

简而言之,没有密钥的算法不能称为加密。

编码是一种将字符集中的字符编码为指定**中的对象(例如,位模式、自然数序列、8位字节或电脉冲)的方法,以便将文本存储在计算机中并通过通信**传输。常见的例子包括将拉丁字母编码成莫尔斯码和ASCII码。Base64只是一种编码方式。

哈希是计算机科学中的一种数据处理方法。通过特定的函数/算法(称为哈希函数/算法),将待检索项与用于检索的索引(称为哈希或哈希值)关联起来,生成便于检索的数据结构(称为哈希表)。哈希算法通常用于保护数据库中的密码字符串。因为哈希算法计算出来的哈希值是不可逆的(无法计算回原来的值),所以可以有效的保护密码。常用的哈希算法有md5、sha1、sha256等。

加密是将明文信息变成不可读的密文内容,使其不可读的过程。只有具有解密方法的对象才能通过解密过程将密文恢复为正常可读的内容。加密分为对称加密和非对称加密。常见的对称加密算法有DES、AES等。非对称加密算法包括RSA、椭圆曲线算法等。

在经典的加密算法中,加密算法和密钥是不能公开的,一旦泄露,就有被破解的风险。我们可以用词频估计等方法来知道明文。1972年IBM开发的DES算法(数据加密标准)是人类历史上第一个公开加密算法但不公开密钥的加密方法。后来,它成为美国军方和**机构的标准加密算法。2002年升级为AES算法(AdvancedEncryption Standard)。今天,我们从AES学习加密和解密。

准备工具

通常,加密和解密只需要在服务器端完成,大多数**教程和示例代码也是如此。但是,在一些特殊情况下,当你需要用一种语言加密,用另一种语言解密时,最好有一个中立公正的第三方结果集来验证你的加密结果。否则一旦出了问题,你就不知道是加密算法错了还是解密算法错了。我们从中吸取了痛苦的教训。尤其是如果在一个公司里,前端加密是用js语言写的,而后端加密是用Java语言或者php语言或者go语言写的,那么双方都需要这样一个客观公正的平台。否则,你们必然会陷入无休止的相互指责的局面。前端对,后端解密错,后端解密对,前端加密错。其实双方都是新手。

AES**加密解密就是这样一个工具网站,在这里你可以验证你的加密结果。如果你的加密结果和它的结果完全一样,那你的加密算法就没有问题。否则,您可以调整它,直到它与它的结果完全相同。反过来,如果能从密文中解密出来,而你的代码不能,那么一定是你的算法有问题,而不是数据有问题。

我们先在这个网站上加密一个简单的字符串123456。

es=1698638844 x-signature=/of 41 zzhedg 3 muix NMsSfszNqQ=index=2 ‘ width=’ 360 px ‘,height=’auto’ /

让我们逐一解释网站上的所有选项:

AES加密模式:这里我们选择ECB(ee cc block)模式。这是所有AES模式中最简单的一种。

是最不被人推荐的一种模式,因为它的固定的明文对应的是固定的密文,很容易被破解。但是既然是练习的话,就让我们先从最简单的开始。填充:在这里我们选择 pkcs标准的 pkcs7padding。数据块:我们选择 128位,因为 java端解密算法目前只支持 AES128,所以我们先从 128位开始。密钥:因为我们前面选择了 128位的数据块,所以这里我们用 128 / 8 = 16个字节来处理,我们先简单地填入 16个0,其实你也可以填写任意字符,比如 abcdefg1234567ab或者其它,只要是 16个字节即可。理论上来说,不是16个字节也可以用来当密钥,优秀的算**自动补齐,但是为了简单起见,我们先填入 16个 0。偏移量:置空。因为是 ECB模式,不需要 iv偏移量。输出:我们选择 base64编码方式。字符集:这里因为我们只加密英文字母和***数字,所以选择 utf-8和 gb2312都是一样的。

好了,现在我们知道按照以上选项设置好之后的代码如果加密 123456的话,应该输出 DoxDHHOjfol/2WxpaXAXgQ==,如果不是这个结果,那就是加密端的问题。

AES-ECB

AES-ECB的Javascript加密

为了完成 AES加密,我们并不需要自己手写一个 AES算法,不需要去重复造轮子。但如何选择 js的加密库是个很有意思的挑战。我们尝试了很多方法,一开始我们尝试了aes-js这个库,但它不支持 RSA算法,后来我们看到Web Crypto API这种浏览器自带的加密库,原生支持 AES和 RSA,但它的 RSA实现和 Java不兼容,最终我们还是选择了Forge这个库,它天生支持 AES的各种子集,并且它的 RSA也能和 Java完美配合。

使用 forge编写的 js代码实现 AES-ECB加密的代码就是下面这些:

const cipher = forge.cipher.createCipher(\’AES-ECB\’, \’这里是16字节密钥\’);cipher.start();cipher.update(forge.util.createBuffer(\’这里是明文\’));cipher.finish();const result = forge.util.encode64(cipher.output.getBytes())

forge的 AES缺省就是 pkcs7padding,所以不用特别设置。运行它之后你就会得到正确的加密结果。

AES-ECB的Java解密

接下来我们看看Java端的解密代码该如何写:

try {

Cipher cipher = Cipher.getInstance(“AES/ECB/PKCS5Padding”);

cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(“这里是16字节密钥”.getBytes(), “AES”));

String plaintext = new String(cipher.doFinal(Base64.getDecoder().decode(“这里是明文”.getBytes())), “UTF-8”);

System.out.println(plaintext);

} catch (Exception e) {

System.out.println(“解密出错:” + e.toString());

}

注意这里我们用到的是 PKCS5Padding,上面加密的时候不是用的是 pkcs7padding吗?怎么这里变成 5了呢?

我们先来了解一下什么是 pkcs。 pkcs的全称是 Public Key Cryptography Standards(公钥加密标准),这是 RSA实验室制定的一系列的公钥密码编译标准,比较著名的有 pkcs1, pkcs5, pkcs7, pkcs8这四个,它们分别管理的是不同的内容。在这里我们只是用它来填充,所以我们只关注 pkcs5和 pkcs7就够了。那么 pkcs5和 pkcs7有什么区别呢?其实在填充方面它们两个的算法是一样的, pkcs5是 pkcs7的一个子集,区别在于 pkcs5是 8字节固定的,而 pkcs7可以是 1到 255之间的任意字节。但用在 AES算法上,因为 AES标准规定块大小必须是 16字节或者 24字节或者 32字节,不可能用 pkcs5的 8字节,所以 AES算法只能用 pkcs7填充。但是由于 java早期工程师犯的一个命名上的错误,他们把 AES填充算法的名称设定为 pkcs5,而实际实现中实现的是 pkcs7,所以我们在 java端开发解密的时候需要使用 pkcs5。

AES-CBC

谈完了不安全的 AES-ECB,我们来做一下相对安全一些的 AES-CBC模式。

AES-CBC的Javascript加密

直接上代码:

const cipher = forge.cipher.createCipher(\’AES-CBC\’, \’这里是16字节密钥\’);cipher.start({ iv: \’这里是16字节偏移量\’ });cipher.update(forge.util.createBuffer(\’这里是明文\’));cipher.finish();const result = forge.util.encode64(cipher.output.getBytes());

跟上面的 AES-ECB差不多,唯一区别只是在 start函数里定义了一个 iv。

AES-CBC的Java解密

下面是 Java代码:

try {

Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);

cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(“这里是16字节密钥”.getBytes(), “AES”), new IvParameterSpec(“这里是16字节偏移量”.getBytes()));

String plaintext = new String(cipher.doFinal(Base64.getDecoder().decode(“这里是明文”.getBytes())), “UTF-8”);

System.out.println(plaintext);

} catch (Exception e) {

System.out.println(“解密出错:” + e.toString());

}

也是同样,跟上面用 AES-ECB时的模式几乎一模一样,只是增加了一个 IvParameterSpec,用来生成 iv,在 cipher.init里面增加了一个 iv参数,除此之外完全相同,就这样我们就已经实现了一个简单的 CBC模式。

RSA

以上两种做法都明显是非常不安全的,因为我们把加密用的密钥和 iv参数都直接暴露在了前端,为此我们需要一种更加安全的加密方法—— RSA。因为 RSA是非对称加密,即使我们把加密用的公钥完全暴露在前端也不必担心,别人即使截获了我们的密文,但因为他们没有解密密钥,是无法解出我们的明文的。

生成密钥对

要用 RSA加密,首先我们需要生成一个公钥和一个私钥,我们可以直接执行命令 ssh-keygen。它会问我们密钥文件保存的文件夹,注意一定要单独找一个文件夹存放,不要放在缺省文件夹下,否则你日常使用的 ssh公钥和私钥就都被覆盖了。

得到公钥文件之后,由于这个公钥文件是 rfc4716格式的,而我们的 forge库要求一个 pkcs1格式的公钥,所以这里我们需要把它转换成 pem格式(也就是 pkcs1格式):

ssh-keygen -f 公钥文件名 -m pem -e

RSA的Javascript加密

得到 pem格式的公钥之后,我们来看一下 js的代码:

forge.util.encode64(forge.pki.publicKeyFromPem(\’—–BEGIN RSA PUBLIC KEY—–MIIBCfdsafasfasfafsdaafdsaAB—–END RSA PUBLIC KEY—–\’).encrypt(\’这里是明文\’, \’RSA-OAEP\’, { md: forge.md.sha256.create(), mgf1: { md: forge.md.sha1.create() } });

一句话就完成整个加密过程了,这就是 forge的强大之处。

RSA的Java解密

接下来我们看解密。

对于私钥,因为 Java只支持 PKCS8,而我们用 ssh-keygen生成的私钥是 pkcs1的,所以还需要用以下命令把 pkcs1的私钥转换为 pkcs8的私钥:

openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in 私钥文件名 -out 导出文件名

得到 pkcs8格式的私钥之后,我们把这个文件的头和尾去掉,然后放入以下 Java代码:

try { Cipher cipher = Cipher.getInstance(“RSA/ECB/OAEPWithSHA-256AndMGF1Padding”); cipher.init(Cipher.DECRYPT_MODE, KeyFactory.getInstance(“RSA”).generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(“这里是私钥”)))); String plaintext = new String(cipher.doFinal(Base64.getDecoder().decode(“这里是密文”.getBytes())), “UTF-8”); System.out.println(plaintext);} catch (Exception e) { System.out.println(“解密出错:” + e.toString());}

和上面的 AES解密类似,只是增加了 KeyFactory读取 PKCS8格式私钥的部分,这样我们就完成了 Java端的 RSA解密。以上我们用最简单的方式实现了 js端加密, java端解密的过程,感兴趣的朋友可以在这里下载完整的代码亲自验证一下: