admin管理员组

文章数量:1651294

在本章中,我们将探讨 Java 为保护应用程序之间的通信提供的支持。我们将研究几个主题,包括以下内容:

  • 基本加密过程
  • 使用密钥库存储密钥和证书
  • 向简单的服务器/客户端添加加密
  • 使用 TLS\SSL 保护客户端/服务器通信
  • 安全散列

安全

有许多与安全相关的术语,当它们第一次遇到时,它们的含义和目的可能会令人生畏。大多数这些术语都适用于网络应用程序。我们将从对其中许多术语的简要概述开始。在本章的后面部分,我们将详细介绍与我们的讨论相关的内容。

大多数安全相关问题的核心是加密。这是使用密钥或一组密钥将需要保护的信息转换为加密形式的过程。加密信息的接收者可以使用一个密钥或一组密钥来解密信息并将其恢复为原始形式。这种技术将防止对信息的未授权访问。

我们将演示对称和非对称加密技术的使用。对称加密使用单个密钥来加密和解密消息。非对称加密使用一对密钥。这些密钥经常存储在一个名为keystore的文件中,我们将对此进行演示。

对称加密通常更快,但要求加密数据的发送方和接收方以安全可靠的方式共享其密钥。对于远程分散的各方来说,这可能是一个问题。非对称加密速度较慢,但​​它使用公钥和私钥对,正如我们将看到的,简化了密钥的共享。非对称加密是数字证书的一种使能技术,它提供了一种验证文档真实性的方法。

安全商务很常见,对于每天在全球范围内发生的在线交易至关重要。在传输层安全TLS)和安全套接字层SSL)的协议,允许在互联网上安全可靠的通信。它是用于在 Internet 上进行大多数交易的超文本传输​​协议安全HTTPS )的基础。该协议支持以下内容:

  • 服务器和客户端身份验证
  • 数据加密
  • 数据的完整性

安全散列是一种用于创建证书的技术。甲证书用于验证数据的真实性,并且它使用一个散列值。Java 为这个过程提供了支持,我们将对此进行演示。

让我们首先简要介绍常见的网络安全术语,以提供本章的高级视角。在随后的部分中更详细地探讨了特定术语。

安全通信术语

在处理安全通信时使用了几个术语。这些包括以下内容:

  • 认证:这是的过程验证用户或系统
  • 授权:这是过程,允许访问受保护资源
  • 加密:这是对信息进行编码和随后解码的过程,以保护它免受未经授权的个人的侵害
  • 散列算法:这些提供了一种为文档生成唯一值的方法,它们用于支持其他安全技术
  • 数字签名:这些提供了一种对文档进行数字身份验证的方法
  • 证书:这些通常用作链,它们支持确认委托人和其他参与者的身份

认证和授权是相关的。身份验证是确定一个人或系统是否是他们声称的身份的过程。这通常是使用 ID 和密码来实现的。但是,还有其他身份验证技术(例如智能卡)和生物特征签名(例如指纹或虹膜扫描)。

授权是确定个人或系统可以访问哪些资源的过程。验证一个人是否就是他们所说的人是一回事。确保用户只能访问授权资源是另一回事。

加密已经发展并将继续改进。Java 支持对称和非对称加密技术。该过程从生成密钥开始,密钥通常存储在密钥库中。需要加密或解密数据的应用程序将访问密钥库以检索适当的密钥。密钥库本身需要受到保护,以免被篡改或以其他方式受到损害。

散列是获取数据并返回代表数据的数字的过程。哈希算法执行此操作,并且速度必须很快。然而,如果仅给出散列值,导出原始数据是极其困难的,如果不是不可能的话。这称为单向哈希函数。

这种技术的优点是数据可以与散列值一起发送到接收器。数据未加密,但哈希值使用一组非对称密钥加密。然后接收方可以使用原始散列算法来计算接收数据的散列值。如果这个新的散列值与发送的散列值匹配,那么接收方可以确信数据在传输中没有被修改或损坏。这提供了一种更可靠的数据传输方式,不需要加密,但可以保证数据未被修改。

证书是前一个过程的一部分,它使用散列函数和非对称密钥。甲证书链 提供验证的证书是有效的,假定链的根可以被信任的手段。

加密基础

在本节中,我们将研究 Java 如何支持对称和非对称加密。正如我们将看到的,有多种加密算法可用于这两种技术。

对称加密技术

对称加密使用单个密钥来加密和解密消息。这种类型的加密分为流密码或分组密码。有关这些算法的更多详细信息,访问https://en.wikipedia/wiki/Symmetric-key_algorithm。提供者提供加密算法的实现,我们经常在它们之间进行选择。

Java 支持的对称算法包括以下算法,其中以位为单位的密钥大小括在括号中:

  • AES (128)
  • DES (56)
  • DESede (168)
  • HmacSHA1
  • HmacSHA256

可以加密不同长度的数据。块密码算法用于处理大数据块。有几种分组密码操作模式,如下所列。我们不会在这里详细说明这些模式的工作原理,但可以在https://en.wikipedia/wiki/Block_cipher_mode_of_operation找到更多信息:

  • ECB
  • CBC
  • CFB
  • OFB
  • PCBC

在我们加密或解密数据之前,我们需要一个密钥。

生成密钥

生成密钥的常用方法是使用KeyGenerator类。该类没有公共构造函数,但重载getInstance方法将返回一个KeyGenerator实例。以下示例将 AES 算法与默认提供程序一起使用。此方法的其他版本允许选择提供者:

    KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");

generateKey方法返回一个对象的实例,该对象实现了SecretKey接下来显示的接口。这是用于支持对称加密和解密的密钥:

    SecretKey secretKey = keyGenerator.generateKey();

有了密钥,我们现在可以加密数据。

使用对称密钥加密文本

我们将encrypt在后面的部分中 使用以下方法。此方法传递要加密的文本和密钥。术语纯文本经常用于指代未加密的数据。

Cipher类提供了加密过程的框架。该getInstance方法返回使用 AES 算法的类的实例。该Cipher实例被初始化为加密使用Cipher.ENCRYPT_MODE作为第一个参数,密钥作为第二个参数。该doFinal方法加密纯文本字节数组并返回加密的字节数组。所述Base64类的getEncoder返回编码加密的字节的编码器:

    public static String encrypt(
            String plainText, SecretKey secretKey) {
        try {
            Cipher cipher = Cipher.getInstance("AES");
            byte[] plainTextBytes = plainText.getBytes();
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            byte[] encryptedBytes = 
                cipher.doFinal(plainTextBytes);
            Base64.Encoder encoder = Base64.getEncoder();
            String encryptedText = 
                encoder.encodeToString(encryptedBytes);
            return encryptedText;
        } catch (NoSuchAlgorithmException|NoSuchPaddingException | 
                InvalidKeyException | IllegalBlockSizeException | 
                BadPaddingException ex) {
            // Handle exceptions
        }
        return null;
    }

编码加密的字节数组用于将其转换为字符串,以便我们稍后使用。编码字符串可能是一种有用的安全技术,如http://javarevisited.blogspot.sg/2012/03/why-character-array-is-better-than.html 中所述

解密文本

解密文本的过程在下面显示的解密方法中说明。它使用反向过程,其中对加密的字节进行解码,并初始化Cipher类的init方法以使用密钥解密字节:

    public static String decrypt(String encryptedText, 
            SecretKey secretKey) {
        try {
            Cipher cipher = Cipher.getInstance("AES");
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] encryptedBytes = decoder.decode(encryptedText);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] decryptedBytes = 
                cipher.doFinal(encryptedBytes);
            String decryptedText = new String(decryptedBytes);
            return decryptedText;
        } catch (NoSuchAlgorithmException|NoSuchPaddingException | 
                InvalidKeyException | IllegalBlockSizeException | 
                BadPaddingException ex) {
            // Handle exceptions
        }
        return null;
    }

我们将在对称加密客户端/服务器部分中说明的回显客户端/服务器应用程序中使用这些方法。

非对称加密技术

非对称加密使用公钥和私钥。私钥由一个实体持有。公钥可供所有人使用。可以使用任一密钥加密数据:

  • 如果数据使用私钥加密,则可以使用公钥解密
  • 如果数据使用公钥加密,那么可以使用私钥解密

如果私钥的所有者发出用私钥加密的消息,则该消息的接收者可以用公钥对其进行解密。他们都可以阅读该消息,但他们知道只有私钥所有者才能发送此消息。

如果其他人使用公钥加密消息,则只有私钥所有者才能阅读该消息。但是,所有者无法确定是谁实际发送了消息。它可能是一个冒名顶替者。

但是,如果双方都有自己的一套公钥/私钥,我们可以保证只有发送方和接收方可以看到其内容。我们还可以保证发件人就是他们所说的那样。

假设Sue 想向 Bob 发送消息。Sue将使用她的私钥加密消息 M。我们将此消息称为 M1。然后她将使用 Bob 的公钥加密 M1 给我们 M2。然后将消息 M2 发送给 Bob。现在,只有 Bob 可以使用他的私钥解密此消息。这将返回 M1Bob 现在可以使用 Sue 的公钥解密 M1 以获取原始消息 M。他知道这是来自 Sue,因为只有 Sue 的公钥会起作用。

这个发送消息的过程要求两个参与者都拥有自己的公钥/私钥。除此之外,它不如使用对称密钥有效。另一种方法是使用非对称密钥将秘密密钥传输给参与者。然后可以将密钥用于实际的消息传输。这是与 SSL 一起使用的技术。

有几种非对称算法。Java 支持以下加密算法:

  • RSA
  • Diffie-Hellman
  • DSA

我们将使用AsymmetricKeyUtility接下来声明的实用程序类来演示非对称加密/解密。此类封装了创建、保存、加载和检索公钥和私钥的方法。我们将在此处解释这些方法的工作原理,并稍后在非对称回显客户端/服务器应用程序中使用它们:

public class AsymmetricKeyUtility {

    public static void savePrivateKey(PrivateKey privateKey) {
        ...
    }

    public static PrivateKey getPrivateKey() {
        ...
    }

    public static void savePublicKey(PublicKey publicKey) {
        ...
    }

    public static PublicKey getPublicKey() {
        ...
    }
    
    public static byte[] encrypt(PublicKey publicKey, 
            String message) { 
        ...
    }    
    
    public static String decrypt(PrivateKey privateKey, 
        byte[] encodedData) { 
        ...
    }
    
    public static void main(String[] args) {
        ...
    }
}

生成和保存非对称密钥

main方法将创建密钥、保存它们,然后测试它们以查看它们是否正常工作。该KeyPairGenerator方法将生成密钥。要使用非对称加密,我们使用 RSA 算法获取类的实例。该initialize方法指定密钥使用 1,024 位。该generateKeyPair方法生成的密钥,并且getPrivategetPublic方法分别返回私钥和公钥,:

     public static void main(String[] args) {
        try {
            KeyPairGenerator keyPairGenerator = 
                KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(1024);
            KeyPair keyPair = keyPairGenerator.generateKeyPair();
            PrivateKey privateKey = keyPair.getPrivate();
            PublicKey publicKey = keyPair.getPublic();
            ...
        } catch (NoSuchAlgorithmException ex) {
            // Handle exceptions
        }

我们将使用一组方法将这些密钥保存和检索到单独的文件中。这种方法不是最安全的,但它会简化回声客户端/服务器的使用。下面的语句调用 save 方法:

            savePrivateKey(privateKey);
            savePublicKey(publicKey);

用于检索密钥的方法在此处调用:

            privateKey = getPrivateKey();
            publicKey = getPublicKey();

下一个代码序列测试加密/解密过程。encrypt使用公钥创建消息并将其传递给方法。decrypt调用该方法来解密消息。该encodedData变量引用的加密数据:

            String message = "The message";
            System.out.println("Message: " + message);
            byte[] encodedData = encrypt(publicKey,message);
            System.out.println("Decrypted Message: " + 
                    decrypt(privateKey,encodedData));

这个例子的输出如下:

消息:消息

解密消息:消息

相反,我们可以使用私钥进行加密,使用公钥进行解密,以达到相同的结果。

使用非对称密钥加密/解密文本

现在,让我们来看看具体的encryptdecrypt方法。该encrypt方法用于getInstance获取 RSA 算法的实例。该init方法指定Cipher对象将使用公钥加密消息。该doFinal方法执行实际的加密并返回一个包含加密消息的字节数组:

    public static byte[] encrypt(PublicKey publicKey, String message) { 
        byte[] encodedData = null;
        try {
            Cipher cipher = Cipher.getInstance("RSA ");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            byte[] encryptedBytes = 
                cipher.doFinal(message.getBytes());
            encodedData = Base64.getEncoder().withoutPadding()
                .encode(encryptedBytes);
        } catch (NoSuchAlgorithmException|NoSuchPaddingException | 
                InvalidKeyException | IllegalBlockSizeException | 
                BadPaddingException ex) {
            // Handle exceptions
        }
        return encodedData;
    }

decrypt接下来Cipher描述该方法。它指定实例将使用私钥解密消息。传递给它的加密消息必须先解码,然后该doFinal方法才能对其进行解密。然后返回解密后的字符串:

    public static String decrypt(PrivateKey privateKey, 
            byte[] encodedData) { 
        String message = null;
        try {
            Cipher cipher = Cipher.getInstance("RSA ");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            byte[] decodedData = 
                Base64.getDecoder().decode(encodedData);
            byte[] decryptedBytes = cipher.doFinal(decodedData); 
            message = new String(decryptedBytes);
        } catch (NoSuchAlgorithmException|NoSuchPaddingException | 
                InvalidKeyException | IllegalBlockSizeException | 
                BadPaddingException ex) {
            // Handle exceptions
        }
        return message;
    }

这两种方法都可以捕获加密/解密过程中可能发生的许多异常。我们不会在这里讨论这些例外情况。

将非对称密钥保存到文件

接下来的两种方法说明了一种保存和检索私钥的技术。本PKCS8EncodedKeySpec类支持私有密钥的编码。编码后的密钥保存到private.key文件中:

    public static void savePrivateKey(PrivateKey privateKey) {
        try {
            PKCS8EncodedKeySpec pkcs8EncodedKeySpec = 
                new PKCS8EncodedKeySpec(privateKey.getEncoded());
            FileOutputStream fos = 
                new FileOutputStream("private.key");
            fos.write(pkcs8EncodedKeySpec.getEncoded());
            fos.close();
        } catch (FileNotFoundException ex) {
            // Handle exceptions
        } catch (IOException ex) {
            // Handle exceptions
        }
    }

getPrivateKey接下来描述的方法从文件中返回一个私钥。本KeyFactory类的generatePrivate方法创建一个基于关键PKCS8EncodedKeySpec指标:

    public static PrivateKey getPrivateKey() {
        try {
            File privateKeyFile = new File("private.key");
            FileInputStream fis = 
                new FileInputStream("private.key");
            byte[] encodedPrivateKey = 
                new byte[(int) privateKeyFile.length()];
            fis.read(encodedPrivateKey);
            fis.close();
            PKCS8EncodedKeySpec privateKeySpec = 
                new PKCS8EncodedKeySpec(encodedPrivateKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = 
                keyFactory.generatePrivate(privateKeySpec);
            return privateKey;
        } catch (FileNotFoundException ex) {
            // Handle exceptions
        } catch (IOException | NoSuchAlgorithmException | 
                 InvalidKeySpecException ex) {
            // Handle exceptions
        } 
        return null;
    }

公钥的 save get 方法在下面描述。它们在使用的文件和类的使用上有所不同。这个类代表公钥:X509EncodedKeySpec

    public static void savePublicKey(PublicKey publicKey) {
        try {
            X509EncodedKeySpec x509EncodedKeySpec = 
                new X509EncodedKeySpec(publicKey.getEncoded());
            FileOutputStream fos = 
                new FileOutputStream("public.key");
            fos.write(x509EncodedKeySpec.getEncoded());
            fos.close();
        } catch (FileNotFoundException ex) {
            // Handle exceptions
        } catch (IOException ex) {
            // Handle exceptions
        }
    }

    public static PublicKey getPublicKey() {
        try {
            File publicKeyFile = new File("public.key");
            FileInputStream fis = 
                new FileInputStream("public.key");
            byte[] encodedPublicKey = 
                new byte[(int) publicKeyFile.length()];
            fis.read(encodedPublicKey);
            fis.close();
            X509EncodedKeySpec publicKeySpec = 
                new X509EncodedKeySpec(encodedPublicKey);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = 
                keyFactory.generatePublic(publicKeySpec);
            return publicKey;
        } catch (FileNotFoundException ex) {
            // Handle exceptions
        } catch (IOException | NoSuchAlgorithmException | 
                 InvalidKeySpecException ex) {
            // Handle exceptions
        } 
        return null;
    }

标准加密算法名称可在https://docs.oracle/javase/8/docs/technotes/guides/security/StandardNames.html中找到。http://www.javamex/tutorials/cryptography/ciphers.shtml上提供了对称算法的性能比较。

创建密钥库

密钥库存储加密密钥和证书,并且经常与服务器和客户端结合使用。密钥库通常是一个文件,但也可以是硬件设备。Java 支持以下类型的密钥库条目:

  • PrivateKey:这用于非对称加密
  • 证书:这包含一个公钥
  • SecretKey:这用于对称加密

Java 8 支持五种不同类型的密钥库:JKSJCEKSPKCS12PKCS11 DKS

  • JKS:这是Java KeyStore ( JKS ),它的扩展名为. jks
  • JCEKS:这是Java 加密扩展密钥库( JCE )。它可以存储所有三种密钥库实体类型,为密钥提供更强的保护,并使用扩展。 jceks
  • PKCS12:与 JKS 和 JCEKS 相比,此密钥库可用于其他语言。它可以存储所有三种密钥库实体类型,并使用p12或的扩展名pfx
  • PKCS11:这是一种硬件密钥库类型。
  • DKS:这是域密钥库( DKS ),其中包含其他密钥库的集合。

Java 中的默认密钥库类型是 JKS。可以使用keytool命令行工具或 Java 代码创建和维护密钥库。我们先演示一下keytool

使用 keytool 创建和维护密钥库

keytool 是一个命令行程序,用于创建密钥库。其使用的完整文档可在https://docs.oracle/javase/8/docs/technotes/tools/unix/keytool.html 中找到。有几个 GUI 工具用于维护比 keytool 更易于使用的密钥库。其中之一是位于http://www-01.ibm/software/webservers/httpservers/doc/v1312/ibm/9atikeyu.htm 的IKEYMAN

要在命令提示符下在 Windows 中使用 keytool,您需要配置 PATH 环境变量以定位其包含目录。使用类似于以下的命令:

    C:\Some Directory>set path=C:\Program Files\Java\jdk1.8.0_25\bin;%path%

让我们使用 keytool 创建一个密钥库。在命令提示符下,输入以下命令。这将开始在名为 .key 的文件中创建密钥库的过程keystore.jks。别名是可用于引用密钥库的另一个名称:

    C:\Some Directory>keytool -genkey -alias mykeystore -keystore keystore.jks

然后将提示您输入以下几条信息。根据需要响应提示。您输入的密码将不会显示。对于本章中的示例,我们使用了密码password

输入密钥库密码:

重新输入新密码:

您的名字和姓氏是什么?

  [Unknown]: some name

你的组织单位

叫什么名字?  [未知]:开发

贵组织的名称是什么?

  [未知]:mycom

您所在城市或地区的名称是什么?

  [未知]:某个城市

您所在的州或省的名称是什么?

  [Unknown]: some state

这个单位的两个字母的国家代码是什么?

  [未知]:jv

然后将提示您确认输入,如下所示。yes如果值正确,请回复:

CN=some name, OU=development, O=mycom, L=some city, ST=some state, C=jv 是否正确?

  [否]:是

您可以为密钥分配一个单独的密码,如下所示:

输入 <mykeystore> 的

        密钥密码(如果与密钥库密码相同,则返回):

然后创建密钥库。可以使用–list参数显示密钥库的内容,如下所示。该–v选项会产生详细的输出:

keytool -list -v -keystore keystore.jks -alias mykeystore

这将显示以下输出。需要输入密钥库密码和别名:

输入密钥库密码:

别名:mykeystore

创建日期:2015 年 10 月 22 日

条目类型:PrivateKeyEntry

证书链长度:1

证书[1]:

所有者:CN=some name, OU=development, O=mycom, L=some city , ST=some state, C=jv

发行人:CN=some name, OU=development, O=mycom, L=some city, ST=some state, C=jv

序列号:39f2e11e

有效期:10 月 22 日星期四 18 :11:21 CDT 2015 直到: Wed Jan 20 17:11:21 CST 2016

证书指纹:

         MD5: 64:44:64:27:85:99:01:22:49:FC:41:DA:F7:A8 :4C:35

         SHA1: 48:57:3A:DB:1B:16:92:E6:CC:90:8B:D3:A7:A3:89:B3:9C:9B:7C:BB

         SHA256: B6:B2 :22:A0:64:61:DB:53:33:04:78:77:38:AF:D2:A0:60:37:A6:CB:3F:

3C:47:CC:30:5F:02:86:8F:68:84:7D

         签名算法名称:SHA1withDSA

         版本:3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false

SubjectKeyIdentifier [

KeyIdentifier [

0000: 07 D9 51 BE A7 48 23 34 5F 8E C6 F9 88 C0 36 CA ..Q..H#4_...6.

0010: 27 8E 04 22 '.."

]

]

Keytool 命令行参数

输入密钥库的信息可能很乏味。简化此过程的一种方法是使用命令行参数。以下命令将创建以前的密钥库:

keytool -genkeypair -alias mykeystore -keystore keystore.jks -keypass password -storepass password -dname "cn=some name, ou=development, o=mycom, l=some city, st=some state c=jv

您会注意到命令行末尾没有匹配的双引号。不需要。命令行参数记录在前面列出的 keytool 网站上。

此工具可以创建对称和非对称密钥以及证书。以下一系列命令演示了其中几种类型的操作。我们将为一对非对称密钥创建一个密钥库。然后将导出可用于服务器和客户端应用程序的一对证书。

此命令将serverkeystore.jck使用 RSA 算法创建密钥库文件,密钥大小为 1,024 位,有效期为 365 天:

keytool -genkeypair -alias server -keyalg RSA -keysize 1024 -storetype jceks -validity 365 -keypass password -keystore serverkeystore.jck -storepass password -dname "cn=localhost, ou=Department, o=MyComp Inc, l=Some City, st=合资企业 c=美国

此命令生成clientkeystore.jck供客户端应用程序使用的密钥库:

keytool -genkeypair -alias client -keyalg RSA -keysize 1024 -storetype jceks -validity 365 -keypass password -keystore clientkeystore.jck -storepass password -dname "cn=localhost, ou=Department, o=MyComp Inc, l=Some City, st=合资企业 c=美国

接下来为客户端创建一个证书文件并将其放置在client.crt文件中:

keytool -export -alias client -storetype jceks -keystore clientkeystore.jck -storepass password -file client.crt

服务器的证书在此处导出:

keytool -export -alias server -storetype jceks -keystore serverkeystore.jck -storepass password -file server.crt

信任库是用于验证凭据的文件,而密钥库将生成凭据。凭证通常采用证书的形式。信任存储通常持有来自受信任第三方的证书以形成证书链。

以下命令创建the clienttruststore.jck文件,这是客户端的信任库:

keytool -importcert -alias server -file server.crt -keystore clienttruststore.jck -keypass password -storepass storepassword

此命令生成以下输出:

所有者:CN=localhost, OU=Department, O=MyComp Inc, L=Some City, ST="JV c=US"

发行人:CN=localhost, OU=Department, O=MyComp Inc, L=Some City, ST= “JV c=US”

序列号:2d924315

有效期:2015 年 10 月 20 日星期二 19:26:00 CDT 至:2016 年 10 月 19 日星期三 19:26:00 CDT

证书指纹:

         MD5:9E:3D:0E:D7:02: 7A:F5:23:95:1E:24:B0:55:A9:F7:95

         SHA1: 69:87:CE:EE:11:59:8F:40:A8:14:DA:D3:92:D0 :3F:B6:A9:5A:7B:53

         SHA256: BF:C1:7B:6D:D0:39:67:2D:1C:68:27:79:31:AA:B8:70:2B:FD: 1C:85:18:

EC:5B:D7:4A:48:03:FA:F1:B8:CD:4E

         签名算法名称:SHA256withRSA

         版本:3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false

SubjectKeyIdentifier [

KeyIdentifier [

0000: D3 63 C9 60 6D 04 49 75 FB E8 F7 90 30 1D C6 C1 .c.`m.Iu....0...

0010: 10 DF 00 CF ....

]

]

信任这个证书?[no]: yes

证书已添加到密钥库

服务器的信任库是使用以下命令创建的:

keytool -importcert -alias client -file client.crt -keystore servertruststore.jck -keypass password -storepass password

其输出如下:

所有者:CN=localhost, OU=Department, O=MyComp Inc, L=Some City, ST="JV c=US"

发行人:CN=localhost, OU=Department, O=MyComp Inc, L=Some City, ST= “JV c=US”

序列号:5d5f3c40

有效期自:2015 年 10 月 20 日星期二 19:27:31 CDT 至:2016 年 10 月 19 日星期三 19:27:31 CDT

证书指纹:

         MD5:0E:FE:B3:EB:1B: D2:AD:32:9C:BC:FB:43:40:85:C1:A7

         SHA1: 90:14:1E:17:DF:51:79:0B:1E:A3:EC:38:6B:BA :A6:F4:6F:BF:B6:D2

         SHA256: 7B:3E:D8:2C:04:ED:E5:52:AE:B4:00:A8:63:A1:13:A7:E1:8E: 59:63:E8:

86:38:D8:09:55:EA:3A:7C:F7:EC:4B

         签名算法名称:SHA256withRSA

         版本:3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false

SubjectKeyIdentifier [

KeyIdentifier [

0000: D9 53 34 3B C0 11 F8 75 0F 18 4E 18 23 A2 47 FE .S4;...u..N.#.G.

0010:E6 F5 C1 AF ....

]

]

信任这个证书?[no]: yes

证书已添加到密钥库

我们现在将演示如何在 Java 中执行类似的操作。

使用 Java 创建和维护密钥库

可以直接使用 Java代码创建密钥库、它们的密钥和证书。在本节中,我们将演示如何创建包含密钥的密钥库。我们将在对称加密客户端/服务器部分使用这个类。

SymmetricKeyStoreCreation类被声明如下。该SymmetricKeyStoreCreation方法创建一个密钥库,而该main方法生成并存储密钥:

public class SymmetricKeyStoreCreation {

    private static KeyStore createKeyStore(String fileName, 
            String pw) {
        ...
    }

    public static void main(String[] args) {
        ...
    }
}

createKeyStore接下来描述该方法。它传递了密钥库的文件名和密码。KeyStore创建了一个实例,它指定了一个 JCEKS 密钥库。如果密钥库已经存在,它将返回该密钥库:

 

    private static KeyStore createKeyStore(String fileName, 
            String password) {
        try {
            File file = new File(fileName);
            
            final KeyStore keyStore = 
                KeyStore.getInstance("JCEKS");
            if (file.exists()) {
                keyStore.load(new FileInputStream(file), 
                    password.toCharArray());
            } else {
                keyStore.load(null, null);
                keyStore.store(new FileOutputStream(fileName), 
                    password.toCharArray());
            }
            return keyStore;
        } catch (KeyStoreException | IOException | 
                NoSuchAlgorithmException | 
                CertificateException ex) {
            // Handle exceptions
        }
        return null;
    }

在该main方法中,KeyGenerator使用 AES 算法创建一个实例。该generateKey方法将创建SecretKey实例,如下所示:

    public static void main(String[] args) {
        try {
            final String keyStoreFile = "secretkeystore.jks";
            KeyStore keyStore = createKeyStore(keyStoreFile, 
                "keystorepassword");
            KeyGenerator keyGenerator = 
                KeyGenerator.getInstance("AES");
            SecretKey secretKey = keyGenerator.generateKey();
            ...
        } catch (Exception ex) {
            // Handle exceptions
        }
    }

KeyStore.SecretKeyEntry类表示密钥库的条目。我们需要这个和一个代表密码的KeyStore.PasswordProtection类的实例来存储密钥:

            KeyStore.SecretKeyEntry keyStoreEntry
                    = new KeyStore.SecretKeyEntry(secretKey);
            KeyStore.PasswordProtection keyPassword = 
                new  KeyStore.PasswordProtection(
                        "keypassword".toCharArray());

setEntry方法使用字符串别名、密钥库条目对象和密码来存储条目,如下所示:

            keyStore.setEntry("secretKey", keyStoreEntry, 
                keyPassword);

然后将此条目写入密钥库:

            keyStore.store(new FileOutputStream(keyStoreFile),
                    "keystorepassword".toCharArray());

使用 Java 可以进行其他密钥库操作。

对称加密客户端/服务器

本节演示如何在客户端/服务器应用程序中使用对称加密/解密。下面的示例实现了一个简单的 echo 客户端/服务器,使我们可以专注于基本过程,而不会偏离特定的客户端/服务器问题。服务器使用SymmetricEchoServer类实现,客户端使用SymmetricEchoClient类实现。

客户端将加密消息并将其发送到服务器。然后服务器将解密该消息并以纯文本形式将其发回。如果需要,可以轻松地对响应进行加密。这种单向加密足以说明基本过程。

Windows 中运行本章中讨论的应用程序时,您可能会遇到以下对话框。选择允许访问按钮以允许应用程序运行:

我们还将使用SymmetricKeyStoreCreation在对称加密技术中开发的类。

对称服务器应用

接下来声明对称服务器。它拥有一个maindecryptgetSecretKey方法。该decrypt方法从客户端获取加密消息并对其进行解密。该getSecretKey方法将从以对称加密技术创建的密钥库中提取密钥。该main方法包含用于与客户端通信的基本套接字和流:

public class SymmetricEchoServer {
    private static Cipher cipher;

    public static String decrypt(String encryptedText, 
        SecretKey secretKey) {
        ...
    }

    private static SecretKey getSecretKey() {
        ...
    }

    public static void main(String[] args) {
        ...
    }
}

decrypt方法与对称加密技术中开发的方法相同,此处不再赘述。getSecretKey接下来描述该方法。secretkeystore.jks以对称加密技术创建的文件保存着密钥。此方法使用许多mainSymmetricKeyStoreCreation该类的方法中使用的相同类。KeyStore.PasswordProtection该类的一个实例用于从密钥库中提取密钥。密钥库密码keystorepassword被硬编码到应用程序中。这不是最佳实践,但它简化了示例:

    private static SecretKey getSecretKey() {
        SecretKey keyFound = null;
        try {
            File file = new File("secretkeystore.jks");
            final KeyStore keyStore = 
                KeyStore.getInstance("JCEKS");
            keyStore.load(new FileInputStream(file),
                    "keystorepassword".toCharArray());
            KeyStore.PasswordProtection keyPassword = 
                new KeyStore.PasswordProtection(
                        "keypassword".toCharArray());
            KeyStore.Entry entry = 
                keyStore.getEntry("secretKey", keyPassword);
            keyFound = 
                ((KeyStore.SecretKeyEntry) entry).getSecretKey();
        } catch (KeyStoreException | IOException | 
                NoSuchAlgorithmException | 
                CertificateException ex) {
            // Handle exceptions
        } catch (UnrecoverableEntryException ex) {
            // Handle exceptions;
        } 
        return keyFound;
    }

main方法是非常相似,在开发服务器第1章入门网络编程。主要区别在于 while 循环内。来自客户端的输入decrypt与密钥一起传递给方法,如下所示。然后显示解密的文本并返回给客户端:

            String decryptedText = decrypt(inputLine, 
                getSecretKey());

main方法如下:

    public static void main(String[] args) {
        System.out.println("Simple Echo Server");
        try (ServerSocket serverSocket = new ServerSocket(6000)) {
            System.out.println("Waiting for connection.....");

            Socket clientSocket = serverSocket.accept();
            System.out.println("Connected to client");

            try (BufferedReader br = new BufferedReader(
                new InputStreamReader(
                    clientSocket.getInputStream()));
                    PrintWriter out = new PrintWriter(
                        clientSocket.getOutputStream(), true)) {
                String inputLine;
                while ((inputLine = br.readLine()) != null) {
                    String decryptedText = 
                        decrypt(inputLine, getSecretKey());
                    System.out.println("Client request: " + 
                        decryptedText);
                    out.println(decryptedText;
                }

            } catch (IOException ex) {
                // Handle exceptions
            } catch (Exception ex) {
                // Handle exceptions
            }
        } catch (IOException ex) {
            // Handle exceptions
        }
        System.out.println("Simple Echo Server Terminating");
    }

现在,让我们检查客户端应用程序。

对称客户端应用程序

客户端应用程序描述未来,是非常相似的是在开发客户端应用程序第1章入门网络编程。它使用getSecretKey在服务器中使用的相同方法。encrypt对称加密技术中解释的方法用于加密用户的消息。这两种方法在这里都没有重复:

public class SymmetricEchoClient {
    private static Cipher cipher;

    public static String encrypt(String plainText, 
            SecretKey secretKey) {
        ...
    }

        ...
    }

    public static void main(String args[]) {
        ...
    } 
}

mainwhile循环的版本不同方法第1章入门网络编程。以下语句加密用户消息:

            String encryptedText = encrypt(inputLine, 
                getSecretKey());

main方法如下:

    public static void main(String args[]) {
        System.out.println("Simple Echo Client");

        try (Socket clientSocket
                = new Socket(InetAddress.getLocalHost(), 6000);
                PrintWriter out = new PrintWriter(
                        clientSocket.getOutputStream(), true);
                BufferedReader br = new BufferedReader(
                        new InputStreamReader(
                                clientSocket.getInputStream()))) {
            System.out.println("Connected to server");
            Scanner scanner = new Scanner(System.in);

            while (true) {
                System.out.print("Enter text: ");
                String inputLine = scanner.nextLine();
                if ("quit".equalsIgnoreCase(inputLine)) {
                    break;
                }
                String encryptedText = 
                    encrypt(inputLine, getSecretKey());
                System.out.println(
                    "Encrypted Text After Encryption: "
                    + encryptedText);
                out.println(encryptedText);

                String response = br.readLine();
                System.out.println(
                    "Server response: " + response);
            }
        } catch (IOException ex) {
            // Handle exceptions
        } catch (Exception ex) {
            // Handle exceptions
        }
    }

我们现在准备好看看客户端和服务器是如何交互的。

运行中的对称客户端/服务器

应用程序的行为方式与它们在第 1 章网络编程入门中的行为方式相同。唯一的区别是发送到服务器的消息是加密的。除了在客户端显示加密文本外,此加密在应用程序的输出中不可见。一种可能的交互如下。服务器输出首先显示:

Simple Echo Server

Waiting for connection.....

Connected to client

Client request: The first message

Client request: The second message

Simple Echo Server Terminating

以下是客户端的应用程序输出:

Connected to server

Enter text: The first message

Encrypted Text After Encryption: drkvP3bhnfMXrZluFiqKb0RgjoDqFIJMCo97YqqgNuM=

Server response: drkvP3bhnfMXrZluFiqKb0RgjoDqFIJMCo97YqqgNuM=

Enter text: The second message

Encrypted Text After Encryption: fp9g+AqsVqZpxKMVNx8IkNdDcr9IGHb/qv0qrFinmYs=

Server response: fp9g+AqsVqZpxKMVNx8IkNdDcr9IGHb/qv0qrFinmYs=

Enter text: quit

我们现在将使用非对称密钥复制此功能。

非对称加密客户端/服务器

AsymmetricKeyUtility在非对称加密技术中开发的类用于支持客户端和服务器应用程序。我们将使用它encryptdecrypt方法。客户端和服务器应用程序的结构与前几节中使用的结构相似。客户端将向服务器发送一条加密消息,服务器将对其进行解密,然后以纯文本进行响应。

非对称服务器应用

AsymmetricEchoServer如下声明的类用于服务器。该main方法是它的唯一方法。创建了一个服务器套接字,它阻塞在accept等待客户端请求的方法上:

public class AsymmetricEchoServer {

    public static void main(String[] args) {
        System.out.println("Simple Echo Server");
        try (ServerSocket serverSocket = new ServerSocket(6000)) {
            System.out.println("Waiting for connection.....");
            Socket clientSocket = serverSocket.accept();
            System.out.println("Connected to client");
            ...

        } catch (IOException | NoSuchAlgorithmException | 
                 NoSuchPaddingException ex) {
            // Handle exceptions
        }
        System.out.println("Simple Echo Server Terminating");
    }
}

在接受客户端连接 IO 后,将建立流并inputLine实例化一个大小为的字节数组171。这是正在发送的消息的大小,使用此值将避免各种异常:

            try (DataInputStream in = new DataInputStream(
                    clientSocket.getInputStream());
                    PrintWriter out = new PrintWriter(
                         clientSocket.getOutputStream(), true);) {
                byte[] inputLine = new byte[171];
                ...
                }
            } catch (IOException ex) {
                // Handle exceptions
            } catch (Exception ex) {
                // Handle exceptions
            }

为了执行解密,我们需要一个私钥。这是使用以下getPrivateKey方法获得的:

                PrivateKey privateKey = 
                    AsymmetricKeyUtility.getPrivateKey();

while 循环将从客户端读取加密消息。decrypt使用消息和私钥调用该方法。然后显示解密的消息并将其发送回客户端。如果消息是quit,则服务器终止:

                while (true) {
                    int length = in.read(inputLine);
                    String buffer = AsymmetricKeyUtility.decrypt(
                        privateKey, inputLine);
                    System.out.println(
                        "Client request: " + buffer);

                    if ("quit".equalsIgnoreCase(buffer)) {
                        break;
                    }
                    out.println(buffer);

现在,让我们检查客户端应用程序。

非对称客户端应用程序

客户端应用程序位于AsymmetricEchoClient类中,如下所示。它也只有一种main方法。建立服务器连接后,将建立 IO 流:

public class AsymmetricEchoClient {

    public static void main(String args[]) {
        System.out.println("Simple Echo Client");

        try (Socket clientSocket
                = new Socket(InetAddress.getLocalHost(), 6000);
                DataOutputStream out = new DataOutputStream(
                        clientSocket.getOutputStream());
                BufferedReader br = new BufferedReader(
                        new InputStreamReader(
                                clientSocket.getInputStream()));
                DataInputStream in = new DataInputStream(
                        clientSocket.getInputStream())) {
            System.out.println("Connected to server");
            ...
            }
        } catch (IOException ex) {
            // Handle exceptions
        } catch (Exception ex) {
            // Handle exceptions
        }
    }
}

Scanner班是用来获取用户输入。公钥用于加密用户消息,并使用AsymmetricKeyUtility类的getPublicKey方法获得:

            Scanner scanner = new Scanner(System.in);
            PublicKey publicKey = 
                AsymmetricKeyUtility.getPublicKey();

在接下来的 while 循环中,会提示用户输入一条消息,该消息使用该encrypt方法进行加密。然后将加密的消息发送到服务器。如果消息是quit,则程序终止:

            while (true) {
                System.out.print("Enter text: ");
                String inputLine = scanner.nextLine();

                byte[] encodedData = 
                    AsymmetricKeyUtility.encrypt(
                        publicKey, inputLine);
                System.out.println(encodedData);
                
                out.write(encodedData);
                if ("quit".equalsIgnoreCase(inputLine)) {
                    break;
                }
                String message = br.readLine();
                System.out.println("Server response: " + message);

现在,我们可以一起使用这些应用程序。

非对称客户端/服务器在行动

启动服务器,然后启动客户端。客户端将提示输入一系列消息。下面显示了一种可能的交换的输出。首先显示服务器端:

Simple Echo Server

Waiting for connection.....

Connected to client

Client request: The first message

Client request: The second message

Client request: quit

Simple Echo Server Terminating

下面显示了客户端交互:

Simple Echo Client

Connected to server

Enter text: The first message

[B@6bc168e5

Server response: The first message

Enter text: The second message

[B@7b3300e5

Server response: The second message

Enter text: quit

[B@2e5c649

TLS/SSL

TLS/SSL是一组用于保护 Internet 上许多服务器的协议。SSL TLS 的继承者。然而,它们并不总是可以互换的。SSL 使用消息验证码MAC )算法,而 TLS 使用消息验证码哈希HMAC )算法。

SSL 通常与许多其他协议一起使用,包括文件传输协议FTP )Telnet网络新闻传输协议NNTP )轻型目录访问协议LDAP ) 交互式消息访问协议IMAP )

TLS/SSL 在提供这些功能时确实会导致性能下降。然而,随着互联网速度的提高,这种打击通常并不显着。

当使用 HTTPS 协议时,用户会知道,因为该协议通常存在于浏览器的地址字段中。它甚至用于您可能意想不到的地方,例如以下 Google URL

我们不会深入研究 SSL 协议如何工作的细节。但是,可以在http://www.javacodegeeks/2013/04/understanding-transport-layer-security-secure-socket-layer.html找到对该协议的简要讨论。在本节中,我们将说明如何创建和使用 SSL 服务器以及用于支持此协议的 Java 类。

为了简化应用程序,客户端向服务器发送一条消息,然后服务器将其显示出来。没有响应被发送回客户端。客户端使用 SSL 连接到服务器并与之通信。使用 SSL 将消息返回给客户端作为练习留给读者。

SSL服务器

服务器在以下SSLServer类中实现。所有代码都可以在main方法中找到。我们将使用keystore.jks密钥库访问以对称加密技术创建的密钥。为了提供对密钥库的访问,使用一个Provider实例来指定密钥库及其密码。在代码中硬编码密码不是一个好主意,但它用于简化此示例:

public class SSLServer {

    public static void main(String[] args) throws Exception {
        System.out.println("SSL Server Started");
        Security.addProvider(new Provider());
        System.setProperty("javax.ssl.keyStore", 
            "keystore.jks");
        System.setProperty("javax.ssl.keyStorePassword", 
            "password");
        ...

    }

}

SSLServerSocket该类的一个实例用于在客户端和服务器之间建立通信。这个实例是使用SSLServerSocketFactory类的getDefault方法创建的。与之前的服务器套接字类似,该accept方法会阻塞,直到建立客户端连接:

        SSLServerSocketFactory sslServerSocketfactory =
            (SSLServerSocketFactory) 
            SSLServerSocketFactory.getDefault();
        SSLServerSocket sslServerSocket = (SSLServerSocket) 
                sslServerSocketfactory.createServerSocket(5000);
        System.out.println("Waiting for a connection");
        SSLSocket sslSocket = 
            (SSLSocket) sslServerSocket.accept();
        System.out.println("Connection established");

BufferedReader然后从套接字的输出流创建一个实例:

        PrintWriter pw = 
            new PrintWriter(sslSocket.getOutputStream(), true);
        BufferedReader br = new BufferedReader(
            new InputStreamReader(sslSocket.getInputStream()));

下面的 while 循环读取客户端请求并显示它。如果消息是quit,则服务器终止:

        String inputLine;
        while ((inputLine = br.readLine()) != null) {
            pw.println(inputLine);
            if ("quit".equalsIgnoreCase(inputLine)) {
                break;
            }
            System.out.println("Receiving: " + inputLine);
        }

SSL 套接字自动处理加密和解密。

笔记

Mac 上,服务器在执行时可能会抛出异常。这可以通过创建 PKCS12 密钥库并使用该-Djavax.ssl.keyStoreType=pkcs12 VM选项来避免。

SSL客户端

SSLClient类实现客户端应用程序,下一个,如图所示。它使用与服务器基本相同的过程。while 循环以与以前的客户端应用程序中执行的方式相同的方式处理用户输入:

public class SSLClient {

    public static void main(String[] args) throws Exception {
        System.out.println("SSL Client Started");
        Security.addProvider(new Provider());
        System.setProperty("javax.ssl.trustStore", 
            "keystore.jks");
        System.setProperty("javax.ssl.trustStorePassword", 
            "password");

        SSLSocketFactory sslsocketfactory = (SSLSocketFactory) 
            SSLSocketFactory.getDefault();
        SSLSocket sslSocket = (SSLSocket) 
            sslsocketfactory.createSocket("localhost", 5000);
        System.out.println(
            "Connection to SSL Server Established");

        PrintWriter pw = 
            new PrintWriter(sslSocket.getOutputStream(), true);
        BufferedReader in = new BufferedReader(
            new InputStreamReader(sslSocket.getInputStream()));

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("Enter a message: ");
            String message = scanner.nextLine();
            pw.println(message);
            System.out.println("Sending: " + in.readLine());
            if ("quit".equalsIgnoreCase(message)) {
                break;
            }
        }
        pw.close();
        in.close();
        sslSocket.close();
    }
}

让我们看看他们是如何互动的。

SSL 客户端/服务器运行中

启动服务器,然后启动客户端。在以下输出中,三个消息被发送到服务器,然后显示:

SSL Server Started

Waiting for a connection

Connection established

Receiving: The first message

Receiving: The second message

客户端输入如下所示:

SSL Client Started

Connection to SSL Server Established

Enter a message: The first message

Sending: The first message

Enter a message: The second message

Sending: The second message

Enter a message: quit

Sending: quit

SSLServerSocket类提供执行支持SSL的服务器的简单方法。

安全哈希函数

当给定某种文档时,安全散列函数将生成一个很大的数字,称为散列值。该文档几乎可以是任何类型。我们将在示例中使用简单的字符串。

该函数是一种单向哈希函数,这意味着在给定哈希值时实际上不可能重新创建文档。当与非对称密钥结合使用时,它允许在保证文档未被更改的情况下传输文档。

文档的发送者将使用安全散列函数来生成文档的散列值。发件人将用他们的私钥加密这个哈希值。然后将文档和密钥组合并发送到接收器。文档未加密。

收到文件后,接收方将使用发送方的公钥解密哈希值。然后,接收方将对文档使用相同的安全散列函数来获得散列值。如果该哈希值与解密的哈希值匹配,则保证接收方文档未被修改。

目的不是加密文档。虽然可能,但当向第三方隐藏文档并不重要而仅提供文档未被修改的保证时,此方法很有用。

Java 支持以下散列算法:

  • MD5 : 默认大小为 64 字节
  • SHA1:默认大小为 64 字节

我们将在示例中使用 SHA 哈希函数。这一系列功能是由美国国家安全局NSA)开发的。此哈希函数有三个版本:SHA-0SHA-1 SHA-2SHA-2 是更安全的算法,并使用可变摘要大小:SHA-224SHA-256SHA-384 SHA-512

MessageDigest类可与任意大小的数据生成固定大小的哈希值。这个类没有公共构造函数。getInstance当给定算法名称时,该方法返回类的一个实例。有效名称可在http://docs.oracle/javase/8/docs/technotes/guides/security/StandardNames.html#MessageDigest中找到。在这个例子中,我们使用SHA-256

    MessageDigest messageDigest =
        MessageDigest.getInstance("SHA-256");
    messageDigest.update(message.getBytes());

完整的示例,改编自http://www.mkyong/java/java-sha-hashing-example/,如下所示。该displayHashValue方法提取单个哈希值字节并将它们转换为可打印格式:

public class SHAHashingExample {

    public static void main(String[] args) throws Exception {
        SHAHashingExample example = new SHAHashingExample();
        String message = "This is a simple text message";
        byte hashValue[] = example.getHashValue(message);
        example.displayHashValue(hashValue);
    }

    public void displayHashValue(byte hashValue[]) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < hashValue.length; i++) {
            builder.append(Integer.toString((hashValue[i] & 0xff) 
                + 0x100, 16).substring(1));
        }
        System.out.println("Hash Value: " + builder.toString());
    }

    public byte[] getHashValue(String message) {
        try {
            MessageDigest messageDigest = 
                MessageDigest.getInstance("SHA-256");
            messageDigest.update(message.getBytes());
            return messageDigest.digest();
        } catch (NoSuchAlgorithmException ex) {
            // Handle exceptions
        }
        return null;
    }
}

执行程序。这将产生以下输出:

哈希值:83c660972991049c25e6cad7a5600fc4d7c062c097b9a75c1c4f13238375c26c

可以在http://howtodoinjava/2013/07/22/how-to-generate-secure-password-hash-md5-sha-pbkdf2-bcrypt 中找到对用 Java 实现的安全散列函数的更详细的检查-例子/

概括

在本章中,我们介绍了几种 Java 方法来保护应用程序之间的通信。我们首先简要介绍了与安全相关的术语,然后在介绍之后进行了更详细的讨论。

目前使用两种常见的加密/解密方法。第一种是对称密钥加密,它使用在应用程序之间共享的单个密钥。这种方法要求以安全的方式在应用程序之间传输密钥。

第二种方法使用非对称加密。该技术使用私钥和公钥。用这些密钥之一加密的消息可以用另一个密钥解密。通常,公钥是使用来自可信来源的证书分发的。私钥的持有者需要保护它,以便其他人无法访问它。公钥可以与任何需要它的人自由共享。

加密密钥通常存储在允许以编程方式访问密钥的密钥库中。密钥库是使用 keytool 应用程序创建和维护的。我们演示了在我们的几个应用程序中创建和使用密钥库。此外,我们使用对称密钥和非对称密钥对来支持回显客户端/服务器应用程序。

创建安全客户端和服务器的更常见方法是使用SSLServerSocket该类。这根据在密钥库中找到的秘密密钥执行数据的自动加密和解密。我们演示了如何在服务器和客户端应用程序中使用该类。

我们还研究了安全散列函数的使用。这种技术允许传输未加密的数据并保证它没有被修改。非对称密钥对用于加密散列值。我们提供了此过程的一个简单示例。

在下一章中,我们将研究影响分布式应用程序之间交互的各种因素。

本文标签: 网络安全