0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

SpringBoot 接口簽名算法代碼設(shè)計

jf_ro2CN3Fa ? 來源:csdn ? 2023-11-07 15:11 ? 次閱讀

1概念

開放接口

開放接口是指不需要登錄憑證就允許被第三方系統(tǒng)調(diào)用的接口。為了防止開放接口被惡意調(diào)用,開放接口一般都需要驗簽才能被調(diào)用。提供開放接口的系統(tǒng)下面統(tǒng)一簡稱為"原系統(tǒng)"。

驗簽

驗簽是指第三方系統(tǒng)在調(diào)用接口之前,需要按照原系統(tǒng)的規(guī)則根據(jù)所有請求參數(shù)生成一個簽名(字符串),在調(diào)用接口時攜帶該簽名。原系統(tǒng)會驗證簽名的有效性,只有簽名驗證有效才能正常調(diào)用接口,否則請求會被駁回。

2接口驗簽調(diào)用流程

1. 約定簽名算法

第三方系統(tǒng)作為調(diào)用方,需要與原系統(tǒng)協(xié)商約定簽名算法(下面以SHA256withRSA簽名算法為例)。同時約定一個名稱(callerID),以便在原系統(tǒng)中來唯一標(biāo)識調(diào)用方系統(tǒng)。

2. 頒發(fā)非對稱密鑰對

簽名算法約定后之后,原系統(tǒng)會為每一個調(diào)用方系統(tǒng)專門生成一個專屬的非對稱密鑰對(RSA密鑰對)。私鑰頒發(fā)給調(diào)用方系統(tǒng),公鑰由原系統(tǒng)持有。

注意,調(diào)用方系統(tǒng)需要保管好私鑰(存到調(diào)用方系統(tǒng)的后端)。因為對于原系統(tǒng)而言,調(diào)用方系統(tǒng)是消息的發(fā)送方,其持有的私鑰唯一標(biāo)識了它的身份是原系統(tǒng)受信任的調(diào)用方。調(diào)用方系統(tǒng)的私鑰一旦泄露,調(diào)用方對原系統(tǒng)毫無信任可言。

3. 生成請求參數(shù)簽名

簽名算法約定后之后,生成簽名的原理如下(活動圖)。

abc46c7e-7567-11ee-939d-92fbcf53809c.png

為了確保生成簽名的處理細(xì)節(jié)與原系統(tǒng)的驗簽邏輯是匹配的,原系統(tǒng)一般都提供jar包或者代碼片段給調(diào)用方來生成簽名,否則可能會因為一些處理細(xì)節(jié)不一致導(dǎo)致生成的簽名是無效的。

4. 請求攜帶簽名調(diào)用

路徑參數(shù)中放入約定好的callerID,請求頭中放入調(diào)用方自己生成的簽名

3代碼設(shè)計

1. 簽名配置類

相關(guān)的自定義yml配置如下。RSA的公鑰和私鑰可以使用hutool的SecureUtil工具類來生成,注意公鑰和私鑰是base64編碼后的字符串

abd6939a-7567-11ee-939d-92fbcf53809c.png

定義一個配置類來存儲上述相關(guān)的自定義yml配置

importcn.hutool.crypto.asymmetric.SignAlgorithm;
importlombok.Data;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
importorg.springframework.boot.context.properties.ConfigurationProperties;
importorg.springframework.stereotype.Component;

importjava.util.Map;

/**
*簽名的相關(guān)配置
*/
@Data
@ConditionalOnProperty(value="secure.signature.enable",havingValue="true")//根據(jù)條件注入bean
@Component
@ConfigurationProperties("secure.signature")
publicclassSignatureProps{
privateBooleanenable;
privateMapkeyPair;

@Data
publicstaticclassKeyPairProps{
privateSignAlgorithmalgorithm;
privateStringpublicKeyPath;
privateStringpublicKey;
privateStringprivateKeyPath;
privateStringprivateKey;
}
}

2. 簽名管理類

定義一個管理類,持有上述配置,并暴露生成簽名和校驗簽名的方法。

注意,生成的簽名是將字節(jié)數(shù)組進(jìn)行十六進(jìn)制編碼后的字符串,驗簽時需要將簽名字符串進(jìn)行十六進(jìn)制解碼成字節(jié)數(shù)組

importcn.hutool.core.io.IoUtil;
importcn.hutool.core.io.resource.ResourceUtil;
importcn.hutool.core.util.HexUtil;
importcn.hutool.crypto.SecureUtil;
importcn.hutool.crypto.asymmetric.Sign;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnBean;
importorg.springframework.stereotype.Component;
importorg.springframework.util.ObjectUtils;
importtop.ysqorz.signature.model.SignatureProps;

importjava.nio.charset.StandardCharsets;

@ConditionalOnBean(SignatureProps.class)
@Component
publicclassSignatureManager{
privatefinalSignaturePropssignatureProps;

publicSignatureManager(SignaturePropssignatureProps){
this.signatureProps=signatureProps;
loadKeyPairByPath();
}

/**
*驗簽。驗證不通過可能拋出運(yùn)行時異常CryptoException
*
*@paramcallerID調(diào)用方的唯一標(biāo)識
*@paramrawData原數(shù)據(jù)
*@paramsignature待驗證的簽名(十六進(jìn)制字符串)
*@return驗證是否通過
*/
publicbooleanverifySignature(StringcallerID,StringrawData,Stringsignature){
Signsign=getSignByCallerID(callerID);
if(ObjectUtils.isEmpty(sign)){
returnfalse;
}

//使用公鑰驗簽
returnsign.verify(rawData.getBytes(StandardCharsets.UTF_8),HexUtil.decodeHex(signature));
}

/**
*生成簽名
*
*@paramcallerID調(diào)用方的唯一標(biāo)識
*@paramrawData原數(shù)據(jù)
*@return簽名(十六進(jìn)制字符串)
*/
publicStringsign(StringcallerID,StringrawData){
Signsign=getSignByCallerID(callerID);
if(ObjectUtils.isEmpty(sign)){
returnnull;
}
returnsign.signHex(rawData);
}

publicSignaturePropsgetSignatureProps(){
returnsignatureProps;
}

publicSignatureProps.KeyPairPropsgetKeyPairPropsByCallerID(StringcallerID){
returnsignatureProps.getKeyPair().get(callerID);
}

privateSigngetSignByCallerID(StringcallerID){
SignatureProps.KeyPairPropskeyPairProps=signatureProps.getKeyPair().get(callerID);
if(ObjectUtils.isEmpty(keyPairProps)){
returnnull;//無效的、不受信任的調(diào)用方
}
returnSecureUtil.sign(keyPairProps.getAlgorithm(),keyPairProps.getPrivateKey(),keyPairProps.getPublicKey());
}

/**
*加載非對稱密鑰對
*/
privatevoidloadKeyPairByPath(){
//支持類路徑配置,形如:classpath:secure/public.txt
//公鑰和私鑰都是base64編碼后的字符串
signatureProps.getKeyPair()
.forEach((key,keyPairProps)->{
//如果配置了XxxKeyPath,則優(yōu)先XxxKeyPath
keyPairProps.setPublicKey(loadKeyByPath(keyPairProps.getPublicKeyPath()));
keyPairProps.setPrivateKey(loadKeyByPath(keyPairProps.getPrivateKeyPath()));
if(ObjectUtils.isEmpty(keyPairProps.getPublicKey())||
ObjectUtils.isEmpty(keyPairProps.getPrivateKey())){
thrownewRuntimeException("Nopublicandprivatekeyfilesconfigured");
}
});
}

privateStringloadKeyByPath(Stringpath){
if(ObjectUtils.isEmpty(path)){
returnnull;
}
returnIoUtil.readUtf8(ResourceUtil.getStream(path));
}
}

3. 自定義驗簽注解

有些接口需要驗簽,但有些接口并不需要,為了靈活控制哪些接口需要驗簽,自定義一個驗簽注解

importjava.lang.annotation.*;


/**
*該注解標(biāo)注于Controller類的方法上,表明該請求的參數(shù)需要校驗簽名
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public@interfaceVerifySignature{
}4. AOP實現(xiàn)驗簽邏輯

驗簽邏輯不能放在攔截器中,因為攔截器中不能直接讀取body的輸入流,否則會造成后續(xù)@RequestBody的參數(shù)解析器讀取不到body。

由于body輸入流只能讀取一次,因此需要使用ContentCachingRequestWrapper包裝請求,緩存body內(nèi)容(見第5點),但是該類的緩存時機(jī)是在@RequestBody的參數(shù)解析器中。

因此,滿足2個條件才能獲取到ContentCachingRequestWrapper中的body緩存:

接口的入?yún)⒈仨毚嬖贎RequestBody

讀取body緩存的時機(jī)必須在@RequestBody的參數(shù)解析之后,比如說:AOP、Controller層的邏輯內(nèi)。注意攔截器的時機(jī)是在參數(shù)解析之前的

綜上,標(biāo)注了@VerifySignature注解的controlle層方法的入?yún)⒈仨毚嬖贎RequestBody,AOP中驗簽時才能獲取到body的緩存!

importcn.hutool.crypto.CryptoException;
importlombok.extern.slf4j.Slf4j;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.annotation.Before;
importorg.aspectj.lang.annotation.Pointcut;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnBean;
importorg.springframework.stereotype.Component;
importorg.springframework.util.ObjectUtils;
importorg.springframework.web.context.request.RequestAttributes;
importorg.springframework.web.context.request.ServletWebRequest;
importorg.springframework.web.servlet.HandlerMapping;
importorg.springframework.web.util.ContentCachingRequestWrapper;
importtop.ysqorz.common.constant.BaseConstant;
importtop.ysqorz.config.SpringContextHolder;
importtop.ysqorz.config.aspect.PointCutDef;
importtop.ysqorz.exception.auth.AuthorizationException;
importtop.ysqorz.exception.param.ParamInvalidException;
importtop.ysqorz.signature.model.SignStatusCode;
importtop.ysqorz.signature.model.SignatureProps;
importtop.ysqorz.signature.util.CommonUtils;

importjavax.annotation.Resource;
importjavax.servlet.http.HttpServletRequest;
importjava.nio.charset.StandardCharsets;
importjava.util.Map;

@ConditionalOnBean(SignatureProps.class)
@Component
@Slf4j
@Aspect
publicclassRequestSignatureAspectimplementsPointCutDef{
@Resource
privateSignatureManagersignatureManager;

@Pointcut("@annotation(top.ysqorz.signature.enumeration.VerifySignature)")
publicvoidannotatedMethod(){
}

@Pointcut("@within(top.ysqorz.signature.enumeration.VerifySignature)")
publicvoidannotatedClass(){
}

@Before("apiMethod()&&(annotatedMethod()||annotatedClass())")
publicvoidverifySignature(){
HttpServletRequestrequest=SpringContextHolder.getRequest();

StringcallerID=request.getParameter(BaseConstant.PARAM_CALLER_ID);
if(ObjectUtils.isEmpty(callerID)){
thrownewAuthorizationException(SignStatusCode.UNTRUSTED_CALLER);//不受信任的調(diào)用方
}

//從請求頭中提取簽名,不存在直接駁回
Stringsignature=request.getHeader(BaseConstant.X_REQUEST_SIGNATURE);
if(ObjectUtils.isEmpty(signature)){
thrownewParamInvalidException(SignStatusCode.REQUEST_SIGNATURE_INVALID);//無效簽名
}

//提取請求參數(shù)
StringrequestParamsStr=extractRequestParams(request);
//驗簽。驗簽不通過拋出業(yè)務(wù)異常
verifySignature(callerID,requestParamsStr,signature);
}

@SuppressWarnings("unchecked")
publicStringextractRequestParams(HttpServletRequestrequest){
//@RequestBody
Stringbody=null;
//驗簽邏輯不能放在攔截器中,因為攔截器中不能直接讀取body的輸入流,否則會造成后續(xù)@RequestBody的參數(shù)解析器讀取不到body
//由于body輸入流只能讀取一次,因此需要使用ContentCachingRequestWrapper包裝請求,緩存body內(nèi)容,但是該類的緩存時機(jī)是在@RequestBody的參數(shù)解析器中
//因此滿足2個條件才能使用ContentCachingRequestWrapper中的body緩存
//1.接口的入?yún)⒈仨毚嬖贎RequestBody
//2.讀取body緩存的時機(jī)必須在@RequestBody的參數(shù)解析之后,比如說:AOP、Controller層的邏輯內(nèi)。注意攔截器的時機(jī)是在參數(shù)解析之前的
if(requestinstanceofContentCachingRequestWrapper){
ContentCachingRequestWrapperrequestWrapper=(ContentCachingRequestWrapper)request;
body=newString(requestWrapper.getContentAsByteArray(),StandardCharsets.UTF_8);
}

//@RequestParam
MapparamMap=request.getParameterMap();

//@PathVariable
ServletWebRequestwebRequest=newServletWebRequest(request,null);
MapuriTemplateVarNap=(Map)webRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE,RequestAttributes.SCOPE_REQUEST);

returnCommonUtils.extractRequestParams(body,paramMap,uriTemplateVarNap);
}

/**
*驗證請求參數(shù)的簽名
*/
publicvoidverifySignature(StringcallerID,StringrequestParamsStr,Stringsignature){
try{
booleanverified=signatureManager.verifySignature(callerID,requestParamsStr,signature);
if(!verified){
thrownewCryptoException("Thesignatureverificationresultisfalse.");
}
}catch(Exceptionex){
log.error("Failedtoverifysignature",ex);
thrownewAuthorizationException(SignStatusCode.REQUEST_SIGNATURE_INVALID);//轉(zhuǎn)換為業(yè)務(wù)異常拋出
}
}
}
importorg.aspectj.lang.annotation.Pointcut;

publicinterfacePointCutDef{
@Pointcut("execution(public*top.ysqorz..controller.*.*(..))")
defaultvoidcontrollerMethod(){
}

@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
defaultvoidpostMapping(){
}

@Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)")
defaultvoidgetMapping(){
}

@Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)")
defaultvoidputMapping(){
}

@Pointcut("@annotation(org.springframework.web.bind.annotation.DeleteMapping)")
defaultvoiddeleteMapping(){
}

@Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
defaultvoidrequestMapping(){
}

@Pointcut("controllerMethod()&&(requestMapping()||postMapping()||getMapping()||putMapping()||deleteMapping())")
defaultvoidapiMethod(){
}
}

5. 解決請求體只能讀取一次

解決方案就是包裝請求,緩存請求體。SpringBoot也提供了ContentCachingRequestWrapper來解決這個問題。但是第4點中也詳細(xì)描述了,由于它的緩存時機(jī),所以它的使用有限制條件。也可以參考網(wǎng)上的方案,自己實現(xiàn)一個請求的包裝類來緩存請求體

importlombok.NonNull;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnBean;
importorg.springframework.stereotype.Component;
importorg.springframework.web.filter.OncePerRequestFilter;
importorg.springframework.web.util.ContentCachingRequestWrapper;
importtop.ysqorz.signature.model.SignatureProps;

importjavax.servlet.FilterChain;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjava.io.IOException;

@ConditionalOnBean(SignatureProps.class)
@Component
publicclassRequestCachingFilterextendsOncePerRequestFilter{
/**
*This{@codedoFilter}implementationstoresarequestattributefor
*"alreadyfiltered",proceedingwithoutfilteringagainifthe
*attributeisalreadythere.
*
*@paramrequestrequest
*@paramresponseresponse
*@paramfilterChainfilterChain
*@see#getAlreadyFilteredAttributeName
*@see#shouldNotFilter
*@see#doFilterInternal
*/
@Override
protectedvoiddoFilterInternal(@NonNullHttpServletRequestrequest,@NonNullHttpServletResponseresponse,@NonNullFilterChainfilterChain)
throwsServletException,IOException{
booleanisFirstRequest=!isAsyncDispatch(request);
HttpServletRequestrequestWrapper=request;
if(isFirstRequest&&!(requestinstanceofContentCachingRequestWrapper)){
requestWrapper=newContentCachingRequestWrapper(request);
}
filterChain.doFilter(requestWrapper,response);
}
}

注冊過濾器

importorg.springframework.boot.autoconfigure.condition.ConditionalOnBean;
importorg.springframework.boot.web.servlet.FilterRegistrationBean;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
importtop.ysqorz.signature.model.SignatureProps;

@Configuration
publicclassFilterConfig{
@ConditionalOnBean(SignatureProps.class)
@Bean
publicFilterRegistrationBeanrequestCachingFilterRegistration(
RequestCachingFilterrequestCachingFilter){
FilterRegistrationBeanbean=newFilterRegistrationBean<>(requestCachingFilter);
bean.setOrder(1);
returnbean;
}
}

6. 自定義工具類

importcn.hutool.core.util.StrUtil;
importorg.springframework.lang.Nullable;
importorg.springframework.util.ObjectUtils;

importjava.util.Arrays;
importjava.util.Map;
importjava.util.stream.Collectors;

publicclassCommonUtils{
/**
*提取所有的請求參數(shù),按照固定規(guī)則拼接成一個字符串
*
*@parambodypost請求的請求體
*@paramparamMap路徑參數(shù)(QueryString)。形如:name=zhangsan&age=18&label=A&label=B
*@paramuriTemplateVarNap路徑變量(PathVariable)。形如:/{name}/{age}
*@return所有的請求參數(shù)按照固定規(guī)則拼接成的一個字符串
*/
publicstaticStringextractRequestParams(@NullableStringbody,@NullableMapparamMap,
@NullableMapuriTemplateVarNap){
//body:{userID:"xxx"}

//路徑參數(shù)
//name=zhangsan&age=18&label=A&label=B
//=>["name=zhangsan","age=18","label=A,B"]
//=>name=zhangsan&age=18&label=A,B
StringparamStr=null;
if(!ObjectUtils.isEmpty(paramMap)){
paramStr=paramMap.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(entry->{
//拷貝一份按字典序升序排序
String[]sortedValue=Arrays.stream(entry.getValue()).sorted().toArray(String[]::new);
returnentry.getKey()+"="+joinStr(",",sortedValue);
})
.collect(Collectors.joining("&"));
}

//路徑變量
///{name}/{age}=>/zhangsan/18=>zhangsan,18
StringuriVarStr=null;
if(!ObjectUtils.isEmpty(uriTemplateVarNap)){
uriVarStr=joinStr(",",uriTemplateVarNap.values().stream().sorted().toArray(String[]::new));
}

//{userID:"xxx"}#name=zhangsan&age=18&label=A,B#zhangsan,18
returnjoinStr("#",body,paramStr,uriVarStr);
}

/**
*使用指定分隔符,拼接字符串
*
*@paramdelimiter分隔符
*@paramstrs需要拼接的多個字符串,可以為null
*@return拼接后的新字符串
*/
publicstaticStringjoinStr(Stringdelimiter,@NullableString...strs){
if(ObjectUtils.isEmpty(strs)){
returnStrUtil.EMPTY;
}
StringBuildersbd=newStringBuilder();
for(inti=0;i
                                        
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8596

    瀏覽量

    151145
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4612

    瀏覽量

    92884
  • SpringBoot
    +關(guān)注

    關(guān)注

    0

    文章

    173

    瀏覽量

    178

原文標(biāo)題:SpringBoot 接口簽名校驗實踐

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    基于橢圓曲線算法的數(shù)字簽名技術(shù)研究

    基于橢圓曲線算法的數(shù)字簽名技術(shù)的基本原理及其安全性,展望了公鑰密碼體制未來的發(fā)展方向?!娟P(guān)鍵詞】:橢圓曲線算法;;數(shù)字簽名;;網(wǎng)絡(luò)安全【DOI】:CNKI:SUN:GSKJ.0.201
    發(fā)表于 04-23 11:29

    SpringBoot知識總結(jié)

    SpringBoot干貨學(xué)習(xí)總結(jié)
    發(fā)表于 08-01 10:40

    怎樣去使用springboot

    怎樣去使用springboot呢?學(xué)習(xí)springboot需要懂得哪些?
    發(fā)表于 10-25 07:13

    在哪里可以找到用于導(dǎo)出AN10957上顯示的結(jié)果的確切CMAC簽名/mac代碼算法?

    得到與 AN10957 不匹配的結(jié)果 (mac)。有誰知道我在哪里可以找到用于導(dǎo)出 AN10957 上顯示的結(jié)果的確切 CMAC 簽名/mac 代碼算法
    發(fā)表于 04-06 06:25

    什么是數(shù)字簽名算法(DSA)

    什么是數(shù)字簽名算法(DSA) DSA(Digital Signature Algorithm,數(shù)字簽名算法,用作數(shù)字簽名標(biāo)準(zhǔn)的一部分),它
    發(fā)表于 04-03 16:01 ?3536次閱讀

    一種錯誤簽名混合篩選算法

    針對分級身份密碼( HIBC)批驗簽過程中的錯誤簽名快速識別問題,設(shè)計實現(xiàn)了一種錯誤簽名混合篩選算法。針對HIBC簽名算法不完全聚合的特點,
    發(fā)表于 12-07 15:36 ?0次下載

    如何使用ECDSA算法生成數(shù)字簽名

    。 區(qū)塊鏈中的數(shù)字簽名 ECDSA算法 從A點到B點在橢圓曲線上的切線 根據(jù)wiki ECDSA為: 橢圓曲線密碼體制是一種基于有限域橢圓曲線代數(shù)結(jié)構(gòu)的公鑰密碼體制。與非對稱密碼學(xué)相比
    發(fā)表于 12-27 14:12 ?9157次閱讀
    如何使用ECDSA<b class='flag-5'>算法</b>生成數(shù)字<b class='flag-5'>簽名</b>

    MuSig簽名方案可替代當(dāng)前比特幣的ECDSA簽名算法

    當(dāng)前,比特幣和其他區(qū)塊鏈普遍采用的是ECDSA簽名驗證算法。這顯然是中本聰在2008年根據(jù)當(dāng)時廣泛使用和未授權(quán)的數(shù)字簽名系統(tǒng)所做出的技術(shù)決定。然而,ECDSA簽名存在一些嚴(yán)重的技術(shù)限制
    發(fā)表于 02-20 13:34 ?1581次閱讀

    Schnorr簽名和ECDSA簽名技術(shù)介紹

    Schnorr簽名是一個使BCH區(qū)塊鏈實現(xiàn)技術(shù)領(lǐng)先的強(qiáng)大功能,因為Schnorr簽名方案直接促進(jìn)了BCH的隱私性和交易能力。Schnorr簽名算法是由著名的密碼學(xué)家Claus Schn
    發(fā)表于 05-16 10:32 ?2806次閱讀

    schnorr簽名算法相比ECDSA具有哪些優(yōu)勢

    schnorr 簽名算法相比 ECDSA 來講,對于上述的優(yōu)點,除了尚未標(biāo)準(zhǔn)化之外幾乎沒有缺點。而且由于兩種算法都基于同一個橢圓曲線,整個關(guān)于簽名的升級成本也是很低的。
    發(fā)表于 08-08 11:22 ?3436次閱讀

    基于ECDSA原理的FISCO BCOS交易簽名算法解析

    FISCO BCOS交易簽名算法基于ECDSA原理進(jìn)行設(shè)計,ECDSA也是比特幣和以太坊采用的交易簽名算法。
    發(fā)表于 02-19 16:46 ?1885次閱讀
    基于ECDSA原理的FISCO BCOS交易<b class='flag-5'>簽名</b><b class='flag-5'>算法</b>解析

    數(shù)據(jù)簽名的雙向簽名和重簽名的原理和資料簡介

    什么是數(shù)據(jù)簽名代碼簽名) 1.計算出需要校驗的數(shù)據(jù)HASH值 2.將校驗HASH值進(jìn)行RSA加密 3.這部分利用RSA加密過后的HASH值,我們稱之為“數(shù)字簽名
    發(fā)表于 11-02 08:00 ?14次下載
    數(shù)據(jù)<b class='flag-5'>簽名</b>的雙向<b class='flag-5'>簽名</b>和重<b class='flag-5'>簽名</b>的原理和資料簡介

    基于ElGamal數(shù)字簽名算法的區(qū)塊鏈共識算法

    聯(lián)盟鏈?zhǔn)且环N允許授權(quán)節(jié)點加入網(wǎng)絡(luò)的區(qū)塊鏈,當(dāng)存在網(wǎng)絡(luò)狀況不理想等狀況時,會出現(xiàn)節(jié)點動態(tài)加入退出的問題。為此,在環(huán)簽名理論、 Elgamal數(shù)字簽名算法與PBFT算法的基礎(chǔ)上,提出一種
    發(fā)表于 05-19 11:51 ?10次下載

    SpringBoot如何實現(xiàn)啟動過程中執(zhí)行代碼

    目前開發(fā)的SpringBoot項目在啟動的時候需要預(yù)加載一些資源。而如何實現(xiàn)啟動過程中執(zhí)行代碼,或啟動成功后執(zhí)行,是有很多種方式可以選擇,我們可以在static代碼塊中實現(xiàn),也可以在構(gòu)造方法里實現(xiàn),也可以使用@PostConst
    的頭像 發(fā)表于 06-20 17:32 ?1455次閱讀

    什么是 SpringBoot

    本文從為什么要有 `SpringBoot`,以及 `SpringBoot` 到底方便在哪里開始入手,逐步分析了 `SpringBoot` 自動裝配的原理,最后手寫了一個簡單的 `start` 組件,通過實戰(zhàn)來體會了 `
    的頭像 發(fā)表于 04-07 11:28 ?1314次閱讀
    什么是 <b class='flag-5'>SpringBoot</b>?