使用商店中可用的证书验证文件的签名

时间:2016-03-06 00:01:37

标签: java security bouncycastle verification public-key

我对安全性和签名验证完全陌生,到目前为止,我找不到解释签名验证基础的地方。我需要通过从证书存储区提供的相应证书中获取公钥来验证文件的签名。 Java教程(https://docs.oracle.com/javase/tutorial/security/apisign/versig.html)没有教授如何从可信证书库获取证书并验证使用它。我经历了Bouncy城​​堡WIKI http://www.bouncycastle.org/wiki/display/JA1/BC+Version+2+APIs,但它对初学者来说并不是真正的解释。我该怎么做呢?给定签名文件,如何从证书存储中检查其公钥并验证其是否是发送该文件的合适人员?请指教。

1 个答案:

答案 0 :(得分:1)

因为您没有提供您使用的构建管理,我认为它将是Maven。

首先,在您的依赖项中包含BouncyCastle

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.53</version>
</dependency>

之后,您需要创建一个将用于签名或验证证书的util类。像这样:

package your.pack.location;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * Author: harunalfat
 */
public class SignatureUtils {

    private static final Logger log = LogManager.getLogger(SignatureUtils.class);


    public static String sign(String plainText, PrivateKey privateKey) throws Exception {

        byte[] data = plainText.getBytes("ISO-8859-1");

        Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
        signature.initSign(privateKey);
        signature.update(data);

        return Base64.toBase64String(signature.sign());
    }

    public static boolean verify(String plainText, String signString, PublicKey publicKey) throws Exception{

        byte[] data = plainText.getBytes("ISO-8859-1");

        Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
        signature.initVerify(publicKey);
        signature.update(data);

        byte[] signByte = Base64.decode(signString);
        return signature.verify(signByte);
    }

    private static PemObject getPemObjectFromResource(String fileLocation) throws IOException {

        Resource resource = new ClassPathResource(fileLocation);
        InputStream is = resource.getInputStream();

        PemObject pemObject = new PemReader(new InputStreamReader( is )).readPemObject();
        return pemObject;
    }

    private static X509EncodedKeySpec getPubKeySpec(String fileLocation) throws IOException, NoSuchAlgorithmException {

        PemObject pemObject = getPemObjectFromResource(fileLocation);
        byte[] data = pemObject.getContent();

        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(data);
        return keySpec;
    }

    private static PKCS8EncodedKeySpec getPriKeySpec(String fileLocation) throws IOException, NoSuchAlgorithmException {

        PemObject pemObject = getPemObjectFromResource(fileLocation);
        byte[] data = pemObject.getContent();

        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(data);
        return keySpec;
    }

    public static PublicKey getPublicKey(String fileLocation) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        KeySpec keySpec = getPubKeySpec(fileLocation);
        return kf.generatePublic(keySpec);
    }

    public static PrivateKey getPrivateKey(String fileLocation) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        KeyFactory kf = KeyFactory.getInstance("RSA");
        KeySpec keySpec = getPriKeySpec(fileLocation);
        return kf.generatePrivate(keySpec);
    }
}

然后你会像这样使用它

package your.another.pack;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.mockito.MockitoAnnotations;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;

import static org.junit.Assert.assertTrue;

/**
 * Author: harunalfat
 */
public class SignatureUtilsTest {


    private static final Logger log = LogManager.getLogger(SignatureUtilsTest.class);
    private static final String PLAIN = "attack at dawn";

    @Test
    public void testSignAndVerify() throws Exception {
        Security.addProvider(new BouncyCastleProvider()); // <-- IMPORTANT!!! This will add BouncyCastle as provider in Java Security
        PrivateKey privateKey = SignatureUtils.getPrivateKey("key/private2.pem"); // This is located on src/main/resources/key/private2.pem 
        PublicKey publicKey = SignatureUtils.getPublicKey("key/public2.pem"); // This is located on src/main/resources/key/public2.pem 

    // In this example, I use junit test, so it will be on src/test/resources/...

        log.info("Private Key  : "+Base64.encodeBase64String(privateKey.getEncoded()));
        log.info("Public Key   : "+Base64.encodeBase64String(publicKey.getEncoded()));

        String sign = SignatureUtils.sign(PLAIN, privateKey);
        log.info("Plain String : "+PLAIN);
        log.info("Sign         : "+sign);

        boolean result = SignatureUtils.verify(PLAIN,sign, publicKey);
        log.info("Result       : "+result);

        assertTrue(result);
    }

}

当然,您可以使用其他算法更改Signature实例。在我的情况下,我使用"SHA1WithRSA",但你明白了吗?

有了这个,有人会使用他们的私钥加密他们的数据,然后发送给你。之后,您将使用他们提供的公钥验证数据。

例如,Bob向您发送有关他发送给您的金额($5000)的消息,并使用其私钥对其进行签名,并加密。当数据到达您时,您知道Bob应该发送5000美元,然后您使用文本$5000和公钥Bob共享来验证加密数据,但是它真的是$5000还是它来了来自鲍勃?

如果数据已被更改,或者当有一天你向Bob要求一些钱,但是其他人点击了该消息并且他/她向你发送了除了Bob之外的私钥的金额消息,你就会知道。

随意问:)