Results 1 to 39 of 39

Thread: Certificate Verify

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Certificate Verify

    My knowledge of ECDSA Certificate handling was minimal, but in order to produce a TLS 1.3 web server, RFC 8446 requires support for digitial signatures. So after a laborious learning experience, I decided to post what I learned.

    The private key is recovered from the "privkey.pem" Base64 encoded file, decoded to ASN.1 format, and finally to a Private EC Key.
    Code:
    Base64 Encoded File:
    -----BEGIN EC PRIVATE KEY-----
    MHcCAQEEIMrOBboK+r8CzXUj6EJG67fUX9UGz2K+sMFsGiOlZQProAoGCCqGSM49
    AwEHoUQDQgAEU0GMUttccTAXPsGYQyrF0gZt0KuRWpmnq8Zt0IvG6ZwrMoPg9slF
    sXYQOQ8hOKkQwLYzKbQgifvKw6aB/xnLtQ==
    -----END EC PRIVATE KEY-----
    
    ASN.1 Private Key:
    30 77 02 01 01 04 20 CA CE 05 BA 0A FA BF 02 CD 
    75 23 E8 42 46 EB B7 D4 5F D5 06 CF 62 BE B0 C1 
    6C 1A 23 A5 65 03 EB A0 0A 06 08 2A 86 48 CE 3D 
    03 01 07 A1 44 03 42 00 04 53 41 8C 52 DB 5C 71 
    30 17 3E C1 98 43 2A C5 D2 06 6D D0 AB 91 5A 99 
    A7 AB C6 6D D0 8B C6 E9 9C 2B 32 83 E0 F6 C9 45 
    B1 76 10 39 0F 21 38 A9 10 C0 B6 33 29 B4 20 89 
    FB CA C3 A6 81 FF 19 CB B5 
    
    Private Key:
    04 53 41 8C 52 DB 5C 71 30 17 3E C1 98 43 2A C5 
    D2 06 6D D0 AB 91 5A 99 A7 AB C6 6D D0 8B C6 E9 
    9C 2B 32 83 E0 F6 C9 45 B1 76 10 39 0F 21 38 A9 
    10 C0 B6 33 29 B4 20 89 FB CA C3 A6 81 FF 19 CB 
    B5 CA CE 05 BA 0A FA BF 02 CD 75 23 E8 42 46 EB 
    B7 D4 5F D5 06 CF 62 BE B0 C1 6C 1A 23 A5 65 03 
    EB
    Then the Magic string is added to form the Private Key Blob, which is used to sign the hash of the Hash Message, which is then ASN.1 encoded to form the Certificate Verify record. Note that the Compression byte (&H04) is overwritten and not required.
    Code:
    Private Key Blob:
    45 43 53 32 20 00 00 00 53 41 8C 52 DB 5C 71 30 
    17 3E C1 98 43 2A C5 D2 06 6D D0 AB 91 5A 99 A7 
    AB C6 6D D0 8B C6 E9 9C 2B 32 83 E0 F6 C9 45 B1 
    76 10 39 0F 21 38 A9 10 C0 B6 33 29 B4 20 89 FB 
    CA C3 A6 81 FF 19 CB B5 CA CE 05 BA 0A FA BF 02 
    CD 75 23 E8 42 46 EB B7 D4 5F D5 06 CF 62 BE B0 
    C1 6C 1A 23 A5 65 03 EB                         
    
    Session Hash:
    C8 A1 31 6D 80 8B BE C9 60 3B 1B CF 57 2B 99 02 
    8C B0 0A 64 7A BE C5 93 AA 88 6C C8 60 70 78 54 
    
    Hash Message:
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    54 4C 53 20 31 2E 33 2C 20 73 65 72 76 65 72 20 
    43 65 72 74 69 66 69 63 61 74 65 56 65 72 69 66 
    79 00 C8 A1 31 6D 80 8B BE C9 60 3B 1B CF 57 2B 
    99 02 8C B0 0A 64 7A BE C5 93 AA 88 6C C8 60 70 
    78 54                                           
    
    Hash Of Message:
    39 23 ED 42 85 75 AE 72 12 70 CD E2 BD 9A 1E 25 
    8F D4 8C C4 5E 19 05 48 17 8F 76 07 38 99 93 C7 
    
    Signed Hash:
    C8 15 79 32 1A EF 99 35 D6 9C 5A EE 70 B6 53 75 
    01 42 CE DC C4 D2 2E C1 51 24 2C 9D 97 7D 3A 92 
    FE 8E 5D C1 EA 7F 5A 3B 56 B2 22 31 9D 32 1C 6A 
    A7 D8 A0 05 21 8A 2A 63 EC 73 E4 6F 55 27 3D 36 
    
    Cert Verify:
    0F 00 00 4B 04 03 00 47 30 45 02 21 00 92 3A 7D 
    97 9D 2C 24 51 C1 2E D2 C4 DC CE 42 01 75 53 B6 
    70 EE 5A 9C D6 35 99 EF 1A 32 79 15 C8 02 20 36 
    3D 27 55 6F E4 73 EC 63 2A 8A 21 05 A0 D8 A7 6A 
    1C 32 9D 31 22 B2 56 3B 5A 7F EA C1 5D 8E FE
    That record is then encrypted and sent to the client directly after the Certificate itself. At the client end, the Cert Verify is decrypted, then ASN.1 decoded to get the signed hash. The Public Key is normally extracted from the Certificate, but in this case I extracted it from the Private Key. If anyone knows of an efficient way to extract the key from the Certificate, I would be very interested. Anyway, the Public Key Blob and the Signature are fed into the BCryptVerifySignature function and verified.
    Code:
    Public Key Blob:
    45 43 53 31 20 00 00 00 53 41 8C 52 DB 5C 71 30 
    17 3E C1 98 43 2A C5 D2 06 6D D0 AB 91 5A 99 A7 
    AB C6 6D D0 8B C6 E9 9C 2B 32 83 E0 F6 C9 45 B1 
    76 10 39 0F 21 38 A9 10 C0 B6 33 29 B4 20 89 FB 
    CA C3 A6 81 FF 19 CB B5                         
    
    Hash Message:
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    54 4C 53 20 31 2E 33 2C 20 73 65 72 76 65 72 20 
    43 65 72 74 69 66 69 63 61 74 65 56 65 72 69 66 
    79 00 C8 A1 31 6D 80 8B BE C9 60 3B 1B CF 57 2B 
    99 02 8C B0 0A 64 7A BE C5 93 AA 88 6C C8 60 70 
    78 54                                           
    
    Hash Of Message:
    39 23 ED 42 85 75 AE 72 12 70 CD E2 BD 9A 1E 25 
    8F D4 8C C4 5E 19 05 48 17 8F 76 07 38 99 93 C7 
    
    Hash Signature:
    C8 15 79 32 1A EF 99 35 D6 9C 5A EE 70 B6 53 75 
    01 42 CE DC C4 D2 2E C1 51 24 2C 9D 97 7D 3A 92 
    FE 8E 5D C1 EA 7F 5A 3B 56 B2 22 31 9D 32 1C 6A 
    A7 D8 A0 05 21 8A 2A 63 EC 73 E4 6F 55 27 3D 36 
    Certificate Verified!
    There are a few extra buttons that directly demonstrate some functions.
    1. Verify Test - This routine demonstrates what happens when a Private key does not match the Public key.
    2. Convert String - This was added because I used a different method to convert a Unicode ASCII string to a byte string and back again. I am hoping that this new routine handles ASCII values greater than &H7F, and is more efficient than my old routine.
    3. Cert Verify - This routine demonstrates the conversion of an ASN.1 encoded key to a Base64 encoded file format.

    J.A. Coutts
    Updated: 02/01/2021
    Updated: 02/07/2021
    Attached Files Attached Files
    Last edited by couttsj; Feb 7th, 2021 at 10:54 PM.

  2. #2
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    There is a flaw in the implementation -- the parameters to BCryptSignHash are incorrect. Currently in pbInput you are passing the original message instead of the *hash* of the message so this is effectively "signing" the first 32 bytes of the message (all spaces) instead of the whole message.

    That is why for everyone's surprise bTmp = StrToByte(Space$(32)) always gets verified and this is against the whole point of CertificateVerify message. It's point being to verify the server owns the certificate i.e. it has the private key (which is not part of the certificite transmitted). The server is like "This is my certificate, trust me that I own it. Here is something signed with it's private key so you can use the public key inside the certificate to verify the signature."

    The signed message is Space$(64) & "TLS 1.3, server CertificateVerify" & Chr$(0) & HashOfAllCurrentlyTransmittedMessages in which the appended 32-byte hash is based among other messages and on the initially transmitted client random i.e. something that the server does *not* control. This is important implementation detail as this client random plays the role of the "challenge" in a challenge-response authentication scheme.

    Also note that for this to be usable in TLS you have to pass the BCRYPT_PAD_PSS flag like in this function but certainly always make sure to sign the *hash* of the message and to verify again the *hash* of the message, not the message itself.

    Edit: Ooops, you don't need PSS as this is ECDSA -- no extra flags needed then.

    cheers,
    </wqw>

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    According to RFC 8446:
    -----------------------------------------------
    The content that is covered
    under the signature is the hash output as described in Section 4.4.1,
    namely:
    Transcript-Hash(Handshake Context, Certificate)
    The digital signature is then computed over the concatenation of:
    - A string that consists of octet 32 (0x20) repeated 64 times
    - The context string
    - A single 0 byte which serves as the separator
    - The content to be signed
    ------------------------------------------------
    If you look closely, you will see that the hash used to create this "extended hash" is the Session Hash (AKA Transcript-Hash), which should be the same at both ends. That is why I am having difficulty understanding why 32 spaces works. You can see that the code to provide the full extended hash is still there, but commented out.

    J.A. Coutts

  4. #4
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    Quote Originally Posted by couttsj View Post
    You can see that the code to provide the full extended hash is still there, but commented out.
    Yes, I saw it. The API function is called BCryptSignHash because it can sign a *hash* of the message. Message here means the Space$(64) & "TLS 1.3, server CertificateVerify" & Chr$(0) & TranscriptHash string i.e. you have to hash the string incl. TranscriptHash once more and then sign the resulting hash.

    P-256 can sign a buffer up to 32 bytes so you cannot use SHA-512 for hashing the message for instance but can use SHA-1 or MD-5 besides the cannonical SHA-256 (with some padding). P-384 can sign up to 48 bytes, so SHA-512 is too big again.

    In TLS 1.2 there was an bug in ECDSA signatures specification and it was possible to request SHA-512 to be used with P-256 ECDSA effectively truncating 64-byte SHA-512 output and signing only its first 32-bytes or requesting SHA-256 to be used with P-384 ECDSA which required the 32-bytes SHA-256 output to be *left* padded with zeroes up to 48-bytes P-384 ECDSA input.

    The HashAlgorithm (SHA-512) and the SignatureAlgorithm (ECDSA) where negotiated at handshake but the curve size used for ECDSA (P-256, P-384, etc) was picked from the server certificate. These kind of mismatches cannot happen in TLS 1.3 because the new signature scheme specification simultaneously sets both the hash algorithm and the curve size i.e SHA-256 w/ P-256, SHA-384 w/ P-384, SHA-512 w/ P-521 (this is not P-512)

    cheers,
    </wqw>

  5. #5
    Member Dragokas's Avatar
    Join Date
    Aug 2015
    Location
    Ukraine
    Posts
    740

    Re: Certificate Verify

    Quote Originally Posted by couttsj
    The Public Key is normally extracted from the Certificate, but in this case I extracted it from the Private Key. If anyone knows of an efficient way to extract the key from the Certificate, I would be very interested.
    Which one encoding /format is it?
    See: https://itsme.home.xs4all.nl/project...tificates.html
    For ASN.1 I did manual parsing + CertCreateCertificateContext. See ParseCertBlob() + modVerifyDigiSign.bas
    Hope that can help you.
    Good luck and nice job!
    Malware analyst, VirusNet developer, HiJackThis+ author || my CodeBank works

  6. #6

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by wqweto View Post
    Yes, I saw it. The API function is called BCryptSignHash because it can sign a *hash* of the message. Message here means the Space$(64) & "TLS 1.3, server CertificateVerify" & Chr$(0) & TranscriptHash string i.e. you have to hash the string incl. TranscriptHash once more and then sign the resulting hash.

    cheers,
    </wqw>
    Upon closer examination, RFC 8446 does hint that the "Extended Hash" is hashed again:
    --------------------------------
    For example, if the transcript hash was 32 bytes of 01 (this length
    would make sense for SHA-256), the content covered by the digital
    signature for a server CertificateVerify would be:
    --------------------------------
    So I have modified the post to reflect this extra hash, and I have also added the ASN.1 decoding of the server Certificate Verify.

    I am really having difficulty understanding why all this processing is required to verify that the server posseses the Private key. It seems to me that all that is necessary is to determine that the Public key is on the same curve as the Private key that was used to create the signature in the first place. And even if it was necessary to send a signature, why not just use the Session Hash itself instead of all this extra processing?

    J.A. Coutts

  7. #7
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    Quote Originally Posted by couttsj View Post
    why not just use the Session Hash itself instead of all this extra processing?
    To prevent replay attacks where the attacker is supposed to be able to inspect and capture traffic. The idea is that each session has unique TranscriptHash (based on client random and server random) so that signatures cannot be replayed from one session in a different one.

    Why the design is complicated with TranscriptHash being hashed again has simple explanation -- TranscriptHash might be negotiated to be SHA-512 based but at the same time the ECDSA certificate the server presents might be P-256 based and would need a SignatureScheme with P-256 curve and SHA-256 hash over the Space$(64) & "TLS 1.3, server CertificateVerify" & Chr$(0) & TranscriptHash message, so it's not always possible to directly ECDSA sign the TranscriptHash because the hash sizes might mismatch.

    cheers,
    </wqw>

  8. #8

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by wqweto View Post
    To prevent replay attacks where the attacker is supposed to be able to inspect and capture traffic. The idea is that each session has unique TranscriptHash (based on client random and server random) so that signatures cannot be replayed from one session in a different one.

    Why the design is complicated with TranscriptHash being hashed again has simple explanation -- TranscriptHash might be negotiated to be SHA-512 based but at the same time the ECDSA certificate the server presents might be P-256 based and would need a SignatureScheme with P-256 curve and SHA-256 hash over the Space$(64) & "TLS 1.3, server CertificateVerify" & Chr$(0) & TranscriptHash message, so it's not always possible to directly ECDSA sign the TranscriptHash because the hash sizes might mismatch.

    cheers,
    </wqw>
    There may have been some merit to all that processing (hash the hash, sign the hash, ASN.1 Encode, Encrypt) prior to TLS 1.3, but with the use of PFC (Perfect Forward Secrecy) and encryption of everything after the Hellos, it makes no sense. Perhaps it will get cleaned up on the next TLS version.

    J.A. Coutts

  9. #9
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    Quote Originally Posted by couttsj View Post
    Perhaps it will get cleaned up on the next TLS version.
    There is no obvious replacement for ASN.1 -- this is a binary standard for packing nested structures like ProtoBuf. Can be replaced with XML or JSON for instance if they drop the binary requirement but I highly doubt it considering now widely adopted X509 certificates are for instance.

    Some of the crypto primitives might need to be replaced with quantum-computing resistant ones but the TLS protocol is well equiped for extension and can accept new ciphersuites, new hashes and new signature types.

    What is immediately coming is QUIC -- this will replace the TCP underneath too so obviously a profound change. There are UDP multiplexing proxies by chineese developers that they use to circumvent The Great Firewall with forward error correction built-in which show remarkable performance and resilience so QUIC developers are quick to standardize the HTTP/3 implementation which is to become ubiquitous shortly after.

    Btw, for TLS implementation you never have to encode/decode ASN.1 structures by hand because every single definition needed is supported by CryptEncode/DecodeObject[Ex] API functions natively, incl. X509_ECC_SIGNATURE for CERT_ECC_SIGNATURE structure for ECDSA signatures like this

    Code:
        hResult = BCryptImportKeyPair(hAlg, 0, StrPtr("ECCPRIVATEBLOB"), hKey, uEccKey, sizeof_BCRYPT_ECCKEY_BLOB + UBound(baKeyBlob) + 1, 0)
        If pvCryptoSetApiResult(hResult, "BCryptImportKeyPair") Then
            GoTo QH
        End If
        pvArrayAllocate baTemp, 1024, FUNC_NAME & ".baTemp"
        hResult = BCryptSignHash(hKey, ByVal 0, baHash(0), UBound(baHash) + 1, baTemp(0), UBound(baTemp) + 1, lSize, 0)
        If pvCryptoSetApiResult(hResult, "BCryptSignHash") Then
            GoTo QH
        End If
        pvArrayReallocate baTemp, lSize, FUNC_NAME & ".baTemp"
        '--- ASN.1 encode signature
        pvArrayReverse baTemp
        uEccSig.rValue.pbData = VarPtr(baTemp(uEccKey.cbKey))
        uEccSig.rValue.cbData = uEccKey.cbKey
        uEccSig.sValue.pbData = VarPtr(baTemp(0))
        uEccSig.sValue.cbData = uEccKey.cbKey
        lSize = 1024
        pvArrayAllocate baRetVal, lSize, FUNC_NAME & ".baRetVal"
        lResult = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ECC_SIGNATURE, uEccSig, 0, 0, baRetVal(0), lSize)
        If pvCryptoSetApiError(lResult, "CryptEncodeObjectEx") Then
            GoTo QH
        End If
        pvArrayReallocate baRetVal, lSize, FUNC_NAME & ".baRetVal"
    cheers,
    </wqw>

  10. #10

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by wqweto View Post
    There is no obvious replacement for ASN.1 -- this is a binary standard for packing nested structures like ProtoBuf. Can be replaced with XML or JSON for instance if they drop the binary requirement but I highly doubt it considering now widely adopted X509 certificates are for instance.

    cheers,
    </wqw>
    I have never been a fan of XML or JSON, as they are inefficient. But in this particular case ASN.1 adds no value and takes valuable processing time.

    Session Hash --> Extended Hash --> Hash --> Signature --> ASN.1 --> Encrypted --> Decrypted --> ASN.1 --> Signature --> Verify

    The same thing could be accomplished By:

    Session Hash --> Signature --> Encrypted --> Decrypted --> Signature --> Verify

    Even the Session Hash is questionable, as it is already used in the encryption process.

    QUIC is interesting, but like IPv6, it will have a difficult time gaining traction, and I doubt very much that we will see wide spread use any time soon. It's use of UDP does not sit well with me. I don't use UDP for anything but DNS because it permits spoofing of the IP address.

    J.A. Coutts

  11. #11
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    What does Session Hash --> Extended Hash step actually do?

    You cannot Session Hash --> Signature as the Session Hash can contain more or less bytes that the curve size of the ECDSA certificate. Technically you can (using padding and trimming) but you shouldn't.

    Signature --> ASN.1 is probably redundant indeed, specifying network order encoding for the x and y components of the ECC point of the signature would be enough.

    cheers,
    </wqw>

  12. #12

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by wqweto View Post
    What does Session Hash --> Extended Hash step actually do?

    You cannot Session Hash --> Signature as the Session Hash can contain more or less bytes that the curve size of the ECDSA certificate. Technically you can (using padding and trimming) but you shouldn't.

    Signature --> ASN.1 is probably redundant indeed, specifying network order encoding for the x and y components of the ECC point of the signature would be enough.

    cheers,
    </wqw>
    The Session Hash is prepended by Space$(64) & "TLS 1.3, server CertificateVerify" & Chr$(0)", to get what I call the Extended Hash. The point I was trying to make was that the value to be signed could be anything. It doesn't have to be the Session Hash. The Session Hash is already used in the encryption process. What we are verifying is the signature, not what was used to make the signature. It would make more sense if the Certificate was used to generate the Signature, but that could be time consuming for very long Certificate chains.

    J.A. Coutts

  13. #13
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    Yes, but the message to be signed cannot be static, i.e. it cannot just always use Space$(64) & "TLS 1.3, server CertificateVerify" because signature would be suspect to replay attack -- handshake encryption is not enough to authenticate any of the peers. In principle the handshake encryption is somewhat more vulnerable esp. in 0-RTT scenarios as the attacker has more time and more attempts at breaking the keys established during previous session but this is irrelevant to the signature in CertificateVerify.

    The purpose of using current session Handshake Hash is it to serve as "uniqifier" of the signed message as Handshake Hash is dependent on both server and client random, i.e. if the attacker is pretending to be the server then when the *client* random is established by the unsuspecting peer it is the new Handshake Hash that ensures new message signature cannot match previously captured CertificateVerify. Up until this point the attacker is able to accept the new client random, replay the captured server random and impl all the DH establishment of the ephemeral keys for handshake encryption as in the protocol RFC, replay the server certificate but then trip at CertificateVerify message because the attacker cannot sign the new message (with the new Handshake Hash suffix) as it does not possess the certificate's private key and cannot replay the captured CertificateVerify as it does not verify the new message (with the new Handshake Hash suffix).

    cheers,
    </wqw>

  14. #14

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    I am looking for a quick way to extract the public key from the Certificate, and I don't particularly want to decode the entire Certificate manually. Research has shown that the function "CryptDecodeObjectEx" with Encoding Type "X509_ASN_ENCODING(&H01)" and Structure Type "X509_PUBLIC_KEY_INFO(&H08)" can be used, but I can't get it to work.
    Code:
    Private Declare Function CryptDecodeObjectEx Lib "crypt32.dll" (ByVal dwCertEncodingType As Long, ByVal lpszStructType As Any, pbEncoded As Any, ByVal cbEncoded As Long, ByVal dwFlags As Long, ByVal pDecodePara As Long, pvStructInfo As Any, pcbStructInfo As Long) As Long
    
        lLen = GetbSize(bCert)
        lRet = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, bCert(0), lLen, CRYPT_DECODE_NOCOPY_FLAG, 0&, ByVal 0, lSize)
        If lRet = 0 Then GoTo ReleaseHandles
        ReDim bResult(lSize - 1)
        lRet = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, bCert(0), lLen, CRYPT_DECODE_NOCOPY_FLAG, 0&, bResult(0), lSize)
        If lRet = 0 Then GoTo ReleaseHandles
    I used similar code to decode the Signature Verify, but this one evades me.

    J.A. Coutts

  15. #15
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    Take a look at pvAsn1DecodeCertificate in my thunks backend. It’s using CertCreateCertificateContext API function to extract subject public key info from the X509 certificate.

  16. #16

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Thanks to wqweto, this demo now has the ability to recover the Public ECC Key directly from the Certificate. It uses a somewhat abbreviated version of his "pvAsn1DecodeCertificate" routine.

    Within the first 3 routines, the Certificate is recovered. The 4th routine ("Client Verify") extracts the Public ECC Key along with its Object Identifier and uses that to verify the Certificate Verify record.

    J.A. Coutts

  17. #17
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    Btw, when using CRYPT_DECODE_ALLOC_FLAG with CryptDecodeObject make sure to free the returned pointer i.e. after this

    lRet = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ECC_PRIVATE_KEY, bData(0), GetbSize(bData), CRYPT_DECODE_ALLOC_FLAG Or CRYPT_DECODE_NOCOPY_FLAG, 0, lKeyPtr, lKeySize)

    use lKeyPtr but make sure to free the system allocated buffer with

    Call LocalFree(lKeyPtr)

    just to be on the safe side with memory leaks.

    cheers,
    </wqw>

  18. #18

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by wqweto View Post
    Btw, when using CRYPT_DECODE_ALLOC_FLAG with CryptDecodeObject make sure to free the returned pointer i.e. after this

    lRet = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ECC_PRIVATE_KEY, bData(0), GetbSize(bData), CRYPT_DECODE_ALLOC_FLAG Or CRYPT_DECODE_NOCOPY_FLAG, 0, lKeyPtr, lKeySize)

    use lKeyPtr but make sure to free the system allocated buffer with

    Call LocalFree(lKeyPtr)

    just to be on the safe side with memory leaks.

    cheers,
    </wqw>
    Good point. I noticed that I do experience memory leaks when I am developing because of the abrupt endings in code execution. The system takes longer and longer to wake up, and I have to reboot occasionally to clean it up.

    J.A. Coutts

  19. #19

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by couttsj View Post
    Thanks to wqweto, this demo now has the ability to recover the Public ECC Key directly from the Certificate. It uses a somewhat abbreviated version of his "pvAsn1DecodeCertificate" routine.

    Within the first 3 routines, the Certificate is recovered. The 4th routine ("Client Verify") extracts the Public ECC Key along with its Object Identifier and uses that to verify the Certificate Verify record.

    J.A. Coutts
    A word of warning about the Certificate data. In the demo, I extracted the Certificate directly from the .pem file. The result is the ASN.1 format. When recovering the Certificate from the Handshake data, the Certificate has an 11 byte header, and 2 zero bytes trailing. The "CertCreateCertificateContext" call can handle the trailing bytes, but not the 11 byte header. Those 11 bytes must be stripped from the Certificate before passing it to the call. I do not know if there is always an 11 byte header.

    J.A. Coutts

  20. #20
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    You can check out the exact structure of Certificate message in TLS 1.3 protocol in RFC 8446 in 4.4.2 which is something like this

    Code:
       Structure of this message:
    
          enum {
              X509(0),
              RawPublicKey(2),
              (255)
          } CertificateType;
    
          struct {
              select (certificate_type) {
                  case RawPublicKey:
                    /* From RFC 7250 ASN.1_subjectPublicKeyInfo */
                    opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;
    
                  case X509:
                    opaque cert_data<1..2^24-1>;
              };
              Extension extensions<0..2^16-1>;
          } CertificateEntry;
    
          struct {
              opaque certificate_request_context<0..2^8-1>;
              CertificateEntry certificate_list<0..2^24-1>;
          } Certificate;
    So you have first a certificate request context (opaque blob), then a list of entries each containing an opaque X509 certificate blob (in ASN.1) with a list of optional extensions.

    The 11 byte prefix (1 byte) is actualy 8 bytes for: 1 byte message type, 3 bytes message size, 1 byte empty cert request context, 3 bytes cert list size and then for each certificate a 3 bytes blob size, the opaque blob, and finally 2 bytes for empty extensions list. So its 8 byte prefix and then for each certificate in the a message a 3 bytes prefix and 2 bytes suffix.

    I noticed that you mixed TLS protocol Certificate message decoding and X509 certificate's blob ASN.1 decoding which are completely separate concerns and best would be to keep in separate functions.

    Here is how I parse the TLS message only and store all X509 certificates passed as blobs in RemoteCertificates collection for further processing (i.e. certificate chain validation if not disabled)

    Code:
                    Case TLS_HANDSHAKE_CERTIFICATE
                        If .ProtocolVersion = TLS_PROTOCOL_VERSION_TLS13 Then
                            pvBufferReadBlockStart uInput, BlockSize:=lCertSize
                                uInput.Pos = uInput.Pos + lCertSize '--- skip RemoteCertReqContext
                            pvBufferReadBlockEnd uInput
                        End If
                        Set .RemoteCertificates = New Collection
                        pvBufferReadBlockStart uInput, Size:=3, BlockSize:=lCertSize
                            lCertEnd = uInput.Pos + lCertSize
                            Do While uInput.Pos < lCertEnd
                                pvBufferReadBlockStart uInput, Size:=3, BlockSize:=lCertSize
                                    pvBufferReadArray uInput, baCert, lCertSize
                                    .RemoteCertificates.Add baCert
                                pvBufferReadBlockEnd uInput
                                If .ProtocolVersion = TLS_PROTOCOL_VERSION_TLS13 Then
                                    pvBufferReadBlockStart uInput, Size:=2, BlockSize:=lCertSize
                                        '--- certificate extensions -> skip
                                        uInput.Pos = uInput.Pos + lCertSize
                                    pvBufferReadBlockEnd uInput
                                End If
                            Loop
                        pvBufferReadBlockEnd uInput
    Notice that both cert request context and certificate extensions are TLS 1.3 only features that were not part of TLS 1.2 specification and in the snippet above both are just skipped.

    cheers,
    </wqw>

  21. #21

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by wqweto View Post
    You can check out the exact structure of Certificate message in TLS 1.3 protocol in RFC 8446 in 4.4.2 which is something like this

    cheers,
    </wqw>
    Thank you for the explanation. Part of the reason I find the RFCs so hard to follow is the nomenclature used and the lack of specific examples. I know what opaque means, but I have no idea what it means in reference to a structure, nor could I find a reasonable explanation.

    So what you are saying is that there could be more than 11 bytes of header when a Certificate chain is offered. If so, which one would have the correct Public Key? Would it be the last one in the chain?

    I have succeeded in getting my own client and server to exchange and verify the Certificate, but I still have had no success with getting FireFox to connect with my server software. Instead of a Client Finish, it returns a &H33 Alert code. I am running out of things to try. Also, the Certificate ends with an ECDSA/SHA256 signature 1.2.840.10045.4.3.2. What is that used for and is it the result of self signing the Certificate?

    J.A. Coutts

  22. #22
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    The first certificate is the server certificate. Subsequent certificates include intermediaries which are used in trust chain validation up to a trust anchor both peers have internally listed as trusted.

    So the public key for cert verify is from the first certificate the so called leaf certificate in the chain of trust.

  23. #23

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    FireFox cannot Verify the Certificate used by my Server, and my Client cannot Verify the same Certificate used by OpenSSL. Since my Client can Verify the Certificate used by my Server, what ever I am doing wrong, I am doing wrong with both the Client and the Server software. Hopefully someone can give me an idea of what the problem might be, because I have run out of ideas.

    According to RFC 8446:
    The receiver of a CertificateVerify message MUST verify the signature
    field. The verification process takes as input:
    - The content covered by the digital signature
    Code:
    Session Hash after Certificate rec'd:
    6B DC 5F F7 FE 30 42 4B B6 48 9B 3C 37 23 27 1F 
    16 43 DD CF 04 D0 80 5B 91 B3 0A 4D BD EC B9 2D 
    Signature Content:
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
    54 4C 53 20 31 2E 33 2C 20 73 65 72 76 65 72 20 
    43 65 72 74 69 66 69 63 61 74 65 56 65 72 69 66 
    79 00 6B DC 5F F7 FE 30 42 4B B6 48 9B 3C 37 23 
    27 1F 16 43 DD CF 04 D0 80 5B 91 B3 0A 4D BD EC 
    B9 2D 
    Content Hash (SHA256):
    EF 05 41 A2 2F 73 CF 47 C7 38 A7 03 3D CA BE BA 
    81 6E DA 85 47 8B D0 6D 1C 7B 72 B4 8C 49 54 08
    - The public key contained in the end-entity certificate found in
    the associated Certificate message
    Code:
    45 43 53 31 20 00 00 00 53 41 8C 52 DB 5C 71 30 
    17 3E C1 98 43 2A C5 D2 06 6D D0 AB 91 5A 99 A7 
    AB C6 6D D0 8B C6 E9 9C 2B 32 83 E0 F6 C9 45 B1 
    76 10 39 0F 21 38 A9 10 C0 B6 33 29 B4 20 89 FB 
    CA C3 A6 81 FF 19 CB B5
    - The digital signature received in the signature field of the
    CertificateVerify message
    Code:
    CertificateVerify:
    0F 00 00 4B 04 03 00 47 30 45 02 20 75 FE F5 16 
    36 C5 AE 25 DB 80 A6 76 22 61 6E 76 56 39 0E 52 
    E7 A6 29 88 75 7C 35 CF A5 1F 42 F5 02 21 00 CD 
    D7 D9 4E 3B 9A 99 A6 FC C7 B7 03 74 01 C6 E0 20 
    AF 27 BB 83 07 CA 34 ED 4B 1B CF C9 4D EE 53 
    Signature:
    F5 42 1F A5 CF 35 7C 75 88 29 A6 E7 52 0E 39 56 
    76 6E 61 22 76 A6 80 DB 25 AE C5 36 16 F5 FE 75 
    53 EE 4D C9 CF 1B 4B ED 34 CA 07 83 BB 27 AF 20 
    E0 C6 01 74 03 B7 C7 FC A6 99 9A 3B 4E D9 D7 CD
    J.A. Coutts

  24. #24
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    CryptEncodeObjectEx expects the signature in little endian encoding so you probably need to reverse a byte array or two for things to become compatible with external implementations.

  25. #25

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by wqweto View Post
    CryptEncodeObjectEx expects the signature in little endian encoding so you probably need to reverse a byte array or two for things to become compatible with external implementations.
    Once again you hit the nail on the head. I had assumed (wrongly) that the function handled the reversing of the fields. And it does indeed, but has to be reversed initially so the function can put it back in the right order (how do they dream this stuff up?).

    J.A. Coutts

  26. #26
    New Member
    Join Date
    Jul 2022
    Posts
    6

    Re: Certificate Verify

    i have a hex privete key like "081549973bafbba825b31bcc402a3c4ed8e3185c2f3a31c75e55f423e9629aa3" and i want to conver with ECDSA to hex public key but i undrestand wich function do it :
    MsgBox GetPubKey(HexToByte("081549973bafbba825b31bcc402a3c4ed8e3185c2f3a31c75e55f423e9629aa3"), AlgObjld as Byte ?)
    attention to hdwallet learn site (https://learnmeabitcoin.com/technical/hd-wallets) :

    master extended private key:
    private key: 081549973bafbba825b31bcc402a3c4ed8e3185c2f3a31c75e55f423e9629aa3

    converted to master extended public key:
    public key: 0343b337dec65a47b3362c9620a6e6ff39a1ddfa908abab1666c8a30a3f8a7cccc
    what is this convert alguritm ?

  27. #27
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    No, you don't "conver with ECDSA to hex public key" from a private key.

    In elliptic curve cryptography a public key is a point on a curve (has X and Y coordinate) and a private key is a scalar number like 12345 integer but the private keys are very big numbers e.g. 32 byte integers, not 4 byte uint32_t or 8 byte uint64_t built-in data types.

    You generate a random private key (generate random 32 bytes) and check if this random scalar is suitable for a private key depending on the curve (might need to regenerate it in some edge cases) then you just use a *scalar multiplication* with the curve's base point G to calculate a point P which will becomes the corresponding public key for such newly generated random key.

    All you need to perform is *scalar multiplication* in the Galois Field of the secp256k1 curve which has nothing to do with ECDSA -- a signing algorithm and a totally different beast.

    You need a library which implements scalar multiplication for secp256k1 curve. Scalar multiplication will yield totally different result for curve secp256r1 (so called P-256) for instance or for any other elliptic curve. The ECC curves are different and that's the whole point -- similar but with different generator G points and different coefficients.

    cheers,
    </wqw>

  28. #28

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Thanks for responding wqweto, as I was struggling to determine what Ardata was asking. I have limited knowledge of bitcoin, and the only Public/Private keys that I know of are ECC keys, and that did not correspond with what he was presenting. When the Private key is created in bCrypt, it already contains the Public key. So I put together a brief program to demonstrate the principles.
    Code:
    Option Explicit
    
    Dim KeyAlg As String
    Dim KeySize As Long
    Dim bPubKeyBlob() As Byte
    Dim bPrivKeyBlob() As Byte
    
    Private Type BCryptBuffer
        cbBuffer As Long                'Length of buffer, in bytes
        BufferType As Long              'Buffer type
        pvBuffer As Long                'Pointer to buffer
    End Type
    
    Private Type BCryptBufferDesc
        ulVersion As Long               'Version number
        cBuffers As Long                'Number of buffers
        pBuffers As Long                'Pointer to array of buffers
        rgBuffer(3) As BCryptBuffer     'Allowance for 4 buffers (more can be used)
    End Type
    
    Private Declare Function BCryptOpenAlgorithmProvider Lib "bcrypt" (ByRef hAlgorithm As Long, ByVal pszAlgId As Long, ByVal pszImplementation As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptCloseAlgorithmProvider Lib "bcrypt" (ByVal hAlgorithm As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptGenerateKeyPair Lib "bcrypt" (ByVal hAlgorithm As Long, ByRef hKey As Long, ByVal dwLength As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptFinalizeKeyPair Lib "bcrypt" (ByVal hKey As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptExportKey Lib "bcrypt.dll" (ByVal hKey As Long, ByVal hExportKey As Long, ByVal pszBlobType As Long, ByVal pbOutput As Long, ByVal cbOutput As Long, ByRef cbResult As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptImportKeyPair Lib "bcrypt.dll" (ByVal hAlgorithm As Long, ByVal hImportKey As Long, ByVal pszBlobType As Long, ByRef hKey As Long, ByVal pbInput As Long, ByVal cbInput As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptSecretAgreement Lib "bcrypt.dll" (ByVal hPrivKey As Long, ByVal hPubKey As Long, ByRef phSecret As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptDeriveKey Lib "bcrypt.dll" (ByVal hSharedSecret As Long, ByVal pwszKDF As Long, ByVal pParameterList As Long, ByVal pbDerivedKey As Long, ByVal cbDerivedKey As Long, ByRef pcbResult As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptDestroyKey Lib "bcrypt" (ByVal hAesKey As Long) As Long
    Private Declare Function BCryptDestroySecret Lib "bcrypt.dll" (ByVal hSecret As Long) As Long
    
    Private Sub DebugPrintByte(sDescr As String, bArray() As Byte)
        Dim lPtr As Long
        Debug.Print sDescr & ":"
        If GetbSize(bArray) = 0 Then Exit Sub
        For lPtr = 0 To UBound(bArray)
            Debug.Print Right$("0" & Hex$(bArray(lPtr)), 2) & " ";
            If (lPtr + 1) Mod 16 = 0 Then Debug.Print
        Next lPtr
        Debug.Print
    End Sub
    
    Private Function GetbSize(bArray() As Byte) As Long
        On Error GoTo GetSizeErr
        GetbSize = UBound(bArray) + 1
        Exit Function
    GetSizeErr:
        GetbSize = 0
    End Function
    
    Public Function GetECCKey(ByVal pAlg As Long, ByVal KeyLen As Long, bPublicECCKey() As Byte, bPrivateECCKey() As Byte) As Byte()
        Dim lRet As Long
        Dim hProv As Long
        Dim hPrivKey As Long
        Dim cbPubBlob As Long
        Dim cbPriBlob As Long
        Dim cbPubBlobS As Long
        Dim bPubBlob() As Byte
        Dim bPriBlob() As Byte
        Dim hPubKey As Long
        Dim hAgreedSecret As Long
        Dim cbAgreedSecret As Long
        Dim bAgreedSecret() As Byte
        Dim AuxFlg As Boolean
        Dim lPntr As Long
        Dim bTmp() As Byte
        Dim ParameterList As BCryptBufferDesc
        lRet = BCryptOpenAlgorithmProvider(hProv, pAlg, StrPtr("Microsoft Primitive Provider"), 0)
        If lRet <> 0 Then
            GoTo ReleaseHandles
        End If
        cbPubBlob = GetbSize(bPublicECCKey)
        cbPriBlob = GetbSize(bPrivateECCKey)
        If cbPubBlob < 2 And cbPriBlob < 2 Then 'Return internal Public/Private Keys
            lRet = BCryptGenerateKeyPair(hProv, hPrivKey, KeyLen, 0)
            If lRet <> 0 Then
                GoTo ReleaseHandles
            End If
            lRet = BCryptFinalizeKeyPair(hPrivKey, 0)
            If lRet <> 0 Then
                GoTo ReleaseHandles
            End If
            'Export private key
            lRet = BCryptExportKey(hPrivKey, 0&, StrPtr("ECCPRIVATEBLOB"), 0&, 0&, cbPriBlob, 0)
            If lRet <> 0 Then
                GoTo ReleaseHandles
            End If
            ReDim bPriBlob(cbPriBlob - 1)
            lRet = BCryptExportKey(hPrivKey, 0&, StrPtr("ECCPRIVATEBLOB"), VarPtr(bPriBlob(0)), cbPriBlob, cbPriBlob, 0)
            If lRet <> 0 Then
                GoTo ReleaseHandles
            End If
            'Export public key
            lRet = BCryptExportKey(hPrivKey, 0&, StrPtr("ECCPUBLICBLOB"), 0&, 0&, cbPubBlob, 0)
            If lRet <> 0 Then
                GoTo ReleaseHandles
            End If
            ReDim bPubBlob(cbPubBlob - 1)
            lRet = BCryptExportKey(hPrivKey, 0&, StrPtr("ECCPUBLICBLOB"), VarPtr(bPubBlob(0)), cbPubBlob, cbPubBlob, 0)
            If lRet <> 0 Then
                GoTo ReleaseHandles
            End If
            bPublicECCKey = bPubBlob
            bPrivateECCKey = bPriBlob
            GoTo ReleaseHandles
        End If
        cbPubBlobS = GetbSize(bPrivateECCKey)
        lRet = BCryptImportKeyPair(hProv, 0&, StrPtr("ECCPRIVATEBLOB"), hPrivKey, VarPtr(bPrivateECCKey(0)), cbPubBlobS, 0)
        If lRet <> 0 Then
            GoTo ReleaseHandles
        End If
        'Import Server's public key
        cbPubBlobS = GetbSize(bPublicECCKey)
        lRet = BCryptImportKeyPair(hProv, 0&, StrPtr("ECCPUBLICBLOB"), hPubKey, VarPtr(bPublicECCKey(0)), cbPubBlobS, 0)
        If lRet <> 0 Then
            GoTo ReleaseHandles
        End If
        'Generate the agreed secret
        lRet = BCryptSecretAgreement(hPrivKey, hPubKey, hAgreedSecret, 0)
        If lRet <> 0 Then
            GoTo ReleaseHandles
        End If
        'Get Agreed Secret length
        lRet = BCryptDeriveKey(hAgreedSecret, StrPtr("TRUNCATE"), 0, 0&, 0, cbAgreedSecret, 0)
        If lRet = 0 Then
            ReDim bAgreedSecret(cbAgreedSecret - 1)
        End If
        'Get Agreed Secret
        lRet = BCryptDeriveKey(hAgreedSecret, StrPtr("TRUNCATE"), 0, VarPtr(bAgreedSecret(0)), cbAgreedSecret, cbAgreedSecret, 0)
        If lRet = 0 Then 'Success
            bTmp = bAgreedSecret 'Reverse result (req'd for Win 10?)
            cbAgreedSecret = cbAgreedSecret - 1
            For lPntr = 0 To cbAgreedSecret
                bAgreedSecret(cbAgreedSecret) = bTmp(lPntr)
                cbAgreedSecret = cbAgreedSecret - 1
            Next lPntr
        Else
            GoTo ReleaseHandles
        End If
    ReleaseHandles:
        GetECCKey = bAgreedSecret
        If (hPubKey) Then BCryptDestroyKey (hPubKey)
        If (hPrivKey) Then BCryptDestroyKey (hPrivKey)
        If (hProv) Then BCryptCloseAlgorithmProvider hProv, 0
        If (hAgreedSecret) Then BCryptDestroySecret hAgreedSecret
    End Function
    
    Private Sub Command1_Click()
        KeyAlg = "ECDH_P256"
        KeySize = 256
        GetECCKey StrPtr(KeyAlg), KeySize, bPubKeyBlob, bPrivKeyBlob
        DebugPrintByte "Public ECC Key", bPubKeyBlob
        DebugPrintByte "Private ECC Key", bPrivKeyBlob
    End Sub
    J.A. Coutts

  29. #29
    New Member
    Join Date
    Jul 2022
    Posts
    6

    Re: Certificate Verify

    Thank you wqweto and quttsj friends
    I want to use "keccak-256" hash algoritm but i dont find a function or class or module or dll for vb6 or vba ?
    Do you have any information about this hash ?

  30. #30
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    This BCryptPublicKeyFromPrivate function works for secp256k1 on Win11 and (probably) Win10

    Code:
    '--- mdEccPublicKey.bas
    Option Explicit
    DefObj A-Z
    
    Private Const BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC  As Long = &H56444345  ' ECDV
    Private Const BCRYPT_NO_KEY_VALIDATION            As Long = &H8
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    Private Declare Function BCryptOpenAlgorithmProvider Lib "bcrypt" (ByRef hAlgorithm As Long, ByVal pszAlgId As Long, ByVal pszImplementation As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptCloseAlgorithmProvider Lib "bcrypt" (ByVal hAlgorithm As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptSetProperty Lib "bcrypt" (ByVal hObject As Long, ByVal pszProperty As Long, ByVal pbInput As Long, ByVal cbInput As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptImportKeyPair Lib "bcrypt" (ByVal hAlgorithm As Long, ByVal hImportKey As Long, ByVal pszBlobType As Long, ByRef hKey As Long, pbInput As Any, ByVal cbInput As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptExportKey Lib "bcrypt" (ByVal hKey As Long, ByVal hExportKey As Long, ByVal pszBlobType As Long, pbOutput As Any, ByVal cbOutput As Long, ByRef cbResult As Long, ByVal dwFlags As Long) As Long
    Private Declare Function BCryptDestroyKey Lib "bcrypt" (ByVal hKey As Long) As Long
    Private Declare Function RtlGenRandom Lib "advapi32" Alias "SystemFunction036" (RandomBuffer As Any, ByVal RandomBufferLength As Long) As Long
    
    Private Type BCRYPT_ECCKEY_BLOB
        dwMagic             As Long
        cbKey               As Long
        Buffer(0 To 1000)   As Byte
    End Type
    Private Const sizeof_BCRYPT_ECCKEY_BLOB As Long = 8
    
    Public Function BCryptPublicKeyFromPrivate(sCurveName As String, baPrivKey() As Byte, baPubKey() As Byte) As Boolean
        Dim hAlg            As Long
        Dim hResult         As Long
        Dim hKey            As Long
        Dim lSize           As Long
        Dim uBlob           As BCRYPT_ECCKEY_BLOB
        
        hResult = BCryptOpenAlgorithmProvider(hAlg, StrPtr("ECDSA"), 0, 0)
        If hResult < 0 Then
            GoTo QH
        End If
        hResult = BCryptSetProperty(hAlg, StrPtr("ECCCurveName"), StrPtr(sCurveName), LenB(sCurveName) + 2, 0)
        If hResult < 0 Then
            GoTo QH
        End If
        uBlob.dwMagic = BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC
        uBlob.cbKey = UBound(baPrivKey) + 1
        '--- uBlob.Buffer is expected to contain first PubKey(0 to 63) then PrivKey(0 to 31) so copy baPrivKey at offset 64 and leave PubKey empty
        Call CopyMemory(uBlob.Buffer(2 * uBlob.cbKey), baPrivKey(0), UBound(baPrivKey) + 1)
        '--- flag BCRYPT_NO_KEY_VALIDATION is important to skip PubKey supplied (as we pass PrivKey only)
        hResult = BCryptImportKeyPair(hAlg, 0, StrPtr("ECCPRIVATEBLOB"), hKey, uBlob, sizeof_BCRYPT_ECCKEY_BLOB + 3 * uBlob.cbKey, BCRYPT_NO_KEY_VALIDATION)
        If hResult < 0 Then
            GoTo QH
        End If
        hResult = BCryptExportKey(hKey, 0&, StrPtr("ECCPUBLICBLOB"), uBlob, LenB(uBlob), lSize, 0)
        If hResult < 0 Then
            GoTo QH
        End If
        ReDim baPubKey(0 To 2 * uBlob.cbKey) As Byte
        baPubKey(0) = 4 '--- 4 - uncompressed public key
        Call CopyMemory(baPubKey(1), uBlob.Buffer(0), 2 * uBlob.cbKey)
        '--- success
        BCryptPublicKeyFromPrivate = True
    QH:
        If hKey <> 0 Then
            Call BCryptDestroyKey(hKey)
        End If
        If hAlg <> 0 Then
            Call BCryptCloseAlgorithmProvider(hAlg, 0)
        End If
    End Function
    
    Public Function BCryptCompressPublicKey(baPubKey() As Byte) As Byte()
        Dim lSize           As Long
        Dim baRetVal()      As Byte
        
        If baPubKey(0) = 4 Then
            lSize = UBound(baPubKey)
            ReDim baRetVal(0 To lSize \ 2) As Byte
            '--- 2 - Y is even, 3 - Y is odd
            baRetVal(0) = 2 + (baPubKey(lSize) And 1)
            Call CopyMemory(baRetVal(1), baPubKey(1), lSize \ 2)
            BCryptCompressPublicKey = baRetVal
        Else
            BCryptCompressPublicKey = baPubKey
        End If
    End Function
    
    Public Function GetRandomBytes(ByVal lSize As Long) As Byte()
        Dim baRetVal()      As Byte
        
        ReDim baRetVal(0 To lSize - 1) As Byte
        Call RtlGenRandom(baRetVal(0), lSize)
        GetRandomBytes = baRetVal
    End Function
    Here is how to use BCryptPublicKeyFromPrivate function to obtain Ardata's public key from the private key only

    Code:
    Option Explicit
    
    Private Sub Form_Load()
        Dim baPrivKey()     As Byte
        Dim baPubKey()      As Byte
        
        baPrivKey = FromHex("081549973bafbba825b31bcc402a3c4ed8e3185c2f3a31c75e55f423e9629aa3")
        If BCryptPublicKeyFromPrivate("secP256k1", baPrivKey, baPubKey) Then
            Debug.Print ToHex(baPubKey)
            '-> 0443B337DEC65A47B3362C9620A6E6FF39A1DDFA908ABAB1666C8A30A3F8A7CCCCFC24A7914950B6405729A9313CEC6AE5BB4A082F92D05AC49DF4B6DD8387BFEB
            Debug.Print ToHex(BCryptCompressPublicKey(baPubKey))
            '-> 0343B337DEC65A47B3362C9620A6E6FF39A1DDFA908ABAB1666C8A30A3F8A7CCCC
        End If
    End Sub
    
    Public Function ToHex(baText() As Byte, Optional Delimiter As String) As String
        Dim aText()         As String
        Dim lIdx            As Long
        
        If LenB(CStr(baText)) <> 0 Then
            ReDim aText(0 To UBound(baText)) As String
            For lIdx = 0 To UBound(baText)
                aText(lIdx) = Right$("0" & Hex$(baText(lIdx)), 2)
            Next
            ToHex = Join(aText, Delimiter)
        End If
    End Function
    
    Public Function FromHex(sText As String) As Byte()
        Dim baRetVal()      As Byte
        Dim lIdx            As Long
        
        On Error GoTo QH
        '--- check for hexdump delimiter
        If sText Like "*[!0-9A-Fa-f]*" Then
            ReDim baRetVal(0 To Len(sText) \ 3) As Byte
            For lIdx = 1 To Len(sText) Step 3
                baRetVal(lIdx \ 3) = "&H" & Mid$(sText, lIdx, 2)
            Next
        ElseIf LenB(sText) <> 0 Then
            ReDim baRetVal(0 To Len(sText) \ 2 - 1) As Byte
            For lIdx = 1 To Len(sText) Step 2
                baRetVal(lIdx \ 2) = "&H" & Mid$(sText, lIdx, 2)
            Next
        Else
            baRetVal = vbNullString
        End If
        FromHex = baRetVal
    QH:
    End Function
    Function BCryptPublicKeyFromPrivate can be used with other curves too like secp256r1 for instance.

    cheers,
    </wqw>

  31. #31
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    Quote Originally Posted by Ardata View Post
    Thank you wqweto and quttsj friends
    I want to use "keccak-256" hash algoritm but i dont find a function or class or module or dll for vb6 or vba ?
    Do you have any information about this hash ?
    This guy has some sha-3 implementation for VBA but the code is not very "idiomatic" at all.

    In fact it's so bad I want to submit a replacement implementation to CodeBank immediately but don't have one ready yet.

    cheers,
    </wqw>

  32. #32
    New Member
    Join Date
    Jul 2022
    Posts
    6

    Re: Certificate Verify

    Thank you to all the friends who helped. My last question to find Child Private Key:
    Private Key: 081549973bafbba825b31bcc402a3c4ed8e3185c2f3a31c75e55f423e9629aa3
    Chain Code: 1d7d2a4c940be028b945302ad79dd2ce2afe5ed55e1a2937a5af57f8401e73dd
    With what formula and mechanism or algorithm can I create Private child key?

  33. #33
    New Member
    Join Date
    Jul 2022
    Posts
    6

    Re: Certificate Verify

    Dear wqweto what is this error :
    Attachment 185312

  34. #34
    New Member
    Join Date
    Jul 2022
    Posts
    6

    Re: Certificate Verify

    According to these photos, the mechanism of converting Private Key to Child Private Keys is as follows:
    Attachment 185313
    master extended private key:
    private key: 081549973bafbba825b31bcc402a3c4ed8e3185c2f3a31c75e55f423e9629aa3
    chain code: 1d7d2a4c940be028b945302ad79dd2ce2afe5ed55e1a2937a5af57f8401e73dd

    child 0:
    private key: 0397654f8c58215da8575e495345912bd0d23545cf0017505ab6b6ae1a97eedb
    public key: 021fa96c781e4ba8a11eb6fb8a7bd3f996ad0d6461fb403b0f389605c6f35c2620

    child 1:
    private key: 38512d738064f282d7620205815fa1208258bf031dbf5b6e5887cc3dacf7ae1d

    public key: 03073e48507140bb0e57dd4008db386f554d13a8641f7e55426419af4ba71fa3eb

    child 2:
    private key: 590bfef4b951854f60e90eb24e7c690ac70165eb959b3296c3f51cb5a8a6401f

    public key: 021b323b81b998340368d8fe11496922295cbae6fcd4b8d495f2c4755cd2279d1d

    How do the functions related to these conversions work?
    THNKS

  35. #35

  36. #36

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    I ran into a problem that I am having a very difficult time trouble shooting. To streamline TLS 1.3 encryption, I moved the connection portion from the networking part of the program to its own subroutine. Everything worked like a charm until I tried to verify the certificate signature on the client side. The class modules are identical, and the Certificate and the Certificate Public Key are identical, but the "BCryptImportKeyPair" routine returns "0xC000000D, which is an "Invalid Parameter" error.
    Code:
    DebugPrintByte "Public ECC Key", bPublicECCKey
    Public ECC Key:
    45 43 53 31 20 00 00 00 53 41 8C 52 DB 5C 71 30 
    17 3E C1 98 43 2A C5 D2 06 6D D0 AB 91 5A 99 A7 
    AB C6 6D D0 8B C6 E9 9C 2B 32 83 E0 F6 C9 45 B1 
    76 10 39 0F 21 38 A9 10 C0 B6 33 29 B4 20 89 FB 
    CA C3 A6 81 FF 19 CB B5
    
    cbPubBlob = GetbSize(bPublicECCKey)
    Debug.Print hProv, StrPtr("ECCPUBLICBLOB"), hPubKey, VarPtr(bPublicECCKey), cbPubBlob
     3335232       100777824     0             101087304     72
    lRet = BCryptImportKeyPair(hProv, 0&, StrPtr("ECCPUBLICBLOB"), hPubKey, VarPtr(bPublicECCKey(0)), cbPubBlob, 0)
    Debug.Print Hex$(lRet), hPubKey
    C000000D       0
    The original programs work like a charm, but with the same basic code, the newer ones don't. Any ideas?

    J.A. Coutts

  37. #37
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,120

    Re: Certificate Verify

    Check API declare if not the same b/n both modules or hProv is incorrect (must be P-256 one judging by the size of the pubkey blob which is 64+8)

  38. #38

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: Certificate Verify

    Quote Originally Posted by wqweto View Post
    Btw, here is a pure VB6 implementation of SHA-3 and original Keccak hash.

    cheers,
    </wqw>
    Although the clsCrypt.cls & clsHKDF.cls routines are the same for both the Client and the Server, the networking part of the program had to be rewritten. This was necessary because the working program used SimpleServer, and the new one used SimpleSock. Because it was so difficult to troubleshoot (Invalid Parameter), I ended up manually comparing sections of code. What I discovered is that I had copied 2 values that are used to create Key Blobs from the server program to the client program.
    Code:
        ECS = HexToByte("4543532000000000")
        ECK = HexToByte("45434B2000000000")
    should have been:
        ECS = HexToByte("4543533120000000")
        ECK = HexToByte("45434B3120000000")
    How embarrassing.

    J.A. Coutts

  39. #39

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width