乐趣区

关于token:ECDSA密钥对生成以及在Token中的应用

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版的):

  • 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

退出移动版