Can I get the modulus or exponent from a SecKeyRef object in Swift?

后端 未结 9 2039
灰色年华
灰色年华 2020-12-05 13:34

In Swift, I created a SecKeyRef object by calling SecTrustCopyPublicKey on some raw X509 certificate data. This is what this SecKeyRef object looks like.

<
9条回答
  •  Happy的楠姐
    2020-12-05 14:14

    It is indeed possible to extract modulus and exponent using neither keychains nor private API.

    There is the (public but undocumented) function SecKeyCopyAttributes which extracts a CFDictionary from a SecKey. A useful source for attribute keys is SecItemConstants.c

    Inspecting the content of this dictionary, we find an entry "v_Data" : . Its content is DER-encoded ASN for

    SEQUENCE {
        modulus           INTEGER, 
        publicExponent    INTEGER
    }
    

    Be aware that integers are padded with a zero byte if they are positive and have a leading 1-bit (so as not to confuse them with a two-complement negative number), so you may find one byte more than you expect. If that happens, just cut it away.

    You can implement a parser for this format or, knowing your key size, hard-code the extraction. For 2048 bit keys (and 3-byte exponent), the format turns out to be:

    30|82010(a|0)        # Sequence of length 0x010(a|0)
        02|82010(1|0)    # Integer  of length 0x010(1|0)
            (00)?
        02|03            # Integer  of length 0x03
            
    

    For a total of 10 + 1? + 256 + 3 = 269 or 270 bytes.

    import Foundation
    extension String: Error {}
    
    func parsePublicSecKey(publicKey: SecKey) -> (mod: Data, exp: Data) {
        let pubAttributes = SecKeyCopyAttributes(publicKey) as! [String: Any]
    
        // Check that this is really an RSA key
        guard    Int(pubAttributes[kSecAttrKeyType as String] as! String)
              == Int(kSecAttrKeyTypeRSA as String) else {
            throw "Tried to parse non-RSA key as RSA key"
        }
    
        // Check that this is really a public key
        guard    Int(pubAttributes[kSecAttrKeyClass as String] as! String) 
              == Int(kSecAttrKeyClassPublic as String) 
        else {
            throw "Tried to parse non-public key as public key"
        }
    
        let keySize = pubAttributes[kSecAttrKeySizeInBits as String] as! Int
    
        // Extract values
        let pubData  = pubAttributes[kSecValueData as String] as! Data
        var modulus  = pubData.subdata(in: 8..<(pubData.count - 5))
        let exponent = pubData.subdata(in: (pubData.count - 3).. keySize / 8 { // --> 257 bytes
            modulus.removeFirst(1)
        }
    
        return (mod: modulus, exp: exponent)
    }
    

    (I ended up writing a full ASN parser, so this code is not tested, beware!)


    Note that you can extract details of private keys in very much the same way. Using DER terminology, this is the format of v_Data:

    PrivateKey ::= SEQUENCE {
        version           INTEGER,
        modulus           INTEGER,  -- n
        publicExponent    INTEGER,  -- e
        privateExponent   INTEGER,  -- d
        prime1            INTEGER,  -- p
        prime2            INTEGER,  -- q
        exponent1         INTEGER,  -- d mod (p-1) (dmp1)
        exponent2         INTEGER,  -- d mod (q-1) (dmq1)
        coefficient       INTEGER,  -- (inverse of q) mod p (coeff)
        otherPrimeInfos   OtherPrimeInfos OPTIONAL
     }
    

    Parsing this by hand is probably ill-advised since any of the integers may have been padded.


    Nota bene: The format of the public key is different if the key has been generated on macOS; the structure given above is wrapped like so:

    SEQUENCE {
        id              OBJECTID,
        PublicKey       BITSTRING
    }
    

    The bit-string is DER-encoded ASN of the form above.

提交回复
热议问题