ECDSA密钥对生成以及在Token中的应用

1 概述

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

2 ECDSA

2.1 简介

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

2.2 密钥对生成

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

1openssl ecparam -genkey -name secp521r1 -out private.pem #生成私钥 2openssl 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的库),首先引入包:

1<dependency> 2 <groupId>com.auth0</groupId> 3 <artifactId>java-jwt</artifactId> 4 <version>3.12.0</version> 5</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仓库链接可以戳这里

1package com.auth0.jwt; 2 3import org.bouncycastle.util.io.pem.PemObject; 4import org.bouncycastle.util.io.pem.PemReader; 5 6import java.io.File; 7import java.io.FileNotFoundException; 8import java.io.FileReader; 9import java.io.IOException; 10import java.security.KeyFactory; 11import java.security.NoSuchAlgorithmException; 12import java.security.PrivateKey; 13import java.security.PublicKey; 14import java.security.spec.EncodedKeySpec; 15import java.security.spec.InvalidKeySpecException; 16import java.security.spec.PKCS8EncodedKeySpec; 17import java.security.spec.X509EncodedKeySpec; 18 19public class PemUtils { 20 21 private static byte[] parsePEMFile(File pemFile) throws IOException { 22 if (!pemFile.isFile() || !pemFile.exists()) { 23 throw new FileNotFoundException(String.format("The file '%s' doesn't exist.", pemFile.getAbsolutePath())); 24 } 25 PemReader reader = new PemReader(new FileReader(pemFile)); 26 PemObject pemObject = reader.readPemObject(); 27 byte[] content = pemObject.getContent(); 28 reader.close(); 29 return content; 30 } 31 32 private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) { 33 PublicKey publicKey = null; 34 try { 35 KeyFactory kf = KeyFactory.getInstance(algorithm); 36 EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); 37 publicKey = kf.generatePublic(keySpec); 38 } catch (NoSuchAlgorithmException e) { 39 System.out.println("Could not reconstruct the public key, the given algorithm could not be found."); 40 } catch (InvalidKeySpecException e) { 41 System.out.println("Could not reconstruct the public key"); 42 } 43 44 return publicKey; 45 } 46 47 private static PrivateKey getPrivateKey(byte[] keyBytes, String algorithm) { 48 PrivateKey privateKey = null; 49 try { 50 KeyFactory kf = KeyFactory.getInstance(algorithm); 51 EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); 52 privateKey = kf.generatePrivate(keySpec); 53 } catch (NoSuchAlgorithmException e) { 54 System.out.println("Could not reconstruct the private key, the given algorithm could not be found."); 55 } catch (InvalidKeySpecException e) { 56 System.out.println("Could not reconstruct the private key"); 57 } 58 59 return privateKey; 60 } 61 62 public static PublicKey readPublicKeyFromFile(String filepath, String algorithm) throws IOException { 63 byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); 64 return PemUtils.getPublicKey(bytes, algorithm); 65 } 66 67 public static PrivateKey readPrivateKeyFromFile(String filepath, String algorithm) throws IOException { 68 byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); 69 return PemUtils.getPrivateKey(bytes, algorithm); 70 } 71}

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

1public class Main { 2 public static void main(String[] args) { 3 try { 4 ECPublicKey publicKey = (ECPublicKey) PemUtils.readPublicKeyFromFile("src/main/resources/public.pem","EC"); 5 ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/main/resources/private.pem","EC"); 6 Algorithm algorithm = Algorithm.ECDSA512(publicKey,privateKey); 7 String token = JWT.create() 8 .withIssuer("issuer") 9 .sign(algorithm); 10 JWTVerifier verifier = JWT.require(algorithm).build(); 11 verifier.verify(JWT.decode(token)); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } 15 } 16}

但是会报错说私钥是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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

sourceTree 添加 ssh key 方法

1.使用git客户的生成公私钥:id\rsa、id\rsa.pub1.1设置Git的username和email:$gitconfigglobaluser.name"xxx"$gitconfig\globaluser.email"xxx.mail@xxx.com"1.2.生成SSH密钥过程:1.2.1.检查是不是已经存在密钥(

swap空间的增减方法

(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap

ECDSA密钥对生成以及在Token中的应用 - HelloWorld