如何使用 SHA256withRSA 签名后验证签名字节



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

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. 但我不知道使用私钥进行验证的提供程序。

您的代码存在几个问题:

  1. 您只需使用 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);

你在这里得到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);

使用带有RSA 的摘要 SHA256对某些文本/数据进行签名后,检查此完整且有效的代码以验证签名

/* 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 命令行工具创建私钥和公钥:

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

最新更新