java 接口 安全加密

    技术2024-03-26  92

    关于本教程

    本教程是关于什么的?

    也许没有比应用程序安全更重要的软件工程主题。 攻击是昂贵的,无论是来自内部还是外部,而且某些攻击可能会使软件公司承担赔偿责任。 随着计算机(尤其是Internet)技术的发展,安全攻击变得越来越复杂和频繁。 掌握最新技术和工具是应用程序安全的关键之一。 另一个是成熟的技术的坚实基础,例如数据加密,身份验证和授权。

    Java平台(包括基本语言和库扩展)为编写安全的应用程序奠定了良好的基础。 本教程介绍了密码学的基础知识以及如何用Java编程语言实现密码学,并提供了示例代码来说明这些概念。

    在分两部分的教程的第一部分中,我们介绍了库扩展(现在是JDK 1.4基础的一部分)中的资料,这些扩展称为Java密码术扩展(JCE)和Java安全套接字扩展(JSSE)。 另外,本教程介绍了CertPath API,它是JDK 1.4的新增功能。 在第2部分(请参阅参考资料 )中,我们将扩大讨论范围以涵盖访问控制,访问控制由Java身份验证和授权服务(JAAS)在Java平台中进行管理。

    我应该学习本教程吗?

    这是一个中级教程。 它假定您知道如何读写基本的Java程序,包括应用程序和applet。

    如果您已经是Java程序员,并且对加密技术(诸如私钥和公钥加密,RSA,SSL,证书等主题)以及支持它们的Java库(JCE,JSSE)感到好奇,那么本教程适合您。 它不假定密码学,JCE或JSSE具有任何以前的背景。

    本教程介绍了基本的密码构建块概念。 每个概念后都有Java实现注意事项,代码示例以及示例执行的结果。

    工具,代码示例和安装要求

    您需要以下各项来完成本教程中的编程练习:

    JDK 1.4,标准版 本教程的源代码和类JavaSecurity1-source.jar ,以便您可以按照示例进行操作 RSA示例的Bouncy Castle Crypto库 支持Java 1.4插件的浏览器

    您可以使用JDK 1.3.x,但必须自己安装JCE和JSSE。

    关于代码示例的注释

    这些代码示例将加密的数据直接转储到屏幕上。 在大多数情况下,这将导致外观怪异的控制字符,其中某些字符有时可能会导致屏幕格式问题。 这不是一个好的编程习惯(最好将它们转换为可显示的ASCII字符或十进制表示形式),但是此处已这样做是为了保持代码示例及其输出简短。

    在大多数情况下,在示例执行部分中,实际字符串已被修改为与本教程的字符集要求兼容。 同样,在大多数示例中,我们查找并显示用于给定算法的实际安全提供程序库。 这样做是为了使用户更好地了解针对哪些函数调用了哪些库。 为什么? 因为在大多数安装中,安装了许多这些提供程序。

    Java安全性编程概念

    Java平台如何促进安全编程

    Java编程语言和环境具有许多有助于安全编程的功能:

    没有指针 ,这意味着Java程序无法寻址地址空间中的任意内存位置。 字节码验证程序 ,在对.class文件进行编译后运行,并在执行之前检查安全性问题。 例如,将拒绝访问超出数组大小的数组元素的尝试。 由于缓冲区溢出攻击是造成大多数系统漏洞的原因,因此这是一项重要的安全功能。 对applet和应用程序的资源访问进行细粒度控制 。 例如,可以限制applet读取或写入磁盘空间,或者可以授权applet仅从特定目录读取。 该授权可以基于谁对代码进行签名 (请参阅代码签名的概念 )和代码源的http地址。 这些设置显示在java.policy文件中。 用于所有主要密码构造块和SSL(本教程的主题)以及身份验证和授权(在本系列的第二篇教程中讨论) 的大量库函数 。 此外,许多第三方库可用于其他算法。

    什么是安全编程技术?

    简而言之,可以使用多种编程样式和技术来帮助确保更安全的应用程序。 考虑以下两个一般示例:

    存储/删除密码。 如果密码存储在Java String对象中,则密码将保留在内存中,直到被垃圾回收或过程结束为止。 如果是垃圾回收,它将一直存在于可用内存堆中,直到重新使用内存空间为止。 密码String在内存中保留的时间越长,它越容易受到监听。 更糟的是,如果实际内存不足,则操作系统可能将此密码String分页到磁盘的交换空间,因此容易受到磁盘块监听的攻击。 为了尽量减少(但不能消除)这些风险,您应该将密码存储在char数组中,并在使用后将其归零。 ( String是不可变的,因此您不能将它们归零。) 智能序列化。 当对象被序列化以存储或传输时,默认情况下,流中会存在任何私有字段。 因此,敏感数据容易受到监听。 您可以使用transient关键字标记属性,以便在流中将其跳过。

    在整个教程中,当我们需要这些技术时,我们将更详细地讨论它们。

    安全性集成在JDK 1.4中

    在JDK 1.4之前,必须将许多安全功能作为扩展添加到基本Java代码发行版中。 美国严格的出口限制要求这种功能分离。

    现在,新的宽松法规为更紧密地集成安全功能和基本语言打开了大门。 以下软件包(在1.4版本之前用作扩展)现已集成到JDK 1.4中:

    JCE (Java密码学扩展) JSSE (Java安全套接字扩展) JAAS (Java身份验证和授权服务)

    JDK 1.4还引入了两个新功能:

    JGSS (Java通用安全服务) CertPath API (Java认证路径API)

    JCE,JSSE和CertPath API是本教程的主题。 在本系列的下一个教程中,我们将重点介绍JAAS。 这两个教程都没有涵盖JGSS(它提供了一个通用框架来在应用程序之间安全地交换消息)。

    第三方库丰富了安全性

    我们可以使用第三方库(也称为provider)来增强当前Java语言中已经丰富的功能集。 提供程序添加其他安全算法。

    作为图书馆的一个例子,我们将与充气城堡提供商合作(参见相关主题 )。 Bouncy Castle库提供了其他加密算法,包括什么是公钥加密中讨论的流行的RSA算法。 和什么是数字签名? 本教程。

    虽然目录名称和java.security文件可能有所不同,但这是用于安装Bouncy Castle提供程序的模板。 要安装此库,请下载bcprov-jdk14-112.jar文件并将其放置在j2sdk1.4.0 \ jre \ lib \ ext和Program Files \ Java \ J2re1.4.0 \ lib \ ext目录中。 在与上面相同的目录中的两个java.security文件中,但使用“ security”而不是“ ext”,添加以下行:

    security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider

    到这行代码的末尾:

    security.provider.1=sun.security.provider.Sun security.provider.2=com.sun.net.ssl.internal.ssl.Provider security.provider.3=com.sun.rsajca.Provider security.provider.4=com.sun.crypto.provider.SunJCE security.provider.5=sun.security.jgss.SunProvider security.provider.6=org.bouncycastle.jce.provider.BouncyCastleProvider

    展望未来

    在本节中,我们介绍了Java语言提供的功能(完全集成或基于扩展),这些功能有助于确保编程保持安全。 我们提供了一些安全编程技术的一般示例,以帮助您熟悉该概念。 我们介绍了曾经是扩展程序但现在已集成到1.4版发行中的安全技术。 我们还注意到了两种新的安全技术。 而且我们已经证明,第三方库可以通过提供新技术来增强安全程序。

    在本教程的其余部分,我们将使您熟悉这些旨在提供安全消息传递的概念(因为它们适用于Java编程):

    消息摘要。 结合消息身份验证代码,该技术可确保消息的完整性。 私钥加密。 一种旨在确保您的消息的机密性的技术。 公钥加密。 一种允许两方共享秘密消息而无需事先就秘密密钥达成协议的技术。 数字签名。 一种位模式,用于将另一方的消息标识为来自适当的人。 数字证书。 通过使消息由第三方机构认证来为数字签名增加另一安全级别的技术。 代码签名。 受信任实体在交付的代码中嵌入签名的概念。 SSL / TLS。 用于在客户端和服务器之间建立安全通信通道的协议。 传输层安全性(TLS)替代了安全套接字层(SSL)。

    在讨论每个主题时,我们将提供示例和示例代码。

    确保消息的完整性

    总览

    在本节中,我们将学习消息摘要,这些摘要将消息中的数据带入并生成一个位块,用于表示消息的“指纹”。 我们还将介绍与消息摘要有关的JDK 1.4支持的算法,类和方法,为消息摘要和消息身份验证功能提供代码示例和示例执行代码。

    什么是消息摘要?

    消息摘要是一种确保消息完整性的功能。 消息摘要将一条消息作为输入,并生成通常代表几百位长的位块,以表示消息的指纹。 消息中的微小变化(例如,闯入者或窃听者)使指纹产生了明显的变化。

    消息摘要功能是单向功能。 从消息中生成指纹是一件简单的事情,但是要生成与给定指纹匹配的消息则相当困难。

    消息摘要可以弱也可以强。 校验和(即消息所有字节的XOR)是弱消息摘要功能的示例。 修改一个字节以生成任何所需的校验和指纹很容易。 最强大的功能使用哈希。 消息中的1位更改会导致指纹发生巨大变化(理想情况下,指纹位的50%会发生变化)。

    算法,类和方法

    JDK 1.4支持以下消息摘要算法:

    MD2和MD5 ,它们是128位算法 SHA-1 ,这是一种160位算法 SHA-256, SHA-383和SHA-512 ,分别提供更长的指纹大小,分别为256、383和512位

    MD5和SHA-1是最常用的算法。

    MessageDigest类可操纵消息摘要。 消息摘要代码示例中使用了以下方法:

    MessageDigest.getInstance("MD5") :创建消息摘要。 .update(plaintext) :使用纯文本字符串计算消息摘要。 .digest() :读取消息摘要。

    如果将密钥用作消息摘要生成的一部分,则该算法称为消息身份验证码 。 JDK 1.4支持HMAC / SHA-1和HMAC / MD5消息认证代码算法。

    Mac类使用KeyGenerator类产生的密钥来操作消息验证代码。 在消息身份验证代码示例中使用以下方法:

    KeyGenerator.getInstance("HmacMD5")和.generateKey() :生成密钥。 Mac.getInstance("HmacMD5") :创建一个MAC对象。 .init(MD5key) :初始化MAC对象。 .update(plaintext)和.doFinal() :使用纯文本字符串计算MAC对象。

    消息摘要代码示例

    import java.security.*; import javax.crypto.*; // // Generate a Message Digest public class MessageDigestExample { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java MessageDigestExample text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // get a message digest object using the MD5 algorithm MessageDigest messageDigest = MessageDigest.getInstance("MD5"); // // print out the provider used System.out.println( "\n" + messageDigest.getProvider().getInfo() ); // // calculate the digest and print it out messageDigest.update( plainText); System.out.println( "\nDigest: " ); System.out.println( new String( messageDigest.digest(), "UTF8") ); } }

    消息摘要示例执行

    D:\IBM>java MessageDigestExample "This is a test!" SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests ; SecureRandom; X.509 certificates; JKS keystore; PKIX CertPathValidator ; PKIX CertPathBuilder; LDAP, Collection CertStores) Digest: D93,.x2%$kd8xdp3di5*

    消息认证代码示例

    import java.security.*; import javax.crypto.*; // // Generate a Message Authentication Code public class MessageAuthenticationCodeExample { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println ("Usage: java MessageAuthenticationCodeExample text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // get a key for the HmacMD5 algorithm System.out.println( "\nStart generating key" ); KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5"); SecretKey MD5key = keyGen.generateKey(); System.out.println( "Finish generating key" ); // // get a MAC object and update it with the plaintext Mac mac = Mac.getInstance("HmacMD5"); mac.init(MD5key); mac.update(plainText); // // print out the provider used and the MAC System.out.println( "\n" + mac.getProvider().getInfo() ); System.out.println( "\nMAC: " ); System.out.println( new String( mac.doFinal(), "UTF8") ); } }

    消息认证示例执行

    D:\IBM>java MessageAuthenticationCodeExample "This is a test!" Start generating key Finish generating key SunJCE Provider (implements DES, Triple DES, Blowfish, PBE, Diffie-Hellman, HMAC-MD5, HMAC-SHA1) MAC: Dkdj47x4#.@kd#n8a-x>

    请注意,密钥生成需要很长时间,因为代码使用线程行为的时序生成了高质量的伪随机数。 一旦生成第一个数字,其他数字将花费更少的时间。

    另外,请注意,与消息摘要不同,消息身份验证代码使用加密提供程序。 (有关提供商的更多信息,请参见第三方库丰富了安全性 。)

    对消息保密

    总览

    在本节中,我们将研究私钥加密的用法,并重点关注密码块,填充,流密码和密码模式等概念。 我们将快速详细介绍密码算法,类和方法,并通过代码示例和示例执行来说明此概念。

    什么是私钥密码术?

    消息摘要可以确保消息的完整性,但是不能用于确保消息的机密性。 为此,我们需要使用私钥加密来交换私信。

    考虑这种情况:Alice和Bob都有一个只有他们知道的共享密钥,并且他们同意使用通用的加密算法或密码。 换句话说,他们将密钥保密。 当爱丽丝(Alice)要发送消息给鲍勃(Bob)时,她会加密原始消息(称为纯文本)以创建密文 ,然后将密文发送给鲍勃(Bob)。 鲍勃从爱丽丝那里收到密文,并用他的私钥解密密文以重新创建原始的明文消息。 如果窃听者Eve正在收听通信,则她仅听到密文,因此可以保留消息的机密性。

    您可以加密单个位或大块位,称为块。 这些称为密码块的块通常为64位。 如果消息不是64位的倍数,则必须填充短块(有关填充的更多信息,请参见填充是什么? )。 在硬件实现中单位加密更常见。 单位密码称为流密码 。

    私钥加密的强度由加密算法和密钥的长度确定。 如果算法是合理的,则攻击它的唯一方法是尝试每种可能的密钥的蛮力方法,平均将进行(1/2)* 2 * n次尝试,其中n是位数在关键。

    当美国出口法规具有限制性时,仅允许输出40位密钥。 该密钥长度相当弱。 美国官方标准DES算法使用了56位密钥,随着处理器速度的加快,它变得越来越弱。 通常,当今首选使用128位密钥。 有了它们,如果每秒可以尝试一百万个密钥,那么平均要花费宇宙年龄的很多倍才能找到密钥!

    什么是填充?

    正如我们在上一节中提到的,如果使用块密码并且消息长度不是块长度的倍数,则必须用字节填充最后一个块,以产生完整的块大小。 有许多填充块的方法,例如使用全零或全零。 在本教程中,我们将对私有密钥加密使用PKCS5填充,对于公共密钥加密使用PKCS1。

    对于PKCS5,在短块中填充一个重复字节,该重复字节的值表示剩余的字节数。 在本教程中,我们将不再进一步讨论填充算法,但是为您提供参考,JDK 1.4支持以下填充技术:

    没有填充 PKCS5 OAEP SSL3

    该BouncyCastle的库(见安全富含第三方库和相关信息 )支持其它填充技术。

    模式:指定加密的工作方式

    给定的密​​码可以用于多种模式 。 模式允许您指定加密的工作方式。

    例如,您可以允许一个块的加密取决于上一个块的加密,或者可以使一个块的加密独立于任何其他块。

    选择的模式取决于您的需求,并且必须考虑折衷(安全性,并行处理能力以及对明文和密文错误的容忍度)。 模式的选择超出了本教程的范围(请参阅参考资料,以进行进一步的阅读),但是再次提醒您,Java平台支持以下模式:

    ECB (电子密码书) CBC (密码块链接) CFB (密码反馈模式) OFB (输出反馈模式) PCBC (传播密码块链接)

    算法,类和方法

    JDK 1.4支持以下私钥算法:

    DES。 DES(数据加密标准)是IBM在1970年代发明的,并被美国政府采纳为标准。 它是一个56位的分组密码。 三重DES。 该算法用于处理56位密钥日益严重的弱点,同时利用DES技术,通过两次使用两个密钥的DES算法运行纯文本,有效密钥强度为112位。 TripleDES有时也称为DESede(用于加密,解密和加密,这是三个阶段)。 AES。 AES(高级加密标准)取代了DES作为美国标准。 它是由Joan Daemen和Vincent Rijmen发明的,也被称为Rinjdael算法。 它是128位的块密码,密钥长度为128、192或256位。 RC2, RC4和RC5。 这些是来自领先的加密安全公司RSA Security的算法。 河豚。 该算法由Bruce Schneier开发,是一种块密码,密钥长度从32位到448位(8的倍数)可变,并且被设计用于在微处理器的软件中有效实现。 PBE。 PBE(基于密码的加密)可以与各种消息摘要和私钥算法结合使用。

    Cipher类使用KeyGenerator类产生的密钥来操纵私钥算法。 私钥加密代码示例中使用了以下方法:

    KeyGenerator.getInstance("DES") .init(56)和.generateKey() :生成密钥。 Cipher.getInstance("DES/ECB/PKCS5Padding") :创建Cipher对象(指定算法,模式和填充)。 .init(Cipher.ENCRYPT_MODE, key) :初始化Cipher对象。 .doFinal(plainText) :使用明文字符串计算密文。 .init(Cipher.DECRYPT_MODE, key) :解密密文。 .doFinal(cipherText) :计算密文。

    私钥加密代码示例

    import java.security.*; import javax.crypto.*; // // encrypt and decrypt using the DES private key algorithm public class PrivateExample { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java PrivateExample text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // get a DES private key System.out.println( "\nStart generating DES key" ); KeyGenerator keyGen = KeyGenerator.getInstance("DES"); keyGen.init(56); Key key = keyGen.generateKey(); System.out.println( "Finish generating DES key" ); // // get a DES cipher object and print the provider Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); System.out.println( "\n" + cipher.getProvider().getInfo() ); // // encrypt using the key and the plaintext System.out.println( "\nStart encryption" ); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] cipherText = cipher.doFinal(plainText); System.out.println( "Finish encryption: " ); System.out.println( new String(cipherText, "UTF8") ); // // decrypt the ciphertext using the same key System.out.println( "\nStart decryption" ); cipher.init(Cipher.DECRYPT_MODE, key); byte[] newPlainText = cipher.doFinal(cipherText); System.out.println( "Finish decryption: " ); System.out.println( new String(newPlainText, "UTF8") ); } }

    私钥加密示例执行

    D:\IBM>java PrivateExample "This is a test!" Start generating DES key Finish generating DES key SunJCE Provider (implements DES, Triple DES, Blowfish, PBE, Diffie-Hellman, HMAC-MD5, HMAC-SHA1) Start encryption Finish encryption: Kdkj4338*3n1#kxkgtixo4 Start decryption Finish decryption: This is a test!

    带有公开密钥的秘密消息

    总览

    在本节中,我们将研究公共密钥加密技术,该功能解决了在各方之间无需事先安排密钥就加密消息的问题。 我们将简短地介绍支持公钥功能的算法,类和方法,并提供代码示例和执行以说明该概念。

    什么是公钥加密?

    私钥加密有一个主要缺点:私钥如何首先到达Alice和Bob? 如果Alice生成了它,则必须将其发送给Bob,但这是敏感信息,因此应进行加密。 但是,尚未交换密钥来执行加密。

    1970年代发明的公钥密码术解决了在无需事先就密钥达成协议的情况下在两方之间加密消息的问题。

    在公共密钥加密中,Alice和Bob不仅具有不同的密钥,而且每个都有两个密钥。 一个密钥是私有的,不能与任何人共享。 另一个密钥是公共密钥,可以与任何人共享。

    当爱丽丝(Alice)要向鲍勃(Bob)发送安全消息时,她使用鲍勃(Bob)的公钥对消息进行加密,然后将结果发送给鲍勃(Bob)。 鲍勃使用他的私钥来解密消息。 当Bob想要向Alice发送安全消息时,他使用Alice的公钥对消息进行加密,然后将结果发送给Alice。 爱丽丝使用她的私钥来解密消息。 夏娃可以窃听公共密钥和加密的消息,但是她不能解密消息,因为她没有两个私有密钥。

    公钥和私钥是成对生成的,并且比等效强度的私钥加密密钥需要更长的长度。 RSA算法的典型密钥长度为1,024位。 从另一对派生密钥对中的一个成员是不可行的。

    公钥加密很慢(比私钥加密慢100到1000倍),因此在实践中通常使用混合技术。 公共密钥加密用于将私钥(称为会话密钥 )分发给另一方,然后使用该私密会话密钥的私钥加密用于大部分消息加密。

    算法,类和方法

    公钥加密中使用以下两种算法:

    RSA。 该算法是最流行的公钥密码,但是JDK 1.4不支持该算法。 您必须使用BouncyCastle之类的第三方库来获得此支持。 Diffie-Hellman。 该算法在技术上被称为密钥协商算法 。 它不能用于加密,但可以用于允许两方通过在公共通道上共享信息来导出密钥。 然后可以将此密钥用于私钥加密。

    Cipher类使用KeyPairGenerator类产生的密钥来操纵公共密钥算法。 公钥密码术代码示例示例中使用以下方法:

    KeyPairGenerator.getInstance("RSA") .initialize(1024)和.generateKeyPair() :生成密钥对。 Cipher.getInstance("RSA/ECB/PKCS1Padding")创建一个Cipher对象(指定算法,模式和填充)。 .init(Cipher.ENCRYPT_MODE, key.getPublic()) :初始化Cipher对象。 .doFinal(plainText) :使用明文字符串计算密文。 .init(Cipher.DECRYPT_MODE, key.getPrivate())和.doFinal(cipherText) :解密密文。

    公钥加密代码示例

    import java.security.*; import javax.crypto.*; // // Public Key cryptography using the RSA algorithm. public class PublicExample { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java PublicExample text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // generate an RSA key System.out.println( "\nStart generating RSA key" ); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(1024); KeyPair key = keyGen.generateKeyPair(); System.out.println( "Finish generating RSA key" ); // // get an RSA cipher object and print the provider Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); System.out.println( "\n" + cipher.getProvider().getInfo() ); // // encrypt the plaintext using the public key System.out.println( "\nStart encryption" ); cipher.init(Cipher.ENCRYPT_MODE, key.getPublic()); byte[] cipherText = cipher.doFinal(plainText); System.out.println( "Finish encryption: " ); System.out.println( new String(cipherText, "UTF8") ); // // decrypt the ciphertext using the private key System.out.println( "\nStart decryption" ); cipher.init(Cipher.DECRYPT_MODE, key.getPrivate()); byte[] newPlainText = cipher.doFinal(cipherText); System.out.println( "Finish decryption: " ); System.out.println( new String(newPlainText, "UTF8") ); } }

    公钥密码学样本执行

    D:\IBM>java PublicExample "This is a test!" Start generating RSA key Finish generating RSA key BouncyCastle Security Provider v1.12 Start encryption Finish encryption: Ajsd843*342l,AD;LKJL;1!*AD(XLKASD498asdjlkkKSFJHDuhpja;d(kawe#kjalfcas, .asd+,1LKSDJf;khaouiwheyahdsl87458q9734hjfc*nuywe Start decryption Finish decryption: This is a test!

    无纸签名

    总览

    在本节中,我们将检查数字签名,这是确定交换消息​​的各方的标识的第一级。 我们将说明通过代码示例识别消息源的困难和简便方法。 我们还将列出JDK 1.4支持的数字签名算法,并研究其中涉及的类和方法。

    什么是数字签名?

    您是否注意到什么是公共密钥密码学中描述的公共密钥消息交换中的缺陷? ? 鲍勃(Bob)如何证明该消息确实来自爱丽丝? 夏娃本可以用她的公钥代替爱丽丝的,然后鲍勃将与夏娃交换信息,以为自己是爱丽丝。 这就是所谓的中间人攻击 。

    我们可以使用数字签名来解决此问题-一种位模式,它证明消息来自给定的参与者。

    实现数字签名的一种方法是使用什么是公共密钥密码学中描述的公共密钥过程的相反过程。 。 发件人使用私钥对消息签名,而不是使用公钥加密和私钥解密,而收件人则使用发件人的公钥来解密消息。 因为只有发件人才知道私钥,所以收件人可以确定邮件确实来自发件人。

    实际上,消息摘要( 什么是消息摘要? )不是整个消息,而是由私钥签名的位流。 因此,如果爱丽丝想向鲍勃发送签名消息,则她会生成消息的消息摘要,并使用她的私钥对其进行签名。 她将消息(明文)和签名的消息摘要发送给Bob。 Bob使用Alice的公钥解密已签名的消息摘要,并根据明文消息计算消息摘要,并检查两个摘要是否匹配。 如果他们这样做,鲍勃可以确定消息是来自爱丽丝的。

    请注意,数字签名不提供消息的加密,因此,如果您还需要保密,则必须将加密技术与签名结合使用。

    您可以将RSA算法用于数字签名和加密。 可以将称为DSA(数字签名算法)的美国标准用于数字签名,但不能用于加密。

    演算法

    JDK 1.4支持以下数字签名算法:

    MD2 / RSA MD5 / RSA SHA1 / DSA SHA1 / RSA

    在本节中,我们将研究两个示例。 第一种是硬方法(请参阅数字签名代码示例:硬方法 ),它使用已经讨论过的原语来进行消息摘要和公共密钥加密来实现数字签名。 第二种简便方法(请参见数字签名代码示例:easy method )使用Java语言对签名的直接支持。

    数字签名代码示例:困难的方法

    import java.security.*; import javax.crypto.*; // // This program demonstrates the digital signature technique at the // primative level by generating a message digest of the plaintext // and signing it with an RSA private key, to create the signature. // To verify the signature, the message digest is again generated from // the plaintext and compared with the decryption of the signature // using the public key. If they match, the signature is verified. public class DigitalSignature1Example { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java DigitalSignature1Example text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // get an MD5 message digest object and compute the plaintext digest MessageDigest messageDigest = MessageDigest.getInstance("MD5"); System.out.println( "\n" + messageDigest.getProvider().getInfo() ); messageDigest.update( plainText ); byte[] md = messageDigest.digest(); System.out.println( "\nDigest: " ); System.out.println( new String( md, "UTF8") ); // // generate an RSA keypair System.out.println( "\nStart generating RSA key" ); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(1024); KeyPair key = keyGen.generateKeyPair(); System.out.println( "Finish generating RSA key" ); // // get an RSA cipher and list the provider Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); System.out.println( "\n" + cipher.getProvider().getInfo() ); // // encrypt the message digest with the RSA private key // to create the signature System.out.println( "\nStart encryption" ); cipher.init(Cipher.ENCRYPT_MODE, key.getPrivate()); byte[] cipherText = cipher.doFinal(md); System.out.println( "Finish encryption: " ); System.out.println( new String(cipherText, "UTF8") ); // // to verify, start by decrypting the signature with the // RSA private key System.out.println( "\nStart decryption" ); cipher.init(Cipher.DECRYPT_MODE, key.getPublic()); byte[] newMD = cipher.doFinal(cipherText); System.out.println( "Finish decryption: " ); System.out.println( new String(newMD, "UTF8") ); // // then, recreate the message digest from the plaintext // to simulate what a recipient must do System.out.println( "\nStart signature verification" ); messageDigest.reset(); messageDigest.update(plainText); byte[] oldMD = messageDigest.digest(); // // verify that the two message digests match int len = newMD.length; if (len > oldMD.length) { System.out.println( "Signature failed, length error"); System.exit(1); } for (int i = 0; i < len; ++i) if (oldMD[i] != newMD[i]) { System.out.println( "Signature failed, element error" ); System.exit(1); } System.out.println( "Signature verified" ); } }

    样本执行

    D:\IBM>java DigitalSignature1Example "This is a test!" SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests ; SecureRandom; X.509 certificates; JKS keystore; PKIX CertPathValidator ; PKIX CertPathBuilder; LDAP, Collection CertStores) Digest: D647dbdek12*e,ad.?e Start generating RSA key Finish generating RSA key BouncyCastle Security Provider v1.12 Start encryption Finish encryption: Akjsdfp-9q8237nrcas-9de8fn239-4rb[*[OPOsjkdfJDL:JF;lkjs;ldj Start decryption Finish decryption: iNdf6D213$dcd(ndz!0) Start signature verification Signature verified

    数字签名代码示例:简单方法

    Signature类使用KeyPairGenerator类产生的密钥来操纵数字签名。 在下面的示例中使用以下方法:

    KeyPairGenerator.getInstance("RSA") .initialize(1024)和.generateKeyPair() :生成密钥。 Cipher.getInstance("MD5WithRSA") :创建Signature对象。 .initSign(key.getPrivate()) :初始化Signature对象。 .update(plainText)和.sign() :使用纯文本字符串计算签名。 .initVerify(key.getPublic())和.verify(signature) :验证签名。 import java.security.*; import javax.crypto.*; // // This example uses the digital signature features to generate and // verify a signature much more easily than the previous example public class DigitalSignature2Example { public static void main (String[] args) throws Exception { // // check args and get plaintext if (args.length !=1) { System.err.println("Usage: java DigitalSignature1Example text"); System.exit(1); } byte[] plainText = args[0].getBytes("UTF8"); // // generate an RSA keypair System.out.println( "\nStart generating RSA key" ); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(1024); KeyPair key = keyGen.generateKeyPair(); System.out.println( "Finish generating RSA key" ); // // get a signature object using the MD5 and RSA combo // and sign the plaintext with the private key, // listing the provider along the way Signature sig = Signature.getInstance("MD5WithRSA"); sig.initSign(key.getPrivate()); sig.update(plainText); byte[] signature = sig.sign(); System.out.println( sig.getProvider().getInfo() ); System.out.println( "\nSignature:" ); System.out.println( new String(signature, "UTF8") ); // // verify the signature with the public key System.out.println( "\nStart signature verification" ); sig.initVerify(key.getPublic()); sig.update(plainText); try { if (sig.verify(signature)) { System.out.println( "Signature verified" ); } else System.out.println( "Signature failed" ); } catch (SignatureException se) { System.out.println( "Signature failed" ); } } }

    样本执行

    Start generating RSA key Finish generating RSA key Sun JSSE provider(implements RSA Signatures, PKCS12, SunX509 key/trust factories, SSLv3, TLSv1) Signature: Ldkjahasdlkjfq[?owc42093nhasdk1a;sn;a#a;lksjd;fl@#kjas;ldjf78qwe09r7 Start signature verification Signature verified

    证明你就是你自己

    总览

    在本节中,我们将讨论数字证书,这是确定消息始发者身份的第二级。 我们将研究证书颁发机构及其扮演的角色。 我们将研究密钥和证书存储库以及管理工具(keytool和keystore),并讨论CertPath API,这是一组旨在构建和验证证书路径的功能。

    什么是数字证书?

    您可能已经注意到, 什么是数字签名?中描述的数字签名方案存在问题。 。 它证明消息是由给定的一方发送的,但是我们如何确定发件人确实是她说的那个人。 如果有人声称自己是爱丽丝并签署了一条消息,但实际上是阿曼达,该怎么办? 我们可以使用数字证书来提高我们的安全性, 数字证书将身份和公钥打包在一起,并由称为证书颁发机构或CA的第三方进行数字签名。

    证书颁发机构是一个组织,它在真实世界的物理意义上验证一方的身份,并使用CA私钥对该方的公钥和身份进行签名。 邮件收件人可以获取发件人的数字证书,并使用CA的公钥对其进行验证(或解密)。 这证明证书是有效的,并允许收件人提取发件人的公共密钥以验证其签名或向他发送加密的消息。 浏览器和JDK本身带有来自多个CA的内置证书及其公共密钥。

    JDK 1.4支持X.509数字证书标准。

    了解密钥工具和密钥库

    Java平台使用密钥库作为密钥和证书的存储库。 从物理上讲,密钥库是一个文件(可以选择将其加密),其默认名称为.keystore。 密钥和证书可以具有称为别名的名称,并且每个别名都可以由唯一的密码保护。 密钥库本身也受密码保护。 您可以选择使每个别名密码与主密钥库密码匹配。

    Java平台使用密钥工具操纵密钥库。 该工具提供了许多选项。 下面的示例( keytool示例 )显示了生成公共密钥对和相应证书并通过查询密钥库查看结果的基础。

    可以使用keytool将密钥导出到X.509格式的文件中,该文件可以由证书颁发机构签名,然后重新导入到密钥库中。

    还有一个特殊的密钥库,用于保存证书颁发机构(或任何其他受信任的)证书,依次包含用于验证其他证书的有效性的公共密钥。 此密钥库称为信任库。 Java语言在名为cacerts的文件中带有默认信任库。 如果搜索此文件名,则将至少找到这些文件中的两个。 您可以使用以下命令显示内容:

    keytool -list -keystore cacerts Use a password of "changeit"

    键盘工具示例

    在此示例中,使用默认密钥库.keystore ,我们使用RSA算法(别名为JoeUserKey)生成自签名证书,然后查看创建的证书。 我们将在代码签名的概念中使用此证书对JAR文件进行签名。

    D:\IBM>keytool -genkey -v -alias JoeUserKey -keyalg RSA Enter keystore password: password What is your first and last name? [Unknown]: Joe User What is the name of your organizational unit? [Unknown]: Security What is the name of your organization? [Unknown]: Company, Inc. What is the name of your City or Locality? [Unknown]: User City What is the name of your State or Province? [Unknown]: MN What is the two-letter country code for this unit? [Unknown]: US Is CN=Joe User, OU=Security, O="Company, Inc.", L=User City, ST=MN, C=US correct? [no]: y Generating 1,024 bit RSA key pair and self-signed certificate (MD5WithRSA) for: CN=Joe User, OU=Security, O="Company, Inc.", L=User City, ST=MN, C=US Enter key password for <JoeUserKey> (RETURN if same as keystore password): [Saving .keystore] D:\IBM>keytool -list -v -alias JoeUserKey Enter keystore password: password Alias name: JoeUserKey Creation date: Apr 15, 2002 Entry type: keyEntry Certificate chain length: 1 Certificate[1]: Owner: CN=Joe User, OU=Security, O="Company, Inc.", L=User City, ST=MN, C=US Issuer: CN=Joe User, OU=Security, O="Company, Inc.", L=User City, ST=MN, C=US Serial number: 3cbae448 Valid from: Mon Apr 15 09:31:36 CDT 2002 until: Sun Jul 14 09:31:36 CDT 2002 Certificate fingerprints: MD5: 35:F7:F7:A8:AC:54:82:CE:68:BF:6D:42:E8:22:21:39 SHA1: 34:09:D4:89:F7:4A:0B:8C:88:EF:B3:8A:59:F3:B9:65:AE:CE:7E:C9

    CertPath API

    认证路径API是JDK 1.4的新增功能。 它是用于构建和验证认证路径或链的一组功能。 这在SSL / TLS(请参阅什么是安全套接字层/传输层安全性? )和JAR文件签名验证之类的协议中隐式完成,但是现在可以在具有此支持的应用程序中显式完成。

    如什么是数字证书中所述? ,CA可以使用其私钥对证书进行签名,如果接收者持有的CA证书具有进行签名验证所需的公钥,则它可以验证签名证书的有效性。

    在这种情况下,证书链的长度为两个-信任锚(CA证书)和签名证书。 自签名证书的长度为一-信任的锚点是已签名证书本身。

    链的长度可以是任意的,因此在三链中,信任证书的CA锚可以签署中间证书; 该证书的所有者可以使用其私钥来签署另一个证书。 CertPath API可用于遍历证书链以验证有效性,以及构建这些信任链。

    证书具有到期日期,但是可以在到期之前受到损害,因此必须检查证书吊销列表 (CRL)以真正确保签名证书的完整性。 这些列表可在CA网站上找到,也可以使用CertPath API进行编程操作。

    特定的API和代码示例不在本教程的讨论范围之内,但是Sun除了API文档外还提供了几个代码示例。

    信任代码

    总览

    在本节中,我们将回顾代码签名的概念,重点是管理JAR文件认证的工具Jarsigner。

    代码签名的概念

    JAR文件与ZIP文件在Java平台上等效,允许将多个Java类文件打包为一个扩展名为.jar的文件。 然后可以对该JAR文件进行数字签名,以证明其中的类文件代码的起源和完整性。 JAR文件的接收者可以基于发送者的签名来决定是否信任该代码,并且可以确信在接收之前内容没有被篡改。 JDK附带了提供此功能的jarsigner工具。

    In deployment, access to machine resources can be based on the signer's identity by putting access control statements in the policy file.

    Jarsigner tool

    The jarsigner tool takes a JAR file and a private key and corresponding certificate as input, then generates a signed version of the JAR file as output. It calculates the message digests for each class in the JAR file and then signs these digests to ensure the integrity of the file and to identify the file owner.

    In an applet environment, an HTML page references the class file contained in a signed JAR file. When this JAR file is received by the browser, the signature is checked against any installed certificates or against a certificate authority public signature to verify validity. If no existing certificates are found, the user is prompted with a screen giving the certificate details and asking if the user wants to trust the code.

    Code signing example

    In this example, we first create a JAR file from a .class file and then sign it by specifying the alias for the certificate in the keystore that is used for the signing. We then run a verification check on the signed JAR file.

    D:\IBM>jar cvf HelloWorld.jar HelloWorld.class added manifest adding: HelloWorld.class(in = 372) (out= 269)(deflated 27%) D:\IBM>jarsigner HelloWorld.jar JoeUserKey Enter Passphrase for keystore: password D:\IBM>jarsigner -verify -verbose -certs HelloWorld.jar 137 Mon Apr 15 12:38:38 CDT 2002 META-INF/MANIFEST.MF 190 Mon Apr 15 12:38:38 CDT 2002 META-INF/JOEUSERK.SF 938 Mon Apr 15 12:38:38 CDT 2002 META-INF/JOEUSERK.RSA 0 Mon Apr 15 12:38:00 CDT 2002 META-INF/ smk 372 Mon Apr 15 12:33:02 CDT 2002 HelloWorld.class X.509, CN=Joe User, OU=Security, O="Company, Inc.", L=User City, ST=MN, C=US (joeuserkey) s = signature was verified m = entry is listed in manifest k = at least one certificate was found in keystore i = at least one certificate was found in identity scope jar verified.

    Code signing example execution

    Here is the HTML for this program:

    <HTML> <HEAD> <TITLE> Hello World Program </TITLE> </HEAD> <BODY> <APPLET CODE="HelloWorld.class" ARCHIVE="HelloWorld.jar" WIDTH=150 HEIGHT=25> </APPLET> </BODY> </HTML>

    When this example is executed with a browser that uses the Java plug-in as the Java virtual machine, a dialog box pops up asking if the user wants to install and run the signed applet distributed by "Joe User", and says that the publisher authenticity is verified by "Company, Inc.", but that the security was issued by a company that is not trusted. The security certificate has not expired and is still valid. It cautions that "Joe User" asserts that this content is safe and should only be installed or viewed if you trust "Joe User" to make that assertion. The user is given the following options:

    Grant this session 拒绝 Grant always View certificate

    SSL/TLS: Securing C/S communication

    总览

    In this section, we'll examine the building blocks of the Secure Sockets Layer (and its replacement, Transport Layer Security), the protocol used to authenticate the server to the client. We'll offer a few code examples as illustrations.

    What is Secure Sockets Layer/Transport Layer Security?

    Secure Sockets Layer (SSL) and its replacement, Transport Layer Security (TLS), is a protocol for establishing a secure communications channel between a client and a server. It is also used to authenticate the server to the client and, less commonly, used to authenticate the client to the server. It is usually seen in a browser application, where the lock at the bottom of the browser window indicates SSL/TLS is in effect.

    TLS 1.0 is the same as SSL 3.1.

    SSL/TLS uses a hybrid of three of the cryptographic building blocks already discussed in this tutorial, but all of this is transparent to the user. Here is a simplified version of the protocol:

    When a request is made to a site using SSL/TLS (usually with an https:// URL), a certificate is sent from the server to the client. The client verifies the identify of the server from this certificate using the installed public CA certificates, then checks that the IP name (machine name) matches the machine that the client is connected to. The client generates some random info that can be used to generate a private key for the conversation, known as a session key, and encrypts it with the server's public key and sends it to the server. The server decrypts the message with its private key and uses the random info to derive the same private session key as the client. The RSA public key algorithm is usually used for this phase. The client and server then communicate using the private session key and a private key algorithm, usually RC4. A message-authentication code , using yet another key, is used to ensure the integrity of the message.

    SSL/TLS code sample

    In this example, we write an HTTPS daemon process using an SSL server socket that returns an HTML stream when a browser connects to it. This example also shows how to generate a machine certificate in a special keystore to support the SSL deployment.

    In Java programming, the only thing that needs to be done is to use an SSL Server Socket Factory instead of a Socket Factory, using lines like the following:

    SSLServerSocketFacctory sslf = (SSLServerSocketFactor)SSLServerSocketFactory.getDefault(); ServerSocket serverSocket = sslf.createServerSocket(PORT);

    The complete code example is listed below:

    import java.io.*; import java.net.*; import javax.net.ssl.*; // // Example of an HTTPS server to illustrate SSL certificate and socket public class HTTPSServerExample { public static void main(String[] args) throws IOException { // // create an SSL socket using the factory and pick port 8080 SSLServerSocketFactory sslsf = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); ServerSocket ss = sslsf.createServerSocket(8080); // // loop forever while (true) { try { // // block waiting for client connection Socket s = ss.accept(); System.out.println( "Client connection made" ); // get client request BufferedReader in = new BufferedReader( new InputStreamReader(s.getInputStream())); System.out.println(in.readLine()); // // make an HTML response PrintWriter out = new PrintWriter( s.getOutputStream() ); out.println("<HTML><HEAD> <TITLE>HTTPS Server Example</TITLE> " + "</HEAD><BODY> <H1>Hello World!</H1> </BODY></HTML> \n"); // // Close the stream and socket out.close(); s.close(); } catch (Exception e) { e.printStackTrace(); } } } }

    HTTPS server sample execution

    In this example, we create an HTTPS server daemon that waits for a client browser connection and returns "Hello, World!". The browser connects to this daemon via https://localhost:8080 .

    We first create a machine certificate. The name must match the machine name of the computer where the daemon runs; in this case, localhost . In addition, we cannot use the same .keystore we have used in the past. We must create a separate keystore just for the machine certificate. In this case, it has the name sslKeyStore .

    D:\IBM>keytool -genkey -v -keyalg RSA -alias MachineCert -keystore sslKeyStore Enter keystore password: password What is your first and last name? [Unknown]: localhost What is the name of your organizational unit? [Unknown]: Security What is the name of your organization? [Unknown]: Company, Inc. What is the name of your City or Locality? [Unknown]: Machine Cert City What is the name of your State or Province? [Unknown]: MN What is the two-letter country code for this unit? [Unknown]: US Is CN=localhost, OU=Security, O="Company, Inc.", L=Machine Cert City, ST=MN, C=US correct? [no]: y Generating 1,024 bit RSA key pair and self-signed certificate (MD5WithRSA) for: CN=localhost, OU=Security, O="Company, Inc.", L=Machine Cert City, ST=MN, C=US Enter key password for <MachineCert> (RETURN if same as keystore password): [Saving sslKeyStore]

    Then, we start the server daemon process specifying the special keystore and its password:

    D:\IBM>java -Djavax.net.ssl.keyStore=sslKeyStore -Djavax.net.ssl.keyStorePassword=password HTTPSServerExample

    After waiting a few seconds, fire up a browser and point it to https://localhost:8080 and you should be prompted on whether or not to trust the certificate. Selecting "yes" should display "Hello World!", and clicking on the lock in Internet Explorer will give the certificate details.

    Wrapping up

    摘要

    This tutorial introduced the major cryptographic building blocks that can be used to provide a vast array of application security solutions. You've become familiar with such Java security topics as:

    Built-in features that facilitate secure programming (no pointers, a bytecode verifier, fine-grained control over resource access for both applets and applications, a large number of library functions for all the major cryptographic building blocks, and SSL). Secure programming techniques (proper storage and deletion of passwords and intelligent serialization). Features newly integrated in JDK 1.4 (JCE, JSSE, JAAS, JGSS, and CertPath API). Enriching, third-party security offerings .

    And the following concepts:

    Message digests Message authentication codes Private key cryptography Public key cryptography 数字签名 Digital certificates Certification authorities and paths Code signing SSL/TLS

    You should be well poised to explore Java security in more detail (see the Related topics section) and to take the next tutorial, Java security, Part 2: Authentication and authorization .


    翻译自: https://www.ibm.com/developerworks/java/tutorials/j-sec1/j-sec1.html

    Processed: 0.205, SQL: 9