signature_signature 公钥签名 - CSDN
signature 订阅
Signature 展开全文
Signature
信息
操作系统
未知
开发语言
开源协议
未知
Signature
insert custom text into posts and emails... 已更新 2007 年 03 月 5 日 下载地址
收起全文
精华内容
参与话题
  • java中的Signature

    万次阅读 2020-10-15 20:02:59
    随时随地阅读更多技术实战干货,获取项目源码、学习资料,请关注源代码社区公众号(ydmsq666)、博主微信(guyun297890152)、QQ技术交流群(183198395)。... java.security.Signature 声明:public abs...

    随时随地阅读更多技术实战干货,获取项目源码、学习资料,请关注源代码社区公众号(ydmsq666)、QQ技术交流群(183198395)。

    一、概述

    类结构:

     

    java.lang.Object
      java.security.SignatureSpi
          java.security.Signature
    

    声明:public abstract class Signature extends SignatureSpi

     

     

    二、字段

    1、protected static final int UNINITIALIZED   可能的 state 值,表示尚未初始化此签名对象。 

    2、protected static final int SIGN  可能的 state 值,表示此签名对象已经初始化签名。

    3、protected static final int VERIFY  可能的 state 值,表示此签名对象已经初始化验证。

    4、protected int state  此签名对象的当前状态。

     

    三、构造方法

     

    protected Signature(String algorithm)  创建指定算法的 Signature 对象。
    
     

    参数:algorithm - 算法的标准字符串名称。

    四、方法详细

    1、public static Signature getInstance(String algorithm) throws NoSuchAlgorithmException 返回实现指定签名算法的 Signature 对象。

    此方法从首选的提供者开始遍历已注册安全提供者列表。返回封装 SignatureSpi 实现的新 Signature 对象,该实现取自第一个支持指定算法的 Provider。 

    注意,可以通 Security.getProviders() 方法获取已注册提供者列表。

    参数:algorithm - 所请求算法的标准名称。有关标准算法名称的信息

    返回:新 Signature 对象。

    抛出:NoSuchAlgorithmException - 如果没有 Provider 支持指定算法的 Signature 实现。

    2、public static Signature getInstance(String algorithm,String provider)throws NoSuchAlgorithmException,NoSuchProviderException

     

    返回实现指定签名算法的 Signature 对象。

    返回封装 SignatureSpi 实现的新 Signature 对象,该实现取自指定提供者。指定的提供者必须在安全提供者列表中注册。

    注意,可以通过 Security.getProviders() 方法获取已注册提供者列表。 

     

    参数:

    algorithm - 所请求算法的标准名称。有关标准算法名称的信息,请参阅 Java Cryptography Architecture API Specification & Reference 中的附录 A。

    provider - 提供者的名称。

    返回:

    新 Signature 对象。

    抛出:

    NoSuchAlgorithmException - 如果指定算法的 SignatureSpi 实现不能从指定提供者获得。

    NoSuchProviderException - 如果指定提供者未在安全提供者列表中注册。

    IllegalArgumentException - 如果提供者的名称为 null 或空。

     

    3、public static Signature getInstance(String algorithm,  Provider provider) throws NoSuchAlgorithmException  返回实现指定签名算法的 Signature 对象。

     

    返回封装 SignatureSpi 实现的新 Signature 对象,该实现取自指定 Provider 对象。注意,指定 Provider 对象无需在提供者列表中注册。

     

    参数:

    algorithm - 所请求算法的标准名称。有关标准算法名称的信息,请参阅 Java Cryptography Architecture API Specification & Reference 中的附录 A。

    provider - 提供者。

    返回:

    新 Signature 对象。

    抛出:

    NoSuchAlgorithmException - 如果指定算法的 SignatureSpi 实现不能从指定提供者对象获得。

    IllegalArgumentException - 如果 provider 为 null。

    4、public final Provider getProvider()  返回此签名对象的提供者。

     

     

    5、public final void initVerify(PublicKey publicKey) throws InvalidKeyException  初始化此用于验证的对象。如果使用其他参数再次调用此方法,此调用的结果将无效。

     

    参数:

    publicKey - 将验证其签名的标识的公钥。

    抛出:

    InvalidKeyException - 如果密钥无效。

     

    6、public final void initVerify(Certificate certificate) throws InvalidKeyException  使用来自给定证书的公钥初始化此用于验证的对象。

     

    如果证书是 X.509 类型且具有标记为重要的 key usage 扩展字段,并且 key usage 扩展字段的值暗示着证书中的公钥及其对应的私钥不允许用于数字签名,则抛出 InvalidKeyException

     

    参数:

    certificate - 将验证其签名的标识的证书。

    抛出:

    InvalidKeyException - 如果证书中的公钥未被正确解码,或者未包括所请求的参数信息,或者不能用于数字签名。

    7、public final void initSign(PrivateKey privateKey) throws InvalidKeyException  初始化这个用于签名的对象。如果使用其他参数再次调用此方法,此调用的结果将无效。

     

     

    参数:

    privateKey - 将生成其签名的标识的私钥。

    抛出:

    InvalidKeyException - 如果密钥无效。

     

     

    8、public final void initSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException 

    初始化这个用于签名的对象。如果使用其他参数再次调用此方法,此调用的结果将无效。

     

    参数:

    privateKey - 将生成其签名的标识的私钥。

    random - 此签名的随机源。

    抛出:

    InvalidKeyException - 如果密钥无效。

     

     

    9、public final byte[] sign() throws SignatureException   返回所有已更新数据的签名字节。签名的格式取决于基础签名方案。

     

    对此方法的调用将把此签名对象重新设置到以前为进行签名而通过调用 initSign(PrivateKey) 对其初始化时的状态。也就是说,如果需要,通过重新调用 updatesign,可重新设置对象,并且可以通过它从同一个签署者中生成其他人的签名。

     

    返回:

    签名操作结果的签名字节。

    抛出:

    SignatureException - 如果此签名对象未得到正确初始化,或者此签名算法不能处理所提供的输入数据。

    10、public final int sign(byte[] outbuf,int offset, int len) throws SignatureException  完成签名操作并从 offset 开始,将得到的签名字节存储在提供的缓冲区 outbuf 中。签名的格式取决于基础签名方案。 

     

     

    此签名对象将被重新设置到其初始状态(对其中的一种 initSign 方法调用之后所处的状态),并且可重新使用此签名对象,用同一个私钥生成将来的签名。 

    参数:

    outbuf - 输出签名结果的缓冲区。

    offset - 到存储签名的 outbuf 的偏移量。

    len - outbuf 中分配给签名的字节数。

    返回:

    放入 outbuf 中的字节数。

    抛出:

    SignatureException - 如果此签名对象未得到正确初始化,如果此签名算法无法处理所提供的输入数据,或者 len 少于实际签名长度。

    11、public final boolean verify(byte[] signature)  throws SignatureException  验证传入的签名。

    对此方法的调用将把此签名对象重新设置到以前为进行验证而通过调用 initVerify(PublicKey) 对其初始化时的状态。也就是说,该对象将被重新设置,并且可以用来验证调用 initVerify 时其公钥所指定的标识中的其他签名。 

     

    参数:

    signature - 要验证的签名字节。

    返回:

    如果签名得到验证,则返回 true,否则将返回 false。

    抛出:

    SignatureException - 如果此签名对象未得到正确初始化,传入的签名未得到正确解码,或类型错误,如果此签名算法不能处理所提供的输入数据等。

    12、public final boolean verify(byte[] signature, int offset,int length) throws SignatureException  从指定的偏移量开始,验证指定的 byte 数组中传入的签名。

     

    对此方法的调用将把此签名对象重新设置到以前为进行验证而通过调用 initVerify(PublicKey) 对其初始化时的状态。也就是说,该对象将被重新设置,并且可以用来验证调用 initVerify 时其公钥所指定的标识中的其他签名。

     

    参数:

    signature - 要验证的签名字节。

    offset - byte 数组中起始处的偏移量。

    length - 要使用的字节数(从偏移量起始处算起)。

    返回:

    如果签名得到验证,则返回 true,否则将返回 false。

    抛出:

    SignatureException - 如果此签名对象未得到正确初始化,传入的签名未得到正确解码,或者类型错误,如果此签名算法无法处理所提供的输入数据等。

    IllegalArgumentException - 如果 signature byte 数组为 null,或者 offsetlength 小于 0,或者 offsetlength 的和大于 signature byte 数组的长度。

     

    13、public final void update(byte b) throws SignatureException  更新要由字节签名或验证的数据。

     

    参数:

    b - 用于更新的字节。

    抛出:

    SignatureException - 如果此签名对象未得到正确初始化。

     

    14、public final void update(byte[] data) throws SignatureException  使用指定的 byte 数组更新要签名或验证的数据。

     

    参数:

    data - 用于更新的 byte 数组。

    抛出:

    SignatureException - 如果此签名对象未得到正确初始化。

    15、public final void update(byte[] data, int off, int len) throws SignatureException   从指定的偏移量开始,使用指定的 byte 数组更新要签名或验证的数据。

     

     

    参数:

    data - byte 数组。

    off - 到 byte 数组开始处的偏移量。

    len - 要使用的字节数,从偏移量起始处算起。

    抛出:

    SignatureException - 如果此签名对象未得到正确初始化。

    16、public final void update(ByteBuffer data) throws SignatureException  使用指定的 ByteBuffer 更新要签名或验证的数据。处理从 data.position() 处开始的 data.remaining() 字节。返回时,缓冲区的位置将等于其限制;其限制并未改变。 

     

     

    参数:

    data - ByteBuffer

    抛出:

    SignatureException - 如果此签名对象未得到正确初始化。

     

    17、public final String getAlgorithm()  返回此签名对象的算法名称。

     

    18、public String toString()  返回此签名对象的字符串表示形式,以提供包括对象状态和所用算法名称在内的信息。

     

    19、public final void setParameter(String param, Object value)  throws InvalidParameterException  

     

    已过时。 使用 setParameter 取代。

     

    将指定的算法参数设置为指定的值。此方法提供了一种通用机制,通过它可以设置此对象的各种参数。参数可以是算法的任何可设置参数,如参数大小、签名生成的随机源位(如果适当),或者表示是否执行具体但可选的计算。每个参数都需要统一的、特定于算法的命名方案,但此时尚未指定这种方案。

     

    参数:

    param - 参数的字符串标示符。

    value - 参数值。

    抛出:

    InvalidParameterException - 如果 param 是此算法引擎的无效参数,或者已经设置参数并且不能重新设置,或者出现了安全异常等等。

    另请参见:

    getParameter(java.lang.String)

    20、public final void setParameter(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException

     

     

    使用指定的参数集初始化此签名引擎。

     

    参数:

    params - 参数

    抛出:

    InvalidAlgorithmParameterException - 如果给定的参数不适合此签名引擎

    另请参见:

    getParameters()

    21、public final AlgorithmParameters getParameters()  

     

     

    返回与此签名对象一起使用的参数。

    返回的参数可能与用来初始化此签名的参数相同;如果此签名需要算法参数但却未使用任何参数进行初始化,则返回的参数可能会包含由底层签名实现所使用的默认值和随机生成的参数值的组合。

     

    返回:

    用于此签名的参数,如果此签名不使用任何参数,则返回 null。

    从以下版本开始:

    1.4

    另请参见:

    setParameter(AlgorithmParameterSpec)

    22、public final Object getParameter(String param) throws InvalidParameterException

     

     

    已过时。

     

    获取指定的算法参数的值。此方法提供了一种通用机制,通过它能够获取此对象的各种参数。参数可以是算法的任何可设置参数,如参数大小、签名生成的随机源位(如果适当),或者表示是否执行具体但可选的计算。每个参数都需要统一的、特定于算法的命名方案,但此时尚未指定这种方案。

     

    参数:

    param - 参数的字符串名称。

    返回:

    表示参数值的对象,如果没有,则返回 null。

    抛出:

    InvalidParameterException - 如果 param 是此引擎的无效参数,或者尝试获取此参数时出现其他异常。

    另请参见:

    setParameter(String, Object)

    23、public Object clone() throws CloneNotSupportedException

     

     

    如果此实现可以复制,则返回副本。

     

    覆盖:

    SignatureSpi 中的 clone

    返回:

    如果此实现可以复制,则返回一个副本。

    抛出:

    CloneNotSupportedException - 如果调用一个不支持 Cloneable 的实现。

    另请参见:

    Cloneable

     

     

     

     

     

    
     

     

    展开全文
  • java接口签名(Signature)实现方案续

    千次阅读 热门讨论 2018-12-26 13:24:38
     由于之前写过的一片文章 (java接口签名(Signature)实现方案 )收获了很多好评,此次来说一下另一种简单粗暴的签名方案。相对于之前的签名方案,对body、paramenter、path variable的获取都做了简化的处理。也...

    一、前言

      由于之前写过的一片文章 (java接口签名(Signature)实现方案 )收获了很多好评,此次来说一下另一种简单粗暴的签名方案。相对于之前的签名方案,对body、paramenter、path variable的获取都做了简化的处理。也就是说这种方式针所有数据进行了签名,并不能指定某些数据进行签名。

    二、签名规则

      1、线下分配appid和appsecret,针对不同的调用方分配不同的appid和appsecret

      2、加入timestamp(时间戳),10分钟内数据有效

      3、加入流水号nonce(防止重复提交),至少为10位。针对查询接口,流水号只用于日志落地,便于后期日志核查。 针对办理类接口需校验流水号在有效期内的唯一性,以避免重复请求。

      4、加入signature,所有数据的签名信息。

      以上红色字段放在请求头中。

    三、签名的生成

      signature 字段生成规则如下。

       1、数据部分

      Path Variable:按照path中的字典顺序将所有value进行拼接

      Parameter:按照key=values(多个value按照字典顺序拼接)字典顺序进行拼接

      Body:从request inputstream中获取保存为String形式

           

      如果存在多种数据形式,则按照body、parameter、path variable的顺序进行再拼接,得到所有数据的拼接值。

      上述拼接的值记作 Y。

      2、请求头部分

      X=”appid=xxxnonce=xxxtimestamp=xxx”

      3、生成签名

      最终拼接值=XY

      最后将最终拼接值按照如下方法进行加密得到签名。

      signature=org.apache.commons.codec.digest.HmacUtils.AesEncodeUtil(app secret, 拼接的值);

    四、签名算法实现

      注:省去了X=”appid=xxxnonce=xxxtimestamp=xxx”这部分。

      1、自定义Request对象

      为什么要自定义request对象,因为我们要获取request inputstream(默认只能获取一次)。

    public class BufferedHttpServletRequest extends HttpServletRequestWrapper {
    
        private ByteBuf buffer;
    
        private final AtomicBoolean isCached = new AtomicBoolean();
    
        public BufferedHttpServletRequest(HttpServletRequest request, int initialCapacity) {
            super(request);
            int contentLength = request.getContentLength();
            int min = Math.min(initialCapacity, contentLength);
            if (min < 0) {
                buffer = Unpooled.buffer(0);
            } else {
                buffer = Unpooled.buffer(min, contentLength);
            }
        }
    
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            //Only returning data from buffer if it is readonly, which means the underlying stream is EOF or closed.
            if (isCached.get()) {
                return new NettyServletInputStream(buffer);
            }
            return new ContentCachingInputStream(super.getInputStream());
        }
    
        public void release() {
            buffer.release();
        }
    
        private class ContentCachingInputStream extends ServletInputStream {
    
            private final ServletInputStream is;
    
            public ContentCachingInputStream(ServletInputStream is) {
                this.is = is;
            }
    
            @Override
            public int read() throws IOException {
                int ch = this.is.read();
                if (ch != -1) {
                    //Stream is EOF, set this buffer to readonly state
                    buffer.writeByte(ch);
                } else {
                    isCached.compareAndSet(false, true);
                }
                return ch;
            }
    
            @Override
            public void close() throws IOException {
                //Stream is closed, set this buffer to readonly state
                try {
                    is.close();
                } finally {
                    isCached.compareAndSet(false, true);
                }
            }
    
            @Override
            public boolean isFinished() {
                throw new UnsupportedOperationException("Not yet implemented!");
            }
    
            @Override
            public boolean isReady() {
                throw new UnsupportedOperationException("Not yet implemented!");
            }
    
            @Override
            public void setReadListener(ReadListener readListener) {
                throw new UnsupportedOperationException("Not yet implemented!");
            }
        }
    }

      替换默认的request对象

    @Configuration
    public class FilterConfig {
        @Bean
        public RequestCachingFilter requestCachingFilter() {
            return new RequestCachingFilter();
        }
    
        @Bean
        public FilterRegistrationBean requestCachingFilterRegistration(
                RequestCachingFilter requestCachingFilter) {
            FilterRegistrationBean bean = new FilterRegistrationBean(requestCachingFilter);
            bean.setOrder(1);
            return bean;
        }
    }
    public class RequestCachingFilter extends OncePerRequestFilter {
        private static Logger LOGGER = LoggerFactory.getLogger(RequestCachingFilter.class);
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            boolean isFirstRequest = !isAsyncDispatch(request);
            HttpServletRequest requestToUse = request;
            if (isFirstRequest && !(request instanceof BufferedHttpServletRequest)) {
                requestToUse = new BufferedHttpServletRequest(request, 1024);
            }
            try {
                filterChain.doFilter(requestToUse, response);
            } catch (Exception e) {
                LOGGER.error("RequestCachingFilter>>>>>>>>>", e);
            } finally {
                this.printRequest(requestToUse);
                if (requestToUse instanceof BufferedHttpServletRequest) {
                    ((BufferedHttpServletRequest) requestToUse).release();
                }
            }
        }
    
        private void printRequest(HttpServletRequest request) {
            String body = StringUtils.EMPTY;
            try {
                if (request instanceof BufferedHttpServletRequest) {
                    body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
                }
            } catch (IOException e) {
                LOGGER.error("printRequest 获取body异常...", e);
            }
    
            JSONObject requestJ = new JSONObject();
            JSONObject headers = new JSONObject();
            Collections.list(request.getHeaderNames())
                    .stream()
                    .forEach(name -> headers.put(name, request.getHeader(name)));
            requestJ.put("headers", headers);
            requestJ.put("parameters", request.getParameterMap());
            requestJ.put("body", body);
            requestJ.put("remote-user", request.getRemoteUser());
            requestJ.put("remote-addr", request.getRemoteAddr());
            requestJ.put("remote-host", request.getRemoteHost());
            requestJ.put("remote-port", request.getRemotePort());
            requestJ.put("uri", request.getRequestURI());
            requestJ.put("url", request.getRequestURL());
            requestJ.put("servlet-path", request.getServletPath());
            requestJ.put("method", request.getMethod());
            requestJ.put("query", request.getQueryString());
            requestJ.put("path-info", request.getPathInfo());
            requestJ.put("context-path", request.getContextPath());
    
            LOGGER.info("Request-Info: " + JSON.toJSONString(requestJ, SerializerFeature.PrettyFormat));
        }
    
    }

      2、签名切面

    @Aspect
    @Component
    public class SignatureAspect {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(StringUtils.class);
    
        @Around("execution(* com..controller..*.*(..)) " +
                "&& (@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
                "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" +
                "|| @annotation(org.springframework.web.bind.annotation.PostMapping)" +
                "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)" +
                "|| @annotation(org.springframework.web.bind.annotation.PatchMapping))"
        )
        public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
            try {
                this.checkSign();
                return pjp.proceed();
            } catch (Throwable e) {
                LOGGER.error("SignatureAspect>>>>>>>>", e);
                throw e;
            }
        }
    
        private void checkSign() throws Exception {
            HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
            String oldSign = request.getHeader("X-SIGN");
            if (StringUtils.isBlank(oldSign)) {
                throw new RuntimeException("取消签名Header[X-SIGN]信息");
            }
            //获取body(对应@RequestBody)
            String body = null;
            if (request instanceof BufferedHttpServletRequest) {
                body = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
            }
    
            //获取parameters(对应@RequestParam)
            Map<String, String[]> params = null;
            if (!CollectionUtils.isEmpty(request.getParameterMap())) {
                params = request.getParameterMap();
            }
    
            //获取path variable(对应@PathVariable)
            String[] paths = null;
            ServletWebRequest webRequest = new ServletWebRequest(request, null);
            Map<String, String> uriTemplateVars = (Map<String, String>) webRequest.getAttribute(
                    HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
            if (!CollectionUtils.isEmpty(uriTemplateVars)) {
                paths = uriTemplateVars.values().toArray(new String[]{});
            }
            try {
                String newSign = SignUtil.sign(body, params, paths);
                if (!newSign.equals(oldSign)) {
                    throw new RuntimeException("签名不一致...");
                }
            } catch (Exception e) {
                throw new RuntimeException("验签出错...", e);
            }
        }
    }

      分别获取了request inputstream中的body信息、parameter信息、path variable信息。

      3、签名核心工具类

    public class SignUtil {
        private static final String DEFAULT_SECRET = "1qaz@WSX#$%&";
    
        public static String sign(String body, Map<String, String[]> params, String[] paths) {
            StringBuilder sb = new StringBuilder();
            if (StringUtils.isNotBlank(body)) {
                sb.append(body).append('#');
            }
    
            if (!CollectionUtils.isEmpty(params)) {
                params.entrySet()
                        .stream()
                        .sorted(Map.Entry.comparingByKey())
                        .forEach(paramEntry -> {
                            String paramValue = String.join(",", Arrays.stream(paramEntry.getValue()).sorted().toArray(String[]::new));
                            sb.append(paramEntry.getKey()).append("=").append(paramValue).append('#');
                        });
            }
    
            if (ArrayUtils.isNotEmpty(paths)) {
                String pathValues = String.join(",", Arrays.stream(paths).sorted().toArray(String[]::new));
                sb.append(pathValues);
            }
    
            String createSign = HmacUtils.hmacSha256Hex(DEFAULT_SECRET, sb.toString());
            return createSign;
        }
    
        public static void main(String[] args) {
            String body = "{\n" +
                    "\t\"name\": \"hjzgg\",\n" +
                    "\t\"age\": 26\n" +
                    "}";
            Map<String, String[]> params = new HashMap<>();
            params.put("var3", new String[]{"3"});
            params.put("var4", new String[]{"4"});
    
            String[] paths = new String[]{"1", "2"};
    
            System.out.println(sign(body, params, paths));
        }
    
    }

    五、签名验证

      简单写了一个包含body参数,parameter参数,path variable参数的controller

    @RestController
    @RequestMapping("example")
    public class ExampleController {
    
        @PostMapping(value = "test/{var1}/{var2}", produces = MediaType.ALL_VALUE)
        public String myController(@PathVariable String var1
                , @PathVariable String var2
                , @RequestParam String var3
                , @RequestParam String var4
                , @RequestBody User user) {
            return String.join(",", var1, var2, var3, var4, user.toString());
        }
    
        private static class User {
            private String name;
            private int age;
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public int getAge() {
                return age;
            }
    
            public void setAge(int age) {
                this.age = age;
            }
    
            @Override
            public String toString() {
                return new ToStringBuilder(this)
                        .append("name", name)
                        .append("age", age)
                        .toString();
            }
        }
    }

      通过 签名核心工具类SignUtil 的main方法生成一个签名,通过如下命令验证

    curl -X POST \
      'http://localhost:8080/example/test/1/2?var3=3&var4=4' \
      -H 'Content-Type: application/json' \
      -H 'X-SIGN: 4955125a3aa2782ab3def51dc958a34ca46e5dbb345d8808590fb53e81cc2687' \
      -d '{
        "name": "hjzgg",
        "age": 26
    }'

    六、需要源码

      请关注订阅号,回复:signature, 便可查看。

      就先分享这么多了,更多分享请关注我们的技术公众号!!!

    展开全文
  • 最近无聊想抓一抓抖音的数据玩玩,把整个过程大概的记录给大家,分享一下。 一、寻找接口 ... 通过几个小时的观察,我发现可以从分享页面入手,因为这里包含了所有我们想要的数据。随便挑一个分享出他的个人信息...

     

    From:https://blog.csdn.net/swukong_/article/details/80887940

     

    最近无聊想抓一抓抖音的数据玩玩,把整个过程大概的记录给大家,分享一下。

     

    一、寻找接口

        通过几个小时的观察,我发现可以从分享页面入手,因为这里包含了所有我们想要的数据。随便挑一个分享出他的个人信息页面,这里就以抖音上最火的“莉哥”为例。分享出来后拿到的分享链接是:https://www.douyin.com/share/user/57720812347/?share_type=link。利用Chrome的开发者工具,直接在XHR拿到通讯数据。

    Respone里面便是我们想要的莉哥所有的视频数据,包含播放量,点赞,评论等等各种信息。在让我们看看Headers里面请求的URL会需要哪些参数。

    标出的URL:

    https://www.douyin.com/aweme/v1/aweme/post/?user_id=57720812347&count=21&max_cursor=0&aid=1128&_signature=R8qxlhATHPXt5fEW4KBhFkfKsY

    user_id : 这个在分享出来的链接里面有,看参数名都知道是代表什么了。

    max_cursor :这个第一次是0,之后需要取剩余列表的时候应该就要用上一次请求得到的JSON数据中的“max_cursor”了。

    aid:不清楚用途,直接跟着用1128。

    _signature:签名,这次的目标。

     

    二、寻找_signature

        既然要破解签名,那么肯定是要先找到是哪里生成的。直接在NetWork搜索就是了,马上就发现了签名生成的位置。再在这个js文件搜索一下signature是哪里赋值的。

        这里就看到签名函数_bytedAcrawler.sign,nonce参数传的就是user_id。继续看看_bytedAcrawler是哪里来的。

    可以看到_bytedAcrawler应该就是这个混淆了的函数生成的。看到这里大概心里就知道该怎么样去拿到这个_signature了,我们只要把“_M”的define函数和require函数都拿到手,直接调用就可以了。那么让我们找找define函数和require函数在哪定义的吧。一样继续搜关键词“_M”,如下。

    _M整个都定义在base_327cc85的js文件内,我们想要的define和require都在,而n和e都在上面定义了。

    到这里我们所要找的东西都找齐了。按猜想只要把define和require搬出来,然后“douyin_falcon:node_modules/byted-acrawler/dist/runtime”的定义也搬出来,我们就能够自己生成自己的_bytedAcrawler.sign了。

     

    三、验证

        现在把关键处的几块代码copy出来。

        _M的define和require:

    
     
    1. !function(t) {

    2. if (t.__M = t.__M || {},

    3. !t.__M.require) {

    4. var e, n, r = document.getElementsByTagName("head")[0], i = {}, o = {}, a = {}, u = {}, c = {}, s = {}, l = function(t, n) {

    5. if (!(t in u)) {

    6. u[t] = !0;

    7. var i = document.createElement("script");

    8. if (n) {

    9. var o = setTimeout(n, e.timeout);

    10. i.onerror = function() {

    11. clearTimeout(o),

    12. n()

    13. }

    14. ;

    15. var a = function() {

    16. clearTimeout(o)

    17. };

    18. "onload"in i ? i.onload = a : i.onreadystatechange = function() {

    19. ("loaded" === this.readyState || "complete" === this.readyState) && a()

    20. }

    21. }

    22. return i.type = "text/javascript",

    23. i.src = t,

    24. r.appendChild(i),

    25. i

    26. }

    27. }, f = function(t, e, n) {

    28. var r = i[t] || (i[t] = []);

    29. r.push(e);

    30. var o, a = c[t] || c[t + ".js"] || {}, u = a.pkg;

    31. o = u ? s[u].url || s[u].uri : a.url || a.uri || t,

    32. l(o, n && function() {

    33. n(t)

    34. }

    35. )

    36. };

    37. n = function(t, e) {

    38. "function" != typeof e && (e = arguments[2]),

    39. t = t.replace(/\.js$/i, ""),

    40. o[t] = e;

    41. var n = i[t];

    42. if (n) {

    43. for (var r = 0, a = n.length; a > r; r++)

    44. n[r]();

    45. delete i[t]

    46. }

    47. }

    48. ,

    49. e = function(t) {

    50. if (t && t.splice)

    51. return e.async.apply(this, arguments);

    52. t = e.alias(t);

    53. var n = a[t];

    54. if (n)

    55. return n.exports;

    56. var r = o[t];

    57. if (!r)

    58. throw "[ModJS] Cannot find module `" + t + "`";

    59. n = a[t] = {

    60. exports: {}

    61. };

    62. var i = "function" == typeof r ? r.apply(n, [e, n.exports, n]) : r;

    63. return i && (n.exports = i),

    64. n.exports && !n.exports["default"] && Object.defineProperty && Object.isExtensible(n.exports) && Object.defineProperty(n.exports, "default", {

    65. value: n.exports

    66. }),

    67. n.exports

    68. }

    69. ,

    70. e.async = function(n, r, i) {

    71. function a(t) {

    72. for (var n, r = 0, h = t.length; h > r; r++) {

    73. var p = e.alias(t[r]);

    74. p in o ? (n = c[p] || c[p + ".js"],

    75. n && "deps"in n && a(n.deps)) : p in s || (s[p] = !0,

    76. l++,

    77. f(p, u, i),

    78. n = c[p] || c[p + ".js"],

    79. n && "deps"in n && a(n.deps))

    80. }

    81. }

    82. function u() {

    83. if (0 === l--) {

    84. for (var i = [], o = 0, a = n.length; a > o; o++)

    85. i[o] = e(n[o]);

    86. r && r.apply(t, i)

    87. }

    88. }

    89. "string" == typeof n && (n = [n]);

    90. var s = {}

    91. , l = 0;

    92. a(n),

    93. u()

    94. }

    95. ,

    96. e.resourceMap = function(t) {

    97. var e, n;

    98. n = t.res;

    99. for (e in n)

    100. n.hasOwnProperty(e) && (c[e] = n[e]);

    101. n = t.pkg;

    102. for (e in n)

    103. n.hasOwnProperty(e) && (s[e] = n[e])

    104. }

    105. ,

    106. e.loadJs = function(t) {

    107. l(t)

    108. }

    109. ,

    110. e.loadCss = function(t) {

    111. if (t.content) {

    112. var e = document.createElement("style");

    113. e.type = "text/css",

    114. e.styleSheet ? e.styleSheet.cssText = t.content : e.innerHTML = t.content,

    115. r.appendChild(e)

    116. } else if (t.url) {

    117. var n = document.createElement("link");

    118. n.href = t.url,

    119. n.rel = "stylesheet",

    120. n.type = "text/css",

    121. r.appendChild(n)

    122. }

    123. }

    124. ,

    125. e.alias = function(t) {

    126. return t.replace(/\.js$/i, "")

    127. }

    128. ,

    129. e.timeout = 5e3,

    130. t.__M.define = n,

    131. t.__M.require = e

    132. }

    133. }(this);

        “douyin_falcon:node_modules/byted-acrawler/dist/runtime”模块:

    
     
    1. __M.define("douyin_falcon:node_modules/byted-acrawler/dist/runtime", function(l, e) {

    2. Function(function(l) {

    3. return 'e(e,a,r){(b[e]||(b[e]=t("x,y","x "+e+" y")(r,a)}a(e,a,r){(k[r]||(k[r]=t("x,y","new x[y]("+Array(r+1).join(",x[y]")(1)+")")(e,a)}r(e,a,r){n,t,s={},b=s.d=r?r.d+1:0;for(s["$"+b]=s,t=0;t<b;t)s[n="$"+t]=r[n];for(t=0,b=s=a;t<b;t)s[t]=a[t];c(e,0,s)}c(t,b,k){u(e){v[x]=e}f{g=,ting(bg)}l{try{y=c(t,b,k)}catch(e){h=e,y=l}}for(h,y,d,g,v=[],x=0;;)switch(g=){case 1:u(!)4:f5:u((e){a=0,r=e;{c=a<r;c&&u(e[a]),c}}(6:y=,u((y8:if(g=,lg,g=,y===c)b+=g;else if(y!==l)y9:c10:u(s(11:y=,u(+y)12:for(y=f,d=[],g=0;g<y;g)d[g]=y.charCodeAt(g)^g+y;u(String.fromCharCode.apply(null,d13:y=,h=delete [y]14:59:u((g=)?(y=x,v.slice(x-=g,y:[])61:u([])62:g=,k[0]=65599*k[0]+k[1].charCodeAt(g)>>>065:h=,y=,[y]=h66:u(e(t[b],,67:y=,d=,u((g=).x===c?r(g.y,y,k):g.apply(d,y68:u(e((g=t[b])<"<"?(b--,f):g+g,,70:u(!1)71:n72:+f73:u(parseInt(f,3675:if(){bcase 74:g=<<16>>16g76:u(k[])77:y=,u([y])78:g=,u(a(v,x-=g+1,g79:g=,u(k["$"+g])81:h=,[f]=h82:u([f])83:h=,k[]=h84:!085:void 086:u(v[x-1])88:h=,y=,h,y89:u({e{r(e.y,arguments,k)}e.y=f,e.x=c,e})90:null91:h93:h=0:;default:u((g<<16>>16)-16)}}n=this,t=n.Function,s=Object.keys||(e){a={},r=0;for(c in e)a[r]=c;a=r,a},b={},k={};r'.replace(/[-]/g, function(e) {

    4. return l[15 & e.charCodeAt(0)]

    5. })

    6. }("v[x++]=v[--x]t.charCodeAt(b++)-32function return ))++.substrvar .length(),b+=;break;case ;break}".split("")))()('gr$Daten Иb/s!l y͒yĹg,(lfi~ah`{mv,-n|jqewVxp{rvmmx,&effkx[!cs"l".Pq%widthl"@q&heightl"vr*getContextx$"2d[!cs#l#,*;?|u.|uc{uq$fontl#vr(fillTextx$$龘ฑภ경2<[#c}l#2q*shadowBlurl#1q-shadowOffsetXl#$$limeq+shadowColorl#vr#arcx88802[%c}l#vr&strokex[ c}l"v,)}eOmyoZB]mx[ cs!0s$l$Pb<k7l l!r&lengthb%^l$1+s$jl s#i$1ek1s$gr#tack4)zgr#tac$! +0o![#cj?o ]!l$b%s"o ]!l"l$b*b^0d#>>>s!0s%yA0s"l"l!r&lengthb<k+l"^l"1+s"jl s&l&z0l!$ +["cs\'(0l#i\'1ps9wxb&s() &{s)/s(gr&Stringr,fromCharCodes)0s*yWl ._b&s o!])l l Jb<k$.aj;l .Tb<k$.gj/l .^b<k&i"-4j!+& s+yPo!]+s!l!l Hd>&l!l Bd>&+l!l <d>&+l!l 6d>&+l!l &+ s,y=o!o!]/q"13o!l q"10o!],l 2d>& s.{s-yMo!o!]0q"13o!]*Ld<l 4d#>>>b|s!o!l q"10o!],l!& s/yIo!o!].q"13o!],o!]*Jd<l 6d#>>>b|&o!]+l &+ s0l-l!&l-l!i\'1z141z4b/@d<l"b|&+l-l(l!b^&+l-l&zl\'g,)gk}ejo{cm,)|yn~Lij~em["cl$b%@d<l&zl\'l $ +["cl$b%b|&+l-l%8d<@b|l!b^&+ q$sign ', [Object.defineProperty(e, "__esModule", {

    7. value: !0

    8. })])

    9. });

        调用处:

    
     
    1. dycs = __M.require("douyin_falcon:node_modules/byted-acrawler/dist/runtime") ;

    2. signc = dycs.sign(57720812347);

    3. document.write(signc);

    
    

        直接Chrome打开就可以看到了,真的生成了。

        放入接口参数试试看能不能用?

     

    可以看到里面都是部分的视频列表封面和视频点赞数据等等数据。

     

    四、结语

    感觉有点标题党,其实并没有把整个算法破解出来,不过既然都已经都到了这个地步了,把算法破解出来看起来也不是很难而且也没什么意思,有点懒,能用就行吧。整个过程好像挺简单,其实也花了一些时间并没有写的这么顺利,本想详细过程都写下来。不过都是些基础操作,懒得写了。“_M”的定义那块代码,应该是有很多没有用的。比如document相关的,f函数和n函数。想拿去用的话最好精简一下吧。

    有什么问题再留言,有空的话回尽量回复。不过抖音也真是重视反爬,昨天还能拿到视频播放量,评论数以及各种个人信息,今天写Blog的时候突然发现很多数据都没了,只留下了个点赞数等等对页面有用的数据而已了,还以为我出了什么问题。要是大家发现了其他什么有好玩的数据接口,告诉我一声。偷偷的~

    这里献上文中的源码:https://github.com/swukong/douyin_signature

     

     

     

     

    展开全文
  • 抖音signature.js文件

    2020-07-28 23:32:39
    爬取抖音视频列表url中的signature参数解密的js文件。
  • 提示说是签名不对,但没搞懂签名具体是啥,以为之前做过,有正确的,就一点点比对,最后发现竟然是下载的文件路径,里面必须是/,而不能是\或\\,搞得我哭笑不得。比如,要下载的文件路径是:soft/cszt.exe,里面就...

    提示说是签名不对,但没搞懂签名具体是啥,以为之前做过,有正确的,就一点点比对,最后发现竟然是下载的文件路径,里面必须是/,而不能是\或\\,搞得我哭笑不得。比如,要下载的文件路径是:soft/cszt.exe,里面就必须是/,我开始用的是@soft\cszt.exe,就一直提示我上面的错误,莫名其妙

    转载于:https://www.cnblogs.com/dachuang/p/10141164.html

    展开全文
  • jar signature和full apk signature的选择

    千次阅读 2017-06-01 10:49:22
    在生成正式签名的APK时有一个"签名版本号的选项"(Signature Vwesions),如图: 其实这是从 Android 7.0引入的一个新的签名机制,它为APK附加了一些特性使其更具安全性,所以这个选项虽然不是强制性的,但...
  • JWT signature

    千次阅读 2019-01-31 22:58:47
    JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted." 背景:在Windows搭建Hygieia,第一次注册没问题。过了几天,重新启动Hygieia,在...
  • 这个问题是在使用poi导入excel文件时产生的: 产生的原因是:excel是从网页下载或者其他第三方软件导出的,所以它的后缀名虽然是xsl,但是它文件头签名仍然是原来的格式,所以poi不能识别,所以pio不能读取它 ...
  • 解决方案 使用压缩软件打开jar包 删除:META-INF目录下的除了mainfest.mf,.rsa及maven文件夹 以外的其他所有文件
  • 程序运行到 PdfReader reader = new PdfReader("D:\\createSamplePDF.pdf");... Exception in thread "main" com.itextpdf.text.exceptions.InvalidPdfException: PDF header signature not found.
  • 出现题目出现的错误是因为 支付宝的公钥 使用了RSA2的公钥(但是你验签是用的是RSA的验签方式),造成支付宝回调验签是错误, 把支付宝公钥配置成RSA的就能验签成功了,以下截图说明:
  • 代码转换使用ascii。socket通讯不是人干的活
  • 今天使用AndroidStudio进行打包的时候,出现了如图所示的内容需要我们勾选两个签名包类型,如果我们不勾选直接点击Finish按钮,会提示并且,部分码友反应自己的App在勾选V2之后,app会出现安装失败的问题。...
  • Error:error: failed to read PNG signature: file does not start with PNG signature. android studio 打包apk时报错. 错误:错误:无法读取PNG签名:文件没有从PNG签名开始。 一般都是图片格式有问题 可能是...
  • Driver Signature Enforcement Defeating Windows Driver Signature Enforcement #1: default drivers Defeating Windows Driver Signature Enforcement #2: CSRSS and thread desktops Defeating Windows ...
  • x509Certificate.verify(x509Certificate.getPublicKey()); 使用此方法进行证书链验证时,一直出现签名不匹配问题,源于自签名证书(即根证书)原因。 此处需要使用subjectCert.verify(issuerCert.getPublicKey...
  • 如题:what is method signature in java? answer:Definition: Two of the components of a method declaration comprise the method signature—the method's name and the parameter types. 方法声明的两个组件...
  • 报错:在linux环境下,通过bash运行python文件,传递参数给函数,中途报错:ufunc 'subtract' did not contain a loop with signature matching types dtype('&lt;U32') dtype('&lt;U32') dtype('&lt;U32...
  • [Java] 方法签名(method signature)

    万次阅读 2018-05-31 17:11:40
    方法头指定修饰符(例如static)、返回值类型、方法名、和形式参数。 方法头中定义的变量称为形参 (形式参数,formal parameter 或 parameter),形参如同占位符。当方法被调用时,传递一个值给形参,此值称为实参...
  • org.apache.coyote.ajp.AjpMessage processHeader 严重: Invalid message received with signature 27648 ![图片说明](https://img-ask.csdn.net/upload/201508/30/1440945607_851581.png) 吴福气一切正常,但是有这...
1 2 3 4 5 ... 20
收藏数 175,890
精华内容 70,356
关键字:

signature