1 概述
本文次要讲述了如何利用Openssl
生成ECDSA
密钥对,并利用Auth0
库进行Token
生成及验证的过程。
2 ECDSA
2.1 简介
ECC
(Elliptic 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 #生成公钥
参数阐明如下:
ecparam
:EC
参数设置以及生成命令-genkey
:应用特定参数生成EC
私钥-name
:ec
参数,能够应用openssl ecparam -list_curves
查看,这里用的是secp521r1
-out
:输入文件名ec
:EC
密钥解决命令-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
版的):
- Github
- 码云
- CODE.CHINA
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