Documentation

The Java™ Tutorials
Hide TOC
Weaknesses and Alternatives弱点和替代方案
Trail: Security Features in Java SE
Lesson: Generating and Verifying Signatures
Section: Verifying a Digital Signature

Weaknesses and Alternatives弱点和替代方案

The GenSig and VerSig programs in this lesson illustrate the use of the JDK Security API to generate a digital signature for data and to verify that a signature is authentic. 本课程中的GenSigVerSig程序说明了使用JDKSecurity API为数据生成数字签名并验证签名的真实性。However, the actual scenario depicted by those programs, in which a sender uses the JDK Security API to generate a new public/private key pair, the sender stores the encoded public key bytes in a file, and the receiver reads in the key bytes, is not necessarily realistic, and has a potential major flaw.然而,这些程序所描述的实际场景,即发送方使用JDK Security API生成新的公钥/私钥对,发送方将编码的公钥字节存储在文件中,接收方读取密钥字节,并不一定现实,并且具有潜在的重大缺陷。

In many cases the keys do not need to be generated; they already exist, either as encoded keys in files or as entries in a keystore.在许多情况下,不需要生成密钥;它们已经存在,要么作为文件中的编码密钥,要么作为密钥库中的条目。

The potential major flaw is that nothing guarantees the authenticity of the public key the receiver receives, and the VerSig program correctly verifies the authenticity of a signature only if the public key it is supplied is itself authentic!潜在的主要缺陷是,没有什么能保证接收者收到的公钥的真实性,只有当提供的公钥本身是真实的时,VerSig程序才能正确验证签名的真实性!

Working with Encoded Key Bytes使用编码密钥字节

Sometimes encoded key bytes already exist in files for the key pair to be used for signing and verification. 有时,用于签名和验证的密钥对的文件中已经存在编码的密钥字节。If that's the case the GenSig program can import the encoded private key bytes and convert them to a PrivateKey needed for signing, via the following, assuming that the name of the file containing the private key bytes is in the privkeyfile String and that the bytes represent a DSA key that has been encoded by using the PKCS #8 standard.如果是这样的话,GenSig程序可以通过以下方式导入编码的私钥字节并将其转换为签名所需的私钥,假设包含私钥字节的文件名在privkeyfile String中,并且这些字节表示使用PKCS#8标准编码的DSA密钥。

FileInputStream keyfis = new FileInputStream(privkeyfile);
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();

PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);

KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PrivateKey privKey = keyFactory.generatePrivate(privKeySpec);

GenSig no longer needs to save the public key bytes in a file, as they're already in one.不再需要将公钥字节保存在文件中,因为它们已经在一个文件中了。

In this case the sender sends the receiver在这种情况下,发送方发送接收方

The VerSig program remains unchanged, as it already expects encoded public key bytes in a file.VerSig程序保持不变,因为它已经期望文件中的编码公钥字节。

But what about the potential problem of a malicious user intercepting the files and replacing them all in such a way that their switch cannot be detected? In some cases this is not an issue, because people have already exchanged public keys face to face or via a trusted third party that does the face-to-face exchange. 但是,恶意用户拦截文件并以无法检测到其切换的方式替换所有文件的潜在问题呢?在某些情况下,这不是问题,因为人们已经面对面或通过进行面对面交换的可信第三方交换了公钥。After that, multiple subsequent file and signature exchanges may be done remotely (that is, between two people in different locations), and the public keys may be used to verify their authenticity. 之后,可以远程进行多次后续文件和签名交换(即在不同位置的两个人之间),并使用公钥验证其真实性。If a malicious user tries to change the data or signature, this is detected by VerSig.如果恶意用户试图更改数据或签名,VerSig会检测到。

If a face-to-face key exchange is not possible, you can try other methods of increasing the likelihood of proper receipt. For example, you could send your public key via the most secure method possible prior to subsequent exchanges of data and signature files, perhaps using less secure mediums.如果无法进行面对面的密钥交换,您可以尝试其他方法来增加正确接收的可能性。例如,您可以在后续交换数据和签名文件之前,通过最安全的方法发送公钥,或许可以使用不太安全的介质。

In general, sending the data and the signature separately from your public key greatly reduces the likelihood of an attack. Unless all three files are changed, and in a certain manner discussed in the next paragraph, VerSig will detect any tampering.一般来说,将数据和签名与公钥分开发送可以大大降低攻击的可能性。除非所有三个文件都被更改,并且以下一段中讨论的某种方式更改,否则VerSig将检测到任何篡改。

If all three files (data document, public key, and signature) were intercepted by a malicious user, that person could replace the document with something else, sign it with a private key, and forward on to you the replaced document, the new signature, and the public key corresponding to the private key used to generate the new signature. 如果所有三个文件(数据文档、公钥和签名)都被恶意用户拦截,该用户可以用其他东西替换文档,用私钥签名,并将替换后的文档、新签名和与用于生成新签名的私钥对应的公钥转发给您。Then VerSig would report a successful verification, and you'd think that the document came from the original sender. 然后VerSig会报告验证成功,您会认为该文档来自原始发件人。Thus you should take steps to ensure that at least the public key is received intact (VerSig detects any tampering of the other files), or you can use certificates to facilitate authentication of the public key, as described in the next section.因此,您应该采取措施确保至少收到完整的公钥(VerSig检测到其他文件的任何篡改),或者您可以使用证书来促进公钥的身份验证,如下一节所述。

Working with Certificates使用证书

It is more common in cryptography to exchange certificates containing public keys rather than the keys themselves.在密码学中,交换包含公钥的证书比交换密钥本身更常见。

One benefit is that a certificate is signed by one entity (the issuer) to verify that the enclosed public key is the actual public key of another entity (the subject or owner). 一个好处是,证书由一个实体(颁发者)签署,以验证所附的公钥是另一个实体的实际公钥(主体或所有者)。Typically a trusted third-party certification authority (CA) verifies the identity of the subject and then vouches for its being the owner of the public key by signing the certificate.通常,受信任的第三方证书颁发机构(CA)会验证主体的身份,然后通过签署证书来保证其是公钥的所有者。

Another benefit of using certificates is that you can check to ensure the validity of a certificate you received by verifying its digital signature, using its issuer's (signer's) public key, which itself may be stored in a certificate whose signature can be verified by using the public key of that certificate issuer; that public key itself may be stored in a certificate, and so on, until you reach a public key that you already trust.使用证书的另一个好处是,您可以通过使用其颁发者(签名者)的公钥验证其数字签名来检查以确保您收到的证书的有效性,该公钥本身可能存储在证书中,其签名可以通过使用该证书颁发者的公钥进行验证;该公钥本身可能存储在证书中,以此类推,直到您到达已经信任的公钥。

If you cannot establish a trust chain (perhaps because the required issuer certificates are not available to you), the certificate fingerprint(s) can be calculated. Each fingerprint is a relatively short number that uniquely and reliably identifies the certificate. 如果您无法建立信任链(可能是因为您无法获得所需的颁发者证书),则可以计算证书指纹。每个指纹都是一个相对较短的数字,可以唯一可靠地识别证书。(Technically it's a hash value of the certificate information, using a message digest, also known as a one-way hash function.) You can call up the certificate owner and compare the fingerprints of the certificate you received with the ones sent. If they're the same, the certificates are the same.(从技术上讲,它是证书信息的哈希值,使用消息摘要,也称为单向哈希函数。)您可以调用证书所有者,并将您收到的证书指纹与发送的指纹进行比较。如果它们相同,则证书也相同。

It would be more secure for GenSig to create a certificate containing the public key and for VerSig to then import the certificate and extract the public key. GenSig创建一个包含公钥的证书,然后VerSig导入证书并提取公钥,这样会更安全。However, the JDK has no public certificate APIs that would allow you to create a certificate from a public key, so the GenSig program cannot create a certificate from the public key it generated. 但是,JDK没有允许您从公钥创建证书的公共证书API,因此GenSig程序无法从它生成的公钥创建证书。(There are public APIs for extracting a public key from a certificate, though.)(不过,有一些公共API可以从证书中提取公钥。)

If you want, you can use the various security tools, not APIs, to sign your important document(s) and work with certificates from a keystore, as was done in the Exchanging Files lesson.如果你愿意,你可以使用各种安全工具,而不是API,来签署你的重要文档,并使用密钥库中的证书,就像交换文件课程中所做的那样。

Alternatively you can use the API to modify your programs to work with an already existing private key and corresponding public key (in a certificate) from your keystore. To start, modify the GenSig program to extract a private key from a keystore rather than generate new keys. First, let's assume the following:或者,您可以使用API修改程序,以便使用密钥库中已经存在的私钥和相应的公钥(在证书中)。首先,修改GenSig程序,从密钥库中提取私钥,而不是生成新密钥。首先,让我们假设如下:

Then you can extract the private key from the keystore via the following.然后,您可以通过以下步骤从密钥库中提取私钥。

KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream ksfis = new FileInputStream(ksName); 
BufferedInputStream ksbufin = new BufferedInputStream(ksfis);

ks.load(ksbufin, spass);
PrivateKey priv = (PrivateKey) ks.getKey(alias, kpass);

You can extract the public key certificate from the keystore and save its encoded bytes to a file named suecert, via the following.您可以通过以下方式从密钥库中提取公钥证书,并将其编码字节保存到名为suecert的文件中。

java.security.cert.Certificate cert = ks.getCertificate(alias);
byte[] encodedCert = cert.getEncoded();

// Save the certificate in a file named "suecert" 

FileOutputStream certfos = new FileOutputStream("suecert");
certfos.write(encodedCert);
certfos.close();

Then you send the data file, the signature, and the certificate to the receiver. The receiver verifies the authenticity of the certificate by first getting the certificate's fingerprints, via the keytool -printcert command.然后,您将数据文件、签名和证书发送给接收者。接收者首先通过keytool -printcert命令获取证书的指纹,从而验证证书的真实性。

keytool -printcert -file suecert
Owner: CN=Susan Jones, OU=Purchasing, O=ABC, L=Cupertino, ST=CA, C=US
Issuer: CN=Susan Jones, OU=Purchasing, O=ABC, L=Cupertino, ST=CA, C=US
Serial number: 35aaed17
Valid from: Mon Jul 13 22:31:03 PDT 1998 until:
Sun Oct 11 22:31:03 PDT 1998
Certificate fingerprints:
MD5:  1E:B8:04:59:86:7A:78:6B:40:AC:64:89:2C:0F:DD:13
SHA1: 1C:79:BD:26:A1:34:C0:0A:30:63:11:6A:F2:B9:67:DF:E5:8D:7B:5E

Then the receiver verifies the fingerprints, perhaps by calling the sender up and comparing them with those of the sender's certificate or by looking them up in a public repository.然后,接收方验证指纹,可能是通过调用发送方并将其与发送方证书的指纹进行比较,或者在公共存储库中查找指纹。

The receiver's verification program (a modified VerSig) can then import the certificate and extract the public key from it via the following, assuming that the certificate file name (for example, suecert) is in the String certName.然后,接收方的验证程序(修改后的VerSig)可以导入证书,并通过以下方式从中提取公钥,假设证书文件名(例如suecert)在String certName中。

FileInputStream certfis = new FileInputStream(certName);
java.security.cert.CertificateFactory cf =
    java.security.cert.CertificateFactory.getInstance("X.509");
java.security.cert.Certificate cert =  cf.generateCertificate(certfis);
PublicKey pub = cert.getPublicKey();

Ensuring Data Confidentiality确保数据保密

Suppose that you want to keep the contents of the data confidential so people accidentally or maliciously trying to view it in transit (or on your own machine or disk) cannot do so. To keep the data confidential, you should encrypt it and store and send only the encryption result (referred to as ciphertext). The receiver can decrypt the ciphertext to obtain a copy of the original data.假设你想对数据内容保密,这样人们在传输过程中(或在你自己的机器或磁盘上)意外或恶意地试图查看它时就无法这样做。为了对数据保密,你应该对其进行加密,并仅存储和发送加密结果(称为密文)。接收器可以解密密文以获得原始数据的副本。


Previous page: Verify the Signature
Next page: Weaknesses and Alternatives