Java密码学 - 9. 数字签名

  1. 定义
  2. 代码片段
    1. 非PKCS#7
      1. 签名
      2. 验证
    2. PKCS#7
      1. 签名
      2. 验证并获取原文
    3. 参考:ASN.1格式

定义

数字签名Digital signature用户验证数据的真实性。在现实生活种,签名者用某种事物作为签名标识,标识某一数据的真实;而验证方需使用某种手段签证该签名标识是否真实。

数字签名Digital signature有两个阶段:签名sign和验证verify

Image for post

图片来源

签名方signer

  1. 数据Data通过散列算法Hash Algorithm,得到摘要Digest。该过程减少了传输数据的体0积。
  2. 使用私钥Private Key对摘要Digest进行加密,得到数字签名Digital signature,保证摘要的正确性。
  3. 签名方signer将数字签名Digital signature和数据Data发送给验证方verifier

验证方verifier:

  1. 数据Data通过散列算法Hash Algorithm,得到摘要Digest
  2. 使用公钥Public Key对数字签名Digital signature解密,得到摘要Digest
  3. 将两个摘要进行比较,以此确定正确性。

扩展参考:

代码片段

非PKCS#7

源代码地址

签名

1
2
3
4
5
6
7
	//The method that signs the data using the private key that is stored in keyFile path
	public byte[] sign(String data, String keyFile) throws InvalidKeyException, Exception{
		Signature rsa = Signature.getInstance("SHA1withRSA");
		rsa.initSign(getPrivate(keyFile));//keyFile=私钥
		rsa.update(data.getBytes());//data=原文
		return rsa.sign();//签名
	}

验证

1
2
3
4
5
6
private boolean verifySignature(byte[] data, byte[] signature, String keyFile) throws Exception {
		Signature sig = Signature.getInstance("SHA1withRSA");
		sig.initVerify(getPublic(keyFile));//keyFil= 公钥
		sig.update(data);//data=加密后的摘要
		return sig.verify(signature);//验证
	}

PKCS#7

签名

需要参数:

  1. 证书(可以一个, 也可以多个, 目的, 让对方知道自己的身份)
  2. 私钥(签名使用)
  3. 原文
  4. 摘要以及加密算法
1
2
3
4
5
6
7
8
9
10
11
12
public static CMSSignedData generateCMSSignedData(PrivateKey privateKey, X509Certificate signCert, byte[] inputData, String algo)throws Exception {
		Security.addProvider(new BouncyCastleProvider());//这里我们使用BC的代码库
		CMSTypedData msg = new CMSProcessableByteArray(inputData);//
		CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
		gen.addSignerInfoGenerator(
				new JcaSimpleSignerInfoGeneratorBuilder()
						.setProvider("BC")
						.build(algo, privateKey, signCert));//算法可选"SHA1withRSA"
		gen.addCertificate(new JcaX509CertificateHolder(signCert));
		//gen.addCertificates(new JcaCertStore(Arrays.asList(signCert))); 添加若干个证书时候用
		return gen.generate(msg, true);//这里的true和false, 是原文是否要放在CMSSignedData里
	}

验证并获取原文

需要参数:

  1. PKCS#7文档(内含:SignedDataSignerInfo
    • SignedData包含数据
    • SignerInfo包含用于验证的加密摘要
  2. 公钥
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void doverify(PublicKey publickey,byte[] data) throws Exception {
		Security.addProvider(new BouncyCastleProvider());
		CMSSignedData cms = new CMSSignedData(data);//密文
		//获取签名者所有信息
		SignerInformationStore signers = cms.getSignerInfos();
		Iterator it = signers.getSigners().iterator();
		while (it.hasNext()) {
			SignerInformation signer = (SignerInformation) it.next();
			if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder() .setProvider("BC").build(publickey))) {//共要
				System.out.println("verified");
				System.out.println();
			}

		}
	}

获取原文:

1
2
3
CMSSignedData cms=...
CMSTypedData signedContent = cms.getSignedContent();
System.out.println(new String((byte[])signedContent.getContent()));//获得原文数据

参考:ASN.1格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- Signed Data
signed-data PKCS7-CONTENT-TYPE ::= {SignedData
                                    IDENTIFIED BY  id-signed-data
}

SignedData ::= SEQUENCE {
  version           Version,
  digestAlgorithms  DigestAlgorithmIdentifiers,
  contentInfo       ContentInfo,
  certificates      [0]  CertificateSet OPTIONAL,
  crls              [1]  CertificateRevocationLists OPTIONAL,
  signerInfos       SignerInfos
}

SignerInfo ::= SEQUENCE {
  version                    Version,
  signerIdentifier           SignerIdentifier,
  digestAlgorithm            DigestAlgorithmIdentifier,
  authenticatedAttributes    [0]  Attributes OPTIONAL,
  digestEncryptionAlgorithm  DigestEncryptionAlgorithmIdentifier,
  encryptedDigest            EncryptedDigest,
  unauthenticatedAttributes  [1]  Attributes OPTIONAL
}