Click here to Skip to main content
Click here to Skip to main content

Parsing strong name signatures generated with sn.exe

, 4 May 2004
Rate this:
Please Sign up or sign in to vote.
This article shows how to parse Assembly "strong name keyfiles" generated with sn.exe

Introduction

This application shows how to parse Assembly "strong name keyfiles" generated with sn.exe.

Background

System.Security.Cryptography provides functions to work with RSA keypairs. These functions are wrappers on the Win32 Crypto API. However, the methods provided in the 1.1 Framework do not easily parse SNK keyfiles created by sn.exe. The reason is that none of the methods will accept a PRIVATEKEYBLOB or PUBLICKEYBLOB structure directly. Therefore, it is necessary to write some extra code to parse those structures. Furthermore, there are additional (some would say obscure) tweaks on CpParameters required to get an SNK keyfile to import correctly.

Using the code

You can make a new assembly keypair by running sn.exe which is available from the Framework SDK. To make a new keypair run "sn.exe -k keypair.snk". To export just the public key from your keypair, use "sn.exe -p keypair.snk pubkey.pub".

The app I wrote works pretty much as you would expect. You can create new keys, or open an SNK/PUB keypair files by pressing the buttons. If you are using a public key file, the "sign it" button will be disabled. You can also open an XML file with an encoded RSA keypair.

After your keypair is loaded, type some text on the left, hit "sign it", and then hit "verify it". Test that the signature verification really works by twiddling one character in the signature or in the original plaintext, then hitting "verify it" again - it should fail.

The guts of the app lie in RSA1024Util.cs. I hardcoded the class to use 1024 bit encryption because currently sn.exe only generates 1024 bit keyfiles. (As an aside, I wonder why 2048 bits aren't supported for assembly keys? Cryptographers have lately been warning about the eventual fall of 1024 bit keys..)

Here is the function that converts the SNK byte buffer to RSAParameters. If the buffer is "public length" - 160 bytes - then it will only fill in the public key fields, "exponent" and "modulus". Otherwise it will read the rest of the private key fields.

The byte offsets were discovered by digging around the Win32 CryptoAPI documentation and helpful articles by Dr. Michel I. Gallant, of http://jensign.com . Note that each byte array must be reversed due to a big endian vs. little endian ordering issue.

public const int _magic_priv_idx = 0x08; 
public const int _magic_pub_idx = 0x14; 
public const int _magic_size = 4; 

public static RSAParameters FigureParams(
byte[] keypair)
{
    RSAParameters ret = new RSAParameters(); 

    if ((keypair == null) || (keypair.Length < 1)) 
        return ret; 

    bool pubonly = SnkBufIsPubLength(keypair); 

    if ((pubonly) && (!CheckRSA1(keypair)))
        return ret; 

    if ((!pubonly) && (!CheckRSA2(keypair)))
        return ret; 

    int magic_idx = pubonly ? _magic_pub_idx : _magic_priv_idx;


    // Bitlen is stored here, but note this 
    // class is only set up for 1024 bit length keys 
    int bitlen_idx = magic_idx + _magic_size; 
    int bitlen_size = 4; // DWORD

    // Exponent 
    // In read file, will usually be { 1, 0, 1, 0 } or 65537
    int exp_idx = bitlen_idx + bitlen_size; 
    int exp_size = 4; 


    //BYTE modulus[rsapubkey.bitlen/8]; == MOD; Size 128 
    int mod_idx = exp_idx + exp_size;
    int mod_size = 128; 

    //BYTE prime1[rsapubkey.bitlen/16]; == P; Size 64 
    int p_idx = mod_idx + mod_size; 
    int p_size = 64; 

    //BYTE prime2[rsapubkey.bitlen/16]; == Q; Size 64 
    int q_idx = p_idx + p_size; 
    int q_size = 64; 

    //BYTE exponent1[rsapubkey.bitlen/16]; == DP; Size 64
    int dp_idx = q_idx + q_size; 
    int dp_size = 64; 

    //BYTE exponent2[rsapubkey.bitlen/16]; == DQ; Size 64 
    int dq_idx = dp_idx + dp_size; 
    int dq_size = 64; 

    //BYTE coefficient[rsapubkey.bitlen/16]; == InverseQ; Size 64
    int invq_idx = dq_idx + dq_size; 
    int invq_size = 64; 

    //BYTE privateExponent[rsapubkey.bitlen/8]; == D; Size 128 
    int d_idx = invq_idx + invq_size; 
    int d_size = 128; 


    // Figure public params 
    // Must reverse order (little vs. big endian issue)
    ret.Exponent = BlockCopy(keypair, exp_idx, exp_size); 
    Array.Reverse(ret.Exponent); 
    ret.Modulus = BlockCopy(keypair, mod_idx, mod_size); 
    Array.Reverse(ret.Modulus); 

    if (pubonly) return ret; 

    // Figure private params 
    // Must reverse order (little vs. big endian issue)
    ret.P = BlockCopy(keypair, p_idx, p_size); 
    Array.Reverse(ret.P); 

    ret.Q = BlockCopy(keypair, q_idx, q_size); 
    Array.Reverse(ret.Q); 

    ret.DP = BlockCopy(keypair, dp_idx, dp_size); 
    Array.Reverse(ret.DP); 

    ret.DQ = BlockCopy(keypair, dq_idx, dq_size); 
    Array.Reverse(ret.DQ); 

    ret.InverseQ = BlockCopy(keypair, invq_idx, invq_size); 
    Array.Reverse(ret.InverseQ); 

    ret.D = BlockCopy(keypair, d_idx, d_size); 
    Array.Reverse(ret.D); 

    return ret; 
}

There is one other not-well-documented switch that needs to be thrown in order to get the SNK file to be imported correctly. Keys generated by sn.exe have the KeyNumber set to 2, which is "AT_SIGNATURE". For more information see http://www.jensign.com/JavaScience/dotnet/keyinfo/.

protected void CtorFromSnkBuf(byte[] snkbuf)
{
    if ((snkbuf == null) || (snkbuf.Length < 1))
    {
        CtorFromXml(null);
        return; 
    }

    RSAParameters param = FigureParams(snkbuf); 

    // Must set KeyNumber to AT_SIGNATURE for strong 
    // name keypair to correctly be imported. 
    CspParameters cp = new CspParameters(); 
    cp.KeyNumber = 2; // AT_SIGNATURE 

    _rsa = new RSACryptoServiceProvider(1024, cp); 
    _rsa.ImportParameters(param); 

}

Why did the Framework authors make it inconvenient to work with SNK files generated by sn.exe? I don't know, but in any case there's the code to work around that issue.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Per R. Anderson
Web Developer
United States United States
Per Anderson is the founder of Sunfrog Technologies LLC, http://sunfrog-tech.com .

Comments and Discussions

 
GeneralThank you! Pinmemberbarrd2-Jun-11 19:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 5 May 2004
Article Copyright 2004 by Per R. Anderson
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid