Jwt Token 安全策略使用 ECDSA 椭圆曲线加密算法签名/验证
椭圆曲线密码学(Elliptic curve cryptography),简称 ECC,是一种建立公开密钥加密的算法,也就是非对称加密,ECDH 与 ECDSA 是基于 ECC 的算法。类似的还有 RSA,ElGamal 算法等。ECC 被公认为在给定密钥长度下最安全的加密算法。比特币中的公私钥生成以及签名算法 ECDSA 都是基于 ECC 的。之前介绍 JWT 相关的知识介绍过了 HS256(MAC),RS256 (RSA) 相关的签名与验证,还有一种非对称签名算法 ES256 算法(ECDSA)也是推荐使用的一种。这三种算法也是不同语言主要实现,微软 System.IdentityModel.Tokens.Jwt 支持情况可以在 https://jwt.io/ 中找到。
https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/ 一文中提到了使用 JWT的“none”算法的安全性以及提供了一个密钥字段(kid)验证的重要性。
JSON Web Algorithms (JWA)[RFC7518]
+--------------+-------------------------------+--------------------+
| "alg" Param | Digital Signature or MAC | Implementation |
| Value | Algorithm | Requirements |
+--------------+-------------------------------+--------------------+
| HS256 | HMAC using SHA- | Required |
| HS384 | HMAC using SHA- | Optional |
| HS512 | HMAC using SHA- | Optional |
| RS256 | RSASSA-PKCS1-v1_5 using | Recommended |
| | SHA- | |
| RS384 | RSASSA-PKCS1-v1_5 using | Optional |
| | SHA- | |
| RS512 | RSASSA-PKCS1-v1_5 using | Optional |
| | SHA- | |
| ES256 | ECDSA using P- and SHA- | Recommended+ |
| ES384 | ECDSA using P- and SHA- | Optional |
| ES512 | ECDSA using P- and SHA- | Optional |
| PS256 | RSASSA-PSS using SHA- and | Optional |
| | MGF1 with SHA- | |
| PS384 | RSASSA-PSS using SHA- and | Optional |
| | MGF1 with SHA- | |
| PS512 | RSASSA-PSS using SHA- and | Optional |
| | MGF1 with SHA- | |
| none | No digital signature or MAC | Optional |
| | performed | |
+--------------+-------------------------------+--------------------+
ECDSA(Digital Signature Algorithm,椭圆曲线签名与校验,数字签名算法)它是另一种公开密钥算法,它不能用作加密,只用作数字签名。DSA使用公开密钥,为接受者验证数据的完整性和数据发送者的身份。它也可用于由第三方去确定签名和所签数据的真实性。
openssl 创建证书
openssl ecparam -genkey -name secp256r1 -out ecdas.key
openssl req -new -key ecdas.key -out myreq.csr
openssl req -x509 -days -key ecdas.key -in myreq.csr -out ecdas.crt
openssl pkcs12 -export -out ecdas.pfx -inkey ecdas.key -in ecdas.crt
简化
openssl ecparam -genkey -name secp256r1 | openssl ec -out ecdas.key
openssl req -new -x509 -days -key ecdas.key -out ecdas.crt -subj "/C=CN/L=SH/O=PICC/CN=idsvr4.com"
openssl pkcs12 -export -in ecdas.crt -inkey ecdas.key -out ecdas.pfx
.net core 实现
class Program
{
static void Main(string[] args)
{
//获得证书文件
var filePath = Path.Combine(AppContext.BaseDirectory, "Certs\\ecdas.pfx");
if (!File.Exists(filePath))
{
throw new FileNotFoundException("Signing Certificate is missing!");
}
var x509Cert = new X509Certificate2(filePath, "");
var data = new byte[] { , , , , }; //test signature
var signature = ECDsaSignData(x509Cert, data);
Console.WriteLine(ECDsaVerifyData(x509Cert, data, signature) ? "Valid!" : "Not Valid..."); //test certs signature jwt(openssl)
var jwtToken = CreateSignedJwt(x509Cert.GetECDsaPrivateKey());
Console.WriteLine(VerifySignedJwt(x509Cert.GetECDsaPublicKey(), jwtToken) ? "Valid!" : "Not Valid..."); //test certs signature jwt by BouncyCastle
string privateKey = "c711e5080f2b58260fe19741a7913e8301c1128ec8e80b8009406e5047e6e1ef";
string publicKey = "04e33993f0210a4973a94c26667007d1b56fe886e8b3c2afdd66aa9e4937478ad20acfbdc666e3cec3510ce85d40365fc2045e5adb7e675198cf57c6638efa1bdb";
var privateECDsa = LoadPrivateKey(FromHexString(privateKey));
var publicECDsa = LoadPublicKey(FromHexString(publicKey));
var jwt = CreateSignedJwt(privateECDsa);
var isValid = VerifySignedJwt(publicECDsa, jwt);
Console.WriteLine(isValid ? "Valid!" : "Not Valid..."); //test certs signature jwt by Create Private-Public Key pair(https://github.com/smuthiya/EcdsaJwtSigning/blob/master/Program.cs)
var key = CngKey.Create(CngAlgorithm.ECDsaP256, "ECDsaKey", new CngKeyCreationParameters
{
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey,
KeyUsage = CngKeyUsages.AllUsages,
ExportPolicy = CngExportPolicies.AllowPlaintextExport
});
var cngKey_privateKey = new ECDsaCng(CngKey.Import(key.Export(CngKeyBlobFormat.EccPrivateBlob), CngKeyBlobFormat.EccPrivateBlob));
cngKey_privateKey.HashAlgorithm = CngAlgorithm.ECDsaP256;
var cngKey_publicKey = new ECDsaCng(CngKey.Import(key.Export(CngKeyBlobFormat.EccPublicBlob), CngKeyBlobFormat.EccPublicBlob));
cngKey_publicKey.HashAlgorithm = CngAlgorithm.ECDsaP256;
var jwt_sign = CreateSignedJwt(privateECDsa);
Console.WriteLine(VerifySignedJwt(publicECDsa, jwt_sign) ? "Valid!" : "Not Valid...");
Console.ReadKey();
} private static byte[] ECDsaSignData(X509Certificate2 cert, byte[] data)
{
using (ECDsa ecdsa = cert.GetECDsaPrivateKey())
{
if (ecdsa == null)
throw new ArgumentException("Cert must have an ECDSA private key", nameof(cert));
return ecdsa.SignData(data, HashAlgorithmName.SHA256);
}
} private static bool ECDsaVerifyData(X509Certificate2 cert, byte[] data, byte[] signature)
{
using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
if (ecdsa == null)
throw new ArgumentException("Cert must be an ECDSA cert", nameof(cert));
return ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);
}
} private static byte[] FromHexString(string hex)
{
var numberChars = hex.Length;
var hexAsBytes = new byte[numberChars / ];
for (var i = ; i < numberChars; i += )
hexAsBytes[i / ] = Convert.ToByte(hex.Substring(i, ), );
return hexAsBytes;
} private static bool VerifySignedJwt(ECDsa eCDsa, string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var claimsPrincipal = tokenHandler.ValidateToken(token, new TokenValidationParameters
{
ValidIssuer = "me",
ValidAudience = "you",
IssuerSigningKey = new ECDsaSecurityKey(eCDsa)
}, out var parsedToken);
return claimsPrincipal.Identity.IsAuthenticated;
} private static string CreateSignedJwt(ECDsa eCDsa)
{
var now = DateTime.UtcNow;
var tokenHandler = new JwtSecurityTokenHandler();
var jwtToken = tokenHandler.CreateJwtSecurityToken(
issuer: "me",
audience: "you",
subject: null,
notBefore: now,
expires: now.AddMinutes(),
issuedAt: now,
signingCredentials: new SigningCredentials(new ECDsaSecurityKey(eCDsa), SecurityAlgorithms.EcdsaSha256));
return tokenHandler.WriteToken(jwtToken);
} private static ECDsa LoadPrivateKey(byte[] key)
{
var privKeyInt = new Org.BouncyCastle.Math.BigInteger(+, key);
var parameters = SecNamedCurves.GetByName("secp256r1");
var ecPoint = parameters.G.Multiply(privKeyInt);
var privKeyX = ecPoint.Normalize().XCoord.ToBigInteger().ToByteArrayUnsigned();
var privKeyY = ecPoint.Normalize().YCoord.ToBigInteger().ToByteArrayUnsigned(); return ECDsa.Create(new ECParameters
{
Curve = ECCurve.NamedCurves.nistP256,
D = privKeyInt.ToByteArrayUnsigned(),
Q = new ECPoint
{
X = privKeyX,
Y = privKeyY
}
});
}
private static ECDsa LoadPublicKey(byte[] key)
{
var pubKeyX = key.Skip().Take().ToArray();
var pubKeyY = key.Skip().ToArray();
return ECDsa.Create(new ECParameters
{
Curve = ECCurve.NamedCurves.nistP256,
Q = new ECPoint
{
X = pubKeyX,
Y = pubKeyY
}
});
}
}
备注:IdentityServer4 目前暂时仅支持 RS256 ( RSA with SHA256) ,还不支持 ES256 (https://github.com/IdentityServer/IdentityServer4/issues/2493)。
JAVA 的话可以选用 Google 的 Tink
@Test
public void testECDSA_P256() { try {
TinkConfig.register();
String plaintext = "napier";
KeysetHandle privateKeysetHandle = KeysetHandle.generateNew(SignatureKeyTemplates.ECDSA_P256);
PublicKeySign signer = PublicKeySignFactory.getPrimitive(privateKeysetHandle);
byte[] signature = signer.sign(plaintext.getBytes());
byte[] encoded = Base64.getEncoder().encode(signature);
System.out.println("\nMAC (Base64):\t" + new String(encoded));
System.out.println("MAC (Hex):" + toHexString(encoded));
KeysetHandle publicKeysetHandle = privateKeysetHandle.getPublicKeysetHandle();
PublicKeyVerify verifier = PublicKeyVerifyFactory.getPrimitive(publicKeysetHandle);
try {
verifier.verify(signature, plaintext.getBytes());
System.out.println("\nValid Signature");
} catch (GeneralSecurityException e) {
System.out.println("In Valid Signature");
}
System.out.println("\nPrinting out key:");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CleartextKeysetHandle.write(privateKeysetHandle, JsonKeysetWriter.withOutputStream(outputStream));
System.out.println(new String(outputStream.toByteArray()));
} catch (Exception e) {
System.out.println(e);
System.exit(1);
}
} public static String toHexString(byte[] bytes) {
StringBuffer sb = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
sb.append(toHex(bytes[i] >> 4));
sb.append(toHex(bytes[i]));
}
return sb.toString();
} private static char toHex(int nibble) {
final char[] hexDigit =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
return hexDigit[nibble & 0xF];
}
备注:
今天找到了一个好的 OIDC 的 JAVA 客户端,也包含了JWT 不同算法签名 ,更多参考:https://connect2id.com/products/nimbus-jose-jwt/examples
REFER:
https://docs.microsoft.com/en-gb/dotnet/api/system.security.cryptography?view=netcore-2.0
https://www.ibm.com/support/knowledgecenter/zh/SSMNED_5.0.0/com.ibm.apic.toolkit.doc/rapim_ref_ootb_policyjwtgen.html
https://www.scottbrady91.com/C-Sharp/JWT-Signing-using-ECDSA-in-dotnet-Core
https://www.openssl.org/docs/manmaster/man1/ecparam.html
https://www.bouncycastle.org/csharp/
https://yq.aliyun.com/articles/551057
http://www.cnblogs.com/linianhui/p/security-based-toolbox.html
http://bobao.360.cn/news/detail/1377.html
https://www.cnblogs.com/Kalafinaian/p/7392505.html
https://zhuanlan.zhihu.com/p/27615345
https://zhuanlan.zhihu.com/p/36326221
http://8btc.com/thread-1240-1-1.html
https://juejin.im/post/5a67f3836fb9a01c9b661bd3
https://zhuanlan.zhihu.com/p/36326221
https://www.bouncycastle.org/
https://medium.com/coinmonks/cryptography-with-google-tink-33a70d71918d
Jwt Token 安全策略使用 ECDSA 椭圆曲线加密算法签名/验证的更多相关文章
- IdentityServer4实战 - 谈谈 JWT Token 的安全策略
原文:IdentityServer4实战 - 谈谈 JWT Token 的安全策略 一.前言 众所周知,IdentityServer4 默认支持两种类型的 Token,一种是 Reference To ...
- Spring Cloud OAuth2.0 微服务中配置 Jwt Token 签名/验证
关于 Jwt Token 的签名与安全性前面已经做了几篇介绍,在 IdentityServer4 中定义了 Jwt Token 与 Reference Token 两种验证方式(https://www ...
- 关于 IdentityServer4 中的 Jwt Token 与 Reference Token
OpenID Connect(Core),OAuth 2.0(RFC 6749),JSON Web Token (JWT)(RFC 7519) 之间有着密不可分联系,对比了不同语言的实现,还是觉得 I ...
- IdentityServer4实战 - 谈谈 JWT 的安全策略
一.前言 众所周知,IdentityServer4 默认支持两种类型的 Token,一种是 Reference Token,一种是 JWT Token .前者的特点是 Token 的有效与否是由 To ...
- 【ASP.NET Core快速入门】(十一)应用Jwtbearer Authentication、生成jwt token
准备工作 用VSCode新建webapi项目JwtAuthSample,并打开所在文件夹项目 dotnet new webapi --name JwtAuthSample 编辑JwtAuthSampl ...
- 菜鸟入门【ASP.NET Core】11:应用Jwtbearer Authentication、生成jwt token
准备工作 用VSCode新建webapi项目JwtAuthSample,并打开所在文件夹项目 dotnet new webapi --name JwtAuthSample 编辑JwtAuthSampl ...
- JWT token心得
token的组成 token串的生成流程. token在客户端与服务器端的交互流程 Token的优点和思考 参考代码:核心代码使用参考,不是全部代码 JWT token的组成 头部(Header),格 ...
- jwt token
1 ,session 认证机制: ,用户登录,传递用户名和密码给客户端 ,服务器进行用户名和密码的校验,如果校验成功,将用户保存到session ,将sessionid通过cookie返回给客服端,客 ...
- jwt token Example - Python
0 Pre Install Python3 Install PyCrypto Install PyJWT 1 token 由三部分组成 header, payload, sign 并用逗号连接各部分 ...
随机推荐
- js 加减乘除失精
js 计算失精是因为js 先将10十进制代码转化为2进制,再计算导致 具体解决方案: 1. 加 function accAdd(arg1,arg2){ var r1,r2,m; ].length}} ...
- Python爬虫项目--爬取链家热门城市新房
本次实战是利用爬虫爬取链家的新房(声明: 内容仅用于学习交流, 请勿用作商业用途) 环境 win8, python 3.7, pycharm 正文 1. 目标网站分析 通过分析, 找出相关url, 确 ...
- 学习UI设计书籍推荐
在学习UI设计的过程当中,特别想学或者零基础的人来说,需要学习到很多知识,比如软件 PS AI ,理论 色彩 排版 规范 UE 等,这些都是一名UI设计师需要学习的知识,而学习到这些知识,可以通过视频 ...
- WPF 凭证分录控件
凭证分录编辑控件,效果如下: 源码:https://gitee.com/orchis/VoucherGridCtl.git
- 【转】权限管理学习 一、ASP.NET Forms身份认证
[转]权限管理学习 一.ASP.NET Forms身份认证 说明:本文示例使用的VS2017和MVC5. 系统无论大小.牛逼或屌丝,一般都离不开注册.登录.那么接下来我们就来分析下用户身份认证. 简单 ...
- Python从入门到精通之First!
Python的种类 Cpython Python的官方版本,使用C语言实现,使用最为广泛,CPython实现会将源文件(py文件)转换成字节码文件(pyc文件),然后运行在Python虚拟机上. Jy ...
- # 2019-2020-3 《Java 程序设计》实验一:Java开发环境的熟悉
2019-2020-3 <Java 程序设计>实验一:Java开发环境的熟悉-------1 一.实验要求: 1 建立"自己学号exp1"的目录 2 在"自己 ...
- Ubuntu Apache 不同端口监听不同站点
在/etc/apache2/apache2.conf 中,把项目根目录设置成默认的/var/www 不要设置在某个站点的路径下(我就是配置第一个站点时改了这里才会配置第二个站点时好久弄不出来) 在 / ...
- drf2 FBV和CBV
FBV 基于函数的视图 CBV 基于类的视图 也就是说我们是用函数编写视图~还是类编写视图 urlpatterns = [ path('admin/', admin.site.urls), path( ...
- ubuntu上vsftpd服务配置
Ubuntu上提供两种常用的ftp服务应用:vsftpd 和 tftpd,区别如下: 1)vsftpd 支持客户端上下传文件,支持浏览器显示及下载,支持用户名密码认证,支持匿名访问,默认端口TCP:2 ...