JWT
一个 Objective-C 的 JSON Web Token 实现。
maater 和 bleeding edge 中新功能。
- 支持 EC 算法。
- 从 Pem 文件中提取密钥已更新。
支持 EC 算法。
前提条件。
- 对应于公钥和私钥的证书和 P12。
- 带有密钥的 Pem 文件,采用 ANSI X9.63 格式。
示例。
NSString *privateKeyString = @"<ANSI X9.63 formatted key>";
NSString *publicKeyString = @"<ANSI X9.63 formatted key>";
// Note: We should pass type of key. Default type is RSA.
NSDictionary *parameters = @{JWTCryptoKey.parametersKeyBuilder : JWTCryptoKeyBuilder.new.keyTypeEC};
id <JWTCryptoKeyProtocol> privateKey = [[JWTCryptoKeyPrivate alloc] initWithPemEncoded:privateKeyString parameters:parameters error:nil];
id <JWTCryptoKeyProtocol> publicKey = [[JWTCryptoKeyPublic alloc] initWithPemEncoded:publicKeyString parameters:parameters error:nil];
// Note: JWTAlgorithmRSFamilyDataHolder will be renamed to something more appropriate. It can holds any asymmetric keys pair (private and public).
id <JWTAlgorithmDataHolderProtocol> holder = [JWTAlgorithmRSFamilyDataHolder new].signKey(privateKey).verifyKey(publicKey).algorithmName(JWTAlgorithmNameES256);
3.0版本中的新功能
- Fluent风格扩展。
- 添加编码结果类型。
- 算法和数据持有者。
- 算法和数据持有者链。
- 从Pem文件加载密钥。
算法数据持有者和链的介绍。
你有一个算法,一个秘密数据和未知jwt令牌。让我们试着解码它。
// create token
NSString *token = @"...";
// possible that algorithm could return error.
// you could try use algorithm and data chain.
NSString *firstSecret = @"first";
NSString *firstAlgorithmName = JWTAlgorithmNameHS384;
id <JWTAlgorithmDataHolderProtocol> firstHolder = [JWTAlgorithmHSFamilyDataHolder new].algorithmName(firstAlgorithmName).secret(firstSecret);
id <JWTAlgorithmDataHolderProtocol> errorHolder = [JWTAlgorithmNoneDataHolder new];
// chain together.
JWTAlgorithmDataHolderChain *chain = [[JWTAlgorithmDataHolderChain alloc] initWithHolders:@[firstHolder, errorHolder]];
// or add them in builder
[JWTDecodingBuilder decodeMessage:token].addHolder(firstHolder).addHolder(errorHolder);
// or add them as chain
[JWTDecodingBuilder decodeMessage:token].chain(chain);
也许你想尝试不同的秘密。
// possible that your algorithm has several secrets.
// you don't know which secret to use.
// but you want to decode it.
NSString *firstSecret = @"first";
NSArray *manySecrets = @[@"second", @"third", @"forty two"];
// translate to data
NSArray *manySecretsData = @[];
for (NSString *secret in manySecrets) {
NSData *secretData = [JWTBase64Coder dataWithBase64UrlEncodedString:secret];
if (secret) {
manySecretsData = [manySecretsData arrayByAddingObject:secretData];
}
}
NSString *algorithmName = JWTAlgorithmNameHS384;
id <JWTAlgorithmDataHolderProtocol> firstHolder = [JWTAlgorithmHSFamilyDataHolder new].algorithmName(algorithmName).secret(firstSecret);
// lets create chain
JWTAlgorithmDataHolderChain *chain = [JWTAlgorithmDataHolderChain chainWithHolder:firstHolder];
// and lets populate chain with secrets.
NSLog(@"chain has: %@", chain.debugDescription);
JWTAlgorithmDataHolderChain *expandedChain = [chain chainByPopulatingAlgorithm:firstHolder.currentAlgorithm withManySecretData:manySecretsData];
// now we have expanded chain with many secrets and one algorithm.
NSLog(@"expanded chain has: %@", expandedChain.debugDescription);
通过链解码和编码。
JWTClaimsSet *claimsSet = [[JWTClaimsSet alloc] init];
// fill it
claimsSet.issuer = @"Facebook";
claimsSet.subject = @"Token";
claimsSet.audience = @"https://jwt.net.cn";
// encode it
NSString *secret = @"secret";
NSString *algorithmName = @"HS384";
NSDictionary *headers = @{@"custom":@"value"};
id<JWTAlgorithmDataHolderProtocol>holder = [JWTAlgorithmHSFamilyDataHolder new].algorithmName(algorithmName).secret(secret);
JWTCodingResultType *result = [JWTEncodingBuilder encodeClaimsSet:claimsSet].headers(headers).addHolder(holder).result;
NSString *encodedToken = result.successResult.encoded;
if (result.successResult) {
// handle encoded result
NSLog(@"encoded result: %@", result.successResult.encoded);
}
else {
// handle error
NSLog(@"encode failed, error: %@", result.errorResult.error);
}
// decode it
// you can set any property that you want, all properties are optional
JWTClaimsSet *trustedClaimsSet = [claimsSet copy];
NSNumber *options = @(JWTCodingDecodingOptionsNone);
NSString *yourJwt = encodedToken; // from previous example
JWTCodingResultType *decodedResult = [JWTDecodingBuilder decodeMessage:yourJwt].claimsSet(claimsSet).addHolder(holder).options(options).and.result;
if (decodedResult.successResult) {
// handle decoded result
NSLog(@"decoded result: %@", decodedResult.successResult.headerAndPayloadDictionary);
NSLog(@"headers: %@", decodedResult.successResult.headers);
NSLog(@"payload: %@", decodedResult.successResult.payload);
}
else {
// handle error
NSLog(@"decode failed, error: %@", decodedResult.errorResult.error);
}
从Pem文件加载的密钥。
你在pem文件中有一个密钥。你想要直接用它来签名/验证。假设"public_rsa.pem"和"private_rsa.pem"是pem格式的公开和私有密钥。
// Load keys
- (NSString *)pemKeyStringFromFileWithName:(NSString *)string inBundle:(NSBundle *)bundle {
NSURL *fileURL = [bundle URLForResource:name withExtension:@"pem"];
NSError *error = nil;
NSString *fileContent = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:&error];
if (error) {
NSLog(@"%@ error: %@", self.debugDescription, error);
return nil;
}
}
// Sign and verify
- (void)signAndVerifyWithPrivateKeyPemString:(NSString *)privateKey publicKeyPemString:(NSString *)publicKey privateKeyPassphrase:(NSString *)passphrase {
NSString *algorithmName = @"RS256";
id <JWTAlgorithmDataHolderProtocol> signDataHolder = [JWTAlgorithmRSFamilyDataHolder new].keyExtractorType([JWTCryptoKeyExtractor privateKeyWithPEMBase64].type).privateKeyCertificatePassphrase(passphrase).algorithmName(algorithmName).secret(privateKey);
id <JWTAlgorithmDataHolderProtocol> verifyDataHolder = [JWTAlgorithmRSFamilyDataHolder new].keyExtractorType([JWTCryptoKeyExtractor publicKeyWithPEMBase64].type).algorithmName(algorithmName).secret(publicKey);
// sign
NSDictionary *payloadDictionary = @{@"hello": @"world"};
JWTCodingBuilder *signBuilder = [JWTEncodingBuilder encodePayload:payloadDictionary].addHolder(signDataHolder);
JWTCodingResultType *signResult = signBuilder.result;
NSString *token = nil;
if (signResult.successResult) {
// success
NSLog(@"%@ success: %@", self.debugDescription, signResult.successResult.encoded);
token = signResult.successResult.encoded;
}
else {
// error
NSLog(@"%@ error: %@", self.debugDescription, signResult.errorResult.error);
}
// verify
if (token == nil) {
NSLog(@"something wrong");
}
JWTCodingBuilder *verifyBuilder = [JWTDecodingBuilder decodeMessage:token].addHolder(verifyDataHolder);
JWTCodingResultType *verifyResult = verifyBuilder.result;
if (verifyResult.successResult) {
// success
NSLog(@"%@ success: %@", self.debugDescription, verifyResult.successResult.payload);
token = verifyResult.successResult.encoded;
}
else {
// error
NSLog(@"%@ error: %@", self.debugDescription, verifyResult.errorResult.error);
}
}
2.0版本中的实验
白名单中的可能算法。
当您需要用多个算法解码JWT时,可以在白名单中指定它们的名字。稍后此功能可能将迁移到选项。例如,有人返回结果或错误。
限制
由于唯一 secret
的限制,只限于配对(算法或无)。
NSString *jwtResultOrError = /*...*/;
NSString *secret = @"secret";
JWTBuilder *builder = [JWT decodeMessage:jwtResultOrError].secret(@"secret").whitelist(@[@"HS256", @"none"]);
NSDictionary *decoded = builder.decode;
if (builder.jwtError) {
// oh!
}
else {
NSDictionary *payload = decoded[@"payload"];
NSDictionary *header = decoded[@"header"];
NSArray *tries = decoded[@"tries"]; // will be evolded into something appropriate later.
}
版本2.0的新特性
- 旧的普通样式已弃用。
- 请改为使用现代流畅风格。
NSDictionary *payload = @{@"foo" : @"bar"};
NSString *secret = @"secret";
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@"HS256"];
// Deprecated
[JWT encodePayload:payload withSecret:secret algorithm:algorithm];
// Modern
[JWTBuilder encodePayload:payload].secret(secret).algorithm(algorithm).encode;
安装
将以下内容添加到您的 CocoaPods Podfile
pod "JWT"
通过 Cartfile 进行安装
github "yourkarma/JWT" "master"
和 import JWT
文档
用法
JWTBuilder
要编码和解码JWT,请使用带有 JWTBuilder
接口的流畅风格
+ (JWTBuilder *)encodePayload:(NSDictionary *)payload;
+ (JWTBuilder *)encodeClaimsSet:(JWTClaimsSet *)claimsSet;
+ (JWTBuilder *)decodeMessage:(NSString *)message;
如您所见,JWTBuilder同时提供了解码和解码的接口。
注意:一些属性仅用于编码或解码。
#pragma mark - Encode only
*payload;
*headers;
*algorithm;
#pragma mark - Decode only
*message
*options // as forcedOption from jwt decode functions interface.
*whitelist //optional array of algorithm names to whitelist for decoding
您可以通过前缀为jwt
的属性检查JWTBuilder。
您可以通过流畅样式(块接口)设置JWTBuilder属性。
您可以这样编码任意负载
NSDictionary *payload = @{@"foo" : @"bar"};
NSString *secret = @"secret";
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@"HS256"];
[JWTBuilder encodePayload:payload].secret(@"secret").algorithm(algorithm).encode;
如果您正在使用保留声明名称,您可以像这样编码声明集(所有属性都是可选的)
JWTClaimsSet *claimsSet = [[JWTClaimsSet alloc] init];
claimsSet.issuer = @"Facebook";
claimsSet.subject = @"Token";
claimsSet.audience = @"http://yourkarma.com";
claimsSet.expirationDate = [NSDate distantFuture];
claimsSet.notBeforeDate = [NSDate distantPast];
claimsSet.issuedAt = [NSDate date];
claimsSet.identifier = @"thisisunique";
claimsSet.type = @"test";
NSString *secret = @"secret";
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@"HS256"];
[JWTBuilder encodeClaimsSet:claimsSet].secret(secret).algorithm(algorithm).encode;
您可以像这样解码JWT
NSString *jwtToken = @"header.payload.signature";
NSString *secret = @"secret";
NSString *algorithmName = @"HS256"; //Must specify an algorithm to use
NSDictionary *payload = [JWTBuilder decodeMessage:jwtToken].secret(secret).algorithmName(algorithmName).decode;
如果您在解码时想检查声明,可以使用以下代码示例(所有属性都是可选的)
// Trusted Claims Set
JWTClaimsSet *trustedClaimsSet = [[JWTClaimsSet alloc] init];
trustedClaimsSet.issuer = @"Facebook";
trustedClaimsSet.subject = @"Token";
trustedClaimsSet.audience = @"http://yourkarma.com";
trustedClaimsSet.expirationDate = [NSDate date];
trustedClaimsSet.notBeforeDate = [NSDate date];
trustedClaimsSet.issuedAt = [NSDate date];
trustedClaimsSet.identifier = @"thisisunique";
trustedClaimsSet.type = @"test";
NSString *message = @"encodedJwt";
NSString *secret = @"secret";
NSString *algorithmName = @"chosenAlgorithm"
JWTBuilder *builder = [JWTBuilder decodeMessage:jwt].secret(secret).algorithmName(algorithmName).claimsSet(trustedClaimsSet);
NSDictionary *payload = builder.decode;
if (builder.jwtError == nil) {
// do your work here
}
else {
// handle error
}
如果您想强制使用有效的算法列表
NSArray *whitelist = @[@"HS256", @"HS512"];
NSString *jwtToken = @"header.payload.signature";
NSString *secret = @"secret";
NSString *algorithmName = @"HS256";
//Returns nil
NSDictionary *payload = [JWTBuilder decodeMessage:jwtToken].secret(secret).algorithmName(algorithmName).whitelist(@[]).decode;
//Returns the decoded payload
NSDictionary *payload = [JWTBuilder decodeMessage:jwtToken].secret(secret).algorithmName(algorithmName).whitelist(whitelist).decode;
编码/解码示例
// suppose, that you create ClaimsSet
JWTClaimsSet *claimsSet = [[JWTClaimsSet alloc] init];
// fill it
claimsSet.issuer = @"Facebook";
claimsSet.subject = @"Token";
claimsSet.audience = @"http://yourkarma.com";
// encode it
NSString *secret = @"secret";
NSString *algorithmName = @"HS384";
NSDictionary *headers = @{@"custom":@"value"};
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:algorithmName];
JWTBuilder *encodeBuilder = [JWT encodeClaimsSet:claimsSet];
NSString *encodedResult = encodeBuilder.secret(secret).algorithm(algorithm).headers(headers).encode;
if (encodeBuilder.jwtError == nil) {
// handle encoded result
NSLog(@"encoded result: %@", encodedResult);
}
else {
// handle error
NSLog(@"encode failed, error: %@", encodeBuilder.jwtError);
}
// decode it
// you can set any property that you want, all properties are optional
JWTClaimsSet *trustedClaimsSet = [claimsSet copy];
// decode forced ? try YES
BOOL decodeForced = NO;
NSNumber *options = @(decodeForced);
NSString *yourJwt = encodedResult; // from previous example
NSString *yourSecret = secret; // from previous example
NSString *yourAlgorithm = algorithmName; // from previous example
JWTBuilder *decodeBuilder = [JWT decodeMessage:yourJwt];
NSDictionary *decodedResult = decodeBuilder.message(yourJwt).secret(yourSecret).algorithmName(yourAlgorithm).claimsSet(trustedClaimsSet).options(options).decode;
if (decodeBuilder.jwtError == nil) {
// handle decoded result
NSLog(@"decoded result: %@", decodedResult);
}
else {
// handle error
NSLog(@"decode failed, error: %@", decodeBuilder.jwtError);
}
NSData
您还可以使用表示为NSData对象的秘密进行编码/解码。
//Encode
NSData *secretData = "<your data>";
NSString *algorithmName = @"HS384";
NSDictionary *headers = @{@"custom":@"value"};
id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:algorithmName];
JWTBuilder *encodeBuilder = [JWT encodeClaimsSet:claimsSet];
NSString *encodedResult = encodeBuilder.secretData(secretData).algorithm(algorithm).headers(headers).encode;
//Decode
NSString *jwtToken = @"header.payload.signature";
NSData *secretData = "<your data>"
NSString *algorithmName = @"HS256"; //Must specify an algorithm to use
NSDictionary *payload = [JWTBuilder decodeMessage:jwtToken].secretData(secretData).algorithmName(algorithmName).decode;
算法
以下算法得到支持
- RS256
- HS512 - 使用SHA-512的HMAC。
- HS256 / HS384 / HS512
- None
RS256用法。
例如,您有一个包含私钥的文件:file.p12
。并且您为此文件有一个秘密通行短语:secret
。
// Encode
NSDictionary *payload = @{@"payload" : @"hidden_information"};
NSString *algorithmName = @"RS256";
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"secret_key" ofType:@"p12"];
NSData *privateKeySecretData = [NSData dataWithContentsOfFile:filePath];
NSString *passphraseForPrivateKey = @"secret";
JWTBuilder *builder = [JWTBuilder encodePayload:payload].secretData(privateKeySecretData).privateKeyCertificatePassphrase(passphraseForPrivateKey).algorithmName(algorithmName);
NSString *token = builder.encode;
// check error
if (builder.jwtError == nil) {
// handle result
}
else {
// error occurred.
}
// Decode
// Suppose, that you get token from previous example. You need a valid public key for a private key in previous example.
// Private key stored in @"secret_key.p12". So, you need public key for that private key.
NSString *publicKey = @"..."; // load public key. Or use it as raw string.
algorithmName = @"RS256";
JWTBuilder *decodeBuilder = [JWTBuilder decodeMessage:token].secret(publicKey).algorithmName(algorithmName);
NSDictionary *envelopedPayload = decodeBuilder.decode;
// check error
if (decodeBuilder.jwtError == nil) {
// handle result
}
else {
// error occurred.
}
可以通过实现JWTAlgorithm协议添加其他算法。
在提交请求之前
在提交请求之前,请阅读贡献说明。