Skip to content

Commit

Permalink
Exclude non-GOST certificates
Browse files Browse the repository at this point in the history
We expect key pair and certificate are linked by CKA_ID attribute. So
certificate search has been reworked:
* look for certificates on token;
* get certificate id and value;
* filter non-GOST certificates out;
* find public key by certificate id: if only one is found than
  certificate is stored.
  • Loading branch information
dfortinskiy1 authored and Andrey Trifonov committed Apr 15, 2019
1 parent a568eb0 commit cf6bfcf
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 142 deletions.
8 changes: 5 additions & 3 deletions Certificate.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@

#import <rtpkcs11ecp/rtpkcs11.h>

#import <openssl/x509.h>

@interface Certificate : NSObject

@property(nonatomic, readonly) NSString* cn;
@property(nonatomic, readwrite) NSData* id;
@property(nonatomic, readonly) NSData* value;
@property(nonatomic, readonly) NSData* id;
@property(nonatomic, readonly) X509* x509;

-(id)initWithSession:(CK_SESSION_HANDLE)session object:(CK_OBJECT_HANDLE)object;
-(id)initWithSession:(CK_SESSION_HANDLE)session withObjectId:(CK_OBJECT_HANDLE)object withId:(NSData*)id withX509:(X509*)x509;

@end
87 changes: 21 additions & 66 deletions Certificate.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,74 +62,29 @@ - (NSData*) reversedData

@implementation Certificate

- (id)initWithSession:(CK_SESSION_HANDLE)session object:(CK_OBJECT_HANDLE)object {

- (id)initWithSession:(CK_SESSION_HANDLE)session withObjectId:(CK_OBJECT_HANDLE)object withId:(NSData*)id withX509:(X509*)x509 {
self = [super init];
if (nil == self) return self;

@try{
unsigned long length = 0;
unsigned char* data;

CK_FUNCTION_LIST_PTR functions;
CK_FUNCTION_LIST_EXTENDED_PTR extendedFunctions;

C_GetFunctionList(&functions);
C_EX_GetFunctionListExtended(&extendedFunctions);

CK_RV rv = extendedFunctions->C_EX_GetCertificateInfoText(session, object, &data, &length);
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];

NSString *cert=[[NSString alloc]initWithBytes:data length:length encoding:NSUTF8StringEncoding];

rv = extendedFunctions->C_EX_FreeBuffer(data);

NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"Subject: [A-Za-z,=]*?CN=([^\n,]*)" options:NSRegularExpressionCaseInsensitive error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:cert options:0 range:NSMakeRange(0, [cert length])];

if (match != nil) {
_cn = [cert substringWithRange:[match rangeAtIndex:1]];
} else {
_cn = @"";
NSLog(@"Failed to find CN");
}

NSRegularExpression *regexX = [NSRegularExpression regularExpressionWithPattern:@"X:([0-9A-F]*)\n" options:0 error:&error];
NSRegularExpression *regexY = [NSRegularExpression regularExpressionWithPattern:@"Y:([0-9A-F]*)\n" options:0 error:&error];
NSTextCheckingResult *matchX = [regexX firstMatchInString:cert options:0 range:NSMakeRange(0, [cert length])];
NSTextCheckingResult *matchY = [regexY firstMatchInString:cert options:0 range:NSMakeRange(0, [cert length])];

if (matchX != nil && matchY != nil) {
NSMutableData* valueData = [NSMutableData dataWithData:[[[cert substringWithRange:[matchX rangeAtIndex:1]] dataFromHexString] reversedData]];
[valueData appendData:[[[cert substringWithRange:[matchY rangeAtIndex:1]] dataFromHexString] reversedData]];
_value = [NSData dataWithData:valueData];
} else {
_value = nil;
NSLog(@"Failed to find value");
}

CK_ATTRIBUTE attributes[] = {
{CKA_ID, nil, 0}
};

rv = functions->C_GetAttributeValue(session, object, attributes, ARRAY_LENGTH(attributes));
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];

NSMutableData* idData = [NSMutableData dataWithLength:attributes[0].ulValueLen];
attributes[0].pValue = [idData mutableBytes];

rv = functions->C_GetAttributeValue(session, object, attributes, ARRAY_LENGTH(attributes));
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];

_id = [NSData dataWithData:idData];

return self;

} @catch (NSError* e){
NSLog(@"Error in certificate's init, reason: %ld (%@)", (long)[e code], [e localizedDescription]);
return nil;
}

_id = id;
_x509 = x509;

int cnLength = X509_NAME_get_text_by_NID(X509_get_subject_name(_x509), NID_commonName, 0, 0);
if (cnLength <= 0) {
NSLog(@"Failed to find CN");
_cn = @"";
} else {
cnLength += 1; // zero byte
NSMutableData* cn = [NSMutableData dataWithLength:cnLength];
X509_NAME_get_text_by_NID(X509_get_subject_name(_x509), NID_commonName, [cn mutableBytes], cnLength);
_cn = [[NSString alloc] initWithCString:[cn mutableBytes] encoding:NSUTF8StringEncoding];
}

return self;
}

- (void)dealloc {
X509_free(_x509);
}

@end
13 changes: 8 additions & 5 deletions README.mdown
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,24 @@ Rutoken DemoBank is a demonstration application which show typical usage of elec

##Requirements

Rutoken DemoBank should be built using iOS SDK 8.1 and supports iOS 7.0 and newer.
Rutoken DemoBank should be built using iOS SDK 8.1 and supports iOS 9.0 and newer.

External dependencies are located in [Rutoken SDK] (http://www.rutoken.ru/developers/sdk/)

Required frameworks:
* mobile/ios/pcsc/lib/RtPcsc.framework (Rutoken SDK)
* mobile/ios/pkcs11/lib/rtpkcs11ecp.framework (Rutoken SDK)
* openssl/rtengine/bin/ios-arm64/rtengine.framework (Rutoken SDK)

Required libraries:
* openssl/openssl-shared-1.1/ios-arm64/* (Rutoken SDK)

##How to build

* copy frameworks to example/Frameworks
* copy openssl library libcrypto.so and include folder to example/Frameworks/openssl
* open example/demobank.xcodeproj
* in General/Identity set correct Signing Identity
* in Build Phases/Link Binary With Libraries add frameworks from Rutoken SDK:
- RtPcsc.framework
- rtpkcs11ecp.framework

##Preliminary actions

Expand All @@ -39,7 +42,7 @@ To create key pairs and certificate on Rutoken ECP Bluetooth, follow these steps
##Restriction

* Rutoken DemoBank can only be run on physical iOS devices, not on emulators.
* Currently only iPad screen is supported.
* Currently only iPad and iPhone screens are supported.

##License

Expand Down
19 changes: 11 additions & 8 deletions README_RUS.mdown
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,27 @@

##Требования

Рутокен DemoBank собирается на iOS SDK 8.1 и запускается на устройствах c iOS начиная с 6.1.3
Рутокен DemoBank собирается на iOS SDK 8.1 и запускается на устройствах c iOS начиная с 9.0.

Внешние зависимости находятся в [Рутокен SDK] (http://www.rutoken.ru/developers/sdk/)

Необходимые фреймворки:
* Libs/ios/pcsc/RtPcsc.framework (Rutoken SDK)
* Libs/ios/pkcs11/static/RtPKCS11ECP.framework (Rutoken SDK)
* mobile/ios/pcsc/lib/RtPcsc.framework (Rutoken SDK)
* mobile/ios/pkcs11/lib/rtpkcs11ecp.framework (Rutoken SDK)
* openssl/rtengine/bin/ios-arm64/rtengine.framework (Rutoken SDK)

Необходимые библиотеки:
* openssl/openssl-shared-1.1/ios-arm64/* (Rutoken SDK)

##Как собрать

* положить фреймворки в example/external/pkcs11bin
* положить фреймворки в example/Frameworks
* положить библиотеку libcrypto.so и папку include от openssl в example/Frameworks/openssl
* открыть example/demobank.xcodeproj
* в разделе General/Identity установить правильный Signing Identity
* в разделе Build Phases/Link Binary With Libraries добавить фреймворки из Рутокен SDK:
- RtPcsc.framework
- RtPKCS11ECP.framework

##Предварительные действия

Чтобы создать на Рутокен ЭЦП Bluetooth ключевую пару и сертификат, проделайте следующие действия
* На desktop компьютере загрузите [Рутокен Плагин](http://www.rutoken.ru/products/all/rutoken-plugin/) и установите его.
* Перезагрузите браузер для полной установки плагина
Expand All @@ -38,7 +41,7 @@
##Ограничения

* Приложение Рутокен DemoBank может быть запущено только на физических iOS устройствах, не в эмуляторе
* В настоящий момент поддерживается только экран iPad
* В настоящий момент поддерживается только экраны iPad и iPhone

##Лицензия

Expand Down
163 changes: 104 additions & 59 deletions Token.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) 2015, CJSC Aktiv-Soft. See the LICENSE file at the top-level directory of this distribution.
// All Rights Reserved.

#import <openssl/x509.h>

#import "Token.h"

#import "Pkcs11Error.h"
Expand Down Expand Up @@ -35,71 +37,115 @@ - (NSString*)removeTrailingSpaceFromCString:(const char*) string length:(size_t)
return [[NSString alloc] initWithBytes:string length:i encoding:NSUTF8StringEncoding];
}

- (void)readCertificatesWithCategory:(CK_ULONG) category {
CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
CK_ATTRIBUTE template[] = {
{CKA_CLASS, &certClass, sizeof(certClass)},
{CKA_CERTIFICATE_CATEGORY, &category, sizeof(category)}
- (void)readCertificates {
CK_CERTIFICATE_TYPE certificateType = CKC_X_509;
CK_OBJECT_CLASS certificateClass = CKO_CERTIFICATE;
CK_ATTRIBUTE certificateTemplate[] = {
{CKA_CLASS, &certificateClass, sizeof(certificateClass)},
{CKA_CERTIFICATE_TYPE, &certificateType, sizeof(certificateType)},
};
CK_OBJECT_HANDLE certificates[32];
CK_ULONG certificateCount;
CK_RV rv, rv2;

CK_RV rv = [self functions]->C_FindObjectsInit(_session, template, ARRAY_LENGTH(template));
// Find all certificates on token
rv = [self functions]->C_FindObjectsInit(_session, certificateTemplate, ARRAY_LENGTH(certificateTemplate));
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];

while (TRUE) {
CK_OBJECT_HANDLE objects[30];
CK_ULONG count;
rv = [self functions]->C_FindObjects(_session, objects, ARRAY_LENGTH(objects), &count);
if (CKR_OK != rv) break;

for (int i = 0; i < count; ++i) {
Certificate* c = [[Certificate alloc] initWithSession:_session object:objects[i]];
if (nil != c) [_certificates addObject:c];
}

if (count < ARRAY_LENGTH(objects)) break;
}
rv = [self functions]->C_FindObjects(_session, certificates, ARRAY_LENGTH(certificates), &certificateCount);

CK_RV rv2 = [self functions]->C_FindObjectsFinal(_session); // we should always call C_FindObjectsFinal, even after an error (see pkcs11 standart for more info...)
rv2 = [self functions]->C_FindObjectsFinal(_session);
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];
if (CKR_OK != rv2) @throw [Pkcs11Error errorWithCode:rv2];

//This code only for thoose who hasn't read pkcs11 standart
//and set different CKA_ID for key and cert
//You should delete it, unless you know, that key's and cert's CKA_IDs are different
for (Certificate* cert in _certificates) {
CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;

CK_ATTRIBUTE keyTemplatebyValue[] = {
{CKA_CLASS, &keyClass, sizeof(keyClass)},
{CKA_VALUE, (void*)[[cert value] bytes], [[cert value] length]}

for (int i = 0; i < certificateCount; ++i) {
NSMutableData* value;
NSMutableData* id;

const unsigned char* certificateValue;

EVP_PKEY* key;
X509* x509;

CK_ATTRIBUTE certificateAttributes[] = {
{CKA_VALUE, NULL_PTR, 0},
{CKA_ID, NULL_PTR, 0},
};

rv = [self functions]->C_FindObjectsInit(_session, keyTemplatebyValue, ARRAY_LENGTH(keyTemplatebyValue));
if (CKR_OK != rv) continue;

CK_OBJECT_HANDLE objects[2];
CK_ULONG count;
rv = [self functions]->C_FindObjects(_session, objects, ARRAY_LENGTH(objects), &count);

rv2 = [self functions]->C_FindObjectsFinal(_session); // we should always call C_FindObjectsFinal, even after an error (see pkcs11 standart for more info...)
if (CKR_OK != rv) [Pkcs11Error errorWithCode:rv];
if (CKR_OK != rv2) [Pkcs11Error errorWithCode:rv2];

if (count == 1) { //we found exactly one key, so it's cert's public key, now we have to check CKA_ID to avoid pkcs11 violation.
CK_ATTRIBUTE attributes[] = {
{CKA_ID, nil, 0}
};

rv = [self functions]->C_GetAttributeValue(_session, objects[0], attributes, ARRAY_LENGTH(attributes));
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];

NSMutableData* idData = [NSMutableData dataWithLength:attributes[0].ulValueLen];
attributes[0].pValue = [idData mutableBytes];

rv = [self functions]->C_GetAttributeValue(_session, objects[0], attributes, ARRAY_LENGTH(attributes));
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];

[cert setId:[NSData dataWithData:idData]];
CK_OBJECT_CLASS publicKeyClass = CKO_PUBLIC_KEY;
CK_ATTRIBUTE publicKeyTemplate[] = {
{CKA_CLASS, &publicKeyClass, sizeof(publicKeyClass)},
{CKA_ID, NULL_PTR, 0},
};
CK_OBJECT_HANDLE publicKeys[16];
CK_ULONG publicKeyCount;

// Get certificate value and id
rv = [self functions]->C_GetAttributeValue(_session, certificates[i], certificateAttributes, ARRAY_LENGTH(certificateAttributes));
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];

value = [NSMutableData dataWithLength:certificateAttributes[0].ulValueLen];
certificateAttributes[0].pValue = [value mutableBytes];
id = [NSMutableData dataWithLength:certificateAttributes[1].ulValueLen];
certificateAttributes[1].pValue = [id mutableBytes];

rv = [self functions]->C_GetAttributeValue(_session, certificates[i], certificateAttributes, ARRAY_LENGTH(certificateAttributes));
if (CKR_OK != rv) @throw [Pkcs11Error errorWithCode:rv];

// Filter non-GOST certificates
certificateValue = [value mutableBytes];
x509 = d2i_X509(NULL, &certificateValue, [value length]);
if (!x509) continue;

key = X509_get0_pubkey(x509);
if (!key) {
X509_free(x509);
continue;
}

switch (EVP_PKEY_base_id(key)) {
case NID_id_GostR3410_2001:
case NID_id_GostR3410_2012_256:
case NID_id_GostR3410_2012_512:
break;
default:
X509_free(x509);
continue;
}

// Find public keys by certificate id
publicKeyTemplate[1].pValue = certificateAttributes[1].pValue;
publicKeyTemplate[1].ulValueLen = certificateAttributes[1].ulValueLen;

rv = [self functions]->C_FindObjectsInit(_session, publicKeyTemplate, ARRAY_LENGTH(publicKeyTemplate));
if (CKR_OK != rv) {
X509_free(x509);
@throw [Pkcs11Error errorWithCode:rv];
}

rv = [self functions]->C_FindObjects(_session, publicKeys, ARRAY_LENGTH(publicKeys), &publicKeyCount);

rv2 = [self functions]->C_FindObjectsFinal(_session);
if (CKR_OK != rv) {
X509_free(x509);
@throw [Pkcs11Error errorWithCode:rv];
}
if (CKR_OK != rv2) {
X509_free(x509);
@throw [Pkcs11Error errorWithCode:rv2];
}

switch (publicKeyCount) {
case 0:
// Nothing has been found.
X509_free(x509);
continue;
case 1:
[_certificates addObject:[[Certificate alloc] initWithSession:_session withObjectId:certificates[i] withId:id withX509:x509]];
break;
default:
// There are several public keys with certificate ID. We dont know which to choose so skip.
X509_free(x509);
continue;
}
}
}
Expand Down Expand Up @@ -170,8 +216,7 @@ - (id)initWithSlotId:(CK_SLOT_ID)slotId{
@try{
_certificates = [NSMutableArray array];

[self readCertificatesWithCategory:CertificateCategoryUnspecified];
[self readCertificatesWithCategory:CertificateCategoryUser];
[self readCertificates];
} @catch (NSError* e) {
_functions->C_CloseSession(_session);
return nil;
Expand Down
Loading

0 comments on commit cf6bfcf

Please sign in to comment.