最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot整合ECC實現(xiàn)文件簽名與驗簽功能

 更新時間:2026年03月30日 08:52:01   作者:彭于晏Yan  
本文以SpringBoot為開發(fā)框架,基于BouncyCastle加密組件、Commons 系列工具包,實現(xiàn)了ECC算法下的文件簽名與驗簽功能,需要的朋友可以參考下

本文以SpringBoot為開發(fā)框架,基于BouncyCastle加密組件、Commons 系列工具包,實現(xiàn)了ECC(橢圓曲線密碼學) 算法下的文件簽名與驗簽功能,核心采用 SHA256withECDSA 簽名算法,配套完成了密鑰的初始化、簽名生成、驗簽驗證的全流程開發(fā),同時提供了 OpenSSL 生成 P-256 橢圓曲線密鑰對(DER 私鑰 + PEM 公鑰)的完整命令。

使用場景

該實現(xiàn)基于 ECC 橢圓曲線算法,相比 RSA 算法具有密鑰長度短、加密效率高、安全性強的特點,其簽名驗簽?zāi)芰蓮V泛應(yīng)用于對數(shù)據(jù)完整性、不可篡改性、身份真實性有要求的業(yè)務(wù)場景,核心適用場景包括:

  1. 文件傳輸安全:如 FTP / 文件服務(wù)器的文件更新、下載場景,對傳輸?shù)奈募ㄈ?zip 包、定制化文件 vbf)生成簽名,接收方驗簽確認文件未被篡改、來源合法;
  2. 分布式系統(tǒng)文件同步:微服務(wù)、分布式集群間的配置文件、靜態(tài)資源同步,通過簽名驗簽保證同步文件的完整性,避免節(jié)點間文件不一致;
  3. 定制化業(yè)務(wù)文件校驗:如自研業(yè)務(wù)格式文件(如vbf 文件)的生成、分發(fā),為文件添加唯一簽名,接收方通過驗簽驗證文件有效性;
  4. API 接口參數(shù)防篡改:對接口的核心參數(shù)(如文件路徑、業(yè)務(wù)標識)生成簽名,接口接收方驗簽,防止參數(shù)在傳輸過程中被惡意修改(如本文模擬接口對文件路徑簽名的場景)。

此外,該實現(xiàn)支持靈活替換算法(如切換為 SHA256withRSA),只需修改配置文件參數(shù)即可適配 RSA 簽名驗簽場景,具備良好的擴展性,可適配更多加密算法的業(yè)務(wù)需求。

1. 引入依賴

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.68</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.5</version>
</dependency>
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.15</version>
</dependency>

2. yaml配置

signature:
  privateKey: sign/signature-private.der
  publicKey: sign/signature-public.pem
  algorithm: SHA256withECDSA
  keyFactory: EC
  provider: BC

3. 配置文件

@Data
@ConfigurationProperties(prefix = "signature")
@Configuration
public class SignatureProperties {

    /**
     * 簽名私鑰
     */
    private String privateKey;
    /**
     * 公鑰
     */
    private String publicKey;

    /**
     * signatureInfo.put("sun.security.provider.DSA$RawDSA", TRUE);
     * signatureInfo.put("sun.security.provider.DSA$SHA1withDSA", TRUE);
     * signatureInfo.put("sun.security.rsa.RSASignature$MD2withRSA", TRUE);
     * signatureInfo.put("sun.security.rsa.RSASignature$MD5withRSA", TRUE);
     * signatureInfo.put("sun.security.rsa.RSASignature$SHA1withRSA", TRUE);
     * signatureInfo.put("sun.security.rsa.RSASignature$SHA256withRSA", TRUE);
     * signatureInfo.put("sun.security.rsa.RSASignature$SHA384withRSA", TRUE);
     * signatureInfo.put("sun.security.rsa.RSASignature$SHA512withRSA", TRUE);
     * signatureInfo.put("com.sun.net.ssl.internal.ssl.RSASignature", TRUE);
     * signatureInfo.put("sun.security.pkcs11.P11Signature", TRUE);
     * 簽名算法 (SHA256withECDSA、SHA256withRSA)
     */
    private String algorithm;

    /**
     * key算法(RSA、EC)
     */
    private String keyFactory;

    private String provider;
}

4. vbf文件簽名key初始化

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * vbf文件簽名key初始化
 */
@Slf4j
@Configuration
public class SignatureConfig {

    @Resource
    private SignatureProperties signatureProperties;

    @Bean
    public PrivateKey GetPrivateKey() throws IOException {
        String privateKey = signatureProperties.getPrivateKey();
        String keyFactory = signatureProperties.getKeyFactory();
        Security.addProvider(new BouncyCastleProvider());
        ClassPathResource classPathResource = new ClassPathResource(privateKey);
//        File keystore = new ClassPathResource("sample.jks").getFile();
        File file = new File(privateKey);
        if (!file.exists()) {
            FileUtils.copyInputStreamToFile(classPathResource.getInputStream(), file);
        }
        try {
            byte[] pk = FileUtils.readFileToByteArray(new File(privateKey));
            PKCS8EncodedKeySpec priSpec = new PKCS8EncodedKeySpec(pk);
            KeyFactory kf = KeyFactory.getInstance(keyFactory);
            log.info("初始化密鑰:{}", privateKey);
            return kf.generatePrivate(priSpec);
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    @Bean
    public PublicKey getPemPublicKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
        String publicKey = signatureProperties.getPublicKey();
        String keyFactory = signatureProperties.getKeyFactory();
        ClassPathResource classPathResource = new ClassPathResource(publicKey);
//        File keystore = new ClassPathResource("sample.jks").getFile();
        File file = new File(publicKey);
        if (!file.exists()) {
            FileUtils.copyInputStreamToFile(classPathResource.getInputStream(), file);
        }
        String b64pk = FileUtils.readFileToString(new File(publicKey), "UTF-8");
        b64pk = b64pk.replace("-----BEGIN PUBLIC KEY-----", "");
        b64pk = b64pk.replace("-----END PUBLIC KEY-----", "");

        byte[] pk = Base64.decode(b64pk);
        X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pk);
        KeyFactory kf = KeyFactory.getInstance(keyFactory);
        log.info("初始化公鑰:{}",publicKey);
        log.info("初始化keyFactory:{}", keyFactory);
        return kf.generatePublic(pubSpec);
    }

}

5. 簽名和驗簽

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;


/**
 * 簽名和驗簽
 */
@Slf4j
@Service
public class ECDSASignUtils {
    @Resource
    private PrivateKey privateKey;
    @Resource
    private PublicKey publicKey;
    @Resource
    private SignatureProperties signatureProperties;
    public static final int BLOCK_SIZE = 4 * 1024;


    /**
     * 使用默認key進行簽名
     */
    public String signature(InputStream inputStream) {
        String s = Hex.encodeHexString(signature(inputStream, privateKey));
        log.info("對文件簽名:{}", s);
        return s;
    }

    /**
     * 使用默認key進行簽名
     */
    public String signature(String filePath) {
        try {
            FileInputStream inputStream = new FileInputStream(filePath);
            return signature(inputStream);
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    /**
     * 驗證簽名
     */
    public boolean verify(String signatureStr, String filePath) throws FileNotFoundException {
        String publicKey = signatureProperties.getPublicKey();
        File file = ResourceUtils.getFile(publicKey);
        try {
            return verify(Hex.decodeHex(signatureStr), filePath, file.getAbsolutePath());
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    public boolean verify(String signatureStr, InputStream filePath) throws FileNotFoundException {
        File file = ResourceUtils.getFile(signatureProperties.getPublicKey());
        try {
            return verify(Hex.decodeHex(signatureStr), filePath, file.getAbsolutePath());
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }
    
    /**
     * 驗證簽名
     */
    public boolean verify(byte[] signature, String filePath) {
        try {
            File file = ResourceUtils.getFile(signatureProperties.getPublicKey());
            return verify(signature, filePath, file.getAbsolutePath());
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    private byte[] signature(InputStream inputStream, PrivateKey privateKey) {
        String algorithm = signatureProperties.getAlgorithm();
        String provider = signatureProperties.getProvider();
        Signature decode;
        try {
            if (algorithm.equalsIgnoreCase("SHA256withECDSA")) {
                decode = Signature.getInstance("SHA256withECDSA", "BC");
            } else {
                decode = Signature.getInstance(algorithm, provider);
            }
            decode.initSign(privateKey);
            log.info("簽名: {},算法:{}", algorithm, provider);

            byte[] buff = new byte[BLOCK_SIZE];
            int read = inputStream.read(buff, 0, BLOCK_SIZE);

            while (read > -1) {
                decode.update(buff, 0, read);
                read = inputStream.read(buff, 0, BLOCK_SIZE);
            }
            inputStream.close();

            return decode.sign();
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }


    private boolean verify(byte[] signature, String filePath, String pubKeyPath) {
        String algorithm = signatureProperties.getAlgorithm();
        String provider = signatureProperties.getProvider();
        Signature decode;
        try {
            if (algorithm.equalsIgnoreCase("SHA256withECDSA")) {
                decode = Signature.getInstance("SHA256withECDSA", "BC");
            } else {
                decode = Signature.getInstance(algorithm, provider);
            }
            log.info("pubKeyPath: {}", pubKeyPath);
            log.info("驗證:{},簽名算法:{}", algorithm, provider);
            decode.initVerify(publicKey);

            File vbf = new File(filePath);
            byte[] buff = new byte[BLOCK_SIZE];

            try (InputStream inputStream = new FileInputStream(vbf)) {
                int read = inputStream.read(buff, 0, BLOCK_SIZE);
                while (read > -1) {
                    decode.update(buff, 0, read);
                    read = inputStream.read(buff, 0, BLOCK_SIZE);
                }
            } catch (Exception e) {
                throw e;
            }
            return decode.verify(signature);
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    private boolean verify(byte[] signature, InputStream inputStream, String pubKeyPath) {
        String algorithm = signatureProperties.getAlgorithm();
        String provider = signatureProperties.getProvider();
        Signature decode;
        try {
            if (algorithm.equalsIgnoreCase("SHA256withECDSA")) {
                decode = Signature.getInstance("SHA256withECDSA", "BC");
            } else {
                decode = Signature.getInstance(algorithm, provider);
            }
            log.info("pubKeyPath: {}", pubKeyPath);
            log.info("驗證:{},簽名算法:{}", algorithm, provider);
            decode.initVerify(publicKey);
            byte[] buff = new byte[BLOCK_SIZE];
            try {
                int read = inputStream.read(buff, 0, BLOCK_SIZE);
                while (read > -1) {
                    decode.update(buff, 0, read);
                    read = inputStream.read(buff, 0, BLOCK_SIZE);
                }
            } catch (Exception e) {
                throw e;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
            }
            return decode.verify(signature);
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }
}

6. 模擬接口

@RestController
@Slf4j
public class DemoController {

    @Resource
    private ECDSASignUtils ecdsaSignUtils;

        @GetMapping("/updateFile")
    public ResponseEntity<String> updateFile(String zipFilePath) {
            String signature = ecdsaSignUtils.signature(new ByteArrayInputStream(zipFilePath.getBytes()));
            return ResponseEntity.ok(signature);
    }
}

7. OpenSSL生成P-256橢圓曲線密鑰(DER私鑰+PEM公鑰)

7.1. 前置準備

安裝 OpenSSL

  • Windows:官網(wǎng)下載安裝,或用 Git Bash 自帶的 openssl
  • Mac:brew install openssl
  • Linux:sudo apt install openssl

創(chuàng)建存放密鑰的文件夾

# 創(chuàng)建 sign 文件夾(必須先建,否則命令報錯)
mkdir sign

7.2. 執(zhí)行命令

# 1. 生成 prime256v1 橢圓曲線私鑰(PEM格式,中間文件)
openssl ecparam -genkey -name prime256v1 -noout -out sign/signature-private.pem
# 2. 轉(zhuǎn)換為 PKCS8 標準的 DER 格式私鑰(最終使用的私鑰)
openssl pkcs8 -topk8 -nocrypt -in sign/signature-private.pem -outform DER -out sign/signature-private.der
# 3. 從私鑰導(dǎo)出 PEM 格式公鑰(最終使用的公鑰)
openssl ec -in sign/signature-private.pem -pubout -out sign/signature-public.pem

7.3. 命令作用

命令作用
ecparam -genkey -name prime256v1生成 secp256r1/prime256v1 橢圓曲線密鑰(最常用的 ECC 算法)
pkcs8 -topk8 -outform DER把私鑰轉(zhuǎn)為 標準 PKCS8 + DER 二進制格式(程序/硬件常用)
ec -pubout從私鑰提取 公鑰,輸出 PEM 文本格式

7.4. 生成的 3 個文件說明

  1. signature-private.pem:臨時中間文件,可以刪除
  2. signature-private.der:最終私鑰(二進制 DER 格式,用于簽名)
  3. signature-public.pem:最終公鑰(文本 PEM 格式,用于驗簽)

7.5. 驗證密鑰是否正確

  1. 查看公鑰內(nèi)容
cat sign/signature-public.pem

正常輸出格式:

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
  1. 查看私鑰信息(驗證格式)
openssl ec -in sign/signature-private.der -inform DER -text -noout

顯示 prime256v1 曲線信息,就說明密鑰完全正常。

以上就是SpringBoot整合ECC實現(xiàn)文件簽名與驗簽功能的詳細內(nèi)容,更多關(guān)于SpringBoot ECC文件簽名與驗簽的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Springboot的application.properties或application.yml環(huán)境的指定運行與配置方式

    Springboot的application.properties或application.yml環(huán)境的指定運行與配置方

    本文主要介紹了Spring?Boot中配置文件的多種使用方式,包括配置文件的命名、激活、路徑指定以及優(yōu)先級,并結(jié)合示例進行了詳細說明
    2025-11-11
  • Springboot微服務(wù)打包Docker鏡像流程解析

    Springboot微服務(wù)打包Docker鏡像流程解析

    這篇文章主要介紹了Springboot微服務(wù)打包Docker鏡像流程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-08-08
  • MyBatis一級緩存避坑完全指南

    MyBatis一級緩存避坑完全指南

    這篇文章主要給大家介紹了關(guān)于MyBatis一級緩存避坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-11-11
  • VS Code配置Java環(huán)境全過程

    VS Code配置Java環(huán)境全過程

    文章介紹了如何在VSCode中配置和運行Java項目,包括安裝JDK、配置環(huán)境變量、安裝VSCode插件、處理不同場景的Java項目運行以及常見優(yōu)化方法
    2026-02-02
  • Spring Cloud Alibaba使用Sentinel實現(xiàn)接口限流

    Spring Cloud Alibaba使用Sentinel實現(xiàn)接口限流

    這篇文章主要介紹了Spring Cloud Alibaba使用Sentinel實現(xiàn)接口限流,本文詳細的介紹了Sentinel組件的用法以及接口限流,感興趣的可以了解一下
    2019-04-04
  • java 如何把byte轉(zhuǎn)化為KB、MB、GB的方法

    java 如何把byte轉(zhuǎn)化為KB、MB、GB的方法

    這篇文章主要介紹了java 如何把byte轉(zhuǎn)化為KB、MB、GB的方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • mybatis test標簽如何判斷值是否相等

    mybatis test標簽如何判斷值是否相等

    這篇文章主要介紹了mybatis test標簽判斷值是否相等的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 老生常談JVM的內(nèi)存溢出說明及參數(shù)調(diào)整

    老生常談JVM的內(nèi)存溢出說明及參數(shù)調(diào)整

    下面小編就為大家?guī)硪黄仙U凧VM的內(nèi)存溢出說明及參數(shù)調(diào)整。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • Java如何固定大小的線程池

    Java如何固定大小的線程池

    這篇文章主要介紹了Java固定大小的線程池操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • java中的JsonSerializer用法,前后端單位轉(zhuǎn)換必備

    java中的JsonSerializer用法,前后端單位轉(zhuǎn)換必備

    這篇文章主要介紹了java中的JsonSerializer用法,前后端單位轉(zhuǎn)換必備!具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評論

长治县| 荃湾区| 深圳市| 龙州县| 康定县| 什邡市| 黄陵县| 腾冲县| 珠海市| 乌拉特中旗| 山西省| 临沂市| 连江县| 井研县| 桃园市| 新乡市| 股票| 奉节县| 金湖县| 安福县| 隆化县| 克东县| 上犹县| 腾冲县| 紫阳县| 宁阳县| 温州市| 明光市| 米易县| 泽普县| 舟曲县| 双峰县| 呼图壁县| 英德市| 城市| 葵青区| 徐闻县| 大理市| 正安县| 田阳县| 全椒县|