1 概述

本文主要讲述了如何利用Openssl生成ECDSA密钥对,并利用Auth0库进行Token生成及验证的过程。

2 ECDSA

2.1 简介

ECCElliptic Curve Cryptography,椭圆曲线加密)是一种基于椭圆曲线数学的公钥加密算法,而ECDSA是使用ECC对数字签名算法(DSA)的模拟,总的来说ECC相比起常见的RSA更加安全并且生成密钥对的过程会更快。本文不会涉及过多原理性的东西,只是作简单的介绍,想要详情了解这些算法的可以戳这里

2.2 密钥对生成

Openssl中生成ECDSA密钥对的流程如下:

openssl ecparam -genkey -name secp521r1 -out private.pem #生成私钥
openssl ec -in private.pem -pubout -out public.pem #生成公钥

参数说明如下:

  • ecparamEC参数设置以及生成命令
  • -genkey:使用特定参数生成EC私钥
  • -nameec参数,可以使用openssl ecparam -list_curves 查看,这里用的是secp521r1
  • -out:输出文件名
  • ecEC密钥处理命令
  • -in:输入文件
  • -pubout:默认情况下会输出私钥,加上该选项会变成输出公钥(如果输入是公钥的情况下该参数会自动设置)

执行完命令后就成功生成密钥对了,可以查看一下:

密钥对生成之后就可以准备一下生成Token了。

3 Auth0中的Token应用

3.1 Auth0

Auth0提供了验证以及授权服务,这里利用官方提供的Java实现去生成Token(这里插一句题外话,Java常用的Token实现还有一个叫JJWT的库),首先引入包:

<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.12.0</version>
</dependency>

Gradle

compile group: 'com.auth0', name: 'java-jwt', version: '3.12.0'

引入后来看一下支持的加密算法,如下图:

最简单的使用HMAC算法生成的Token如下:

System.out.println(JWT.create().withIssuer("issuer").withAudience("content").sign(Algorithm.HMAC512("password")));

当然这不是本文的重点,本文的重点是介绍如何利用ECDSA去生成Token

首先Auth0提供的签名api如下:

JWT.create().sign(Algorithm)

其中Algorithm可以取值如下:

想要使用ECDSA算法需要提供一个ECDSAKeyProvider或一个ECPublicKey和一个ECPrivateKey,这里选择后一种方式实现。

3.2 密钥对处理

官方并没有提供如何生成ECPublicKey/ECPrivateKey的方法,甚至连从文件读取密钥对的方法都没有提供,笔者从官方提供的测试代码中发现了如下方法:

其中核心就是读取密钥对的两个方法:

  • readPublicKeyFromFile
  • readPrivateKeyFromFile

import结果可以看到这是一个工具类:

但问题是官方该工具类是测试使用的,换句话说不对外暴露的,在IDEA中直接引入会报错:

因此直接找到该工具类的源码(链接可以戳这里,需要引入bouncycastle包,Maven仓库链接可以戳这里

package com.auth0.jwt;

import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; public class PemUtils { private static byte[] parsePEMFile(File pemFile) throws IOException {
if (!pemFile.isFile() || !pemFile.exists()) {
throw new FileNotFoundException(String.format("The file '%s' doesn't exist.", pemFile.getAbsolutePath()));
}
PemReader reader = new PemReader(new FileReader(pemFile));
PemObject pemObject = reader.readPemObject();
byte[] content = pemObject.getContent();
reader.close();
return content;
} private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) {
PublicKey publicKey = null;
try {
KeyFactory kf = KeyFactory.getInstance(algorithm);
EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
publicKey = kf.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
System.out.println("Could not reconstruct the public key, the given algorithm could not be found.");
} catch (InvalidKeySpecException e) {
System.out.println("Could not reconstruct the public key");
} return publicKey;
} private static PrivateKey getPrivateKey(byte[] keyBytes, String algorithm) {
PrivateKey privateKey = null;
try {
KeyFactory kf = KeyFactory.getInstance(algorithm);
EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
privateKey = kf.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException e) {
System.out.println("Could not reconstruct the private key, the given algorithm could not be found.");
} catch (InvalidKeySpecException e) {
System.out.println("Could not reconstruct the private key");
} return privateKey;
} public static PublicKey readPublicKeyFromFile(String filepath, String algorithm) throws IOException {
byte[] bytes = PemUtils.parsePEMFile(new File(filepath));
return PemUtils.getPublicKey(bytes, algorithm);
} public static PrivateKey readPrivateKeyFromFile(String filepath, String algorithm) throws IOException {
byte[] bytes = PemUtils.parsePEMFile(new File(filepath));
return PemUtils.getPrivateKey(bytes, algorithm);
}
}

直接复制该工具类后,将前一步生成的private.pem以及public.pem放置合适位置,通过工具类读取并生成Token

public class Main {
public static void main(String[] args) {
try {
ECPublicKey publicKey = (ECPublicKey) PemUtils.readPublicKeyFromFile("src/main/resources/public.pem","EC");
ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/main/resources/private.pem","EC");
Algorithm algorithm = Algorithm.ECDSA512(publicKey,privateKey);
String token = JWT.create()
.withIssuer("issuer")
.sign(algorithm);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(JWT.decode(token));
} catch (Exception e) {
e.printStackTrace();
}
}
}

但是会报错说私钥是null

从官方issue中查到了类似的问题

回答说是密钥格式的问题,其中提到的pkcs8私钥格式转换命令

将私钥的格式进行转换:

openssl pkcs8 -topk8 -inform pem -in private.pem -outform pem -nocrypt -out newprivate.pem

参数说明:

  • pkcs8:私钥格式转换命令
  • -topk8:读取私钥并转换为PKCS#8格式
  • -inform:指定输入格式,默认pem,使用该参数并配合-topk8后会生成加密后的PKCS#8格式的私钥
  • -in:输入文件
  • -outform:与-inform类似
  • -nocrypt:在这里主要配合-inform+-topk8使用,生成不加密的PrivateKeyInfo而不是加密的PKCS#8 EncryptedPrivateKeyInfo,因为一些软件(比如某些版本的Java代码)使用的是不加密格式的私钥
  • -out:输出文件

转换后就可以生成Token了。

3.3 生成Token

最后readPrivateKeyFromFile中的参数修改为新的私钥即可:

ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/main/resources/newprivate.pem","EC");

4 参考源码

包含了示例密钥对以及如何使用(Gradle版的):

5 参考网站

1、知乎-ECC椭圆曲线加密算法:介绍

2、什么是椭圆曲线数字签名算法(ECDSA)?

3、Elliptic Curve Cryptography: breaking security and a comparison with RSA

4、OpenSSL doc

5、StackExange-https://superuser.com/questions/1103401/generate-an-ecdsa-key-and-csr-with-openssl

6、auth0/java-jwt Issue - ECDSA key version mismatch from openssl pem files #270

7、auth0/java-jwt Github

8、codota-How to useECDSA512methodincom.auth0.jwt.algorithms.Algorithm

如果觉得文章好看,欢迎点赞。

同时欢迎关注微信公众号:氷泠之路。

ECDSA密钥对生成以及在Token中的应用的更多相关文章

  1. jwt认证生成后的token如何传回后端并解析的详解

    jwt认证生成后的token后端解析 一.首先前端发送token token所在的位置headers {'authorization':token的值',Content-Type':applicati ...

  2. EF CodeFirst生成数据库到Sqlserver中

    EF CodeFirst简单实例这篇文章介绍了如何用EF去快速生成数据库.但是这个并没有生成到sqlserver中,总觉得不爽.下面就来讲一下,如何将数据库生成到sqlserver中. 按照EF Co ...

  3. 如果使用mybatis的逆向工程生成的po类及mapper,如果我们想要进行的对数据库的操作在mapper中没有对应的接口函数:比如生成的mapper接口中没有按照姓名及性别混合条件查询。我们的解决办法是:使用逆向工程生成的对应表的Example文件。

    1.使用mybatis逆向工程生成的po类中包含UserExample文件(我的数据库表名为User). 2. 创建UserExample对象,然后对加入条件.对应的测试代码为: /* * 通过姓名和 ...

  4. Springboot中使用自定义参数注解获取 token 中用户数据

    使用自定义参数注解获取 token 中User数据 使用背景 在springboot项目开发中需要从token中获取用户信息时通常的方式要经历几个步骤 拦截器中截获token TokenUtil工具类 ...

  5. COMMENT方法 用于在生成的SQL语句中添加注释内容,

    COMMENT方法 用于在生成的SQL语句中添加注释内容,例如: $this->comment('查询考试前十名分数') ->field('username,score') ->li ...

  6. C#怎么在生成解决方案的过程中执行perl脚本(C#早期绑定)

    转载 怎么在生成解决方案的过程中执行perl脚本 早期绑定在编译期间识别并检查方法.属性.函数,并在应用程序执行之前执行其他优化.在这个绑定中,编译器已经知道它是什么类型的对象以及它拥有的方法或属性. ...

  7. 支付宝打造公共账号业务网关, RSA密钥对生成

    作者: 玉龙      版权全部,同意转载. 请注明出处(创建金融_玉龙  http://www.weibo.com/u/1872245125) 原文地址: http://blog.csdn.net/ ...

  8. 如何生成Azure SAS Token

    在Azure PaaS服务密钥的安全性文章中,提到过客户端实际上发送的是Token,而不是密钥.那么Token是该如何生成呢? Azure相应服务的SDK其实都提供了或者内置了生成Token的方法,可 ...

  9. Git SSH密钥对生成以及多个SSH存在情况配置

    一.使用Git Bash 生成一个新的SSH密钥 1. 打开 Git Bash. 2. 邮箱设置粘贴下面的文字,替换成为你自己的邮箱. Github SSH 1 $ ssh-keygen -t rsa ...

随机推荐

  1. 注解处理器APT详解

    本文转载自ANNOTATION PROCESSING 101 Introduction In this blog entry I would like to explain how to write ...

  2. 分布式事务 SEATA-1.4.1 AT模式 配合NACOS 应用

    SEATA 配置 目录 SEATA 配置 TC (Transaction Coordinator) - 事务协调者 配置参数 nacos bash 脚本 同步 config 配置到 nacos 使用 ...

  3. Hystrix熔断器的使用步骤

    1.添加熔断器依赖 2.在配置文件中开启熔断器 feign.hystrix.enabled=true 3.写接口的实现类VodFileDegradeFeignClient,在实现类中写如果出错了输出的 ...

  4. 主键策略+mybayisPlus自动增长

    主键策略: 1.自动增长 有一点小缺陷:例如当一张表里的数据过于庞大时我们会进行分表操作,若是用自动增长策略,那么除了第一张表外的每一张表都必须知道上一张的表的的最后ID值.这个操作便会造成效率的变低 ...

  5. 适配三星Galaxy S8及S8+ 屏幕比例为 18.5:9

    开发者只需在App的AndroidManifest.xml文件<application> </application>中添加如下代码: <meta-data androi ...

  6. Asp.NET Core 限流控制-AspNetCoreRateLimit

    起因: 近期项目中,提供了一些调用频率较高的api接口,需要保障服务器的稳定运行:需要对提供的接口进行限流控制.避免因客户端频繁的请求导致服务器的压力. 一.AspNetCoreRateLimit 介 ...

  7. C# 处理PPT水印(三)—— 在PPT中添加多行(平铺)文本水印效果

    在PPT幻灯片中,可通过添加形状的方式,来实现类似水印的效果,可添加单一文本水印效果,即幻灯片中只有一个文本水印:也可以添加多行(平铺)文本水印效果,即幻灯片中以一定方式平铺排列多个文本水印效果.本文 ...

  8. const成员函数可以将非const指针作为返回值吗?

    先给出一段代码 class A { int *x; public: int *f() const { return x; } }; 成员函数f返回指向私有成员 x 的非常量指针,我认为这会修改成员x ...

  9. 使用windbg定位内存问题【入门级】

    1. 背景 在开发过程中,我们可能遇到应用程序线程占用过大的问题,可以通过windbg命令去定位哪些类型,哪些内存一直占用堆资源,从而查出问题,解决问题. 2. 准备工作 工具: 抓取DUMP文件的工 ...

  10. 摄像机+LookAt矩阵+视角移动+欧拉角

    一: 摄像机 OpenGL本身没有摄像机(Camera)的概念,但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机,产生一种我们在移动的感觉,而不是场景在移动. 以摄像机的视角作为场景 ...