Code:
/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / infocard / Service / managed / Microsoft / InfoCards / RsaKeyGen.cs / 1 / RsaKeyGen.cs
//------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace Microsoft.InfoCards
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.IO;
using System.Text;
using System.Diagnostics;
using Microsoft.InfoCards.Diagnostics;
using IDT = Microsoft.InfoCards.Diagnostics.InfoCardTrace;
//
// Summary:
// Utility class for RSA Key Pair Generation
//
sealed class RsaKeyGen
{
//////////////////////////////////////////////////////////////////
// Key pair size (size of the modulus n) is fixed at 2048 bits
//////////////////////////////////////////////////////////////////
const int Xpq12Length = 14;
const int XpqLength = 128;
const int NumberOfSha1Hashes = 16;
const int InfoCardKeyLength = 2048;
//
// Overall approach
// Since the msbignum code wants little endian byte order for large integers Xp and Xq
// (which are laid out as a sequence of bytes), we want to generate Xp and Xq in little endian.
//
// The spec says to generate
// H4 (big endian) + H5 (big endian) + H6 (big endian) + H0[4 MSB] (big endian)
// + H10 (big endian) + H11 (big endian) + H12 (big endian) + H2[4 MSB] (big endian)
//
// In little endian this equals:
// Array.Reverse( H4 (big endian) + H5 (big endian) + H6 (big endian) + H0[4 MSB] (big endian)
// + H10 (big endian) + H11 (big endian) + H12 (big endian) + H2[4 MSB] (big endian) )
//
// Which is the same as (note the order of H has been reversed):
// H2[4 MSB] (little endian) + H12 (little endian) + H11 (little endian) + H10 (little endian)
// + H0[4 MSB] (little endian) + H6 (little endian) + H5 (little endian) + H4 (little endian)
//
// Therefore the approach is to generate each of the H's in little endian, and then
// append them in reverse order [H2 H12 H11 H10 H0 H6 H5 H4] compared to the big endian order [H4 H5 H6 H0 H10 H11 H12 H2]
// that is detailed in the spec.
//
static int[ ] hashesToWriteP = new int[ 8 ] { 2, 12, 11, 10, 0, 6, 5, 4 };
static int[ ] numBytesToWriteP = new int[ 8 ] { 4, HnLength, HnLength, HnLength,
4, HnLength, HnLength, HnLength };
static int[] srcIndexBytesToWriteP = new int[ 8 ] { 16, 0, 0, 0, 16, 0, 0, 0 };
static int[ ] hashesToWriteQ = new int[ 8 ] { 3, 15, 14, 13, 1, 9, 8, 7 };
static int[ ] numBytesToWriteQ = numBytesToWriteP;
static int[ ] srcIndexBytesToWriteQ = srcIndexBytesToWriteP;
////////////////////////////////////////////////////////////////
// Uncomment below if we want asymm key pairs to be 1024 bits.
////////////////////////////////////////////////////////////////
//
// const int Xpq12Length = 14;
// const int XpqLength = 64;
// const int NumberOfSha1Hashes = 16;
// const int InfoCardKeyLength = 1024;
//
// static int[ ] hashesToWriteP = new int[ 4 ] { 0, 6, 5, 4 };
// static int[ ] numBytesToWriteP = new int[ 4 ] { 4, HnLength, HnLength, HnLength };
// static int[ ] srcIndexBytesToWriteP = new int[ 4 ] { 16, 0, 0, 0 };
//
// static int[ ] hashesToWriteQ = new int[ 4 ] { 1, 9, 8, 7 };
// static int[ ] numBytesToWriteQ = numBytesToWriteP;
// static int[ ] srcIndexBytesToWriteQ = srcIndexBytesToWriteP;
//
////////////////////////////////////////////////////////////////
// Size of a SHA1 hash in bytes
const int HnLength = 20;
// A recommended public exponent value
const int PublicExponentValue = 65537;
// The encryption key used when regenerating input params
const string Aes128EncryptionKey = "f561e58b-3ebc-4e0c-940d-0a6ddc219dfd";
//
// Represents the byte in which 2^100 is located.
//
const int ByteForHundredthBit = 100/8;
//
// Set up AES 128 using Electronic Code Book mode
//
static RijndaelManaged s_rjAes128 = new RijndaelManaged();
//
// Summary:
// Set up AES 128 using Electronic Code Book mode
//
static RsaKeyGen()
{
s_rjAes128.Mode = CipherMode.ECB;
s_rjAes128.Padding = PaddingMode.Zeros;
s_rjAes128.Key = ( new Guid( Aes128EncryptionKey ) ).ToByteArray();
}
//
// Summary:
// Implementation of the InfoCard RSA Key Gen Algorithm complying to
// X9.31 to create a privatekey blob.
//
// Params:
// masterKey - M (infocard masterkey)
// TargetId - T (target recipient entropy)
//
// Return:
// The CryptoAPI PRIVATEKEYBLOB
//
public static byte[ ] CreateRsaKeyPairX931( byte[ ] masterKey, byte[ ] targetId )
{
//
// Sanity Check: Only 1024 or 2048 bit keys supported!
//
IDT.Assert( InfoCardKeyLength == InfoCard.KeySize, "Only 2048 bit key sizes supported" );
//
// Calculate all the Hn = n + T + M (LITTLE ENDIAN)
// i.e. Hn = M + T + n (BIG ENDIAN)
//
byte[ ][ ] hnArray = CalculateHn( masterKey, targetId );
// Will be populate with the return blob
byte[ ] cryptoBlob = null;
//
// Allocate bytes that will contain the 6 integers. We'll copy these to
// native memory and then call the native RSA Key Gen function.
//
//
// Question: Why create the extra copy?
// Why not just copy over appropriate number of bytes from Hn to an IntPtr
// and pass that to native memory?
//
// Answer: We create Xp*ByteArray as local variables which comes in useful
// when rsakeygen fails and we regenerate input params. So if Xp*
// need to be regenerated, they are copied into the original buffer locations
//
byte[ ] Xp1ByteArray = new byte[ Xpq12Length ];
byte[ ] Xp2ByteArray = new byte[ Xpq12Length ];
byte[ ] Xq1ByteArray = new byte[ Xpq12Length ];
byte[ ] Xq2ByteArray = new byte[ Xpq12Length ];
byte[ ] XpByteArray = new byte[ XpqLength ];
byte[ ] XqByteArray = new byte[ XpqLength ];
//
// Init Xp1ByteArray, Xp2ByteArray, Xq1ByteArray, Xq2ByteArray
// hnArray = 20 bytes, Xp1ByteArray = 14 bytes
//
Array.Copy( hnArray[ 0 ], 0, Xp1ByteArray, 0, Xp1ByteArray.Length );
Array.Copy( hnArray[ 1 ], 0, Xp2ByteArray, 0, Xp2ByteArray.Length );
Array.Copy( hnArray[ 2 ], 0, Xq1ByteArray, 0, Xq1ByteArray.Length );
Array.Copy( hnArray[ 3 ], 0, Xq2ByteArray, 0, Xq2ByteArray.Length );
//
// Init XpByteArray - H2 H12 H11 H10 H0 H6 H5 H4 (little endian)
//
// Sanity check: We want H4 as part of MSB.
// I.e. stored last in buffer in little endian.
//
WriteToArray( hashesToWriteP, numBytesToWriteP, srcIndexBytesToWriteP,
hnArray, XpByteArray );
//
// Init XqByteArray - H3 H15 H14 H13 H1 H9 H8 H7 (little endian)
//
WriteToArray( hashesToWriteQ, numBytesToWriteQ, srcIndexBytesToWriteQ,
hnArray, XqByteArray );
try
{
KeyGenRestartCodes restartCode = KeyGenRestartCodes.X931RestartNone;
//
// The chance of failure in each iteration is very rare.
// So the likelyhood of 2 iterations failing in a row is
// even smaller. So even a while( true ), should suffice.
// Plan to run this with lots of input combinations overnight
// or determine actual probabilities
// to provide a confidence level in termination of algorithm
//
do
{
//
// The X931KeyGen checks that the seeds will produce a
// modulus of the specified length, this means the upper
// bits of the most significant byte needs to be set to 0xF0
// so that we get a 2048 bit modulus. Confirmed with
// Josh Benaloh on this is Ok.
//
// Question: Why | with 0xC0 as opposed to 0x1000 ?
// Answer: The problem is that 0x80 x 0x80 = 0x4000,
// so the high bit of the modulus is not set. Since 0xC0 x 0xC0 = 0x9000,
// we guarantee the high bit is set.
//
XpByteArray[ XpqLength - 1 ] |= ( byte ) 0xC0;
XqByteArray[ XpqLength - 1 ] |= ( byte ) 0xC0;
//
// Make sure Xpq12 are greater than 2^100 as required by X9.31
// The maximum i.e. Xpq12 < 2^120 is automatically met becuase
// Xpq12 are only 112 bits long.
//
Xp1ByteArray[ ByteForHundredthBit ] |= ( byte ) 0x10;
Xp2ByteArray[ ByteForHundredthBit ] |= ( byte ) 0x10;
Xq1ByteArray[ ByteForHundredthBit ] |= ( byte ) 0x10;
Xq2ByteArray[ ByteForHundredthBit ] |= ( byte ) 0x10;
cryptoBlob = NativeMcppMethods.X931KeyGenWrapper(
Xp1ByteArray,
Xp2ByteArray,
Xq1ByteArray,
Xq2ByteArray,
XpByteArray,
XqByteArray,
PublicExponentValue,
ref restartCode );
IDT.TraceDebug( "InfoCardKeyGen: Restart code is : {0}", restartCode );
switch ( restartCode )
{
case KeyGenRestartCodes.X931RestartNone:
// Success
IDT.Assert( null != cryptoBlob, "Cryptoblob should not be null since API succeeded" );
break;
// decryexp < 2^bitlen_pq
case KeyGenRestartCodes.X931RestartDTooSmall:
// As per Josh Benaloh, with e fixed at 65537,
// d will be guaranteed to be much larger than 1/2 the length of n
IDT.Assert( null == cryptoBlob, "Cryptoblob should be null" );
IDT.Assert( false, "Not expected for d < 2^1024 with e = 65537" );
InfoCardRegenerateParam( Xq1ByteArray );
InfoCardRegenerateParam( Xq2ByteArray );
InfoCardRegenerateParam( XqByteArray );
break;
// GCD(p1, p2) > 1 or GCD(q1, q2) > 1
case KeyGenRestartCodes.X931RestartNonTrivialGcd:
IDT.Assert( null == cryptoBlob, "Cryptoblob should be null" );
InfoCardRegenerateParam( Xp1ByteArray );
InfoCardRegenerateParam( Xp2ByteArray );
InfoCardRegenerateParam( Xq1ByteArray );
InfoCardRegenerateParam( Xq2ByteArray );
break;
// p or q > 2^bitlen_pq
case KeyGenRestartCodes.X931RestartPQOverflow:
IDT.Assert( null == cryptoBlob, "Cryptoblob should be null" );
InfoCardRegenerateParam( XpByteArray );
InfoCardRegenerateParam( XqByteArray );
break;
// p1 or p2 or q1 or q2 > 2^X931_MAXBITS_PQ12
case KeyGenRestartCodes.X931RestartPQ12OverFlow:
IDT.Assert( null == cryptoBlob, "Cryptoblob should be null" );
InfoCardRegenerateParam( Xp1ByteArray );
InfoCardRegenerateParam( Xp2ByteArray );
InfoCardRegenerateParam( Xq1ByteArray );
InfoCardRegenerateParam( Xq2ByteArray );
break;
// abs(Xp - Xq) too small
case KeyGenRestartCodes.X931RestartXpXqClose:
IDT.Assert( null == cryptoBlob, "Cryptoblob should be null" );
InfoCardRegenerateParam( XqByteArray );
break;
}
}while( restartCode != KeyGenRestartCodes.X931RestartNone );
IDT.Assert( null != cryptoBlob, "Cryptoblob should NOT be null outside while loop" );
}
finally
{
//
// Clear out all arrays allocated
//
Array.Clear( Xp1ByteArray, 0, Xp1ByteArray.Length );
Array.Clear( Xp2ByteArray, 0, Xp2ByteArray.Length );
Array.Clear( Xq1ByteArray, 0, Xq1ByteArray.Length );
Array.Clear( Xq2ByteArray, 0, Xq2ByteArray.Length );
Array.Clear( XpByteArray, 0, XpByteArray.Length );
Array.Clear( XqByteArray, 0, XqByteArray.Length );
}
return cryptoBlob;
}
//
// Summary:
// Utility function to copy appropriate number of bytes to appropriate index
// in destArray
//
// hashesToWrite - represents index in sourceArrayOfHashes that we want to copy
// numBytesToWrite - num of bytes to copy
// srcIndexBytesToWrite - index of bytes to begin copying from
// sourceArrayOfHashes - a 2d array of the SHA1 hashes we created
// destArray - the buffer where we'll copy this to
//
static void WriteToArray(
int[ ] hashesToWrite,
int[ ] numBytesToWrite,
int[ ] srcIndexBytesToWrite,
byte[ ][ ] sourceArrayOfHashes,
byte[ ] destArray )
{
IDT.Assert( hashesToWrite.Length == numBytesToWrite.Length, "Size must be equal" );
IDT.Assert( numBytesToWrite.Length == srcIndexBytesToWrite.Length, "Size must be equal" );
int destIndex = 0;
for ( int index = 0; index < hashesToWrite.Length; index++ )
{
int indexInHashArray = hashesToWrite[ index ];
Array.Copy(
sourceArrayOfHashes[ indexInHashArray ],
srcIndexBytesToWrite[ index ],
destArray,
destIndex,
numBytesToWrite[ index ] );
destIndex += numBytesToWrite[ index ];
}
}
//
// Summary:
// Create byte array of H(n) for n = 0, 1, 2, ...15
// where H(n) = M + T + C(n) = M + T + n in Big Endian.
//
// Params:
// masterKey - M (infocard masterkey)
// subjectEntropy - T (Recipient entropy)
//
// Remarks:
// n - constant represented by C(n), must be in big endian.
// M + T - does not really make sense if this means big endian or not becuase we're simply concatenating bytes
//
// Return:
// 16 byte arrays (representing H0, H1, ..., H15) each of length 20 (i.e. 160 bits)
//
static byte[ ][ ] CalculateHn( byte[ ] masterKey, byte[ ] subjectEntropy )
{
byte[ ][ ] hnArray = new byte[ NumberOfSha1Hashes ][ ];
//
// Setup array common to all H(n)
//
int byteArraySize = masterKey.Length + subjectEntropy.Length + sizeof( int );
byte[ ] inputBytes = new byte[ byteArraySize ];
//
// Put in M
//
Array.Copy( masterKey, 0, inputBytes, 0, masterKey.Length );
//
// Put in T after M
//
Array.Copy( subjectEntropy, 0, inputBytes, masterKey.Length, subjectEntropy.Length );
//
// Now fill in n for all the Hn's
//
for ( uint n = 0; n < hnArray.Length; n++ )
{
//
// Convert n to a byte and fill it in the last element of the array (since we want big-endian n)
// (We assert that n does not exceed a byte.)
//
// POTENTIAL TEST ?
// Passing in constant integer 2 (instead of n) means we'd generate identical Xp Xq etc.
// which will cause the code the to shutle thru X931RestartXpXqClose, X931RestartNonTrivialGcd,
// followed by success
//
IDT.Assert( n <= 0xFF, "n must fit in a byte." );
IDT.Assert( inputBytes.Length - 1 == masterKey.Length + subjectEntropy.Length + 3, "Index location of n must be last element of array." );
inputBytes[ inputBytes.Length - 1 ] = ( byte ) n;
//
// SHA digest (an integer) is returned in big endian
//
hnArray[ n ] = SHA1.Create().ComputeHash( inputBytes );
//
// Our code deals with little endian byte order (as detailed at the start of this file), so flip the bytes
//
Array.Reverse( hnArray[ n ] );
}
Array.Clear( inputBytes, 0, inputBytes.Length );
return hnArray;
}
//
// Summary:
// Regenerates input params as per infocard key gen algorithm
// Copies new data on top of input array
//
// Params:
// prevBytes - the byte array to encrypt
//
// Return:
// Nil, the input array contains the newly generated bytes
//
static void InfoCardRegenerateParam(
byte[ ] prevBytes
)
{
//
// AESEncrypt previous bytes
//
byte[ ] newBytes = AESEncryptECB( prevBytes );
//
// Copy it into old memory
//
Array.Copy( newBytes, 0, prevBytes, 0, prevBytes.Length );
//
// Zero out previous buffer
//
Array.Clear( newBytes, 0, newBytes.Length );
}
//
// Summary:
// Does AES 128 using Electronic Code Book mode
// Encrytion Key = C = f561e58b-3ebc-4e0c-940d-0a6ddc219dfd
// Padding = zero-bits such that bit-length is a multiple of 128
//
// Params:
// plaintextbyte - the byte array to encrypt
//
static byte[ ] AESEncryptECB( byte[ ] plaintextbyte )
{
//
// Create a CryptoStream from a memory stream
//
MemoryStream ciphertextmem = new MemoryStream();
CryptoStream crystm = new CryptoStream(
ciphertextmem,
s_rjAes128.CreateEncryptor(),
CryptoStreamMode.Write );
//
// Get the cipher text
//
crystm.Write( plaintextbyte, 0, plaintextbyte.Length );
crystm.Close();
Byte[ ] ciphertextbyte = ciphertextmem.ToArray();
ciphertextmem.Close();
return ciphertextbyte;
}
//
// Summary:
// This class is purely for utility purposes
//
private RsaKeyGen()
{
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ComponentCommands.cs
- ManipulationCompletedEventArgs.cs
- Membership.cs
- MethodAccessException.cs
- OrCondition.cs
- WsdlBuildProvider.cs
- CategoryGridEntry.cs
- DataGridViewColumnHeaderCell.cs
- NumberFormatter.cs
- RolePrincipal.cs
- Vector3DAnimationUsingKeyFrames.cs
- DataChangedEventManager.cs
- Cursor.cs
- SelfIssuedSamlTokenFactory.cs
- CompressedStack.cs
- ContextBase.cs
- JavaScriptObjectDeserializer.cs
- DataServicePagingProviderWrapper.cs
- DoubleAnimationClockResource.cs
- RSAPKCS1SignatureDeformatter.cs
- SR.cs
- EventData.cs
- ITextView.cs
- Typeface.cs
- ContainerParaClient.cs
- ContentElementAutomationPeer.cs
- WebBrowserBase.cs
- DataServiceProviderWrapper.cs
- EventDescriptor.cs
- HttpVersion.cs
- SignatureHelper.cs
- RuntimeEnvironment.cs
- PeerHelpers.cs
- OpenTypeLayoutCache.cs
- CssStyleCollection.cs
- UniqueIdentifierService.cs
- KeyEventArgs.cs
- InvalidPropValue.cs
- ProviderBase.cs
- _TransmitFileOverlappedAsyncResult.cs
- MultiTargetingUtil.cs
- DataGridViewColumnEventArgs.cs
- ActivationWorker.cs
- CorrelationManager.cs
- DBPropSet.cs
- SessionStateUtil.cs
- LogRestartAreaEnumerator.cs
- StateWorkerRequest.cs
- ParameterElement.cs
- ProcessProtocolHandler.cs
- _RequestCacheProtocol.cs
- ToolStripStatusLabel.cs
- EpmSourceTree.cs
- RoleManagerModule.cs
- CacheDependency.cs
- KoreanLunisolarCalendar.cs
- MarkupExtensionParser.cs
- MasterPageCodeDomTreeGenerator.cs
- SafeThreadHandle.cs
- InvokeMethod.cs
- SynchronizedMessageSource.cs
- HostnameComparisonMode.cs
- StylusButtonEventArgs.cs
- Axis.cs
- SystemIcons.cs
- CellLabel.cs
- PathSegment.cs
- TemplatedWizardStep.cs
- LocalBuilder.cs
- ColumnMapCopier.cs
- Command.cs
- ToolStripButton.cs
- IisTraceListener.cs
- DllNotFoundException.cs
- ErrorInfoXmlDocument.cs
- SelectQueryOperator.cs
- StrokeSerializer.cs
- BorderSidesEditor.cs
- ComponentManagerBroker.cs
- TextTreeExtractElementUndoUnit.cs
- HtmlDocument.cs
- EnvironmentPermission.cs
- ActivationServices.cs
- TrailingSpaceComparer.cs
- ObjectDataSourceFilteringEventArgs.cs
- ResourceManager.cs
- CompilerGeneratedAttribute.cs
- StickyNoteAnnotations.cs
- SafeNativeMethods.cs
- UserControlBuildProvider.cs
- OperationInfoBase.cs
- Rotation3D.cs
- Parser.cs
- OrderedDictionaryStateHelper.cs
- RelOps.cs
- SerialPort.cs
- PropertyTab.cs
- WebPartVerb.cs
- OutOfMemoryException.cs
- TimeSpanSecondsConverter.cs