renew politica para movil identifiers google dispositivo cuenta configuracion certificates apps apple alerta iphone rsa public-key pem der

iphone - politica - ¿Cómo puedo obtener SecKeyRef desde el archivo DER/PEM?



g suite device policy (3)

Necesito integrar mi aplicación de iPhone con un sistema, y ​​requieren cifrar los datos mediante una clave pública determinada, hay 3 archivos en 3 formatos diferentes .xml .der y .pem. He investigado y encontrado algunos artículos sobre cómo obtener SecKeyRef de DER / PEM, pero siempre se devuelven a cero. A continuación se muestra mi código:

NSString *pkFilePath = [[NSBundle mainBundle] pathForResource:@"PKFile" ofType:@"der"]; NSData *pkData = [NSData dataWithContentsOfFile:pkFilePath]; SecCertificateRef cert; cert = SecCertificateCreateWithData(NULL, (CFDataRef) pkData); assert(cert != NULL); OSStatus err; if (cert != NULL) { err = SecItemAdd( (CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys: (id) kSecClassCertificate, kSecClass, (id) cert, kSecValueRef, nil ], NULL ); if ( (err == errSecSuccess) || (err == errSecDuplicateItem) ) { CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **) &cert, 1, NULL); SecPolicyRef policy = SecPolicyCreateBasicX509(); SecTrustRef trust; SecTrustCreateWithCertificates(certs, policy, &trust); SecTrustResultType trustResult; SecTrustEvaluate(trust, &trustResult); if (certs) { CFRelease(certs); } if (trust) { CFRelease(trust); } return SecTrustCopyPublicKey(trust); } } return NULL;

El problema ocurre en SecCertificateCreateWithData, siempre se devuelve cero, incluso si el archivo leído está bien. Cualquiera que haya hecho esto por favor ayúdame, gracias!

EDITAR: El archivo cert fue la firma MD5.


A partir de iOS 10, es realmente posible importar claves privadas PEM sin convertirlas a PKCS # 12 (que es un formato de contenedor muy universal para todo lo relacionado con la criptografía) y, por lo tanto, también sin OpenSSL en la línea de comandos o estáticamente enlazando aplicaciones con él. En macOS es posible incluso desde 10.7 usando una función diferente a las mencionadas aquí (pero hasta ahora no existe para iOS). Sin embargo, la forma descrita a continuación también funcionará en macOS 10.12 y posteriores.

Para importar un certificado, es suficiente simplemente despojar el

-----BEGIN CERTIFICATE-----

y

-----END CERTIFICATE-----

líneas, luego ejecute la decodificación de base64 sobre los datos que quedan, el resultado es un certificado en formato DER estándar, que solo se puede SecCertificateCreateWithData() a SecCertificateCreateWithData() para obtener un SecCertificateRef . Esto siempre ha estado funcionando, también antes de iOS 10.

Para importar una clave privada, es posible que se requiera un poco de trabajo adicional. Si la clave privada está envuelta con

-----BEGIN RSA PRIVATE KEY-----

entonces es muy facil Una vez más, la primera y la última línea deben eliminarse, los datos restantes deben descodificarse en base64 y el resultado es una clave RSA en formato PKCS # 1 . Este formato solo puede contener claves RSA y se puede leer directamente, solo SecKeyCreateWithData() datos descodificados en SecKeyCreateWithData() para obtener un SecKeyRef . El diccionario de attributes solo necesita los siguientes pares clave / valor:

  • kSecAttrKeyType : kSecAttrKeyTypeRSA
  • kSecAttrKeyClass : kSecAttrKeyClassPrivate
  • kSecAttrKeySizeInBits : CFNumberRef con el número de bits en la clave (por ejemplo, 1024, 2048, etc.) Si no se conoce, esta información puede leerse realmente desde los datos de clave en bruto, que son datos ASN.1 (está un poco más allá del alcance de esta respuesta, pero proporcionaré algunos enlaces útiles a continuación sobre cómo analizar ese formato). Este valor es tal vez opcional! En mis pruebas en realidad no era necesario establecer este valor; si está ausente, la API determinó el valor por sí mismo y siempre se configuró correctamente más adelante.

En caso de que la clave privada esté envuelta por -----BEGIN PRIVATE KEY----- , entonces los datos codificados en base64 no están en el formato PKCS # 1 , sino en el formato PKCS # 8 , sin embargo, esto es simplemente un método más genérico. contenedor que también puede contener claves no RSA pero para claves RSA, los datos internos de ese contenedor son iguales a PKCS # 1 , por lo que se podría decir que para claves RSA PKCS # 8 es PKCS # 1 con un encabezado adicional y todo lo que necesita hacer está quitando ese encabezado extra. Simplemente elimine los primeros 26 bytes de los datos descodificados en base64 y tendrá PKCS # 1 nuevamente. Sí, es realmente tan simple.

Para obtener más información sobre los formatos PKCS # x en codificaciones PEM, eche un vistazo a este sitio . Para obtener más información sobre el formato ASN.1, aquí hay un buen sitio para eso . Y si necesita un analizador ASN.1 en línea simple pero potente e interactivo para jugar con diferentes formatos, uno que pueda leer directamente datos PEM, así como ASN.1 en base64 y hexdump, pruebe este sitio .

Muy importante: cuando agregue una clave privada a keychain, que creó como se mencionó anteriormente, tenga en cuenta que dicha clave privada no contiene un hash de clave pública, sin embargo, un hash de clave pública es importante para que la API de llavero forme una identidad ( SecIdentityRef ), ya que usar el hash de la clave pública es cómo la API encuentra la clave privada correcta que pertenece a un certificado importado (un SecIdentityRef es solo un SecKeyRef de una clave privada y un SecCertificateRef de un SecCertificateRef forma un objeto combinado y es la clave pública hash, que los une juntos). Por lo tanto, cuando planea agregar la clave privada al llavero, asegúrese de establecer un hash de clave pública de forma manual, de lo contrario, nunca podrá obtener una identidad para él y, sin eso, no puede usar la API del llavero para tareas como la firma o el descifrado. datos. El hash de la clave pública debe almacenarse en un atributo llamado kSecAttrApplicationLabel (nombre estúpido, lo sé, pero en realidad no es una etiqueta y nada que el usuario pueda ver, consulte la documentación). P.ej:

OSStatus error = SecItemAdd( (__bridge CFDictionaryRef)@{ (__bridge NSString *)kSecClass: (__bridge NSString *)kSecClassKey, (__bridge NSString *)kSecAttrApplicationLabel: hashOfPublicKey, // hashOfPublicKey is NSData * #if TARGET_OS_IPHONE (__bridge NSString *)kSecValueRef: (__bridge id)privateKeyToAdd, // privateKeyToAdd is SecKeyRef #else (__bridge NSString *)kSecUseItemList: @[(__bridge id)privateKeyToAdd], // privateKeyToAdd is SecKeyRef // @[ ... ] wraps it into a NSArray object, // as kSecUseItemList expects an array of items #endif }, &outReference // Can also be NULL, // otherwise reference to added keychain entry // that must be released with CFRelease() );


Después de horas de esfuerzo investigando en línea con la ayuda de este post, finalmente lo hago funcionar perfectamente. Aquí están las notas con código Swift de trabajo de la versión más actual. Espero que pueda ayudar a alguien!

  1. Recibió un certificado en la cadena codificada en base64 intercalada entre el encabezado y la cola de esta manera (formato PEM):

    -----BEGIN CERTIFICATE----- -----END CERTIFICATE-----

  2. despojar el encabezado y la cola, tales como

    // remove the header string let offset = ("-----BEGIN CERTIFICATE-----").characters.count let index = certStr.index(cerStr.startIndex, offsetBy: offset+1) cerStr = cerStr.substring(from: index) // remove the tail string let tailWord = "-----END CERTIFICATE-----" if let lowerBound = cerStr.range(of: tailWord)?.lowerBound { cerStr = cerStr.substring(to: lowerBound) }

  3. decodificar la cadena base64 a NSData:

    let data = NSData(base64Encoded: cerStr, options:NSData.Base64DecodingOptions.ignoreUnknownCharacters)!

  4. Convertirlo de formato NSdata a SecCertificate:

    let cert = SecCertificateCreateWithData(kCFAllocatorDefault, data)

  5. Ahora, este certificado se puede utilizar para comparar con el certificado recibido de la confianza urlSession:

    certificateFromUrl = SecTrustGetCertificateAtIndex(...) if cert == certificate { }


Luché mucho con el mismo problema y finalmente encontré una solución. Mi problema era que necesitaba usar una clave privada y pública externa para cifrar / descifrar datos en una aplicación de iOS y no quería usar el llavero. Resulta que también necesita un certificado firmado para que la biblioteca de seguridad de iOS pueda leer los datos clave y, por supuesto, los archivos deben estar en el formato correcto. El procedimiento es básicamente el siguiente:

Supongamos que tiene una clave privada en formato PEM (con las teclas ----- BEGIN RSA PRIVATE ----- y ----- END RSA PRIVATE KEY -----): rsaPrivate.pem

//Create a certificate signing request with the private key openssl req -new -key rsaPrivate.pem -out rsaCertReq.csr //Create a self-signed certificate with the private key and signing request openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey rsaPrivate.pem -out rsaCert.crt //Convert the certificate to DER format: the certificate contains the public key openssl x509 -outform der -in rsaCert.crt -out rsaCert.der //Export the private key and certificate to p12 file openssl pkcs12 -export -out rsaPrivate.p12 -inkey rsaPrivate.pem -in rsaCert.crt

Ahora tiene dos archivos que son compatibles con el marco de seguridad de iOS: rsaCert.der (clave pública) y rsaPrivate.p12 (clave privada). El siguiente código se lee en la clave pública, asumiendo que el archivo se agrega a su paquete:

- (SecKeyRef)getPublicKeyRef { NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"rsaCert" ofType:@"der"]; NSData *certData = [NSData dataWithContentsOfFile:resourcePath]; SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef)certData); SecKeyRef key = NULL; SecTrustRef trust = NULL; SecPolicyRef policy = NULL; if (cert != NULL) { policy = SecPolicyCreateBasicX509(); if (policy) { if (SecTrustCreateWithCertificates((CFTypeRef)cert, policy, &trust) == noErr) { SecTrustResultType result; OSStatus res = SecTrustEvaluate(trust, &result); //Check the result of the trust evaluation rather than the result of the API invocation. if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) { key = SecTrustCopyPublicKey(trust); } } } } if (policy) CFRelease(policy); if (trust) CFRelease(trust); if (cert) CFRelease(cert); return key; }

Para leer la clave privada usa el siguiente código:

SecKeyRef getPrivateKeyRef() { NSString *resourcePath = [[NSBundle mainBundle] pathForResource:@"rsaPrivate" ofType:@"p12"]; NSData *p12Data = [NSData dataWithContentsOfFile:resourcePath]; NSMutableDictionary * options = [[NSMutableDictionary alloc] init]; SecKeyRef privateKeyRef = NULL; //change to the actual password you used here [options setObject:@"password_for_the_key" forKey:(id)kSecImportExportPassphrase]; CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL); OSStatus securityError = SecPKCS12Import((CFDataRef) p12Data, (CFDictionaryRef)options, &items); if (securityError == noErr && CFArrayGetCount(items) > 0) { CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0); SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity); securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef); if (securityError != noErr) { privateKeyRef = NULL; } } [options release]; CFRelease(items); return privateKeyRef; }