使用 SHA256withRSA 签名后如何验证 signatureBytes?

How to verify signatureBytes after signing it with SHA256withRSA?

我正在使用 "Windows-MY" KeyStore 签署一些文本。 我想使用我的私钥签名并使用 Public 密钥进行验证。

KeyStore keyStore = KeyStore.getInstance("Windows-MY");
                keyStore.load(null, null); 
Enumeration en = keyStore.aliases();
while (en.hasMoreElements()) {   
    KeyStore keyStore = KeyStore.getInstance("Windows-MY");
                keyStore.load(null, null);
    String alias = en.nextElement().toString();
    X509Certificate c = (X509Certificate) keyStore.getCertificate(alias);
    String serialNumber = c.getSerialNumber().toString();
    System.out.println("--" + aliasName);
                PrivateKey privateKey = (PrivateKey) keyStore.getKey(aliasName, null);
                PublicKey publicKey = (PublicKey) c.getPublicKey();
                Certificate[] chain = keyStore.getCertificateChain(aliasName);
    DataOutputStream fout = new DataOutputStream(outstream);
    // -------------------------------------------------------
    String data = "Monika";
    byte[] content = data.getBytes();
    Provider p = keyStore.getProvider();
    // ----------------------signature---start---------------------------

    Signature signature = Signature.getInstance("SHA256withRSA", p);
    System.out.println(" signature.getProvider():"+ signature.getProvider());
    signature.initSign(privateKey);
    signature.update(content);
    byte[] signatureBytes = signature.sign();
    System.out.println("signatureBytes-------------"+ signatureBytes.toString());
    // ----------------------signature----------end------------------

    // ------------------------verification---------------
    Signature signature1 = Signature.getInstance("SHA256withRSA", p);
    System.out.println(" signature1.getProvider():"+ signature1.getProvider());
    signature1.initVerify(publicKey);
    signature1.update(content);
    boolean verifies = signature1.verify(signatureBytes);
    System.out.println("signature verifies: " + verifies);
    // ------------------------------------------------
    fout.close();
} // while

输出:

privateKey:RSAPrivateKey [size=2048 bits, type=Exchange, container=AC0BEBA9-A361-4611-96D9-B365B671FBC3]
 signature.getProvider():SunMSCAPI version 1.6
signatureBytes-------------[B@1402d5a
 signature1.getProvider():SunRsaSign version 1.5
signature verifies: false

注意:

  1. 我的私钥已经是 RSAPrivateKey 了。
  2. 签名提供程序是 SunMSCAPI。
  3. 但是我不知道Provider for Verification with PrivateKey

你正在null这里

byte[] encodedPrivateKey = privateKey.getEncoded(); // are you sure that this byte array is not null ?

为了让事情更安全,请在此处查看:

 PrivateKey privateKey = (PrivateKey) keyStore.getKey(
                    DSCName, null); // this maybe returning null

所以在给出错误的行之前,进行检查:

if(encodedPrivateKey==null){
    System.out.println("private key is null");
}               
 PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
                    encodedPrivateKey);

您的代码存在几个问题:

  1. 您只是在使用 first 证书/public 来自您的 windows 密钥库的密钥。这实际上可能是正确的,但密钥库中可能有多个证书,然后您使用哪个证书进行验证只是巧合。

    String alias = en.nextElement().toString();
    X509Certificate c = (X509Certificate) keyStore.getCertificate(alias);
    PublicKey publicKey = c.getPublicKey();
    PrivateKey privateKey = (PrivateKey) keyStore.getKey(DSCName, null);
    

    您应该写 keyStore.getCertificate(DSCName) 以确保它与私钥匹配。

  2. 您正在无缘无故地生成密钥(相应地尝试转换现有密钥)。您可以完全删除此代码。这也将解决您的 NullPointerException:

    问题
    byte[] encodedPrivateKey = privateKey.getEncoded();
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
    RSAPrivateKey privateKey1 = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    
  3. 你的问题中有很多不必要的代码,比如加载证书链,但从未使用它。这使得它更难修复。一个最小的(工作)示例如下所示:

    String alias = "myAlias";
    String myData = "data to encrypt";
    
    KeyStore keyStore = KeyStore.getInstance("Windows-MY");
    keyStore.load(null, null);
    
    X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
    PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, null);
    PublicKey publicKey = cert.getPublicKey();
    
    Signature instance = Signature.getInstance("SHA256withRSA");
    instance.initSign(privateKey, new SecureRandom());
    instance.update(myData.getBytes("UTF-8"));
    byte[] signedBytes = instance.sign();
    
    instance.initVerify(publicKey);
    instance.update(myData.getBytes("UTF-8"));
    System.out.println(instance.verify(signedBytes));
    
Signature signature = Signature.getInstance("SHA256withRSA",p);
                        System.out.println(" signature.getProvider():"+ signature.getProvider());
                        signature.initSign(privateKey, new SecureRandom());
                        signature.update(byteData);
                        byte[] signatureBytes = signature.sign();




//                      X509Certificate cert1 =signatureBytes. 
                        System.out.println("signatureBytes-------------"+ signatureBytes.toString());
                        // ----------------------signature----------end------------------

                        // ------------------------verification---------------
                        Signature signature1 = Signature.getInstance("SHA256withRSA");
                        System.out.println(" signature1.getProvider():"+ signature1.getProvider());
                        signature1.initVerify(publicKey);
                        signature1.update(byteData);

                        boolean verifies = signature1.verify(signatureBytes);
                        System.out.println("signature verifies: " + verifies);

使用摘要 SHA256with[=22= 对一些 text/data 进行签名后,检查此完整且有效的代码以验证 signature ]RSA:

/* verifyRSAsha256.java */
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import javax.xml.bind.DatatypeConverter;

/*
Compile:
clear && javac verifyRSAsha256.java && java verifyRSAsha256

Create private key:
openssl genrsa -des3 -out encrypted.pem 2048 && openssl rsa -in encrypted.pem -out private.pem -outform PEM && openssl rsa -in private.pem -pubout > public.pem

Create signature:
/bin/echo -n "some text that you want to be trusted" > data.txt
openssl dgst -sha256 -sign private.pem data.txt > signature.tmp
base64 signature.tmp

Verify signature:
openssl dgst -sha256 -verify public.pem -signature signature.tmp data.txt
*/

public class verifyRSAsha256 {

    public static void main(String args[]){
        String publicKey = 
        //  "-----BEGIN PUBLIC KEY-----"+
            "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwns0JWYgEshlpLYsZQFc"+
            "d5iVSqIHDO0zISLlO1aK4bbbosSvRE81+inKrG5mlnkIrv0+mJ/qTLY1RdBAVAe4"+
            "GPLTpHmLJhEtq7stydm2cCTEPRwfJNjoHqATDHEm1KLVGA8k0hztfMr8fLChE3/K"+
            "n2MHxzs7qhMLyBdPqbVC9RNja3i+Nl814xPTSXJ50zdJMLC56VtIU0xjqNjXN8iQ"+
            "pLZ2EfcP55nZ/venD01yxfsUn4sQLFTAlXqygA10fdDv9y0eZvgaGGSb4MuPT7yD"+
            "BfgNEU3tl4nRdSzPNkCkCmkuaa/pqZ5uw+G0HBwaQlHDwsnIcwE/xo6aHpt4xF4W"+
            "/QIDAQAB";
        //  "-----END PUBLIC KEY-----";

        byte[] signature, data;

        // the signature is a binary data and I encoded it with base64, so the signature must be decoded from base64 to binary again
        signature = DatatypeConverter.parseBase64Binary("Yy9CdQDfdYWwZkSu2SZgoFABHk5Bd3tzYvX73QR+GDCWpUsWrO5CXRF+j3dBz+bq1SRQ+1c1hdez5mMeE1587s4Mos8EsT1sqNemu4l4535P+jYicwG1m2MAesquAHhIIAyY9iGID576ehX0/34rCCeGuVZablpL+2ki6cEwxPVlH7xtWNIc1AdxivHjkWorkWC1LrbfcNdbZhUrNuz7DZsxHP2sr+2TQdD4L9CA2bgpj6HeQt+MTfCf2PKSdVoLFdwnM8638jHL6MVcEJxeIow/YUDZGEAyR743RdRk4CGU1bJ7er9M1Q4hFfYWGOBsLBok2XXUJcchLgWET1eKdA==");

        // the signature length have to be 256 bytes
        System.out.print("Signature length 256 = ");
        System.out.println(signature.length);

        // the data used the generate the signature
        data = "some text that you want to be trusted".getBytes();

        // verify if signature is ok
        try {System.out.println(verify(data,signature,publicKey));}catch(GeneralSecurityException e){e.printStackTrace();}

        // if any byte of data changes (ex: change last byte from d to D)
        data = "some text that you want to be trusteD".getBytes();

        // the signature doesn't math and method verify will fail
        try {System.out.println(verify(data,signature,publicKey));}catch(GeneralSecurityException e){e.printStackTrace();}

    }

    private static boolean verify(byte[] data, byte[] signature, String publicKey) throws GeneralSecurityException{
        X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(DatatypeConverter.parseBase64Binary(publicKey));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(pubKey);
        sig.update(data);
        return sig.verify(signature);
    }
}

您可以使用 openssl 命令行工具创建私有和 public 密钥:

openssl genrsa -des3 -out encrypted.pem 2048 
openssl rsa -in encrypted.pem -out private.pem -outform PEM
openssl rsa -in private.pem -pubout > public.pem

您可以使用 openssl 命令行工具使用您的私钥创建一个签名:

/bin/echo -n "some text that you want to be trusted" > data.txt
openssl dgst -sha256 -sign private.pem data.txt > signature.tmp

您可以使用openssl命令行工具验证签名是否正确:

openssl dgst -sha256 -verify public.pem -signature signature.tmp data.txt