Java密码学算法娄老师

只用一样东西,不明白它的道理,实在不高明

只知道How,不知道Why,出了一点小问题时就无能为力了。我们课上鼓励大家在Linux下学习编程,尽量在命令行中编辑/编译/调试程序,Git的使用,数据库的管理都先会命令方式下使用,这样在IDE中,在GUI界面中出了问题,我们有更好的方法查找。

结果是:

这个“会用,明理,扩展”方法论可以用到很多技术的学习上。明理之前要会用。

程序开发中,程序员有两种角色:

在很多课程中,同学们学习是一人分饰两种角色,但很多人不清楚这两种角色。

《ThinkinJava》中说:

开发一个程序时,最好的方法就是将对象想象为“服务提供者”,程序通过调用若干对象提供的服务来实现预期目标。

Java的一个好处是API极其丰富,想学习一门技术可以先用Java写点程序,再深入学习就能明理了。

我们每个人都有自己的秘密,所谓秘密就是不希望被别人知道的信息。

在现代社会中,很多信息都存储在计算机里,这让信息的使用变得非常方便,不过,也正是因为如此,在现代社会中要保护好自己的秘密信息已经变得非常困难。即便别人复制了你的秘密信息,你也不会有所察觉,因为你手上的信息并没有丢失;正是因为信息可以很容易地被修改,所以你的重要文件也存在被他人篡改的风险;此外,如果有人将你的秘密信息通过邮件发送给第三者或者是发布在网页上,也会给你带来大麻烦。为了解决这些问题,人们开发出形形色色的密码技术。

我们遇到的安全问题可以归结为安全的三个属性(CIA金三角):

其他安全技术目标:

密码学逐步发展成为一门学科,那么什么是密码学呢?

密码学:主要是研究保密通信和信息保密的学科,包括信息保密传输和信息加密存储等。

密码学包含密码编码学(Cryptography)和密码分析学(Cryptanalyst)两个分支。编码学与分析学相互促进,又相互制约。一方面,两者在加强密码分析的安全上相互促进;另一方面,两者在实施更为有效的攻击方面也相互影响。

让我们来看一下密码学常用术语,如下所示:

密码学并不是孤立存在的,它需要有一个环境——保密通信模型。密码学的目的在于确保信息的保密传送。通常这样理解这层含义:信息的发送者和接收者在不安全的信道上进行通信,而破译者不能理解他们通信的内容。用保密通信模型来诠释这种信息传送方式,如下图所示:

在保密通信模型中,密码技术在保证信息传输安全中发挥着重要的作用:

我们将上述六种技术统称为密码学家的工具箱。我们将信息安全所面临的威胁与用来应对这些威胁的密码技术之间的关系用一张图表来表示出来:

根据密钥的使用方法,可以将密码分为对称密码和公钥密码两种。

将对称密码和公钥密码结合起来的密码方式称为混合密码系统(hybridcryptosystem),这种系统结合了对称密码和公钥密码两者的优势。

我们在各种系统中输入的“密码”,我们应该叫口令(Password),主要用来身份验证的,加密过程中可以用PBE(PasswordBasedEncryption,基于口令加密)算法,使用口令产生密钥,而口令由用户自己掌管。

密码与信息安全常识

Java安全体系结构总共分为4个部分:

JCA和JCE是Java平台提供的用于安全和加密服务的两组API。它们并不执行任何算法,它们只是连接应用和实际算法实现程序的一组接口。软件开发商可以根据JCE接口(又称安全提供者接口)将各种算法实现后,打包成一个Provider(安全提供者),动态地加载到Java运行环境中。根据美国出口限制规定,JCA可出口,但JCE对部分国家是限制出口的。因此,要实现一个完整的安全结构,就需要一个或多个第三方厂商提供的JCE产品,称为安全提供者。BouncyCastleJCE就是其中的一个安全提供者。

安全提供者是承担特定安全机制实现的第三方。有些提供者是完全免费的,而另一些提供者则需要付费。提供安全提供者的公司有Sun、BouncyCastle等,Sun(Oracle)提供了如何开发安全提供者的细节。BouncyCastle提供了可以在J2ME/J2EE/J2SE平台得到支持的API,而且BouncyCastle的API是免费的。JDK1.4版本及其后续版本中包含了上述扩展包,无须进行配置。

密码学是在战争中发家的,即使最简单的凯撒密码也不例外。凯撒密码是罗马扩张时期朱利斯凯撒(JuliusCaesar)创造的,用于加密通过信使传递的作战命令。它将字母表中的字母移动一定位置而实现加密。

在密码学中存在着各种各样的置换方式,但所有不同的置换方式都包含2个相同的元素。密钥和协议(算法)。凯撒密码的密钥是3,算法是将普通字母表中的字母用密钥对应的字母替换。置换加密的优点就在于它易于实施却难于破解.发送方和接收方很容易事先商量好一个密钥,然后通过密钥从明文中生成密文,即是敌人若获取密文,通过密文直接猜测其代表的意义,在实践中是不可能的。

凯撒密码的加密算法极其简单。其加密过程如下:

在这里,我们做此约定:明文记为m,密文记为c,加密变换记为E(k1,m)(其中k1为密钥),解密变换记为D(k2,m)(k2为解密密钥)(在这里k1=k2,不妨记为k)。凯撒密码的加密过程可记为如下一个变换:

c≡m+kmodn(其中n为基本字符个数)

同样,解密过程可表示为:m≡c+kmodn(其中n为基本字符个数)

publicstaticvoidmain(Stringargs[])throwsException{Strings=args[0];intkey=Integer.parseInt(args[1]);Stringes="";for(inti=0;i='a'&&c<='z')//是小写字母{c+=key%26;//移动key%26位if(c<'a')c+=26;//向左超界if(c>'z')c-=26;//向右超界 }elseif(c>='A'&&c<='Z')//是大写字母{c+=key%26;if(c<'A')c+=26;if(c>'Z')c-=26;}es+=c;}System.out.println(es);}该程序既可用于加密又可用于解密。只要执行:

javaCaesar明文(要加密的字符串)密钥(移动的位数)

即可加密。在密钥前面加上负号,将运行

javaCaesar明文(要加密的字符串)-密钥(移动的位数)

即可解密。

如为了加密字符串“HelloWorld!”,可随意取一个密钥如4,运行:

javaCaesar"HelloWorld!"4

将输出“LippsAsvph!”。这里“HelloWorld!”是明文,“LippsAsvph!”是密文。

如果密钥大于26,程序中移位前会和26取模而将其调整到26以下。因此运行:

javaCaesar"HelloWorld!"30

同样将输出“LippsAsvph!”。

为了将密文“LippsAsvph!”解密,需要知道加密该密文所用的密钥4,这样,执行:

javaCaesar"LippsAsvph!"-4

将得到明文“HelloWorld!”。

如果密钥和加密时所用的不同,则解密时将得到无意义的输出,如运行

javaCaesar"LippsAsvph!"–3

程序将输出“IfmmpXpsme!”。这样,只有知道密钥才能得到原来的密文。

一般人见到"LippsAsvph!",能否猜出是“HelloWorld!”?我们可以通过暴力破解把0-25都试一遍,也可以通过统计手段进行破解。

本实例给出Java中创建对称密钥的步骤,并通过对象序列化方式保存在文件中。

(1)获取密钥生成器KeyGeneratorkg=KeyGenerator.getInstance("DESede");分析:Java中KeyGenerator类中提供了创建对称密钥的方法。Java中的类一般使用new操作符通过构造器创建对象,但KeyGenerator类不是这样,它预定义了一个静态方法getInstance(),通过它获得KeyGenerator类型的对象。这种类成为工厂类或工厂。方法getInstance()的参数为字符串类型,指定加密算法的名称。可以是“Blowfish”、“DES”、“DESede”、“HmacMD5”或“HmacSHA1”等。这些算法都可以实现加密,这里我们不关心这些算法的细节,只要知道其使用上的特点即可。其中“DES”是目前最常用的对称加密算法,但安全性较差。针对DES安全性的改进产生了能满足当前安全需要的TripleDES算法,即“DESede”。“Blowfish”的密钥长度可达448位,安全性很好。“AES”是一种替代DES算法的新算法,可提供很好的安全性。

(2)初始化密钥生成器kg.init(168);分析:该步骤一般指定密钥的长度。如果该步骤省略的话,会根据算法自动使用默认的密钥长度。指定长度时,若第一步密钥生成器使用的是“DES”算法,则密钥长度必须是56位;若是“DESede”,则可以是112或168位,其中112位有效;若是“AES”,可以是128,192或256位;若是“Blowfish”,则可以是32至448之间可以被8整除的数;“HmacMD5”和“HmacSHA1”默认的密钥长度都是64个字节。

(3)生成密钥SecretKeyk=kg.generateKey();分析:使用第一步获得的KeyGenerator类型的对象中generateKey()方法可以获得密钥。其类型为SecretKey类型,可用于以后的加密和解密。

(4)通过对象序列化方式将密钥保存在文件中FileOutputStreamf=newFileOutputStream("key1.dat");ObjectOutputStreamb=newObjectOutputStream(f);b.writeObject(k);分析:ObjectOutputStream类中提供的writeObject方法可以将对象序列化,以流的方式进行处理。这里将文件输出流作为参数传递给ObjectOutputStream类的构造器,这样创建好的密钥将保存在文件key1.data中。

importjava.io.*;importjavax.crypto.*;publicclassSkey_DES{publicstaticvoidmain(Stringargs[])throwsException{KeyGeneratorkg=KeyGenerator.getInstance("DESede");kg.init(168);SecretKeyk=kg.generateKey();FileOutputStreamf=newFileOutputStream("key1.dat"); ObjectOutputStreamb=newObjectOutputStream(f); b.writeObject(k);}}运行javaSkey_DES,在当前目录下将生成文件key1.dat,其中包含的密钥可以用于使用Triple-DES算法的加密和解密。

上面的示例将密钥通过对象序列化方式保存在文件中,在文件中保存的是对象,本实例以另一种方式保存在文件中,即以字节保存在文件中。

Java中所有的密钥类都有一个getEncoded()方法,通过它可以从密钥对象中获取主要编码格式,其返回值是字节数组。其主要步骤为:

(1)获取密钥

FileInputStreamf=newFileInputStream("key1.dat");ObjectInputStreamb=newObjectInputStream(f);Keyk=(Key)b.readObject();分析:首先创建文件输入流,然后将其作为参数传递给对象输入流,最后执行对象输入流的readObject()方法读取密钥对象。由于readObject()返回的是Object类型,因此需要强制转换成Key类型。

(2)获取主要编码格式

byte[]kb=k.getEncoded();

分析:执行SecretKey类型的对象k的getEncoded()方法,返回的编码放在byte类型的数组中。

(3)保存密钥编码格式

FileOutputStreamf2=newFileOutputStream("keykb1.dat");f2.write(kb);

分析:先创建文件输出流对象,在其参数中指定文件名,如keykb1.dat。然后执行文件输出流的write()方法将第2步中得到的字节数组中的内容写入文件。

importjava.io.*;importjava.security.*;publicclassSkey_kb{publicstaticvoidmain(Stringargs[])throwsException{FileInputStreamf=newFileInputStream("key1.dat");ObjectInputStreamb=newObjectInputStream(f);Keyk=(Key)b.readObject();byte[]kb=k.getEncoded();FileOutputStreamf2=newFileOutputStream("keykb1.dat"); f2.write(kb);//打印密钥编码中的内容for(inti=0;i

运行程序

输入javaSkey_kb运行程序,在程序的当前目录中将产生文件名为keykb1.dat的文件,屏幕输出如下:

11,-105,-119,50,4,-105,16,38,-14,-111,21,-95,70,-15,76,-74,67,-88,59,-71,55,-125,104,42,

此即程序中创建的密钥的编码内容,如果用文本编辑器打开keykb1.dat,看到的不是上面的数字而是类似下面的字符:

棄2&驊馤禖僪*

这是因为keykb1.dat是一个二进制文件,存放的是任意二进制数。(Linux下可以用od或xxd命令查看)

读者运行时肯定结果和上面会有所不同,每次运行时生成的密钥都不会相同,这就保证了密钥的唯一性。作为对称密钥,只要保证若加密某段文字用的是某个密钥,则解密这段密文时用同样的密钥即可。

首先要从文件中获取已经生成的密钥,然后考虑如何使用密钥进行加密。这涉及到各种算法。Java中已经提供了常用的加密算法,我们执行Java中Cipher类的各个方法就可以完成加密过程,其主要步骤为:

(1)从文件中获取密钥FileInputStreamf=newFileInputStream("key1.dat");ObjectInputStreamb=newObjectInputStream(f);Keyk=(Key)b.readObject();

(2)创建密码对象(Cipher对象)Ciphercp=Cipher.getInstance("DESede");

分析:和KeyGenerator类一样,Cipher类是一个工厂类,它不是通过new方法创建对象,而是通过其中预定义的一个静态方法getInstance()获取Cipher对象。

getInstance()方法的参数是一个字符串,该字符串给出Cipher对象应该执行哪些操作,因此把传入的字符串称为转换(transformation)。通常通过它指定加密算法或解密所用的算法的名字,如本例的"DESede"。此外还可以同时指定反馈模式及填充方案等,如"DESede/ECB/PKCS5Padding"。

(3)初始化密码对象cp.init(Cipher.ENCRYPT_MODE,k);分析:该步骤执行Cipher对象的init()方法对Cipher对象进行初始化。该方法包括两个参数,第一个参数指定密码器准备进行加密还是解密,若传入Cipher.ENCRYPT_MODE则进入加密模式。第二个参数则传入加密或解密所使用的密钥,即第1步从文件中读取的密钥对象k。

(4)获取等待加密的明文

Strings="HelloWorld!";byteptext[]=s.getBytes("UTF8");分析:Cipher对象所作的操作是针对字节数组的,因此需要将要加密的内容转换成字节数组。本例中要加密的是一个字符串s,可以使用字符串的getBytes()方法获得对应的字节数组。getBytes()方法中必须使用参数"UTF8"指定…,否则…

(5)执行加密

bytectext[]=cp.doFinal(ptext);分析:执行Cipher对象的doFinal()方法,该方法的参数中传入待加密的明文,从而按照前面几步设置的算法及各种模式对所传入的明文进行加密操作,该方法返回加密的结果。

(6)处理加密结果

FileOutputStreamf2=newFileOutputStream("SEnc.dat");f2.write(ctext);分析:第5步得到的加密结果是字节数组,对其可作各种处理,如在网上传递、保存在文件中等。这里将其保存在文件Senc.dat中。

importjava.io.*;importjava.security.*;importjavax.crypto.*;publicclassSEnc{publicstaticvoidmain(Stringargs[])throwsException{Strings="HelloWorld!"; FileInputStreamf=newFileInputStream("key1.dat"); ObjectInputStreamb=newObjectInputStream(f); Keyk=(Key)b.readObject(); Ciphercp=Cipher.getInstance("DESede");cp.init(Cipher.ENCRYPT_MODE,k);byteptext[]=s.getBytes("UTF8");for(inti=0;i

当前目录下必须有前面生成的密钥文件key1.dat,

输入javaSEnc运行程序,在程序的当前目录中将产生文件名为SEnc.dat的文件,屏幕输出如下:

72,101,108,108,111,32,87,111,114,108,100,33,-57,119,0,-45,-9,23,37,-56,-60,-34,-99,105,99,113,-17,76,其中第一行为字符串"HelloWorld!"的字节数组编码方式,第二行为加密后的内容,第二行的内容会随着密钥的不同而不同。

第一行的内容没有加过密,任何人若得到第一行数据,只要将其用二进制方式写入文本文件,用文本编辑器打开文件就可以看到对应的字符串“HelloWorld!”。而第二行的内容由于是加密过的,没有密钥的人即使得到第二行的内容也无法知道其内容。

密文同时保存在SEnc.dat文件中,将其提供给需要的人时,需要同时提供加密时使用的密钥(key1.dat,或keykb1.dat),这样收到SEnc.dat中密文的人才能够解密文件中的内容。

前面加密后的密文SEnc.dat,以及加密时所使用的密钥key1.dat或keykb1.dat,本实例对SEnc.dat中的密文进行解密,得到明文。

首先要从文件中获取加密时使用的密钥,然后考虑如何使用密钥进行解密。其主要步骤为:

(1)获取密文

FileInputStreamf=newFileInputStream("SEnc.dat");intnum=f.available();byte[]ctext=newbyte[num];f.read(ctext);分析:密文存放在文件SEnc.dat中,由于解密是针对字节数组进行操作的,因此要先将密文从文件中读入字节数组。首先创建文件输入流,然后使用文件输入流的available()方法判断密文将占用多少字节,从而创建相应大小的字节数组ctext,最后使用文件输入流的read()方法一次性读入数组ctext。

(2)获取密钥

FileInputStreamf2=newFileInputStream("keykb1.dat");intnum2=f2.available();byte[]keykb=newbyte[num2];f2.read(keykb);SecretKeySpeck=newSecretKeySpec(keykb,"DESede");SecretKeySpec类的构造器中第2个参数则指定加密算法。由于keykb1.dat中的密钥原来使用的是DESede算法,因此这里仍旧使用字符串“DESede”作为参数。

(3)创建密码对象(Cipher对象)

Ciphercp=Cipher.getInstance("DESede");

(4)初始化密码对象cp.init(Cipher.DECRYPT_MODE,k);

(5)执行解密

byte[]ptext=cp.doFinal(ctext);

importjava.io.*;importjava.security.*;importjavax.crypto.*;importjavax.crypto.spec.*;publicclassSDec{publicstaticvoidmain(Stringargs[])throwsException{//获取密文FileInputStreamf=newFileInputStream("SEnc.dat");intnum=f.available();byte[]ctext=newbyte[num];f.read(ctext);//获取密钥FileInputStreamf2=newFileInputStream("keykb1.dat");intnum2=f2.available();byte[]keykb=newbyte[num2];f2.read(keykb);SecretKeySpeck=newSecretKeySpec(keykb,"DESede");//解密Ciphercp=Cipher.getInstance("DESede");cp.init(Cipher.DECRYPT_MODE,k);byte[]ptext=cp.doFinal(ctext);//显示明文Stringp=newString(ptext,"UTF8");System.out.println(p);}}程序中最后将明文生成字符串加以显示。

当前目录下必须有前面生成的密钥文件keykb1.dat,以及密文文件SEnc.dat。

输入javaSDec运行程序,将输出明文字符串“HelloWorld!”。

下面演示如何使用Java中定义好的类创建RSA公钥和私钥。

Java的KeyPairGenerator类提供了一些方法来创建密钥对以便用于非对称加密,密钥对创建好后封装在KeyPair类型的对象中,在KeyPair类中提供了获取公钥和私钥的方法。具体步骤如下:

(1)创建密钥对生成器

KeyPairGeneratorkpg=KeyPairGenerator.getInstance("RSA");

分析:密钥对生成器即KeyPairGenerator类型的对象,和2.2.1小节的第1步中介绍的KeyGenerator类一样,KeyPairGenerator类是一个工厂类,它通过其中预定义的一个静态方法getInstance()获取KeyPairGenerator类型的对象。getInstance()方法的参数是一个字符串,指定非对称加密所使用的算法,常用的有RSA,DSA等。

(2)初始化密钥生成器

kpg.initialize(1024);

分析:对于密钥长度。对于RSA算法,这里指定的其实是RSA算法中所用的模的位数。可以在512到2048之间。

(3)生成密钥对

KeyPairkp=kpg.genKeyPair();

分析:使用KeyPairGenerator类的genKeyPair()方法生成密钥对,其中包含了一对公钥和私钥的信息。

(4)获取公钥和私钥

PublicKeypbkey=kp.getPublic();PrivateKeyprkey=kp.getPrivate();

分析:使用KeyPair类的getPublic()和getPrivate()方法获得公钥和私钥对象。

importjava.io.*;importjava.security.*;importjavax.crypto.*;importjavax.crypto.spec.*;publicclassSkey_RSA{publicstaticvoidmain(Stringargs[])throwsException{KeyPairGeneratorkpg=KeyPairGenerator.getInstance("RSA");kpg.initialize(1024);KeyPairkp=kpg.genKeyPair();PublicKeypbkey=kp.getPublic();PrivateKeyprkey=kp.getPrivate();//保存公钥FileOutputStreamf1=newFileOutputStream("Skey_RSA_pub.dat");ObjectOutputStreamb1=newObjectOutputStream(f1);b1.writeObject(pbkey);//保存私钥FileOutputStreamf2=newFileOutputStream("Skey_RSA_priv.dat");ObjectOutputStreamb2=newObjectOutputStream(f2);b2.writeObject(prkey);}}分析:使用对象流将密钥保存在文件中,加密所用的公钥和解密所用的私钥分开保存。将公钥对外公布,供其他人加密使用,而把私钥秘密保存,在需要解密时使用。

输入javaSkey_RSA运行程序,当前目录下将生成两个文件:Skey_RSA_pub.dat和Skey_RSA_priv.dat,前者保存着公钥,后者保存着私钥。将文件Skey_RSA_pub.dat对外公布(如放在Web服务器上给大家下载,或者直接拷贝给所有需要的人),而Skey_RSA_priv.dat秘密保存。

以加密一串最简单的字符串“HelloWorld!”为例,演示了如何使用上面生成的RSA公钥文件Skey_RSA_pub.dat进行加密。

编程思路:

使用RSA公钥进行加密的代码和使用DESede进行加密其实没什么大的区别,只是Cipher类的getInstance()方法的参数中应该指定使用RSA。但由于J2SDK1.4中只实现了RSA密钥的创建,没有实现RSA算法,因此需要安装其他加密提供者软件才能直接使用Cipher类执行加密解密。其实有了RSA公钥和私钥后,自己编写程序从底层实现RSA算法也并不复杂。本实例给出简单的例子实现了RSA加密,使读者只使用J2SDK1.4便能直观地了解非对称加密算法。

RSA算法是使用整数进行加密运算的,在RSA公钥中包含了两个信息:公钥对应的整数e和用于取模的整数n。对于明文数字m,计算密文的公式是:memodn。因此,编程步骤如下:

(1)获取公钥

FileInputStreamf=newFileInputStream("Skey_RSA_pub.dat");ObjectInputStreamb=newObjectInputStream(f);RSAPublicKeypbk=(RSAPublicKey)b.readObject();分析:从公钥文件Skey_RSA_pub.dat中读取公钥,由于生成使用的是RSA算法,因此从文件读取公钥对象后强制转换为RSAPublicKey类型,以便后面读取RSA算法所需要的参数。

(2)获取公钥的参数(e,n)

BigIntegere=pbk.getPublicExponent();BigIntegern=pbk.getModulus();分析:使用RSAPublicKey类的getPublicExponent()和getModulus()方法可以分别获得公始中e和n的值。由于密钥很长,因此对应的整数值非常大,无法使用一般的整型来存储,Java中定义了BigInteger类来存储这类很大的整数并可进行各种运算。

(3)获取明文整数(m)

Strings="HelloWorld!";byteptext[]=s.getBytes("UTF8");BigIntegerm=newBigInteger(ptext);分析:明文是一个字符串,为了用整数表达这个字符串,先使用字符串的getBytes()方法将其转换为byte类型数组,它其实是字符串中各个字符的二进制表达方式,这一串二进制数转换为一个整数将非常大,因此仍旧使用BigInteger类将这个二进制串转换为整型。

本实例中出于简化,将整个字符串转换为一个整数。实际使用中,应该对明文进行分组,因为RSA算法要求整型数m的值必须小于n。

(4)执行计算

BigIntegerc=m.modPow(e,n);分析:计算前面的公式:memodn。BigInteger类中已经提供了方法modPow()来执行这个计算。底数m执行这个方法,方法modPow()的第一个参数即指数e,第二个参数即模n。方法返回的结果即公式memodn的计算结果,即密文。

importjava.security.*;importjava.security.spec.*;importjavax.crypto.*;importjavax.crypto.spec.*;importjavax.crypto.interfaces.*;importjava.security.interfaces.*;importjava.math.*;importjava.io.*;publicclassEnc_RSA{publicstaticvoidmain(Stringargs[])throwsException{Strings="HelloWorld!";//获取公钥及参数e,nFileInputStreamf=newFileInputStream("Skey_RSA_pub.dat");ObjectInputStreamb=newObjectInputStream(f);RSAPublicKeypbk=(RSAPublicKey)b.readObject();BigIntegere=pbk.getPublicExponent();BigIntegern=pbk.getModulus();System.out.println("e="+e);System.out.println("n="+n);//明文mbyteptext[]=s.getBytes("UTF8");BigIntegerm=newBigInteger(ptext);//计算密文c,打印BigIntegerc=m.modPow(e,n);System.out.println("c="+c);//保存密文Stringcs=c.toString();BufferedWriterout=newBufferedWriter(newOutputStreamWriter(newFileOutputStream("Enc_RSA.dat")));out.write(cs,0,cs.length());out.close();}}程序最后将密文c打印出来,并以字符串形式保存在文件中。

输入javaEnc_RSA运行程序,得到如下结果:

其中显示了公钥中的参数以及加密的结果c,这些都是很大的整数,n和c多达上百位。程序运行后密文c以字符串形式保存在文件Enc_RSA.dat中。

下面实例使用私钥文件Skey_RSA_priv.dat,对密文文件Enc_RSA.dat进行解密。

使用RSA私钥进行解密的代码也可以在Cipher类的getInstance()方法的参数中指定使用RSA,使用解密模式进行解密。但需要安装其他加密提供者软件才能直接使用Cipher类执行加密解密。本实例给出简单的例子从底层实现RSA解密,以便只使用J2SDK1.4便能直观地了解非对称加密算法。

RSA算法的解密和加密类似,在RSA私钥中包含了两个信息:私钥对应的整数d和用于取模的整数n。其中的n和加密时的n完全相同。对于密文数字c,计算明文的公式是:cdmodn,之所以加密时由公式memodn得到的密文c通过这个公式计算一下就可以反过来得到原来的明文m,有其本身的数学规律决定。从编程角度只需要知道这个结果就行了。编程步骤如下:

(1)读取密文

BufferedReaderin=newBufferedReader(newInputStreamReader(newFileInputStream("Enc_RSA.dat")));Stringctext=in.readLine();BigIntegerc=newBigInteger(ctext);分析:从密文文件Enc_RSA.dat中读取密文,由于保存的只是一行字符串,因此只要一条readLine()语句即可。由于这一行字符串表示的是一个很大的整型数,因此使用BigInteger类来表示这个整型数。

(2)获取私钥

FileInputStreamf=newFileInputStream("Skey_RSA_priv.dat");ObjectInputStreamb=newObjectInputStream(f);RSAPrivateKeyprk=(RSAPrivateKey)b.readObject();分析:从私钥文件Skey_RSA_priv.dat中读取私钥,由于使用的是RSA算法,因此从文件读取私钥对象后强制转换为RSAPrivateKey类型,以便后面读取RSA算法所需要的参数。

(3)获取私钥的参数(d,n)

BigIntegerd=prk.getPrivateExponent();BigIntegern=prk.getModulus();分析:使用RSAPrivateKey类的getPrivateExponent()和getModulus()方法可以分别获得私钥中d和n的值。

BigIntegerm=c.modPow(d,n);

分析:使用BigInteger的modPow()方法计算前面的公式:cdmodn。方法返回的结果即公式cdmodn的计算结果,即明文对应的整型数m。

(5)计算明文整型数对应的字符串

byte[]mt=m.toByteArray();for(inti=0;i

importjava.security.*;importjava.security.spec.*;importjavax.crypto.*;importjavax.crypto.spec.*;importjavax.crypto.interfaces.*;importjava.security.interfaces.*;importjava.math.*;importjava.io.*;publicclassDec_RSA{publicstaticvoidmain(Stringargs[])throwsException{//读取密文BufferedReaderin=newBufferedReader(newInputStreamReader(newFileInputStream("Enc_RSA.dat")));Stringctext=in.readLine();BigIntegerc=newBigInteger(ctext);//读取私钥FileInputStreamf=newFileInputStream("Skey_RSA_priv.dat");ObjectInputStreamb=newObjectInputStream(f);RSAPrivateKeyprk=(RSAPrivateKey)b.readObject();BigIntegerd=prk.getPrivateExponent();//获取私钥参数及解密BigIntegern=prk.getModulus();System.out.println("d="+d);System.out.println("n="+n);BigIntegerm=c.modPow(d,n);//显示解密结果System.out.println("m="+m);byte[]mt=m.toByteArray();System.out.println("PlainTextis");for(inti=0;i

其中显示了私钥中的参数以及解密的结果,其中整型的明文转换后显示出字符串“HelloWorld!”。

非对称加密解决了密钥分发的难题,但其计算量比对称密钥大,因此一般并不使用非对称加密加密大量数据。常见的做法是:主要数据通过对称密钥加密,而使用非对称加密来分发对称密钥,这样就将两者的优势结合了起来。

例如若A和B之间想秘密传送大量数据,一方(如A)先创建公私钥对,公钥对外公布,另一方(如B)创建对称密钥,然后使用A的公钥加密对称密钥,传递给A,A收到后用自己的私钥解密,得到对称密钥,以后A和B之间就可以使用对称密钥加密通信了。

除了这种方式以外,还可以使用密钥协定来交换对称密钥。执行密钥协定的标准算法是DH算法(Diffie-Hellman算法),本节介绍在Java中如何使用DH算法来交换共享密钥。

DH算法是建立在DH公钥和私钥的基础上的,A需要和B共享密钥时,A和B各自生成DH公钥和私钥,公钥对外公布而私钥各自秘密保存。本实例将介绍Java中如何创建并部署DH公钥和私钥,以便后面一小节利用它创建共享密钥。

程思路:

和上面使用KeyPairGenerator类创建RSA公钥和私钥类似,只是其参数中指定“DH”,此外在初始化时需要为DH指定特定的参数。

代码与分析:

运行程序:

建立两个目录A和B,模拟需要秘密通信的A、B双方,由于DH算法需要A和B各自生成DH公钥和私钥,因此在这两个目录下都拷贝编译后文件Key_DH。

首先由A创建自己的公钥和私钥,即在A目录下输入“javaKey_DHApub.datApri.dat”运行程序,这时在目录A下将产生文件Apub.dat和Apri.dat,前者保存着A的公钥,后者保存着A的私钥。然后由B创建自己的公钥和私钥,即在B目录下输入“javaKey_DHBpub.datBpri.dat”运行程序,这时在目录B下将产生文件Bpub.dat和Bpri.dat,前者保存着B的公钥,后者保存着B的私钥。最后发布公钥,A将Apub.dat拷贝到B目录,B将Bpub.dat拷贝到A的目录。这样,A、B双方的DH公钥和私钥已经创建并部署完毕。

DH算法中,A可以用自己的密钥和B的公钥按照一定方法生成一个密钥,B也可以用自己的密钥和A的公钥按照一定方法生成一个密钥,由于一些数学规律,这两个密钥完全相同。这样,A和B间就有了一个共同的密钥可以用于各种加密。本实例介绍Java中在上一小节的基础上如何利用DH公钥和私钥各自创建共享密钥。

Java中KeyAgreement类实现了密钥协定,它使用init()方法传入自己的私钥,使用doPhase()方法传入对方的公钥,进而可以使用generateSecret()方法生成共享的信息具体步骤如下:

(1)读取自己的DH私钥和对方的DH公钥

FileInputStreamf1=newFileInputStream(args[0]);ObjectInputStreamb1=newObjectInputStream(f1);PublicKeypbk=(PublicKey)b1.readObject();FileInputStreamf2=newFileInputStream(args[1]);ObjectInputStreamb2=newObjectInputStream(f2);PrivateKeyprk=(PrivateKey)b2.readObject();分析:从文件中获取密钥。只是分为公钥和私钥两个文件,通过命令行参数传入公钥和私钥文件名,第一个命令行参数为对方的公钥文件名,第二个命令行参数为自己的私钥文件名。

(2)创建密钥协定对象

KeyAgreementka=KeyAgreement.getInstance("DH");分析:密钥协定对象即KeyAgreement类型的对象,和KeyPairGenerator类类似,KeyAgreement类是一个工厂类,通过其中预定义的一个静态方法getInstance()获取KeyAgreement类型的对象。getInstance()方法的参数指定为“DH”。

(3)初始化密钥协定对象

ka.init(prk);分析:执行密钥协定对象的init()方法,传入第1步获得的自己的私钥,它在第1步中通过第2个命令行参数提供。

(4)执行密钥协定

ka.doPhase(pbk,true);分析:执行密钥协定对象的doPhase()方法,其第一个参数中传入对方的公钥。在本实例中,只有A、B两方需要共享密钥,因此对方只有一个,因此第二个参数设置为true。如果有A、B、C三方需要共享密钥,则对方有两个,doPhase()方法要写两次,每次在第1个参数中传入一个公钥,第2个参数最初设置为false,最后一次设置为true。例如C方应该执行ka.doPhase(pbk_of_A,false);ka.doPhase(pbk_of_B,true);。一次类推,可以用密钥协定实现多方共享一个密钥。

(5)生成共享信息

byte[]sb=ka.generateSecret();分析:执行密钥协定对象的generateSecret()方法,返回字节类型的数组。A、B双方得到的该数组的内容完全相同,用它创建密钥也各方完全相同。如可使用SecretKeySpeck=newSecretKeySpec(sb,"DESede");创建密钥。

importjava.io.*;importjava.math.*;importjava.security.*;importjava.security.spec.*;importjavax.crypto.*;importjavax.crypto.spec.*;importjavax.crypto.interfaces.*;publicclassKeyAgree{publicstaticvoidmain(Stringargs[])throwsException{//读取对方的DH公钥FileInputStreamf1=newFileInputStream(args[0]);ObjectInputStreamb1=newObjectInputStream(f1);PublicKeypbk=(PublicKey)b1.readObject();//读取自己的DH私钥FileInputStreamf2=newFileInputStream(args[1]);ObjectInputStreamb2=newObjectInputStream(f2);PrivateKeyprk=(PrivateKey)b2.readObject();//执行密钥协定KeyAgreementka=KeyAgreement.getInstance("DH");ka.init(prk);ka.doPhase(pbk,true);//生成共享信息byte[]sb=ka.generateSecret();for(inti=0;i

将程序KeyAgree编译后分别拷贝在A和B两个目录,首先在A目录输入“javaKeyAgreeBpub.datApri.dat”运行程序,它使用文件Bpub.dat中对方的公钥和文件Apri.dat中自己的私钥创建了一段共享的字节数组。

使用Java计算指定字符串的消息摘要。java.security包中的MessageDigest类提供了计算消息摘要的方法,

首先生成对象,执行其update()方法可以将原始数据传递给该对象,然后执行其digest()方法即可得到消息摘要。具体步骤如下:

(2)传入需要计算的字符串m.update(x.getBytes("UTF8"));分析:x为需要计算的字符串,update传入的参数是字节类型或字节类型数组,对于字符串,需要先使用getBytes()方法生成字符串数组。

(3)计算消息摘要bytes[]=m.digest();分析:执行MessageDigest对象的digest()方法完成计算,计算的结果通过字节类型的数组返回。

(4)处理计算结果必要的话可以使用如下代码将计算结果s转换为字符串。

Stringresult="";for(inti=0;i

importjava.security.*;publicclassDigestPass{publicstaticvoidmain(Stringargs[])throwsException{Stringx=args[0];MessageDigestm=MessageDigest.getInstance("MD5");m.update(x.getBytes("UTF8"));bytes[]=m.digest();Stringresult="";for(inti=0;i

输入javaDigestCalcabc来运行程序,其中命令行参数abc是原始数据,屏幕输出计算后的消息摘要:900150983cd24fb0d6963f7d28e17f72。

THE END
1.面试题人工智能工程师高频面试题汇总:机器学习深化篇(题目+答案所以,提前准备一些面试常问的问题,比如机器学习的那些算法,或者深度学习的框架,还有怎么优化模型,这些都是加分项,能有效提高面试通过率。 本篇小编整理了一些高频的机器学习深化方面的面试题,这些题目都是从实际面试中总结出来的,非常具有代表性和实用性,希望对你有帮助。 01 以下哪种激活函数因为其导数在某些区域https://zhuanlan.zhihu.com/p/12858758975
2.一年级上册数学第二单元教案1、找一找 (计算机演示图意) 2、师:星期天,小江想到小明家去玩,可他只记得小明家住在三楼的左边,你们能告诉他小明家住几号房吗?(展开讨论,计算机学示结果) 2、想一想 师:我跟大家面对面地站着(老师举起右手),请问:老师举起的是右手吗? 师:(老师把右手放下)请小朋友把右手举起来再判定一下老师举起的是https://www.fwsir.com/jiaoan/html/jiaoan_20230127100425_2297047.html
3.河南省基础教育资源公共服务平台三、探索算法 1、师:过去我们也做过一些十几减几的题目,但大多师在10+5的基础上算出15-5、15-10;在10+3的基础上算出13-3、13-10。 今天要学习的13-9,你们会算吗? 2、讨论。 可能有以下讨论结果: (1)用数的方法一个一个减去。 (2)把13分成10和3,先从10里面减去9,再加上3等于4。 https://www.hner.cn/index.php?r=space/person/blog/view&sid=71b9a25abdd848a0ad72ea0a6ad57138&id=4145173
4.小学数学概念教学设计(通用16篇)师:小朋友,你们都找了这么多有趣的图形,老师也找了一些回来,你们想不想看看?下面进行抢答游戏,知道的小朋友马上站起来说。这是什么?它的面是什么形状的?(老师出示准备的几个实物课件) 3.数一数。 看,数学多有意思,就连我们今天学的图形也能组成一幅幅美丽的图画,孩子们,下面就让我们一起来数数这些有趣的https://xiaoxue.ruiwen.com/jiaoxuesheji/368720.html
5.小学五年级数学说课稿15篇各位领导、老师们,本节课我根据XX年级学生的心理特征及其认知规律,采用直观教学和活动探究的教学方法,以"教师为主导,学生为主体",教师的"导"立足于学生的"学",以学法为重心,放手让学生自主探索的学习,主动地参与到知识形成的整个思维过程,力求使学生在积极、愉快的课堂气氛中提高自己的认识水平,从而达到预期的教学效https://www.yjbys.com/shuokegao/xiaoxue/3052038.html
6.优秀设计灵感精华2022新学期,有这篇宝藏文章就足够了!学建筑,到底该不该听老师的话? 作者:王祥 14 第一前线现场系列 190张照片,近距离感受来自普利兹克奖得住 诺曼·福斯特上海建筑展之震撼! 作者:肃画 170张照片,为你详细揭秘Zaha上海建筑展! 作者:肃画 木构复兴:70张图带你游览 今夏上海最强建筑艺术展! https://www.shangyexinzhi.com/article/4593730.html
7.北师大版小学三年级数学上册教案师:同学们的动手能力太强了,老师佩服你们,在这节课里,你们认识了什么?它是什么样的?还知道了它的哪些知识?四边形还有很多知识,我们以后再学。 北师大版小学三年级数学上册教案7 设计说明 本节课主要探究的是长方形和正方形周长的计算方法。这是在学生知道了长方形和正方形边的特点,理解了周长的实际意义的基础上https://www.cnfla.com/jiaoan/2837122.html
8.高中信息技术课程标准信息技术科目的选修部分包括“选修1:算法与程序设计”“选修2:多媒体技术应用”“选修3:网络技术应用”“选修4:数据管理技术”和“选修5:人工智能初步”五个模块,每个模块2学分。选修部分强调在必修模块的基础上关注技术能力与人文素养的双重建构,是信息素养培养的继续,是支持个性发展的平台。模块内容设计既注重技术深https://www.fqkhzx.cn/index/article/view/id/94.html
9.3《不退位减法》教学设计20篇(全文)例2:老师发现大家学的都非常认真,那么老师现在为了奖励你们,给你们讲一个好听的故事好不好? “有一天,八戒去花果山找孙悟空,悟空不在,于是八戒自己跑到桃园里偷摘桃子吃,不一会,悟空回来了,把八戒逮了个正着,孙悟空又气又恼,他问八戒,你吃了我多少桃子?八戒摸了摸自己的肚子说,我也不知道,悟空说,我这棵https://www.99xueshu.com/w/fileouiwcodz.html
10.学python看b站哪个老师?Worktile社区2. “黑马程序员”:这个老师的视频主要涵盖了编程技术方面,包括Python、Java、Web开发等。如果你想学习编程,尤其是Python语言,那么他的视频非常适合你。 3. “麦叔带你学编程”:这是一个专注于编程教育的老师,他的视频内容包含了Python、C语言、算法等方面的知识。他的教学风格简洁明了,非常适合初学者入门。 https://worktile.com/kb/ask/99097.html
11.网络上哪个老师ai教程最好?通过他的教程,你可以快速了解AI的基本概念和各种算法,同时也可以学到一些实际的应用案例和项目经验。以上是我个人认为比较好的一些AI教程和老师,当然还有很多其他的优秀老师和教程可以选择。选择合适的教程和老师需要根据自己的学习需求和学习风格进行综合考虑。也需要保持持续的学习和实践,不断积累知识和https://zhidao.baidu.com/question/1905799705901722260.html
12.面试经验腾讯WXG一二+面委+HR已拿offer您对我有什么建议?(我觉得你很不错,不论是数学还是算法都做的很快,继续保持学习,加油) HR面 自我介绍 实习期间有什么亮点 3.实习的时候为什么没有留下 4.实习最大的收获是什么 5.大一的时候去找老师做项目是自己主动去的吗? 6.为什么大一就想去做项目了? https://maimai.cn/article/detail?fid=1721129720&efid=6LxxRj4vjQhoCp2UkCWJIQ
13.强的离谱,Transformer模型与联邦机器学习详解!学员使用联邦学习框架与算法,实践金融领域隐私计算与风险检测的任务。 专业团队严格打磨的课程内容,前沿且深入 课程内容经过前期数百小时的打磨设计,保证内容和项目节点设置合理,真正做到学有所得。 就业导向,目标明确 顺利完课后,优秀学员可获得京东、百度等互联网大厂联邦学习工程师岗位的合作内推面试机会。 https://blog.csdn.net/moxibingdao/article/details/124113993
14.邹博机器学习算法最新版(吴恩达前辈唐宇迪老师张志华老师多关于机器学习的理论知识 , 不必慌张,只需要从算法开始 ,先把必要的几个数学知识点搞明白, 再把主流算法的理论、推导能搞个大致,再按照老师的项目实践下,可以说就踏上了机器学习这趟车了,看不在多,适合最好。主流算法就那几个,基础的线性回归、逻辑回归、决策树、xgboost、SVM、神经网络、卷积神经网络这些,老师https://www.jianshu.com/p/f78e353e28dc
15.我学编程全靠B站了,真香(第一期)51CTO博客1、青岛大学王卓老师,YYDS 总当有人问我数据结构与算法推荐入门学哪个的时候,我都会推荐他去看一下王卓老师的这门数据结构与算法课程! 我看过不少数据结构与算法的课程,可讲的清楚明白的没多少,王卓老师算一个! 地址:https://www.bilibili.com/video/BV1nJ411V7bd https://blog.51cto.com/u_15275037/4802576
16.西安市优质教育资源共享平台2.仔细听老师读第一小节,找一找表示颜色的词。 (1)出示:蓝蓝的 黄黄的 雪白雪白的(指导读好轻声“的”) (2) 认读 “蓝”,了解蓝为什么是草字头 (3)读“蓝的”和“蓝蓝的”,哪个词的意思是很蓝很蓝。像这样的重复,就表示颜色或其他方面的的程度深一些。读这样的词语,第二个字轻一些。 http://www.xaeduyun.cn/stuq/acommonapp/csite/ablog/cblog/mblogBrowse.do?blogid=2c908162799dc30f0179d46d63190869
17.满满干货,bobo老师诚挚分享:锻炼内功,高效学习,如果有什么秘诀的我看过不少同学,一上来学习《算法导论》,关于复杂度分析的笔记做了好几页,然后就放弃了,可是连什么是动态规划都不知道。这样完全没有对“算法”这个领域有全面的认识,甚至可以说根本没有学过“算法”!先用薄教材入门,再找“厚”教材,细细体会其中的细节,是我百试不爽的学习方法。https://www.imooc.com/article/26624
18.详情项目公示项目前期,指导老师提供选题和创新点思路,指导确定选题。和团队成员每周开例会听汇报文献研读成果,并作选题背景知识方面的指导,给团队成员答疑解惑,帮助团队确立了项目的整体研究思路。项目中期,指导老师指导团队进行编程和建模的学习,对算法的模拟和数据的处理提出修改自己的建议,保证了本项目的顺利开展。项目后期,指导老师https://sjjx.hhu.edu.cn/hhu/CXCY/HHU/Item/Detail/e8040612-2e31-46b0-8777-2f9541607d9d
19.机器学习和机器人学龄老师有什么区别机器人学龄老师 1、熟悉常用的机器学习算法,包括但不限于XGB、CATBOOST、GBDT ,极佳的工程实现能力,精通Python、hiveSQL; 2、有数理分析方面良好的素养以及数理统计基础; 3、有良好的数据敏感能力、较强的逻辑分析能力; 4、有较强的学习能力,对新事物保有好奇心,并能快速适应新环境; 5、有良好的沟通能力和团队协https://www.jobui.com/gangwei/pk/jiqixuexi-jiqirenxuelinglaoshi/
20.新作为新篇章可在课堂上识别学生情绪,这些领域AI甚至比老师躲不过课前的“刷脸”签到,也躲不过AI课堂情绪识别的“监控”,同学们低头、抬头、举手、托腮的动作和表情都会被一一记录、分析;智能化教与学平台通过记录学生网络学习的进入时间、学习时间、学习内容、学习训练或测试的级别,分析学生学习情况,一点也不含糊;同一台曾与世界级乒乓球冠军邓亚萍竞技的陪练机器人对打乒乓https://www.jfdaily.com/wx/detail.do?id=165929
21.作业帮学习机VS希沃W3:选对学习机,还应该关注这些实力新技术,AI算法诊断学情、AI老师解放家长 当前考察一款学习机,是否有成熟的AI实力也是非常重要的一环。在家庭学习场景里,AI技术可以帮助孩子更加精准地找到学习中存在的问题,并提供针对性的帮助,在解放家长的同时,也让孩子的学习更加高效。 目前来看,希沃W3在一些学科上运用了AI技术,比如针对学前英语的AI伴读,数学科目https://www.techweb.com.cn/news/2023-04-23/2925236.shtml
22.AI专业圈已炸雷,华人科学家发明KAN网络算法,或将洗牌AI但我今天也只能给你们讲到这个份上了,木头老师自己也还在学习,这个东西实在太新了,等他学到了更多之后,我再向他讨教。 KAN 网络是不是会成为颠覆 MLP 的下一代 AI 的底层算法,目前谁也不敢说会还是不会,但是,它的潜力正在快速体现,它的热度也在快速增加中。我想,即便是它最后没有达到预期,它也为寻找 MLPhttps://www.360doc.cn/article/29904046_1124737518.html
23.一个数除以分数六年级数学教学教案(通用6篇)分数除法是人教数学六年级上册的教学重点和难点,常规教学中,用“归一法”来推导算法,过程严谨,算法、算理与应用结合,但却存在一定难度,一些学生不易理解。 如何联系旧知,促进知识正向迁移,渗透“转化”的数学,巧妙探究算理,轻松推导算法,实现难点有效突破?在设计教学时,我尝试曹培英老师分数除法算法多样性的方法,借助数https://www.oh100.com/kaoshi/jiaoan/570220.html
24.在新重庆建设中绽放青春光彩研发团队整整耗时10年,写了超过10万行核心算法,终于突破了机器人控制算法的瓶颈,把操控性、稳定性、精准性匹配到最优。 今年,由重庆金山公司自主研发的腹胸腔手术机器人成功问世,不仅完成了对国外技术的全面反超,还以更小伤口、更少出血、更短恢复期的优势,遥遥领先美国同类产品一个代次。 http://www.cq.gov.cn/ywdt/jrcq/202305/t20230505_11933644.html