public override void FromXmlString(string xmlString) { // ParseDocument does the nullcheck for us. XmlKeyHelper.ParseState state = XmlKeyHelper.ParseDocument(xmlString); byte[] n = ReadRequiredElement(ref state, nameof(RSAParameters.Modulus)); byte[] e = ReadRequiredElement(ref state, nameof(RSAParameters.Exponent)); int halfN = (n.Length + 1) / 2; // .NET Framework doesn't report any element other than Modulus/Exponent as required, // it just lets import fail if they're imbalanced. byte[]? p = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(RSAParameters.P), halfN); byte[]? q = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(RSAParameters.Q), halfN); byte[]? dp = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(RSAParameters.DP), halfN); byte[]? dq = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(RSAParameters.DQ), halfN); byte[]? qInv = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(RSAParameters.InverseQ), halfN); byte[]? d = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(RSAParameters.D), n.Length); RSAParameters keyParameters = new RSAParameters { Modulus = n, Exponent = e, D = d, P = p, Q = q, DP = dp, DQ = dq, InverseQ = qInv, }; ImportParameters(keyParameters); }
public override void FromXmlString(string xmlString) { // ParseDocument does the nullcheck for us. XmlKeyHelper.ParseState state = XmlKeyHelper.ParseDocument(xmlString); byte[] p = ReadRequiredElement(ref state, nameof(DSAParameters.P)); byte[] q = ReadRequiredElement(ref state, nameof(DSAParameters.Q)); byte[] g = ReadRequiredElement(ref state, nameof(DSAParameters.G), p.Length); byte[] y = ReadRequiredElement(ref state, nameof(DSAParameters.Y), p.Length); byte[] j = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(DSAParameters.J)); byte[] seed = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(DSAParameters.Seed)); int counter = 0; byte[] x = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(DSAParameters.X), q.Length); if (seed != null) { byte[] counterBytes = ReadRequiredElement(ref state, CounterElementName); counter = XmlKeyHelper.ReadCryptoBinaryInt32(counterBytes); } DSAParameters dsaParameters = new DSAParameters { P = p, Q = q, G = g, Y = y, J = j, Seed = seed, Counter = counter, X = x, }; // Check for Counter without Seed after getting X, since that prevents an extra cycle in the // canonical element order. if (dsaParameters.Seed == null) { if (XmlKeyHelper.HasElement(ref state, CounterElementName)) { throw new CryptographicException( SR.Format( SR.Cryptography_InvalidFromXmlString, nameof(DSA), nameof(DSAParameters.Seed))); } } ImportParameters(dsaParameters); }
private static byte[] ReadRequiredElement( ref XmlKeyHelper.ParseState state, string name, int sizeHint = -1) { byte[] ret = XmlKeyHelper.ReadCryptoBinary(ref state, name, sizeHint); if (ret == null) { throw new CryptographicException( SR.Format(SR.Cryptography_InvalidFromXmlString, nameof(DSA), name)); } return(ret); }
public override string ToXmlString(bool includePrivateParameters) { // The format of this output is based on the xmldsig ds:DSAKeyValue value, except // * It writes values as xml:base64Binary instead of ds:CryptoBinary // * It doesn't strip off leading 0x00 byte values before base64 // * It doesn't emit the output in a namespace // * When includePrivateParameters is true it writes an X element. // // These deviations are inherited from .NET Framework. // P is KeySizeInBytes long. // Q is 160 to 256 bits long, or 20 to 32 bytes. // G is the same size as P // Y is the same size as P // X is the same size as Q // // Each field gets base64 encoded (after dropping leading 0x00 bytes) // so P is (KeySizeInBytes + 2) / 3 * 4, then plus 7 (<P></P>) // (For 3072 that's 519 chars, for 1024 it's 179.) // Add in maximum-Q: (32 + 2) / 3 * 4 + 7 => 51 // Then the "<DSAKeyValue></DSAKeyValue>" (27). // Grand total, 3 * P + 2 * Q + 27 => 1686 (3072) or 666 (1024). // KeySizeInBytes * 2 / 3 comes out to 2048 or 682, so call that good enough. // Rarely, keys will export the J or Seed values, and they may cause the // StringBuilder to need to grow. DSAParameters keyParameters = ExportParameters(includePrivateParameters); StringBuilder builder = new StringBuilder((keyParameters.P.Length << 1) / 3); builder.Append("<DSAKeyValue>"); XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.P), keyParameters.P, builder); XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.Q), keyParameters.Q, builder); XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.G), keyParameters.G, builder); XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.Y), keyParameters.Y, builder); if (keyParameters.J != null) { XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.J), keyParameters.J, builder); } if (keyParameters.Seed != null) { XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.Seed), keyParameters.Seed, builder); XmlKeyHelper.WriteCryptoBinary(CounterElementName, keyParameters.Counter, builder); } if (includePrivateParameters) { if (keyParameters.X == null) { // NetFx compat when a 3rd party type lets X be null when // includePrivateParameters is true // (the exception would have been from Convert.ToBase64String) throw new ArgumentNullException("inArray"); } XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.X), keyParameters.X, builder); } builder.Append("</DSAKeyValue>"); return(builder.ToString()); }
public override string ToXmlString(bool includePrivateParameters) { // The format of this output is based on the xmldsig ds:RSAKeyValue value, except // * It writes values as xml:base64Binary instead of ds:CryptoBinary // * It doesn't strip off leading 0x00 byte values before base64 // * It doesn't emit the output in a namespace // * When includePrivateParameters is true it writes the private key elements. // * D, P, Q, DP, DQ, InverseQ // // These deviations are inherited from .NET Framework. // For a public-only export, the output is like the following, but with no whitespace // // <RSAKeyValue> // <Modulus>[base64 modulus]</Modulus> // <Exponent>AQAB</Exponent> // </RSAKeyValue> // // (using the knowledge that 99.9(etc)% of RSA keys use the same exponent, 65537). // rsa.KeySize (bits) / 6 will produce a value just slightly smaller than needed: // // KeySize | BytesReq | Div5 | Div6 // --------|----------|------|----- // 16384 | 2732 | 3276 | 2730 // 2048 | 344 | 409 | 341 // 1024 | 172 | 204 | 170 // 512 | 88 | 102 | 85 // // So just add 3 chars to the overhead. // The overhead, otherwise, is 65 chars, plus exponent's actual value. // While most keys are AQAB (0x010001) it's technically a variable. // CAPI has a limit of 32 bits. CNG-Win7 is unbounded, CNG-Win10 is 64-bits. // So call it 12 chars ((64/8 + 2) / 3 * 4). // 65 + 32 + 3 = 100. Nice, round, number. // // For private keys, D is the same size as Modulus, and P/Q/DP/DQ/InverseQ are // each half the size of Modulus. So their variable payload is 5 * (KeySize / 2 / 6). // // Their element tags add 58 extra characters, and sprinkle in another 3 each (18 total) for // base64 vs div6 padding, for a conditional overhead of 76 chars. int keySizeDiv6 = KeySize / 6; int initialCapacity = 100 + keySizeDiv6; if (includePrivateParameters) { initialCapacity += 76 + 5 * keySizeDiv6 / 2; } RSAParameters keyParameters = ExportParameters(includePrivateParameters); StringBuilder builder = new StringBuilder(initialCapacity); builder.Append("<RSAKeyValue>"); XmlKeyHelper.WriteCryptoBinary(nameof(RSAParameters.Modulus), keyParameters.Modulus, builder); XmlKeyHelper.WriteCryptoBinary(nameof(RSAParameters.Exponent), keyParameters.Exponent, builder); if (includePrivateParameters) { // Match .NET Framework field order. XmlKeyHelper.WriteCryptoBinary(nameof(RSAParameters.P), keyParameters.P, builder); XmlKeyHelper.WriteCryptoBinary(nameof(RSAParameters.Q), keyParameters.Q, builder); XmlKeyHelper.WriteCryptoBinary(nameof(RSAParameters.DP), keyParameters.DP, builder); XmlKeyHelper.WriteCryptoBinary(nameof(RSAParameters.DQ), keyParameters.DQ, builder); XmlKeyHelper.WriteCryptoBinary(nameof(RSAParameters.InverseQ), keyParameters.InverseQ, builder); XmlKeyHelper.WriteCryptoBinary(nameof(RSAParameters.D), keyParameters.D, builder); } builder.Append("</RSAKeyValue>"); return(builder.ToString()); }