internal void ParsePublicBlob(byte[] blob, out string crv, out string x, out string y) { if (blob.Length < 2 * sizeof(int)) { throw new Exception("blob is definitely invalid"); } int offset = 0; Func <int, int> increaseOffset = (size) => { var oldOffset = offset; offset += size; return(oldOffset); }; KeyBlobMagicNumber magic = (KeyBlobMagicNumber)BitConverter.ToUInt32(blob, increaseOffset(sizeof(uint))); int byteLength = (int)BitConverter.ToUInt32(blob, increaseOffset(sizeof(uint))); if ((blob.Length - offset) != byteLength * 2) { throw new Exception("expected equal length curve parameters to remain"); } byte[] xBytes = new byte[byteLength]; Buffer.BlockCopy(blob, increaseOffset(xBytes.Length), xBytes, 0, xBytes.Length); byte[] yBytes = new byte[byteLength]; Buffer.BlockCopy(blob, increaseOffset(yBytes.Length), yBytes, 0, yBytes.Length); switch (magic) { case KeyBlobMagicNumber.ECDSA_PUBLIC_P256: { crv = "P-256"; break; } case KeyBlobMagicNumber.ECDSA_PUBLIC_P384: { crv = "P-384"; break; } case KeyBlobMagicNumber.ECDSA_PUBLIC_P521: { crv = "P-521"; break; } default: { throw new Exception("Unknown magic number " + magic); } } x = Base64UrlEncoder.Encode(xBytes); y = Base64UrlEncoder.Encode(yBytes); }
internal static ECParameters ExportNamedCurveParameters(CngKey key, bool includePrivateParameters) { byte[] ecBlob = ExportKeyBlob(key, includePrivateParameters); // We now have a buffer laid out as follows: // BCRYPT_ECCKEY_BLOB header // byte[cbKey] Q.X // byte[cbKey] Q.Y // -- Private only -- // byte[cbKey] D ECParameters ecParams = new ECParameters(); KeyBlobMagicNumber magic = (KeyBlobMagicNumber)BitConverter.ToInt32(ecBlob, 0); // Check the magic value in the key blob header. If the blob does not have the required magic, // then throw a CryptographicException. CheckMagicValueOfKey(magic, includePrivateParameters); unsafe { // Fail-fast if a rogue provider gave us a blob that isn't even the size of the blob header. if (ecBlob.Length < sizeof(BCRYPT_ECCKEY_BLOB)) throw ErrorCode.E_FAIL.ToCryptographicException(); fixed(byte *pEcBlob = ecBlob) { BCRYPT_ECCKEY_BLOB *pBcryptBlob = (BCRYPT_ECCKEY_BLOB *)pEcBlob; int offset = sizeof(BCRYPT_ECCKEY_BLOB); ecParams.Q = new ECPoint { X = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbKey), Y = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbKey) }; if (includePrivateParameters) { ecParams.D = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbKey); } } } ecParams.Curve = ECCurve.CreateFromFriendlyName(key.GetCurveName()); return(ecParams); }
private static bool IsMagicValueOfKeyPrivate(KeyBlobMagicNumber magic) { switch (magic) { case KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_GENERIC_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P256_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_P256_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P384_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_P384_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PRIVATE_P521_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PRIVATE_P521_MAGIC: return(true); } return(false); }
/// <summary> /// This function checks the magic value in the key blob header /// </summary> /// <param name="includePrivateParameters">Private blob if true else public key blob</param> private static void CheckMagicValueOfKey(KeyBlobMagicNumber magic, bool includePrivateParameters) { if (includePrivateParameters) { if (!IsMagicValueOfKeyPrivate(magic)) { throw new CryptographicException(SR.Cryptography_NotValidPrivateKey); } } else { if (!IsMagicValueOfKeyPublic(magic)) { throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey); } } }
private static bool IsMagicValueOfKeyPublic(KeyBlobMagicNumber magic) { switch (magic) { case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P256_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PUBLIC_P256_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P384_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PUBLIC_P384_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDSA_PUBLIC_P521_MAGIC: case KeyBlobMagicNumber.BCRYPT_ECDH_PUBLIC_P521_MAGIC: return(true); } // Private key magic is permissible too since the public key can be derived from the private key blob. return(IsMagicValueOfKeyPrivate(magic)); }
/// <summary> /// Map an algorithm identifier to a key size and magic number /// </summary> internal static void MapAlgorithmIdToMagic(string algorithm, out KeyBlobMagicNumber algorithmMagic, out int keySize) { //Contract.Requires(!String.IsNullOrEmpty(algorithm)); switch (algorithm) { case AlgorithmName.ECDHP256: algorithmMagic = KeyBlobMagicNumber.ECDHPublicP256; keySize = 256; break; case AlgorithmName.ECDHP384: algorithmMagic = KeyBlobMagicNumber.ECDHPublicP384; keySize = 384; break; case AlgorithmName.ECDHP521: algorithmMagic = KeyBlobMagicNumber.ECDHPublicP521; keySize = 521; break; case AlgorithmName.ECDsaP256: algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP256; keySize = 256; break; case AlgorithmName.ECDsaP384: algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP384; keySize = 384; break; case AlgorithmName.ECDsaP521: algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP521; keySize = 521; break; default: throw new ArgumentException("Cryptography_UnknownEllipticCurveAlgorithm"); //throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurveAlgorithm)); } }
internal static void MapAlgorithmIdToMagic(string algorithm, out KeyBlobMagicNumber algorithmMagic, out int keySize) { switch (algorithm) { case "ECDH_P256": algorithmMagic = KeyBlobMagicNumber.ECDHPublicP256; keySize = 0x100; return; case "ECDH_P384": algorithmMagic = KeyBlobMagicNumber.ECDHPublicP384; keySize = 0x180; return; case "ECDH_P521": algorithmMagic = KeyBlobMagicNumber.ECDHPublicP521; keySize = 0x209; return; case "ECDSA_P256": algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP256; keySize = 0x100; return; case "ECDSA_P384": algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP384; keySize = 0x180; return; case "ECDSA_P521": algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP521; keySize = 0x209; return; } throw new ArgumentException(System.SR.GetString("Cryptography_UnknownEllipticCurveAlgorithm")); }
internal static ECParameters ExportPrimeCurveParameters(CngKey key, bool includePrivateParameters) { byte[] ecBlob = ExportFullKeyBlob(key, includePrivateParameters); // We now have a buffer laid out as follows: // BCRYPT_ECCFULLKEY_BLOB header // byte[cbFieldLength] P // byte[cbFieldLength] A // byte[cbFieldLength] B // byte[cbFieldLength] G.X // byte[cbFieldLength] G.Y // byte[cbSubgroupOrder] Order (n) // byte[cbCofactor] Cofactor (h) // byte[cbSeed] Seed // byte[cbFieldLength] Q.X // byte[cbFieldLength] Q.Y // -- Private only -- // byte[cbSubgroupOrder] D ECParameters ecParams = new ECParameters(); KeyBlobMagicNumber magic = (KeyBlobMagicNumber)BitConverter.ToInt32(ecBlob, 0); // Check the magic value in the key blob header. If the blob does not have the required magic, // then throw a CryptographicException. CheckMagicValueOfKey(magic, includePrivateParameters); unsafe { // Fail-fast if a rogue provider gave us a blob that isn't even the size of the blob header. if (ecBlob.Length < sizeof(BCRYPT_ECCFULLKEY_BLOB)) throw ErrorCode.E_FAIL.ToCryptographicException(); fixed(byte *pEcBlob = ecBlob) { BCRYPT_ECCFULLKEY_BLOB *pBcryptBlob = (BCRYPT_ECCFULLKEY_BLOB *)pEcBlob; var primeCurve = new ECCurve(); primeCurve.CurveType = ConvertToCurveTypeEnum(pBcryptBlob->CurveType); primeCurve.Hash = GetHashAlgorithmName(pBcryptBlob->CurveGenerationAlgId); int offset = sizeof(BCRYPT_ECCFULLKEY_BLOB); primeCurve.Prime = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbFieldLength); primeCurve.A = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbFieldLength); primeCurve.B = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbFieldLength); primeCurve.G = new ECPoint() { X = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbFieldLength), Y = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbFieldLength), }; primeCurve.Order = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbSubgroupOrder); primeCurve.Cofactor = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbCofactor); // Optional parameters primeCurve.Seed = pBcryptBlob->cbSeed == 0 ? null : Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbSeed); ecParams.Q = new ECPoint { X = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbFieldLength), Y = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbFieldLength) }; if (includePrivateParameters) { ecParams.D = Interop.BCrypt.Consume(ecBlob, ref offset, pBcryptBlob->cbSubgroupOrder); } ecParams.Curve = primeCurve; return(ecParams); } } }