国密SM2签名完整流程

前提是你 提交于 2020-11-05 10:30:28

因工作要求,需要将原RSA签名改成SM2国密签名,研究了下,记录下整个流程:

  1. 使用openssl(1.1.1版本以上)生成sm2公私钥
    openssl ecparam -genkey -name SM2 -noout -out pri.pem #生成私钥,私钥默认是pem文件格式
    openssl pkcs8 -topk8 -inform PEM -in pri.pem -nocrypt -out pri_pkcs8.pem #将pem文件格式的私钥,转成java可解析的pkcs8格式
    openssl ec -in pri_pkcs8.pem -pubout -out pub.pem #生成公钥
    
    pri.pem内容如下:
    -----BEGIN EC PRIVATE KEY-----
    MHcCAQEEIFa7yVLqfOUKk5ArP2p6XtfuuKWvKmWsZXtABztd0mLtoAoGCCqBHM9V
    AYItoUQDQgAEuiraG1y/mt/eUK61sl/YWTX55x0ZKvxzjunWokbs3B3JzlF4SDG1
    DDYef4uQGUL9DQMZDYQLIidRjpTjlEGYbQ==
    -----END EC PRIVATE KEY-----
    
    pri_pkcs8.pem内容如下:
    -----BEGIN PRIVATE KEY-----
    MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgVrvJUup85QqTkCs/
    anpe1+64pa8qZaxle0AHO13SYu2hRANCAAS6KtobXL+a395QrrWyX9hZNfnnHRkq
    /HOO6daiRuzcHcnOUXhIMbUMNh5/i5AZQv0NAxkNhAsiJ1GOlOOUQZht
    -----END PRIVATE KEY-----
    
    pub.pem内容如下:
    -----BEGIN PUBLIC KEY-----
    MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEuiraG1y/mt/eUK61sl/YWTX55x0Z
    KvxzjunWokbs3B3JzlF4SDG1DDYef4uQGUL9DQMZDYQLIidRjpTjlEGYbQ==
    -----END PUBLIC KEY-----
    
  2. 使用Java程序解析公私钥:
    pom.xml依赖:
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcprov-jdk15on</artifactId>
        <version>1.60</version>
    </dependency>    
    
    java代码:注意本java程序读取公私钥时没去掉声明,需要先手工将声明去掉,也就是去掉文件中首尾行的PRIVATE KEY和PUBLIC KEY声明
    import java.io.BufferedInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.security.KeyFactory;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.SecureRandom;
    import java.security.Signature;
    import java.security.spec.PKCS8EncodedKeySpec;
    import java.security.spec.X509EncodedKeySpec;
    
    import org.apache.commons.codec.binary.Base64;
    import org.bouncycastle.asn1.gm.GMNamedCurves;
    import org.bouncycastle.asn1.x9.X9ECParameters;
    import org.bouncycastle.crypto.engines.SM2Engine;
    import org.bouncycastle.crypto.params.ECDomainParameters;
    import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
    import org.bouncycastle.crypto.params.ECPublicKeyParameters;
    import org.bouncycastle.crypto.params.ParametersWithRandom;
    import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
    import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    
    public class TestSM2 {
        private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1");
        private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN());
        private final static BouncyCastleProvider bc = new BouncyCastleProvider();
        private final static String KEY_ALGORITHM = "EC";
        private final static String SIGNATURE_ALGORITHM = "SM3withSm2";
    
        public static String sign(byte[] data, String privateKeyStr) throws Exception {
            byte[] keyBytes = Base64.decodeBase64(privateKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, bc);
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
            PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, bc);
            signature.initSign(priKey);
            signature.update(data);
            return Base64.encodeBase64String(signature.sign());
        }
    
        public static boolean verify(byte[] data, String publicKeyStr, String sign) throws Exception {
            byte[] keyBytes = Base64.decodeBase64(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, bc);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            PublicKey pubKey = keyFactory.generatePublic(keySpec);
            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, bc);
            signature.initVerify(pubKey);
            signature.update(data);
            return signature.verify(Base64.decodeBase64(sign));
        }
    
        /**
         * c1||c3||c2 私钥解密
         * @param data
         * @param privateKeyStr
         * @return
         * @throws Exception 
         */
        public static byte[] decryptByPrivateKey(byte[] data, String privateKeyStr) throws Exception{
            return sm2DecryptOld(changeC1C3C2ToC1C2C3(data), privateKeyStr);
        } 
    
        /**
         * c1||c3||c2 公钥加密
         * @param data
         * @param publicKeyStr
         * @return
         * @throws Exception  
         */
        public static byte[] encryptByPublicKey(byte[] data, String publicKeyStr) throws Exception {
            return changeC1C2C3ToC1C3C2(sm2EncryptOld(data, publicKeyStr));
        }
    
        /**
         * bc加解密使用旧标c1||c2||c3,此方法在加密后调用,将结果转化为c1||c3||c2
         * @param c1c2c3
         * @return
         */
        private static byte[] changeC1C2C3ToC1C3C2(byte[] c1c2c3) {
            final int c1Len = (x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1; 
            final int c3Len = 32; 
            byte[] result = new byte[c1c2c3.length];
            System.arraycopy(c1c2c3, 0, result, 0, c1Len); 
            System.arraycopy(c1c2c3, c1c2c3.length - c3Len, result, c1Len, c3Len); 
            System.arraycopy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.length - c1Len - c3Len); 
            return result;
        }
    
    
        /**
         * bc加解密使用旧标c1||c2||c3,此方法在解密前调用,将c1||c3||c2密文转化为c1||c2||c3再去解密
         * @param c1c3c2
         * @return
         */
        private static byte[] changeC1C3C2ToC1C2C3(byte[] c1c3c2) {
            final int c1Len = (x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1; 
            final int c3Len = 32; 
            byte[] result = new byte[c1c3c2.length];
            System.arraycopy(c1c3c2, 0, result, 0, c1Len); 
            System.arraycopy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.length - c1Len - c3Len); 
            System.arraycopy(c1c3c2, c1Len, result, c1c3c2.length - c3Len, c3Len); 
            return result;
        }
    
    
        /**
         * c1||c2||c3
         * @param data
         * @param publicKeyStr
         * @return
         * @throws Exception  
         */
        private static byte[] sm2EncryptOld(byte[] data, String publicKeyStr) throws Exception {
            byte[] keyBytes = Base64.decodeBase64(publicKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, bc);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
            PublicKey pubKey = keyFactory.generatePublic(keySpec);
            BCECPublicKey localECPublicKey = (BCECPublicKey) pubKey;
            ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), ecDomainParameters);
            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
            return sm2Engine.processBlock(data, 0, data.length);
        }
    
        /**
         * c1||c2||c3
         * @param data
         * @param privateKeyStr
         * @return
         * @throws Exception 
         */
        private static byte[] sm2DecryptOld(byte[] data, String privateKeyStr) throws Exception {
            byte[] keyBytes = Base64.decodeBase64(privateKeyStr);
            KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM, bc);
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
            PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
            BCECPrivateKey localECPrivateKey = (BCECPrivateKey) priKey;
            ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(localECPrivateKey.getD(), ecDomainParameters);
            SM2Engine sm2Engine = new SM2Engine();
            sm2Engine.init(false, ecPrivateKeyParameters);
            return sm2Engine.processBlock(data, 0, data.length);
        }
    
        public static String read(String filePath) throws IOException {
            try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(filePath)));
                 ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                int size = 0;
                byte[] temp = new byte[128];
                while ((size = bis.read(temp)) > 0) {
                    outputStream.write(temp, 0, size);
                }
                return new String(outputStream.toByteArray());
            } 
        }
    
        public static void main(String[] args) throws Exception {
            String priPath = "C:\\Users\\xxxx\\Desktop\\pri_pkcs8.pem";
            String pubPath = "C:\\Users\\xxxx\\Desktop\\pub.pem";
    
            String data = "123213";
            String sign = sign(data.getBytes(), read(priPath));
            boolean verify = verify(data.getBytes(), read(pubPath), sign);
            System.out.println(verify);
    
            byte[] encryptByPublicKey = encryptByPublicKey(data.getBytes(), read(pubPath));
            byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, read(priPath));
            System.out.println(new String(decryptByPrivateKey));
        }
    
    }
    
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!