diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 41980b256..cb4710669 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -3089,6 +3089,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\agreement\DHStandardGroups.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\agreement\ECDHBasicAgreement.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4449,6 +4454,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\DefaultTlsServer.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\DefaultTlsSignerCredentials.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -4699,6 +4709,11 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "src\crypto\tls\TlsClientProtocol.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "src\crypto\tls\TlsCompression.cs"
SubType = "Code"
BuildAction = "Compile"
@@ -10896,6 +10911,21 @@
BuildAction = "Compile"
/>
<File
+ RelPath = "test\src\crypto\tls\test\MockTlsClient.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\crypto\tls\test\TlsClientTest.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
+ RelPath = "test\src\crypto\tls\test\TlsTestUtilities.cs"
+ SubType = "Code"
+ BuildAction = "Compile"
+ />
+ <File
RelPath = "test\src\math\ec\test\AllTests.cs"
SubType = "Code"
BuildAction = "Compile"
diff --git a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
index dbb07c744..721299105 100644
--- a/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
+++ b/crypto/src/asn1/pkcs/RSAPrivateKeyStructure.cs
@@ -18,7 +18,21 @@ namespace Org.BouncyCastle.Asn1.Pkcs
private readonly BigInteger exponent2;
private readonly BigInteger coefficient;
- public RsaPrivateKeyStructure(
+ public static RsaPrivateKeyStructure GetInstance(Asn1TaggedObject obj, bool isExplicit)
+ {
+ return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+ }
+
+ public static RsaPrivateKeyStructure GetInstance(object obj)
+ {
+ if (obj == null)
+ return null;
+ if (obj is RsaPrivateKeyStructure)
+ return (RsaPrivateKeyStructure)obj;
+ return new RsaPrivateKeyStructure(Asn1Sequence.GetInstance(obj));
+ }
+
+ public RsaPrivateKeyStructure(
BigInteger modulus,
BigInteger publicExponent,
BigInteger privateExponent,
@@ -38,64 +52,65 @@ namespace Org.BouncyCastle.Asn1.Pkcs
this.coefficient = coefficient;
}
- public RsaPrivateKeyStructure(
+ [Obsolete("Use 'GetInstance' method(s) instead")]
+ public RsaPrivateKeyStructure(
Asn1Sequence seq)
{
- BigInteger version = ((DerInteger) seq[0]).Value;
- if (version.IntValue != 0)
+ BigInteger version = ((DerInteger) seq[0]).Value;
+ if (version.IntValue != 0)
throw new ArgumentException("wrong version for RSA private key");
- modulus = ((DerInteger) seq[1]).Value;
- publicExponent = ((DerInteger) seq[2]).Value;
- privateExponent = ((DerInteger) seq[3]).Value;
- prime1 = ((DerInteger) seq[4]).Value;
- prime2 = ((DerInteger) seq[5]).Value;
- exponent1 = ((DerInteger) seq[6]).Value;
- exponent2 = ((DerInteger) seq[7]).Value;
- coefficient = ((DerInteger) seq[8]).Value;
- }
-
- public BigInteger Modulus
- {
- get { return modulus; }
- }
-
- public BigInteger PublicExponent
- {
- get { return publicExponent; }
- }
-
- public BigInteger PrivateExponent
- {
- get { return privateExponent; }
- }
-
- public BigInteger Prime1
- {
- get { return prime1; }
- }
-
- public BigInteger Prime2
- {
- get { return prime2; }
- }
-
- public BigInteger Exponent1
- {
- get { return exponent1; }
- }
-
- public BigInteger Exponent2
- {
- get { return exponent2; }
- }
-
- public BigInteger Coefficient
- {
- get { return coefficient; }
- }
-
- /**
+ modulus = ((DerInteger) seq[1]).Value;
+ publicExponent = ((DerInteger) seq[2]).Value;
+ privateExponent = ((DerInteger) seq[3]).Value;
+ prime1 = ((DerInteger) seq[4]).Value;
+ prime2 = ((DerInteger) seq[5]).Value;
+ exponent1 = ((DerInteger) seq[6]).Value;
+ exponent2 = ((DerInteger) seq[7]).Value;
+ coefficient = ((DerInteger) seq[8]).Value;
+ }
+
+ public BigInteger Modulus
+ {
+ get { return modulus; }
+ }
+
+ public BigInteger PublicExponent
+ {
+ get { return publicExponent; }
+ }
+
+ public BigInteger PrivateExponent
+ {
+ get { return privateExponent; }
+ }
+
+ public BigInteger Prime1
+ {
+ get { return prime1; }
+ }
+
+ public BigInteger Prime2
+ {
+ get { return prime2; }
+ }
+
+ public BigInteger Exponent1
+ {
+ get { return exponent1; }
+ }
+
+ public BigInteger Exponent2
+ {
+ get { return exponent2; }
+ }
+
+ public BigInteger Coefficient
+ {
+ get { return coefficient; }
+ }
+
+ /**
* This outputs the key in Pkcs1v2 format.
* <pre>
* RsaPrivateKey ::= Sequence {
@@ -116,16 +131,16 @@ namespace Org.BouncyCastle.Asn1.Pkcs
*/
public override Asn1Object ToAsn1Object()
{
- return new DerSequence(
- new DerInteger(0), // version
- new DerInteger(Modulus),
- new DerInteger(PublicExponent),
- new DerInteger(PrivateExponent),
- new DerInteger(Prime1),
- new DerInteger(Prime2),
- new DerInteger(Exponent1),
- new DerInteger(Exponent2),
- new DerInteger(Coefficient));
+ return new DerSequence(
+ new DerInteger(0), // version
+ new DerInteger(Modulus),
+ new DerInteger(PublicExponent),
+ new DerInteger(PrivateExponent),
+ new DerInteger(Prime1),
+ new DerInteger(Prime2),
+ new DerInteger(Exponent1),
+ new DerInteger(Exponent2),
+ new DerInteger(Coefficient));
}
}
}
diff --git a/crypto/src/crypto/agreement/DHStandardGroups.cs b/crypto/src/crypto/agreement/DHStandardGroups.cs
new file mode 100644
index 000000000..6c46b60de
--- /dev/null
+++ b/crypto/src/crypto/agreement/DHStandardGroups.cs
@@ -0,0 +1,206 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Agreement
+{
+ /// <summary>Standard Diffie-Hellman groups from various IETF specifications.</summary>
+ public class DHStandardGroups
+ {
+ private static DHParameters FromPG(string hexP, string hexG)
+ {
+ BigInteger p = new BigInteger(1, Hex.Decode(hexP));
+ BigInteger g = new BigInteger(1, Hex.Decode(hexG));
+ return new DHParameters(p, g);
+ }
+
+ private static DHParameters FromPGQ(string hexP, string hexG, string hexQ)
+ {
+ BigInteger p = new BigInteger(1, Hex.Decode(hexP));
+ BigInteger g = new BigInteger(1, Hex.Decode(hexG));
+ BigInteger q = new BigInteger(1, Hex.Decode(hexQ));
+ return new DHParameters(p, g, q);
+ }
+
+ /*
+ * RFC 2409
+ */
+ private static readonly string rfc2409_768_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF";
+ private static readonly string rfc2409_768_g = "02";
+ public static readonly DHParameters rfc2409_768 = FromPG(rfc2409_768_p, rfc2409_768_g);
+
+ private static readonly string rfc2409_1024_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381"
+ + "FFFFFFFFFFFFFFFF";
+ private static readonly string rfc2409_1024_g = "02";
+ public static readonly DHParameters rfc2409_1024 = FromPG(rfc2409_1024_p, rfc2409_1024_g);
+
+ /*
+ * RFC 3526
+ */
+ private static readonly string rfc3526_1536_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_1536_g = "02";
+ public static readonly DHParameters rfc3526_1536 = FromPG(rfc3526_1536_p, rfc3526_1536_g);
+
+ private static readonly string rfc3526_2048_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_2048_g = "02";
+ public static readonly DHParameters rfc3526_2048 = FromPG(rfc3526_2048_p, rfc3526_2048_g);
+
+ private static readonly string rfc3526_3072_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_3072_g = "02";
+ public static readonly DHParameters rfc3526_3072 = FromPG(rfc3526_3072_p, rfc3526_3072_g);
+
+ private static readonly string rfc3526_4096_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
+ + "FFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_4096_g = "02";
+ public static readonly DHParameters rfc3526_4096 = FromPG(rfc3526_4096_p, rfc3526_4096_g);
+
+ private static readonly string rfc3526_6144_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ + "6DCC4024FFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_6144_g = "02";
+ public static readonly DHParameters rfc3526_6144 = FromPG(rfc3526_6144_p, rfc3526_6144_g);
+
+ private static readonly string rfc3526_8192_p = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"
+ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"
+ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"
+ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"
+ + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"
+ + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"
+ + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"
+ + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"
+ + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"
+ + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"
+ + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"
+ + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"
+ + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"
+ + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"
+ + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300"
+ + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A"
+ + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1"
+ + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47"
+ + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF";
+ private static readonly string rfc3526_8192_g = "02";
+ public static readonly DHParameters rfc3526_8192 = FromPG(rfc3526_8192_p, rfc3526_8192_g);
+
+ /*
+ * RFC 4306
+ */
+ public static readonly DHParameters rfc4306_768 = rfc2409_768;
+ public static readonly DHParameters rfc4306_1024 = rfc2409_1024;
+
+ /*
+ * RFC 5114
+ */
+ private static readonly string rfc5114_1024_160_p = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6"
+ + "9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0" + "13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70"
+ + "98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0" + "A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708"
+ + "DF1FB2BC2E4A4371";
+ private static readonly string rfc5114_1024_160_g = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F"
+ + "D6406CFF14266D31266FEA1E5C41564B777E690F5504F213" + "160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1"
+ + "909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A" + "D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24"
+ + "855E6EEB22B3B2E5";
+ private static readonly string rfc5114_1024_160_q = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353";
+ public static readonly DHParameters rfc5114_1024_160 = FromPGQ(rfc5114_1024_160_p, rfc5114_1024_160_g,
+ rfc5114_1024_160_q);
+
+ private static readonly string rfc5114_2048_224_p = "AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1"
+ + "B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" + "EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212"
+ + "9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207" + "C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708"
+ + "B3BF8A317091883681286130BC8985DB1602E714415D9330" + "278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D"
+ + "CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8" + "BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763"
+ + "C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71" + "CF9DE5384E71B81C0AC4DFFE0C10E64F";
+ private static readonly string rfc5114_2048_224_g = "AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF"
+ + "74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA" + "AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7"
+ + "C17669101999024AF4D027275AC1348BB8A762D0521BC98A" + "E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE"
+ + "F180EB34118E98D119529A45D6F834566E3025E316A330EF" + "BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB"
+ + "10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381" + "B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269"
+ + "EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179" + "81BC087F2A7065B384B890D3191F2BFA";
+ private static readonly string rfc5114_2048_224_q = "801C0D34C58D93FE997177101F80535A4738CEBCBF389A99B36371EB";
+ public static readonly DHParameters rfc5114_2048_224 = FromPGQ(rfc5114_2048_224_p, rfc5114_2048_224_g,
+ rfc5114_2048_224_q);
+
+ private static readonly string rfc5114_2048_256_p = "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F2"
+ + "5D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA30" + "16C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD"
+ + "5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B" + "6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C"
+ + "4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0E" + "F13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D9"
+ + "67E144E5140564251CCACB83E6B486F6B3CA3F7971506026" + "C0B857F689962856DED4010ABD0BE621C3A3960A54E710C3"
+ + "75F26375D7014103A4B54330C198AF126116D2276E11715F" + "693877FAD7EF09CADB094AE91E1A1597";
+ private static readonly string rfc5114_2048_256_g = "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF2054"
+ + "07F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555" + "BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18"
+ + "A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B" + "777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC83"
+ + "1D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55" + "A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14"
+ + "C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915" + "B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6"
+ + "184B523D1DB246C32F63078490F00EF8D647D148D4795451" + "5E2327CFEF98C582664B4C0F6CC41659";
+ private static readonly string rfc5114_2048_256_q = "8CF83642A709A097B447997640129DA299B1A47D1EB3750B"
+ + "A308B0FE64F5FBD3";
+ public static readonly DHParameters rfc5114_2048_256 = FromPGQ(rfc5114_2048_256_p, rfc5114_2048_256_g,
+ rfc5114_2048_256_q);
+
+ /*
+ * RFC 5996
+ */
+ public static readonly DHParameters rfc5996_768 = rfc4306_768;
+ public static readonly DHParameters rfc5996_1024 = rfc4306_1024;
+ }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs
index 7a7e636d2..6c663f54d 100644
--- a/crypto/src/crypto/tls/AbstractTlsContext.cs
+++ b/crypto/src/crypto/tls/AbstractTlsContext.cs
@@ -58,19 +58,31 @@ namespace Org.BouncyCastle.Crypto.Tls
public virtual ProtocolVersion ClientVersion
{
get { return mClientVersion; }
- set { this.mClientVersion = value; }
+ }
+
+ internal virtual void SetClientVersion(ProtocolVersion clientVersion)
+ {
+ this.mClientVersion = clientVersion;
}
public virtual ProtocolVersion ServerVersion
{
get { return mServerVersion; }
- set { this.mServerVersion = value; }
+ }
+
+ internal virtual void SetServerVersion(ProtocolVersion serverVersion)
+ {
+ this.mServerVersion = serverVersion;
}
public virtual TlsSession ResumableSession
{
get { return mSession; }
- set { this.mSession = value; }
+ }
+
+ internal virtual void SetResumableSession(TlsSession session)
+ {
+ this.mSession = session;
}
public virtual object UserObject
diff --git a/crypto/src/crypto/tls/CertificateRequest.cs b/crypto/src/crypto/tls/CertificateRequest.cs
index 100398ffb..f3dcb3bbd 100644
--- a/crypto/src/crypto/tls/CertificateRequest.cs
+++ b/crypto/src/crypto/tls/CertificateRequest.cs
@@ -123,8 +123,7 @@ namespace Org.BouncyCastle.Crypto.Tls
* @return a {@link CertificateRequest} object.
* @throws IOException
*/
- public static CertificateRequest Parse(//TlsContext context,
- Stream input)
+ public static CertificateRequest Parse(TlsContext context, Stream input)
{
int numTypes = TlsUtilities.ReadUint8(input);
byte[] certificateTypes = new byte[numTypes];
@@ -133,13 +132,12 @@ namespace Org.BouncyCastle.Crypto.Tls
certificateTypes[i] = TlsUtilities.ReadUint8(input);
}
- // TODO Add TLS 1.2 support here
IList supportedSignatureAlgorithms = null;
- //if (TlsUtilities.IsTLSv12(context))
- //{
- // // TODO Check whether SignatureAlgorithm.anonymous is allowed here
- // supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(false, input);
- //}
+ if (TlsUtilities.IsTlsV12(context))
+ {
+ // TODO Check whether SignatureAlgorithm.anonymous is allowed here
+ supportedSignatureAlgorithms = TlsUtilities.ParseSupportedSignatureAlgorithms(false, input);
+ }
IList certificateAuthorities = Platform.CreateArrayList();
byte[] certAuthData = TlsUtilities.ReadOpaque16(input);
diff --git a/crypto/src/crypto/tls/DefaultTlsServer.cs b/crypto/src/crypto/tls/DefaultTlsServer.cs
new file mode 100644
index 000000000..017ed0d85
--- /dev/null
+++ b/crypto/src/crypto/tls/DefaultTlsServer.cs
@@ -0,0 +1,548 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public abstract class DefaultTlsServer
+ : AbstractTlsServer
+ {
+ public DefaultTlsServer()
+ : base()
+ {
+ }
+
+ public DefaultTlsServer(TlsCipherFactory cipherFactory)
+ : base(cipherFactory)
+ {
+ }
+
+ protected virtual TlsEncryptionCredentials GetRsaEncryptionCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected virtual TlsSignerCredentials GetRsaSignerCredentials()
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ protected virtual DHParameters GetDHParameters()
+ {
+ return DHStandardGroups.rfc5114_1024_160;
+ }
+
+ protected override int[] GetCipherSuites()
+ {
+ return new int[]
+ {
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
+ };
+ }
+
+ public override TlsCredentials GetCredentials()
+ {
+ switch (mSelectedCipherSuite)
+ {
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return GetRsaEncryptionCredentials();
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ return GetRsaSignerCredentials();
+
+ default:
+ /*
+ * Note: internal error here; selected a key exchange we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public override TlsKeyExchange GetKeyExchange()
+ {
+ switch (mSelectedCipherSuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return createRSAKeyExchange();
+
+ default:
+ /*
+ * Note: internal error here; selected a key exchange we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ public override TlsCipher GetCipher()
+ {
+ switch (mSelectedCipherSuite)
+ {
+ case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
+
+ case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_md5);
+
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+ case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
+
+ case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+ case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+ return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SEED_CBC, MacAlgorithm.hmac_sha1);
+
+ default:
+ /*
+ * Note: internal error here; selected a cipher suite we don't implement!
+ */
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange)
+ {
+ return new TlsDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, GetDHParameters());
+ }
+
+ protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange)
+ {
+ return new TlsDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, GetDHParameters());
+ }
+
+ protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
+ {
+ return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
+ mServerECPointFormats);
+ }
+
+ protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange)
+ {
+ return new TlsECDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
+ mServerECPointFormats);
+ }
+
+ protected virtual TlsKeyExchange createRSAKeyExchange()
+ {
+ return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/RecordStream.cs b/crypto/src/crypto/tls/RecordStream.cs
index b71416c10..db5b158bc 100644
--- a/crypto/src/crypto/tls/RecordStream.cs
+++ b/crypto/src/crypto/tls/RecordStream.cs
@@ -3,171 +3,331 @@ using System.IO;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <summary>An implementation of the TLS 1.0 record layer.</summary>
+ /// <summary>An implementation of the TLS 1.0/1.1/1.2 record layer, allowing downgrade to SSLv3.</summary>
internal class RecordStream
{
- private TlsProtocolHandler handler;
- private Stream inStr;
- private Stream outStr;
- private CombinedHash hash;
- private TlsCompression readCompression = null;
- private TlsCompression writeCompression = null;
- private TlsCipher readCipher = null;
- private TlsCipher writeCipher = null;
- private long readSeqNo = 0, writeSeqNo = 0;
- private MemoryStream buffer = new MemoryStream();
+ private const int DEFAULT_PLAINTEXT_LIMIT = (1 << 14);
- internal RecordStream(
- TlsProtocolHandler handler,
- Stream inStr,
- Stream outStr)
+ private TlsProtocol mHandler;
+ private Stream mInput;
+ private Stream mOutput;
+ private TlsCompression mPendingCompression = null, mReadCompression = null, mWriteCompression = null;
+ private TlsCipher mPendingCipher = null, mReadCipher = null, mWriteCipher = null;
+ private long mReadSeqNo = 0, mWriteSeqNo = 0;
+ private MemoryStream mBuffer = new MemoryStream();
+
+ private TlsHandshakeHash mHandshakeHash = null;
+
+ private ProtocolVersion mReadVersion = null, mWriteVersion = null;
+ private bool mRestrictReadVersion = true;
+
+ private int mPlaintextLimit, mCompressedLimit, mCiphertextLimit;
+
+ internal RecordStream(TlsProtocol handler, Stream input, Stream output)
+ {
+ this.mHandler = handler;
+ this.mInput = input;
+ this.mOutput = output;
+ this.mReadCompression = new TlsNullCompression();
+ this.mWriteCompression = this.mReadCompression;
+ }
+
+ internal virtual void Init(TlsContext context)
+ {
+ this.mReadCipher = new TlsNullCipher(context);
+ this.mWriteCipher = this.mReadCipher;
+ this.mHandshakeHash = new DeferredHash();
+ this.mHandshakeHash.Init(context);
+
+ SetPlaintextLimit(DEFAULT_PLAINTEXT_LIMIT);
+ }
+
+ internal virtual int GetPlaintextLimit()
+ {
+ return mPlaintextLimit;
+ }
+
+ internal virtual void SetPlaintextLimit(int plaintextLimit)
+ {
+ this.mPlaintextLimit = plaintextLimit;
+ this.mCompressedLimit = this.mPlaintextLimit + 1024;
+ this.mCiphertextLimit = this.mCompressedLimit + 1024;
+ }
+
+ internal virtual ProtocolVersion ReadVersion
{
- this.handler = handler;
- this.inStr = inStr;
- this.outStr = outStr;
- this.readCompression = new TlsNullCompression();
- this.writeCompression = this.readCompression;
+ get { return mReadVersion; }
+ set { this.mReadVersion = value; }
}
- internal void Init(TlsContext context)
+ internal virtual void SetWriteVersion(ProtocolVersion writeVersion)
{
- this.readCipher = new TlsNullCipher(context);
- this.writeCipher = this.readCipher;
- this.hash = new CombinedHash();
+ this.mWriteVersion = writeVersion;
}
- internal void ClientCipherSpecDecided(TlsCompression tlsCompression, TlsCipher tlsCipher)
+ /**
+ * RFC 5246 E.1. "Earlier versions of the TLS specification were not fully clear on what the
+ * record layer version number (TLSPlaintext.version) should contain when sending ClientHello
+ * (i.e., before it is known which version of the protocol will be employed). Thus, TLS servers
+ * compliant with this specification MUST accept any value {03,XX} as the record layer version
+ * number for ClientHello."
+ */
+ internal virtual void SetRestrictReadVersion(bool enabled)
{
- this.writeCompression = tlsCompression;
- this.writeCipher = tlsCipher;
- this.writeSeqNo = 0;
+ this.mRestrictReadVersion = enabled;
}
- internal void ServerClientSpecReceived()
+ internal virtual void SetPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher)
{
- this.readCompression = this.writeCompression;
- this.readCipher = this.writeCipher;
- this.readSeqNo = 0;
+ this.mPendingCompression = tlsCompression;
+ this.mPendingCipher = tlsCipher;
}
- public void ReadData()
+ internal virtual void SentWriteCipherSpec()
{
- byte contentType = TlsUtilities.ReadUint8(inStr);
- TlsUtilities.CheckVersion(inStr);
- int size = TlsUtilities.ReadUint16(inStr);
- byte[] buf = DecodeAndVerify(contentType, inStr, size);
- handler.ProcessData(contentType, buf, 0, buf.Length);
+ if (mPendingCompression == null || mPendingCipher == null)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ this.mWriteCompression = this.mPendingCompression;
+ this.mWriteCipher = this.mPendingCipher;
+ this.mWriteSeqNo = 0;
}
- internal byte[] DecodeAndVerify(
- byte contentType,
- Stream inStr,
- int len)
+ internal virtual void ReceivedReadCipherSpec()
{
- byte[] buf = new byte[len];
- TlsUtilities.ReadFully(buf, inStr);
- byte[] decoded = readCipher.DecodeCiphertext(readSeqNo++, contentType, buf, 0, buf.Length);
+ if (mPendingCompression == null || mPendingCipher == null)
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
- Stream cOut = readCompression.Decompress(buffer);
+ this.mReadCompression = this.mPendingCompression;
+ this.mReadCipher = this.mPendingCipher;
+ this.mReadSeqNo = 0;
+ }
- if (cOut == buffer)
+ internal virtual void FinaliseHandshake()
+ {
+ if (mReadCompression != mPendingCompression || mWriteCompression != mPendingCompression
+ || mReadCipher != mPendingCipher || mWriteCipher != mPendingCipher)
{
- return decoded;
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
}
+ this.mPendingCompression = null;
+ this.mPendingCipher = null;
+ }
- cOut.Write(decoded, 0, decoded.Length);
- cOut.Flush();
- byte[] contents = buffer.ToArray();
- buffer.SetLength(0);
- return contents;
+ internal virtual bool ReadRecord()
+ {
+ byte[] recordHeader = TlsUtilities.ReadAllOrNothing(5, mInput);
+ if (recordHeader == null)
+ return false;
+
+ byte type = TlsUtilities.ReadUint8(recordHeader, 0);
+
+ /*
+ * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an
+ * unexpected_message alert.
+ */
+ CheckType(type, AlertDescription.unexpected_message);
+
+ if (!mRestrictReadVersion)
+ {
+ int version = TlsUtilities.ReadVersionRaw(recordHeader, 1);
+ if ((version & 0xffffff00) != 0x0300)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ else
+ {
+ ProtocolVersion version = TlsUtilities.ReadVersion(recordHeader, 1);
+ if (mReadVersion == null)
+ {
+ mReadVersion = version;
+ }
+ else if (!version.Equals(mReadVersion))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+ }
+
+ int length = TlsUtilities.ReadUint16(recordHeader, 3);
+ byte[] plaintext = DecodeAndVerify(type, mInput, length);
+ mHandler.ProcessRecord(type, plaintext, 0, plaintext.Length);
+ return true;
}
- internal void WriteMessage(
- byte type,
- byte[] message,
- int offset,
- int len)
+ internal virtual byte[] DecodeAndVerify(byte type, Stream input, int len)
{
+ CheckLength(len, mCiphertextLimit, AlertDescription.record_overflow);
+
+ byte[] buf = TlsUtilities.ReadFully(len, input);
+ byte[] decoded = mReadCipher.DecodeCiphertext(mReadSeqNo++, type, buf, 0, buf.Length);
+
+ CheckLength(decoded.Length, mCompressedLimit, AlertDescription.record_overflow);
+
+ /*
+ * TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for
+ * ensuring that messages cannot cause internal buffer overflows.
+ */
+ Stream cOut = mReadCompression.Decompress(mBuffer);
+ if (cOut != mBuffer)
+ {
+ cOut.Write(decoded, 0, decoded.Length);
+ cOut.Flush();
+ decoded = GetBufferContents();
+ }
+
+ /*
+ * RFC 5264 6.2.2. If the decompression function encounters a TLSCompressed.fragment that
+ * would decompress to a length in excess of 2^14 bytes, it should report a fatal
+ * decompression failure error.
+ */
+ CheckLength(decoded.Length, mPlaintextLimit, AlertDescription.decompression_failure);
+
+ /*
+ * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (decoded.Length < 1 && type != ContentType.application_data)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ return decoded;
+ }
+
+ internal virtual void WriteRecord(byte type, byte[] plaintext, int plaintextOffset, int plaintextLength)
+ {
+ // Never send anything until a valid ClientHello has been received
+ if (mWriteVersion == null)
+ return;
+
+ /*
+ * RFC 5264 6. Implementations MUST NOT send record types not defined in this document
+ * unless negotiated by some extension.
+ */
+ CheckType(type, AlertDescription.internal_error);
+
+ /*
+ * RFC 5264 6.2.1 The length should not exceed 2^14.
+ */
+ CheckLength(plaintextLength, mPlaintextLimit, AlertDescription.internal_error);
+
+ /*
+ * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+ * or ChangeCipherSpec content types.
+ */
+ if (plaintextLength < 1 && type != ContentType.application_data)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
if (type == ContentType.handshake)
{
- UpdateHandshakeData(message, offset, len);
+ UpdateHandshakeData(plaintext, plaintextOffset, plaintextLength);
}
- Stream cOut = writeCompression.Compress(buffer);
+ Stream cOut = mWriteCompression.Compress(mBuffer);
byte[] ciphertext;
- if (cOut == buffer)
+ if (cOut == mBuffer)
{
- ciphertext = writeCipher.EncodePlaintext(writeSeqNo++, type, message, offset, len);
+ ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, plaintext, plaintextOffset, plaintextLength);
}
else
{
- cOut.Write(message, offset, len);
+ cOut.Write(plaintext, plaintextOffset, plaintextLength);
cOut.Flush();
- ciphertext = writeCipher.EncodePlaintext(writeSeqNo++, type, buffer.GetBuffer(), 0, (int)buffer.Position);
- buffer.SetLength(0);
+ byte[] compressed = GetBufferContents();
+
+ /*
+ * RFC5264 6.2.2. Compression must be lossless and may not increase the content length
+ * by more than 1024 bytes.
+ */
+ CheckLength(compressed.Length, plaintextLength + 1024, AlertDescription.internal_error);
+
+ ciphertext = mWriteCipher.EncodePlaintext(mWriteSeqNo++, type, compressed, 0, compressed.Length);
}
- byte[] writeMessage = new byte[ciphertext.Length + 5];
- TlsUtilities.WriteUint8((byte)type, writeMessage, 0);
- TlsUtilities.WriteVersion(writeMessage, 1);
- TlsUtilities.WriteUint16(ciphertext.Length, writeMessage, 3);
- Array.Copy(ciphertext, 0, writeMessage, 5, ciphertext.Length);
- outStr.Write(writeMessage, 0, writeMessage.Length);
- outStr.Flush();
+ /*
+ * RFC 5264 6.2.3. The length may not exceed 2^14 + 2048.
+ */
+ CheckLength(ciphertext.Length, mCiphertextLimit, AlertDescription.internal_error);
+
+ byte[] record = new byte[ciphertext.Length + 5];
+ TlsUtilities.WriteUint8(type, record, 0);
+ TlsUtilities.WriteVersion(mWriteVersion, record, 1);
+ TlsUtilities.WriteUint16(ciphertext.Length, record, 3);
+ Array.Copy(ciphertext, 0, record, 5, ciphertext.Length);
+ mOutput.Write(record, 0, record.Length);
+ mOutput.Flush();
}
- internal void UpdateHandshakeData(
- byte[] message,
- int offset,
- int len)
+ internal virtual void NotifyHelloComplete()
{
- hash.BlockUpdate(message, offset, len);
+ this.mHandshakeHash = mHandshakeHash.NotifyPrfDetermined();
}
- internal byte[] GetCurrentHash()
+ internal virtual TlsHandshakeHash HandshakeHash
{
- return DoFinal(new CombinedHash(hash));
+ get { return mHandshakeHash; }
}
- internal void Close()
+ internal virtual TlsHandshakeHash PrepareToFinish()
+ {
+ TlsHandshakeHash result = mHandshakeHash;
+ this.mHandshakeHash = mHandshakeHash.StopTracking();
+ return result;
+ }
+
+ internal virtual void UpdateHandshakeData(byte[] message, int offset, int len)
+ {
+ mHandshakeHash.BlockUpdate(message, offset, len);
+ }
+
+ internal virtual void SafeClose()
{
- IOException e = null;
try
{
- inStr.Close();
+ mInput.Close();
}
- catch (IOException ex)
+ catch (IOException)
{
- e = ex;
}
try
{
- // NB: This is harmless if outStr == inStr
- outStr.Close();
+ mOutput.Close();
}
- catch (IOException ex)
+ catch (IOException)
{
- e = ex;
}
+ }
- if (e != null)
- {
- throw e;
- }
+ internal virtual void Flush()
+ {
+ mOutput.Flush();
+ }
+
+ private byte[] GetBufferContents()
+ {
+ byte[] contents = mBuffer.ToArray();
+ mBuffer.SetLength(0);
+ return contents;
}
- internal void Flush()
+ private static void CheckType(byte type, byte alertDescription)
{
- outStr.Flush();
+ switch (type)
+ {
+ case ContentType.application_data:
+ case ContentType.alert:
+ case ContentType.change_cipher_spec:
+ case ContentType.handshake:
+ case ContentType.heartbeat:
+ break;
+ default:
+ throw new TlsFatalAlert(alertDescription);
+ }
}
- private static byte[] DoFinal(CombinedHash ch)
+ private static void CheckLength(int length, int limit, byte alertDescription)
{
- byte[] bs = new byte[ch.GetDigestSize()];
- ch.DoFinal(bs, 0);
- return bs;
+ if (length > limit)
+ throw new TlsFatalAlert(alertDescription);
}
}
}
diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs
new file mode 100644
index 000000000..e48c92d30
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -0,0 +1,861 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsClientProtocol
+ : TlsProtocol
+ {
+ protected TlsClient mTlsClient = null;
+ internal TlsClientContextImpl mTlsClientContext = null;
+
+ protected byte[] mSelectedSessionID = null;
+
+ protected TlsKeyExchange mKeyExchange = null;
+ protected TlsAuthentication mAuthentication = null;
+
+ protected CertificateStatus mCertificateStatus = null;
+ protected CertificateRequest mCertificateRequest = null;
+
+ public TlsClientProtocol(Stream stream, SecureRandom secureRandom)
+ : base(stream, secureRandom)
+ {
+ }
+
+ public TlsClientProtocol(Stream input, Stream output, SecureRandom secureRandom)
+ : base(input, output, secureRandom)
+ {
+ }
+
+ /**
+ * Initiates a TLS handshake in the role of client
+ *
+ * @param tlsClient The {@link TlsClient} to use for the handshake.
+ * @throws IOException If handshake was not successful.
+ */
+ public virtual void Connect(TlsClient tlsClient)
+ {
+ if (tlsClient == null)
+ throw new ArgumentNullException("tlsClient");
+ if (this.mTlsClient != null)
+ throw new InvalidOperationException("'Connect' can only be called once");
+
+ this.mTlsClient = tlsClient;
+
+ this.mSecurityParameters = new SecurityParameters();
+ this.mSecurityParameters.entity = ConnectionEnd.client;
+
+ this.mTlsClientContext = new TlsClientContextImpl(mSecureRandom, mSecurityParameters);
+
+ this.mSecurityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(),
+ mTlsClientContext.NonceRandomGenerator);
+
+ this.mTlsClient.Init(mTlsClientContext);
+ this.mRecordStream.Init(mTlsClientContext);
+
+ TlsSession sessionToResume = tlsClient.GetSessionToResume();
+ if (sessionToResume != null)
+ {
+ SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
+ if (sessionParameters != null)
+ {
+ this.mTlsSession = sessionToResume;
+ this.mSessionParameters = sessionParameters;
+ }
+ }
+
+ SendClientHelloMessage();
+ this.mConnectionState = CS_CLIENT_HELLO;
+
+ CompleteHandshake();
+ }
+
+ protected override void CleanupHandshake()
+ {
+ base.CleanupHandshake();
+
+ this.mSelectedSessionID = null;
+ this.mKeyExchange = null;
+ this.mAuthentication = null;
+ this.mCertificateStatus = null;
+ this.mCertificateRequest = null;
+ }
+
+ protected override TlsContext Context
+ {
+ get { return mTlsClientContext; }
+ }
+
+ internal override AbstractTlsContext ContextAdmin
+ {
+ get { return mTlsClientContext; }
+ }
+
+ protected override TlsPeer Peer
+ {
+ get { return mTlsClient; }
+ }
+
+ protected override void HandleHandshakeMessage(byte type, byte[] data)
+ {
+ MemoryStream buf = new MemoryStream(data, false);
+
+ if (this.mResumedSession)
+ {
+ if (type != HandshakeType.finished || this.mConnectionState != CS_SERVER_HELLO)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ ProcessFinishedMessage(buf);
+ this.mConnectionState = CS_SERVER_FINISHED;
+
+ SendFinishedMessage();
+ this.mConnectionState = CS_CLIENT_FINISHED;
+ this.mConnectionState = CS_END;
+
+ return;
+ }
+
+ switch (type)
+ {
+ case HandshakeType.certificate:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ {
+ if (this.mConnectionState == CS_SERVER_HELLO)
+ {
+ HandleSupplementalData(null);
+ }
+
+ // Parse the Certificate message and Send to cipher suite
+
+ this.mPeerCertificate = Certificate.Parse(buf);
+
+ AssertEmpty(buf);
+
+ // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+ if (this.mPeerCertificate == null || this.mPeerCertificate.IsEmpty)
+ {
+ this.mAllowCertificateStatus = false;
+ }
+
+ this.mKeyExchange.ProcessServerCertificate(this.mPeerCertificate);
+
+ this.mAuthentication = mTlsClient.GetAuthentication();
+ this.mAuthentication.NotifyServerCertificate(this.mPeerCertificate);
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mConnectionState = CS_SERVER_CERTIFICATE;
+ break;
+ }
+ case HandshakeType.certificate_status:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_CERTIFICATE:
+ {
+ if (!this.mAllowCertificateStatus)
+ {
+ /*
+ * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the
+ * server MUST have included an extension of type "status_request" with empty
+ * "extension_data" in the extended server hello..
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mCertificateStatus = CertificateStatus.Parse(buf);
+
+ AssertEmpty(buf);
+
+ // TODO[RFC 3546] Figure out how to provide this to the client/authentication.
+
+ this.mConnectionState = CS_CERTIFICATE_STATUS;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.finished:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_CLIENT_FINISHED:
+ case CS_SERVER_SESSION_TICKET:
+ {
+ if (this.mConnectionState == CS_CLIENT_FINISHED && this.mExpectSessionTicket)
+ {
+ /*
+ * RFC 5077 3.3. This message MUST be sent if the server included a
+ * SessionTicket extension in the ServerHello.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ ProcessFinishedMessage(buf);
+ this.mConnectionState = CS_SERVER_FINISHED;
+ this.mConnectionState = CS_END;
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.server_hello:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_CLIENT_HELLO:
+ {
+ ReceiveServerHelloMessage(buf);
+ this.mConnectionState = CS_SERVER_HELLO;
+
+ if (this.mSecurityParameters.maxFragmentLength >= 0)
+ {
+ int plainTextLimit = 1 << (8 + this.mSecurityParameters.maxFragmentLength);
+ mRecordStream.SetPlaintextLimit(plainTextLimit);
+ }
+
+ this.mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context,
+ this.mSecurityParameters.CipherSuite);
+
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
+ * verify_data_length has a verify_data_length equal to 12. This includes all
+ * existing cipher suites.
+ */
+ this.mSecurityParameters.verifyDataLength = 12;
+
+ this.mRecordStream.NotifyHelloComplete();
+
+ if (this.mResumedSession)
+ {
+ this.mSecurityParameters.masterSecret = Arrays.Clone(this.mSessionParameters.MasterSecret);
+ this.mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
+
+ SendChangeCipherSpecMessage();
+ }
+ else
+ {
+ InvalidateSession();
+
+ if (this.mSelectedSessionID.Length > 0)
+ {
+ this.mTlsSession = new TlsSessionImpl(this.mSelectedSessionID, null);
+ }
+ }
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.supplemental_data:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO:
+ {
+ HandleSupplementalData(ReadSupplementalDataMessage(buf));
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ break;
+ }
+ case HandshakeType.server_hello_done:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ case CS_SERVER_CERTIFICATE:
+ case CS_CERTIFICATE_STATUS:
+ case CS_SERVER_KEY_EXCHANGE:
+ case CS_CERTIFICATE_REQUEST:
+ {
+ if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
+ {
+ HandleSupplementalData(null);
+ }
+
+ if (mConnectionState < CS_SERVER_CERTIFICATE)
+ {
+ // There was no server certificate message; check it's OK
+ this.mKeyExchange.SkipServerCredentials();
+ this.mAuthentication = null;
+ }
+
+ if (mConnectionState < CS_SERVER_KEY_EXCHANGE)
+ {
+ // There was no server key exchange message; check it's OK
+ this.mKeyExchange.SkipServerKeyExchange();
+ }
+
+ AssertEmpty(buf);
+
+ this.mConnectionState = CS_SERVER_HELLO_DONE;
+
+ this.mRecordStream.HandshakeHash.SealHashAlgorithms();
+
+ IList clientSupplementalData = mTlsClient.GetClientSupplementalData();
+ if (clientSupplementalData != null)
+ {
+ SendSupplementalDataMessage(clientSupplementalData);
+ }
+ this.mConnectionState = CS_CLIENT_SUPPLEMENTAL_DATA;
+
+ TlsCredentials clientCreds = null;
+ if (mCertificateRequest == null)
+ {
+ this.mKeyExchange.SkipClientCredentials();
+ }
+ else
+ {
+ clientCreds = this.mAuthentication.GetClientCredentials(mCertificateRequest);
+
+ if (clientCreds == null)
+ {
+ this.mKeyExchange.SkipClientCredentials();
+
+ /*
+ * RFC 5246 If no suitable certificate is available, the client MUST Send a
+ * certificate message containing no certificates.
+ *
+ * NOTE: In previous RFCs, this was SHOULD instead of MUST.
+ */
+ SendCertificateMessage(Certificate.EmptyChain);
+ }
+ else
+ {
+ this.mKeyExchange.ProcessClientCredentials(clientCreds);
+
+ SendCertificateMessage(clientCreds.Certificate);
+ }
+ }
+
+ this.mConnectionState = CS_CLIENT_CERTIFICATE;
+
+ /*
+ * Send the client key exchange message, depending on the key exchange we are using
+ * in our CipherSuite.
+ */
+ SendClientKeyExchangeMessage();
+ this.mConnectionState = CS_CLIENT_KEY_EXCHANGE;
+
+ EstablishMasterSecret(Context, mKeyExchange);
+ mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
+
+ TlsHandshakeHash prepareFinishHash = mRecordStream.PrepareToFinish();
+
+ if (clientCreds != null && clientCreds is TlsSignerCredentials)
+ {
+ TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds;
+
+ /*
+ * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+ byte[] hash;
+
+ if (TlsUtilities.IsTlsV12(Context))
+ {
+ signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm;
+ if (signatureAndHashAlgorithm == null)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
+ }
+ else
+ {
+ signatureAndHashAlgorithm = null;
+ hash = GetCurrentPrfHash(Context, prepareFinishHash, null);
+ }
+
+ byte[] signature = signerCredentials.GenerateCertificateSignature(hash);
+ DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
+ SendCertificateVerifyMessage(certificateVerify);
+
+ this.mConnectionState = CS_CERTIFICATE_VERIFY;
+ }
+
+ SendChangeCipherSpecMessage();
+ SendFinishedMessage();
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ this.mConnectionState = CS_CLIENT_FINISHED;
+ break;
+ }
+ case HandshakeType.server_key_exchange:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_HELLO:
+ case CS_SERVER_SUPPLEMENTAL_DATA:
+ case CS_SERVER_CERTIFICATE:
+ case CS_CERTIFICATE_STATUS:
+ {
+ if (mConnectionState < CS_SERVER_SUPPLEMENTAL_DATA)
+ {
+ HandleSupplementalData(null);
+ }
+
+ if (mConnectionState < CS_SERVER_CERTIFICATE)
+ {
+ // There was no server certificate message; check it's OK
+ this.mKeyExchange.SkipServerCredentials();
+ this.mAuthentication = null;
+ }
+
+ this.mKeyExchange.ProcessServerKeyExchange(buf);
+
+ AssertEmpty(buf);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mConnectionState = CS_SERVER_KEY_EXCHANGE;
+ break;
+ }
+ case HandshakeType.certificate_request:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_SERVER_CERTIFICATE:
+ case CS_CERTIFICATE_STATUS:
+ case CS_SERVER_KEY_EXCHANGE:
+ {
+ if (this.mConnectionState != CS_SERVER_KEY_EXCHANGE)
+ {
+ // There was no server key exchange message; check it's OK
+ this.mKeyExchange.SkipServerKeyExchange();
+ }
+
+ if (this.mAuthentication == null)
+ {
+ /*
+ * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server
+ * to request client identification.
+ */
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ this.mCertificateRequest = CertificateRequest.Parse(Context, buf);
+
+ AssertEmpty(buf);
+
+ this.mKeyExchange.ValidateCertificateRequest(this.mCertificateRequest);
+
+ /*
+ * TODO Give the client a chance to immediately select the CertificateVerify hash
+ * algorithm here to avoid tracking the other hash algorithms unnecessarily?
+ */
+ TlsUtilities.TrackHashAlgorithms(this.mRecordStream.HandshakeHash,
+ this.mCertificateRequest.SupportedSignatureAlgorithms);
+
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mConnectionState = CS_CERTIFICATE_REQUEST;
+ break;
+ }
+ case HandshakeType.session_ticket:
+ {
+ switch (this.mConnectionState)
+ {
+ case CS_CLIENT_FINISHED:
+ {
+ if (!this.mExpectSessionTicket)
+ {
+ /*
+ * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a
+ * SessionTicket extension in the ServerHello.
+ */
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ /*
+ * RFC 5077 3.4. If the client receives a session ticket from the server, then it
+ * discards any Session ID that was sent in the ServerHello.
+ */
+ InvalidateSession();
+
+ ReceiveNewSessionTicketMessage(buf);
+ break;
+ }
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ this.mConnectionState = CS_SERVER_SESSION_TICKET;
+ break;
+ }
+ case HandshakeType.hello_request:
+ {
+ AssertEmpty(buf);
+
+ /*
+ * RFC 2246 7.4.1.1 Hello request This message will be ignored by the client if the
+ * client is currently negotiating a session. This message may be ignored by the client
+ * if it does not wish to renegotiate a session, or the client may, if it wishes,
+ * respond with a no_renegotiation alert.
+ */
+ if (this.mConnectionState == CS_END)
+ {
+ /*
+ * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal
+ * handshake_failure alert.
+ */
+ if (TlsUtilities.IsSsl(Context))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+ string message = "Renegotiation not supported";
+ RaiseWarning(AlertDescription.no_renegotiation, message);
+ }
+ break;
+ }
+ case HandshakeType.client_hello:
+ case HandshakeType.client_key_exchange:
+ case HandshakeType.certificate_verify:
+ case HandshakeType.hello_verify_request:
+ default:
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+ }
+
+ protected virtual void HandleSupplementalData(IList serverSupplementalData)
+ {
+ this.mTlsClient.ProcessServerSupplementalData(serverSupplementalData);
+ this.mConnectionState = CS_SERVER_SUPPLEMENTAL_DATA;
+
+ this.mKeyExchange = mTlsClient.GetKeyExchange();
+ this.mKeyExchange.Init(Context);
+ }
+
+ protected virtual void ReceiveNewSessionTicketMessage(MemoryStream buf)
+ {
+ NewSessionTicket newSessionTicket = NewSessionTicket.Parse(buf);
+
+ TlsProtocol.AssertEmpty(buf);
+
+ mTlsClient.NotifyNewSessionTicket(newSessionTicket);
+ }
+
+ protected virtual void ReceiveServerHelloMessage(MemoryStream buf)
+ {
+ ProtocolVersion server_version = TlsUtilities.ReadVersion(buf);
+ if (server_version.IsDtls)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ // Check that this matches what the server is Sending in the record layer
+ if (!server_version.Equals(this.mRecordStream.ReadVersion))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ ProtocolVersion client_version = Context.ClientVersion;
+ if (!server_version.IsEqualOrEarlierVersionOf(client_version))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mRecordStream.SetWriteVersion(server_version);
+ ContextAdmin.SetServerVersion(server_version);
+ this.mTlsClient.NotifyServerVersion(server_version);
+
+ /*
+ * Read the server random
+ */
+ this.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf);
+
+ this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf);
+ if (this.mSelectedSessionID.Length > 32)
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mTlsClient.NotifySessionID(this.mSelectedSessionID);
+
+ this.mResumedSession = this.mSelectedSessionID.Length > 0 && this.mTlsSession != null
+ && Arrays.AreEqual(this.mSelectedSessionID, this.mTlsSession.SessionID);
+
+ /*
+ * Find out which CipherSuite the server has chosen and check that it was one of the offered
+ * ones, and is a valid selection for the negotiated version.
+ */
+ int selectedCipherSuite = TlsUtilities.ReadUint16(buf);
+ if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite)
+ || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+ || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+ || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, server_version))
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ this.mTlsClient.NotifySelectedCipherSuite(selectedCipherSuite);
+
+ /*
+ * Find out which CompressionMethod the server has chosen and check that it was one of the
+ * offered ones.
+ */
+ byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf);
+ if (!Arrays.Contains(this.mOfferedCompressionMethods, selectedCompressionMethod))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);
+
+ /*
+ * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server
+ * hello message when the client has requested extended functionality via the extended
+ * client hello message specified in Section 2.1. ... Note that the extended server hello
+ * message is only sent in response to an extended client hello message. This prevents the
+ * possibility that the extended server hello message could "break" existing TLS 1.0
+ * clients.
+ */
+ this.mServerExtensions = ReadExtensions(buf);
+
+ /*
+ * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
+ * extended client hello message.
+ *
+ * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
+ * Hello is always allowed.
+ */
+ if (this.mServerExtensions != null)
+ {
+ foreach (int extType in this.mServerExtensions.Keys)
+ {
+ /*
+ * RFC 5746 3.6. Note that Sending a "renegotiation_info" extension in response to a
+ * ClientHello containing only the SCSV is an explicit exception to the prohibition
+ * in RFC 5246, Section 7.4.1.4, on the server Sending unsolicited extensions and is
+ * only allowed because the client is signaling its willingness to receive the
+ * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
+ */
+ if (extType == ExtensionType.renegotiation_info)
+ continue;
+
+ /*
+ * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
+ * extensions appearing in the client hello, and Send a server hello containing no
+ * extensions[.]
+ */
+ if (this.mResumedSession)
+ {
+ // TODO[compat-gnutls] GnuTLS test server Sends server extensions e.g. ec_point_formats
+ // TODO[compat-openssl] OpenSSL test server Sends server extensions e.g. ec_point_formats
+ // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats
+ // throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ /*
+ * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the
+ * same extension type appeared in the corresponding ClientHello. If a client
+ * receives an extension type in ServerHello that it did not request in the
+ * associated ClientHello, it MUST abort the handshake with an unsupported_extension
+ * fatal alert.
+ */
+ if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType))
+ throw new TlsFatalAlert(AlertDescription.unsupported_extension);
+ }
+ }
+
+ /*
+ * RFC 5746 3.4. Client Behavior: Initial Handshake
+ */
+ {
+ /*
+ * When a ServerHello is received, the client MUST check if it includes the
+ * "renegotiation_info" extension:
+ */
+ byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info);
+ if (renegExtData != null)
+ {
+ /*
+ * If the extension is present, set the secure_renegotiation flag to TRUE. The
+ * client MUST then verify that the length of the "renegotiated_connection"
+ * field is zero, and if it is not, MUST abort the handshake (by Sending a fatal
+ * handshake_failure alert).
+ */
+ this.mSecureRenegotiation = true;
+
+ if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+ }
+
+ // TODO[compat-gnutls] GnuTLS test server fails to Send renegotiation_info extension when resuming
+ this.mTlsClient.NotifySecureRenegotiation(this.mSecureRenegotiation);
+
+ IDictionary sessionClientExtensions = mClientExtensions, sessionServerExtensions = mServerExtensions;
+ if (this.mResumedSession)
+ {
+ if (selectedCipherSuite != this.mSessionParameters.CipherSuite
+ || selectedCompressionMethod != this.mSessionParameters.CompressionAlgorithm)
+ {
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ sessionClientExtensions = null;
+ sessionServerExtensions = this.mSessionParameters.ReadServerExtensions();
+ }
+
+ this.mSecurityParameters.cipherSuite = selectedCipherSuite;
+ this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod;
+
+ if (sessionServerExtensions != null)
+ {
+ /*
+ * draft-ietf-tls-encrypt-then-mac-03 3. If a server receives an encrypt-then-MAC
+ * request extension from a client and then selects a stream or AEAD cipher suite, it
+ * MUST NOT Send an encrypt-then-MAC response extension back to the client.
+ */
+ bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions);
+ if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(selectedCipherSuite))
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+ this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC;
+
+ this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions,
+ sessionServerExtensions, AlertDescription.illegal_parameter);
+
+ this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions);
+
+ /*
+ * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
+ * a session resumption handshake.
+ */
+ this.mAllowCertificateStatus = !this.mResumedSession
+ && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request,
+ AlertDescription.illegal_parameter);
+
+ this.mExpectSessionTicket = !this.mResumedSession
+ && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket,
+ AlertDescription.illegal_parameter);
+ }
+
+ if (sessionClientExtensions != null)
+ {
+ this.mTlsClient.ProcessServerExtensions(sessionServerExtensions);
+ }
+ }
+
+ protected virtual void SendCertificateVerifyMessage(DigitallySigned certificateVerify)
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify);
+
+ certificateVerify.Encode(message);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendClientHelloMessage()
+ {
+ this.mRecordStream.SetWriteVersion(this.mTlsClient.ClientHelloRecordLayerVersion);
+
+ ProtocolVersion client_version = this.mTlsClient.ClientVersion;
+ if (client_version.IsDtls)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ ContextAdmin.SetClientVersion(client_version);
+
+ /*
+ * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a
+ * Session ID in the TLS ClientHello.
+ */
+ byte[] session_id = TlsUtilities.EmptyBytes;
+ if (this.mTlsSession != null)
+ {
+ session_id = this.mTlsSession.SessionID;
+ if (session_id == null || session_id.Length > 32)
+ {
+ session_id = TlsUtilities.EmptyBytes;
+ }
+ }
+
+ this.mOfferedCipherSuites = this.mTlsClient.GetCipherSuites();
+
+ this.mOfferedCompressionMethods = this.mTlsClient.GetCompressionMethods();
+
+ if (session_id.Length > 0 && this.mSessionParameters != null)
+ {
+ if (!Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite)
+ || !Arrays.Contains(this.mOfferedCompressionMethods, mSessionParameters.CompressionAlgorithm))
+ {
+ session_id = TlsUtilities.EmptyBytes;
+ }
+ }
+
+ this.mClientExtensions = this.mTlsClient.GetClientExtensions();
+
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
+
+ TlsUtilities.WriteVersion(client_version, message);
+
+ message.Write(this.mSecurityParameters.ClientRandom);
+
+ TlsUtilities.WriteOpaque8(session_id, message);
+
+ // Cipher Suites (and SCSV)
+ {
+ /*
+ * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension,
+ * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
+ * ClientHello. Including both is NOT RECOMMENDED.
+ */
+ byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info);
+ bool noRenegExt = (null == renegExtData);
+
+ bool noSCSV = !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+
+ if (noRenegExt && noSCSV)
+ {
+ // TODO Consider whether to default to a client extension instead
+ // this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mClientExtensions);
+ // this.mClientExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes);
+ this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+ }
+
+ TlsUtilities.WriteUint16ArrayWithUint16Length(mOfferedCipherSuites, message);
+ }
+
+ TlsUtilities.WriteUint8ArrayWithUint8Length(mOfferedCompressionMethods, message);
+
+ if (mClientExtensions != null)
+ {
+ WriteExtensions(message, mClientExtensions);
+ }
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendClientKeyExchangeMessage()
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange);
+
+ this.mKeyExchange.GenerateClientKeyExchange(message);
+
+ message.WriteToRecordStream(this);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs
index 889c6932b..64c3c1593 100644
--- a/crypto/src/crypto/tls/TlsEccUtilities.cs
+++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -83,7 +83,7 @@ namespace Org.BouncyCastle.Crypto.Tls
int[] namedCurves = TlsUtilities.ReadUint16Array(length / 2, buf);
- TlsProtocolHandler.AssertEmpty(buf);
+ TlsProtocol.AssertEmpty(buf);
return namedCurves;
}
@@ -101,7 +101,7 @@ namespace Org.BouncyCastle.Crypto.Tls
byte[] ecPointFormats = TlsUtilities.ReadUint8Array(length, buf);
- TlsProtocolHandler.AssertEmpty(buf);
+ TlsProtocol.AssertEmpty(buf);
if (!Arrays.Contains(ecPointFormats, ECPointFormat.uncompressed))
{
diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs
index 764892d0b..2e2ef9214 100644
--- a/crypto/src/crypto/tls/TlsProtocol.cs
+++ b/crypto/src/crypto/tls/TlsProtocol.cs
@@ -2,24 +2,822 @@
using System.Collections;
using System.IO;
+using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
public abstract class TlsProtocol
{
+ private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
+
+ /*
+ * Our Connection states
+ */
+ protected const short CS_START = 0;
+ protected const short CS_CLIENT_HELLO = 1;
+ protected const short CS_SERVER_HELLO = 2;
+ protected const short CS_SERVER_SUPPLEMENTAL_DATA = 3;
+ protected const short CS_SERVER_CERTIFICATE = 4;
+ protected const short CS_CERTIFICATE_STATUS = 5;
+ protected const short CS_SERVER_KEY_EXCHANGE = 6;
+ protected const short CS_CERTIFICATE_REQUEST = 7;
+ protected const short CS_SERVER_HELLO_DONE = 8;
+ protected const short CS_CLIENT_SUPPLEMENTAL_DATA = 9;
+ protected const short CS_CLIENT_CERTIFICATE = 10;
+ protected const short CS_CLIENT_KEY_EXCHANGE = 11;
+ protected const short CS_CERTIFICATE_VERIFY = 12;
+ protected const short CS_CLIENT_FINISHED = 13;
+ protected const short CS_SERVER_SESSION_TICKET = 14;
+ protected const short CS_SERVER_FINISHED = 15;
+ protected const short CS_END = 16;
+
+ /*
+ * Queues for data from some protocols.
+ */
+ private ByteQueue mApplicationDataQueue = new ByteQueue();
+ private ByteQueue mAlertQueue = new ByteQueue(2);
+ private ByteQueue mHandshakeQueue = new ByteQueue();
+ // private ByteQueue mHeartbeatQueue = new ByteQueue();
+
+ /*
+ * The Record Stream we use
+ */
+ internal RecordStream mRecordStream;
+ protected SecureRandom mSecureRandom;
+
+ private TlsStream mTlsStream = null;
+
+ private volatile bool mClosed = false;
+ private volatile bool mFailedWithError = false;
+ private volatile bool mAppDataReady = false;
+ private volatile bool mSplitApplicationDataRecords = true;
+ private byte[] mExpectedVerifyData = null;
+
+ protected TlsSession mTlsSession = null;
+ protected SessionParameters mSessionParameters = null;
+ protected SecurityParameters mSecurityParameters = null;
+ protected Certificate mPeerCertificate = null;
+
+ protected int[] mOfferedCipherSuites = null;
+ protected byte[] mOfferedCompressionMethods = null;
+ protected IDictionary mClientExtensions = null;
+ protected IDictionary mServerExtensions = null;
+
+ protected short mConnectionState = CS_START;
+ protected bool mResumedSession = false;
+ protected bool mReceivedChangeCipherSpec = false;
+ protected bool mSecureRenegotiation = false;
+ protected bool mAllowCertificateStatus = false;
+ protected bool mExpectSessionTicket = false;
+
+ public TlsProtocol(Stream stream, SecureRandom secureRandom)
+ : this(stream, stream, secureRandom)
+ {
+ }
+
+ public TlsProtocol(Stream input, Stream output, SecureRandom secureRandom)
+ {
+ this.mRecordStream = new RecordStream(this, input, output);
+ this.mSecureRandom = secureRandom;
+ }
+
+ protected abstract TlsContext Context { get; }
+
+ internal abstract AbstractTlsContext ContextAdmin { get; }
+
+ protected abstract TlsPeer Peer { get; }
+
+ protected virtual void HandleChangeCipherSpecMessage()
+ {
+ }
+
+ protected abstract void HandleHandshakeMessage(byte type, byte[] buf);
+
+ protected virtual void HandleWarningMessage(byte description)
+ {
+ }
+
+ protected virtual void CleanupHandshake()
+ {
+ if (this.mExpectedVerifyData != null)
+ {
+ Arrays.Fill(this.mExpectedVerifyData, (byte)0);
+ this.mExpectedVerifyData = null;
+ }
+
+ this.mSecurityParameters.Clear();
+ this.mPeerCertificate = null;
+
+ this.mOfferedCipherSuites = null;
+ this.mOfferedCompressionMethods = null;
+ this.mClientExtensions = null;
+ this.mServerExtensions = null;
+
+ this.mResumedSession = false;
+ this.mReceivedChangeCipherSpec = false;
+ this.mSecureRenegotiation = false;
+ this.mAllowCertificateStatus = false;
+ this.mExpectSessionTicket = false;
+ }
+
+ protected virtual void CompleteHandshake()
+ {
+ try
+ {
+ /*
+ * We will now read data, until we have completed the handshake.
+ */
+ while (this.mConnectionState != CS_END)
+ {
+ if (this.mClosed)
+ {
+ // TODO What kind of exception/alert?
+ }
+
+ SafeReadRecord();
+ }
+
+ this.mRecordStream.FinaliseHandshake();
+
+ this.mSplitApplicationDataRecords = !TlsUtilities.IsTlsV11(Context);
+
+ /*
+ * If this was an initial handshake, we are now ready to send and receive application data.
+ */
+ if (!mAppDataReady)
+ {
+ this.mAppDataReady = true;
+
+ this.mTlsStream = new TlsStream(this);
+ }
+
+ if (this.mTlsSession != null)
+ {
+ if (this.mSessionParameters == null)
+ {
+ this.mSessionParameters = new SessionParameters.Builder()
+ .SetCipherSuite(this.mSecurityParameters.cipherSuite)
+ .SetCompressionAlgorithm(this.mSecurityParameters.compressionAlgorithm)
+ .SetMasterSecret(this.mSecurityParameters.masterSecret)
+ .SetPeerCertificate(this.mPeerCertificate)
+ // TODO Consider filtering extensions that aren't relevant to resumed sessions
+ .SetServerExtensions(this.mServerExtensions)
+ .Build();
+
+ this.mTlsSession = new TlsSessionImpl(this.mTlsSession.SessionID, this.mSessionParameters);
+ }
+
+ ContextAdmin.SetResumableSession(this.mTlsSession);
+ }
+
+ Peer.NotifyHandshakeComplete();
+ }
+ finally
+ {
+ CleanupHandshake();
+ }
+ }
+
+ protected internal void ProcessRecord(byte protocol, byte[] buf, int offset, int len)
+ {
+ /*
+ * Have a look at the protocol type, and add it to the correct queue.
+ */
+ switch (protocol)
+ {
+ case ContentType.alert:
+ {
+ mAlertQueue.AddData(buf, offset, len);
+ ProcessAlert();
+ break;
+ }
+ case ContentType.application_data:
+ {
+ if (!mAppDataReady)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ mApplicationDataQueue.AddData(buf, offset, len);
+ ProcessApplicationData();
+ break;
+ }
+ case ContentType.change_cipher_spec:
+ {
+ ProcessChangeCipherSpec(buf, offset, len);
+ break;
+ }
+ case ContentType.handshake:
+ {
+ mHandshakeQueue.AddData(buf, offset, len);
+ ProcessHandshake();
+ break;
+ }
+ case ContentType.heartbeat:
+ {
+ if (!mAppDataReady)
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+
+ // TODO[RFC 6520]
+ // mHeartbeatQueue.AddData(buf, offset, len);
+ // ProcessHeartbeat();
+ break;
+ }
+ default:
+ /*
+ * Uh, we don't know this protocol.
+ *
+ * RFC2246 defines on page 13, that we should ignore this.
+ */
+ break;
+ }
+ }
+
+ private void ProcessHandshake()
+ {
+ bool read;
+ do
+ {
+ read = false;
+ /*
+ * We need the first 4 bytes, they contain type and length of the message.
+ */
+ if (mHandshakeQueue.Available >= 4)
+ {
+ byte[] beginning = new byte[4];
+ mHandshakeQueue.Read(beginning, 0, 4, 0);
+ byte type = TlsUtilities.ReadUint8(beginning, 0);
+ int len = TlsUtilities.ReadUint24(beginning, 1);
+
+ /*
+ * Check if we have enough bytes in the buffer to read the full message.
+ */
+ if (mHandshakeQueue.Available >= (len + 4))
+ {
+ /*
+ * Read the message.
+ */
+ byte[] buf = mHandshakeQueue.RemoveData(len, 4);
+
+ /*
+ * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
+ * starting at client hello up to, but not including, this finished message.
+ * [..] Note: [Also,] Hello Request messages are omitted from handshake hashes.
+ */
+ switch (type)
+ {
+ case HandshakeType.hello_request:
+ break;
+ case HandshakeType.finished:
+ default:
+ if (type == HandshakeType.finished && this.mExpectedVerifyData == null)
+ {
+ this.mExpectedVerifyData = CreateVerifyData(!Context.IsServer);
+ }
+
+ mRecordStream.UpdateHandshakeData(beginning, 0, 4);
+ mRecordStream.UpdateHandshakeData(buf, 0, len);
+ break;
+ }
+
+ /*
+ * Now, parse the message.
+ */
+ HandleHandshakeMessage(type, buf);
+ read = true;
+ }
+ }
+ }
+ while (read);
+ }
+
+ private void ProcessApplicationData()
+ {
+ /*
+ * There is nothing we need to do here.
+ *
+ * This function could be used for callbacks when application data arrives in the future.
+ */
+ }
+
+ private void ProcessAlert()
+ {
+ while (mAlertQueue.Available >= 2)
+ {
+ /*
+ * An alert is always 2 bytes. Read the alert.
+ */
+ byte[] tmp = mAlertQueue.RemoveData(2, 0);
+ byte level = tmp[0];
+ byte description = tmp[1];
+
+ Peer.NotifyAlertReceived(level, description);
+
+ if (level == AlertLevel.fatal)
+ {
+ /*
+ * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+ * without proper close_notify messages with level equal to warning.
+ */
+ InvalidateSession();
+
+ this.mFailedWithError = true;
+ this.mClosed = true;
+
+ mRecordStream.SafeClose();
+
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+ else
+ {
+
+ /*
+ * RFC 5246 7.2.1. The other party MUST respond with a close_notify alert of its own
+ * and close down the connection immediately, discarding any pending writes.
+ */
+ // TODO Can close_notify be a fatal alert?
+ if (description == AlertDescription.close_notify)
+ {
+ HandleClose(false);
+ }
+
+ /*
+ * If it is just a warning, we continue.
+ */
+ HandleWarningMessage(description);
+ }
+ }
+ }
+
+ /**
+ * This method is called, when a change cipher spec message is received.
+ *
+ * @throws IOException If the message has an invalid content or the handshake is not in the correct
+ * state.
+ */
+ private void ProcessChangeCipherSpec(byte[] buf, int off, int len)
+ {
+ for (int i = 0; i < len; ++i)
+ {
+ byte message = TlsUtilities.ReadUint8(buf, off + i);
+
+ if (message != ChangeCipherSpec.change_cipher_spec)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ if (this.mReceivedChangeCipherSpec
+ || mAlertQueue.Available > 0
+ || mHandshakeQueue.Available > 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.unexpected_message);
+ }
+
+ mRecordStream.ReceivedReadCipherSpec();
+
+ this.mReceivedChangeCipherSpec = true;
+
+ HandleChangeCipherSpecMessage();
+ }
+ }
+
+ protected internal virtual int ApplicationDataAvailable()
+ {
+ return mApplicationDataQueue.Available;
+ }
+
+ /**
+ * Read data from the network. The method will return immediately, if there is still some data
+ * left in the buffer, or block until some application data has been read from the network.
+ *
+ * @param buf The buffer where the data will be copied to.
+ * @param offset The position where the data will be placed in the buffer.
+ * @param len The maximum number of bytes to read.
+ * @return The number of bytes read.
+ * @throws IOException If something goes wrong during reading data.
+ */
+ protected internal virtual int ReadApplicationData(byte[] buf, int offset, int len)
+ {
+ if (len < 1)
+ return 0;
+
+ while (mApplicationDataQueue.Available == 0)
+ {
+ /*
+ * We need to read some data.
+ */
+ if (this.mClosed)
+ {
+ if (this.mFailedWithError)
+ {
+ /*
+ * Something went terribly wrong, we should throw an IOException
+ */
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+
+ /*
+ * Connection has been closed, there is no more data to read.
+ */
+ return 0;
+ }
+
+ SafeReadRecord();
+ }
+
+ len = System.Math.Min(len, mApplicationDataQueue.Available);
+ mApplicationDataQueue.RemoveData(buf, offset, len, 0);
+ return len;
+ }
+
+ protected virtual void SafeReadRecord()
+ {
+ try
+ {
+ if (!mRecordStream.ReadRecord())
+ {
+ // TODO It would be nicer to allow graceful connection close if between records
+ // this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
+ throw new EndOfStreamException();
+ }
+ }
+ catch (TlsFatalAlert e)
+ {
+ if (!mClosed)
+ {
+ this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to read record", e);
+ }
+ throw e;
+ }
+ catch (Exception e)
+ {
+ if (!mClosed)
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
+ }
+ throw e;
+ }
+ }
+
+ protected virtual void SafeWriteRecord(byte type, byte[] buf, int offset, int len)
+ {
+ try
+ {
+ mRecordStream.WriteRecord(type, buf, offset, len);
+ }
+ catch (TlsFatalAlert e)
+ {
+ if (!mClosed)
+ {
+ this.FailWithError(AlertLevel.fatal, e.AlertDescription, "Failed to write record", e);
+ }
+ throw e;
+ }
+ catch (Exception e)
+ {
+ if (!mClosed)
+ {
+ this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Send some application data to the remote system.
+ * <p/>
+ * The method will handle fragmentation internally.
+ *
+ * @param buf The buffer with the data.
+ * @param offset The position in the buffer where the data is placed.
+ * @param len The length of the data.
+ * @throws IOException If something goes wrong during sending.
+ */
+ protected internal virtual void WriteData(byte[] buf, int offset, int len)
+ {
+ if (this.mClosed)
+ {
+ if (this.mFailedWithError)
+ throw new IOException(TLS_ERROR_MESSAGE);
+
+ throw new IOException("Sorry, connection has been closed, you cannot write more data");
+ }
+
+ while (len > 0)
+ {
+ /*
+ * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are
+ * potentially useful as a traffic analysis countermeasure.
+ *
+ * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting.
+ */
+
+ if (this.mSplitApplicationDataRecords)
+ {
+ /*
+ * Protect against known IV attack!
+ *
+ * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
+ */
+ SafeWriteRecord(ContentType.application_data, buf, offset, 1);
+ ++offset;
+ --len;
+ }
+
+ if (len > 0)
+ {
+ // Fragment data according to the current fragment limit.
+ int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit());
+ SafeWriteRecord(ContentType.application_data, buf, offset, toWrite);
+ offset += toWrite;
+ len -= toWrite;
+ }
+ }
+ }
+
+ protected void WriteHandshakeMessage(byte[] buf, int off, int len)
+ {
+ while (len > 0)
+ {
+ // Fragment data according to the current fragment limit.
+ int toWrite = System.Math.Min(len, mRecordStream.GetPlaintextLimit());
+ SafeWriteRecord(ContentType.handshake, buf, off, toWrite);
+ off += toWrite;
+ len -= toWrite;
+ }
+ }
+
+ /// <summary>The secure bidirectional stream for this connection</summary>
+ public virtual Stream Stream
+ {
+ get { return this.mTlsStream; }
+ }
+
/**
- * Make sure the Stream is now empty. Fail otherwise.
- *
- * @param is The Stream to check.
- * @throws IOException If is is not empty.
- */
+ * Terminate this connection with an alert. Can be used for normal closure too.
+ *
+ * @param alertLevel
+ * See {@link AlertLevel} for values.
+ * @param alertDescription
+ * See {@link AlertDescription} for values.
+ * @throws IOException
+ * If alert was fatal.
+ */
+ protected virtual void FailWithError(byte alertLevel, byte alertDescription, string message, Exception cause)
+ {
+ /*
+ * Check if the connection is still open.
+ */
+ if (!mClosed)
+ {
+ /*
+ * Prepare the message
+ */
+ this.mClosed = true;
+
+ if (alertLevel == AlertLevel.fatal)
+ {
+ /*
+ * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+ * without proper close_notify messages with level equal to warning.
+ */
+ // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete.
+ InvalidateSession();
+
+ this.mFailedWithError = true;
+ }
+ RaiseAlert(alertLevel, alertDescription, message, cause);
+ mRecordStream.SafeClose();
+ if (alertLevel != AlertLevel.fatal)
+ {
+ return;
+ }
+ }
+
+ throw new IOException(TLS_ERROR_MESSAGE);
+ }
+
+ protected virtual void InvalidateSession()
+ {
+ if (this.mSessionParameters != null)
+ {
+ this.mSessionParameters.Clear();
+ this.mSessionParameters = null;
+ }
+
+ if (this.mTlsSession != null)
+ {
+ this.mTlsSession.Invalidate();
+ this.mTlsSession = null;
+ }
+ }
+
+ protected virtual void ProcessFinishedMessage(MemoryStream buf)
+ {
+ byte[] verify_data = TlsUtilities.ReadFully(mExpectedVerifyData.Length, buf);
+
+ AssertEmpty(buf);
+
+ /*
+ * Compare both checksums.
+ */
+ if (!Arrays.ConstantTimeAreEqual(mExpectedVerifyData, verify_data))
+ {
+ /*
+ * Wrong checksum in the finished message.
+ */
+ throw new TlsFatalAlert(AlertDescription.decrypt_error);
+ }
+ }
+
+ protected virtual void RaiseAlert(byte alertLevel, byte alertDescription, string message, Exception cause)
+ {
+ Peer.NotifyAlertRaised(alertLevel, alertDescription, message, cause);
+
+ byte[] error = new byte[]{ alertLevel, alertDescription };
+
+ SafeWriteRecord(ContentType.alert, error, 0, 2);
+ }
+
+ protected virtual void RaiseWarning(byte alertDescription, string message)
+ {
+ RaiseAlert(AlertLevel.warning, alertDescription, message, null);
+ }
+
+ protected virtual void SendCertificateMessage(Certificate certificate)
+ {
+ if (certificate == null)
+ {
+ certificate = Certificate.EmptyChain;
+ }
+
+ if (certificate.IsEmpty)
+ {
+ TlsContext context = Context;
+ if (!context.IsServer)
+ {
+ ProtocolVersion serverVersion = Context.ServerVersion;
+ if (serverVersion.IsSsl)
+ {
+ string errorMessage = serverVersion.ToString() + " client didn't provide credentials";
+ RaiseWarning(AlertDescription.no_certificate, errorMessage);
+ return;
+ }
+ }
+ }
+
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate);
+
+ certificate.Encode(message);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendChangeCipherSpecMessage()
+ {
+ byte[] message = new byte[]{ 1 };
+ SafeWriteRecord(ContentType.change_cipher_spec, message, 0, message.Length);
+ mRecordStream.SentWriteCipherSpec();
+ }
+
+ protected virtual void SendFinishedMessage()
+ {
+ byte[] verify_data = CreateVerifyData(Context.IsServer);
+
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.Length);
+
+ message.Write(verify_data, 0, verify_data.Length);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected virtual void SendSupplementalDataMessage(IList supplementalData)
+ {
+ HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data);
+
+ WriteSupplementalData(message, supplementalData);
+
+ message.WriteToRecordStream(this);
+ }
+
+ protected byte[] CreateVerifyData(bool isServer)
+ {
+ TlsContext context = Context;
+ string asciiLabel = isServer ? ExporterLabel.server_finished : ExporterLabel.client_finished;
+ byte[] sslSender = isServer ? TlsUtilities.SSL_SERVER : TlsUtilities.SSL_CLIENT;
+ byte[] hash = GetCurrentPrfHash(context, mRecordStream.HandshakeHash, sslSender);
+ return TlsUtilities.CalculateVerifyData(context, asciiLabel, hash);
+ }
+
+ /**
+ * Closes this connection.
+ *
+ * @throws IOException If something goes wrong during closing.
+ */
+ public virtual void Close()
+ {
+ HandleClose(true);
+ }
+
+ protected virtual void HandleClose(bool user_canceled)
+ {
+ if (!mClosed)
+ {
+ if (user_canceled && !mAppDataReady)
+ {
+ RaiseWarning(AlertDescription.user_canceled, "User canceled handshake");
+ }
+ this.FailWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null);
+ }
+ }
+
+ protected internal virtual void Flush()
+ {
+ mRecordStream.Flush();
+ }
+
+ protected internal virtual bool IsClosed
+ {
+ get { return mClosed; }
+ }
+
+ protected virtual short ProcessMaxFragmentLengthExtension(IDictionary clientExtensions, IDictionary serverExtensions,
+ byte alertDescription)
+ {
+ short maxFragmentLength = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(serverExtensions);
+ if (maxFragmentLength >= 0 && !this.mResumedSession)
+ {
+ if (maxFragmentLength != TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions))
+ throw new TlsFatalAlert(alertDescription);
+ }
+ return maxFragmentLength;
+ }
+
+ /**
+ * Make sure the InputStream 'buf' now empty. Fail otherwise.
+ *
+ * @param buf The InputStream to check.
+ * @throws IOException If 'buf' is not empty.
+ */
protected internal static void AssertEmpty(MemoryStream buf)
{
if (buf.Position < buf.Length)
throw new TlsFatalAlert(AlertDescription.decode_error);
}
+ protected internal static byte[] CreateRandomBlock(bool useGmtUnixTime, IRandomGenerator randomGenerator)
+ {
+ byte[] result = new byte[32];
+ randomGenerator.NextBytes(result);
+
+ if (useGmtUnixTime)
+ {
+ TlsUtilities.WriteGmtUnixTime(result, 0);
+ }
+
+ return result;
+ }
+
+ protected internal static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
+ {
+ return TlsUtilities.EncodeOpaque8(renegotiated_connection);
+ }
+
+ protected internal static void EstablishMasterSecret(TlsContext context, TlsKeyExchange keyExchange)
+ {
+ byte[] pre_master_secret = keyExchange.GeneratePremasterSecret();
+
+ try
+ {
+ context.SecurityParameters.masterSecret = TlsUtilities.CalculateMasterSecret(context, pre_master_secret);
+ }
+ finally
+ {
+ // TODO Is there a way to ensure the data is really overwritten?
+ /*
+ * RFC 2246 8.1. The pre_master_secret should be deleted from memory once the
+ * master_secret has been computed.
+ */
+ if (pre_master_secret != null)
+ {
+ Arrays.Fill(pre_master_secret, (byte)0);
+ }
+ }
+ }
+
+ /**
+ * 'sender' only relevant to SSLv3
+ */
+ protected internal static byte[] GetCurrentPrfHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender)
+ {
+ IDigest d = handshakeHash.ForkPrfHash();
+
+ if (sslSender != null && TlsUtilities.IsSsl(context))
+ {
+ d.BlockUpdate(sslSender, 0, sslSender.Length);
+ }
+
+ return DigestUtilities.DoFinal(d);
+ }
+
protected internal static IDictionary ReadExtensions(MemoryStream input)
{
if (input.Position >= input.Length)
@@ -51,6 +849,27 @@ namespace Org.BouncyCastle.Crypto.Tls
return extensions;
}
+ protected internal static IList ReadSupplementalDataMessage(MemoryStream input)
+ {
+ byte[] supp_data = TlsUtilities.ReadOpaque24(input);
+
+ AssertEmpty(input);
+
+ MemoryStream buf = new MemoryStream(supp_data, false);
+
+ IList supplementalData = Platform.CreateArrayList();
+
+ while (buf.Position < buf.Length)
+ {
+ int supp_data_type = TlsUtilities.ReadUint16(buf);
+ byte[] data = TlsUtilities.ReadOpaque16(buf);
+
+ supplementalData.Add(new SupplementalDataEntry(supp_data_type, data));
+ }
+
+ return supplementalData;
+ }
+
protected internal static void WriteExtensions(Stream output, IDictionary extensions)
{
MemoryStream buf = new MemoryStream();
@@ -68,5 +887,216 @@ namespace Org.BouncyCastle.Crypto.Tls
TlsUtilities.WriteOpaque16(extBytes, output);
}
+
+ protected internal static void WriteSupplementalData(Stream output, IList supplementalData)
+ {
+ MemoryStream buf = new MemoryStream();
+
+ foreach (SupplementalDataEntry entry in supplementalData)
+ {
+ int supp_data_type = entry.DataType;
+ TlsUtilities.CheckUint16(supp_data_type);
+ TlsUtilities.WriteUint16(supp_data_type, buf);
+ TlsUtilities.WriteOpaque16(entry.Data, buf);
+ }
+
+ byte[] supp_data = buf.ToArray();
+
+ TlsUtilities.WriteOpaque24(supp_data, output);
+ }
+
+ protected internal static int GetPrfAlgorithm(TlsContext context, int ciphersuite)
+ {
+ bool isTLSv12 = TlsUtilities.IsTlsV12(context);
+
+ switch (ciphersuite)
+ {
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+ case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+ case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+ {
+ if (isTLSv12)
+ {
+ return PrfAlgorithm.tls_prf_sha256;
+ }
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+ case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+ {
+ if (isTLSv12)
+ {
+ return PrfAlgorithm.tls_prf_sha384;
+ }
+ throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+ }
+
+ case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+ case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+ {
+ if (isTLSv12)
+ {
+ return PrfAlgorithm.tls_prf_sha384;
+ }
+ return PrfAlgorithm.tls_prf_legacy;
+ }
+
+ default:
+ {
+ if (isTLSv12)
+ {
+ return PrfAlgorithm.tls_prf_sha256;
+ }
+ return PrfAlgorithm.tls_prf_legacy;
+ }
+ }
+ }
+
+ internal class HandshakeMessage
+ : MemoryStream
+ {
+ internal HandshakeMessage(byte handshakeType)
+ : this(handshakeType, 60)
+ {
+ }
+
+ internal HandshakeMessage(byte handshakeType, int length)
+ : base(length + 4)
+ {
+ TlsUtilities.WriteUint8(handshakeType, this);
+ // Reserve space for length
+ TlsUtilities.WriteUint24(0, this);
+ }
+
+ internal void Write(byte[] data)
+ {
+ Write(data, 0, data.Length);
+ }
+
+ internal void WriteToRecordStream(TlsProtocol protocol)
+ {
+ // Patch actual length back in
+ long length = Length - 4;
+ TlsUtilities.CheckUint24(length);
+ this.Position = 1;
+ TlsUtilities.WriteUint24((int)length, this);
+ protocol.WriteHandshakeMessage(GetBuffer(), 0, (int)Length);
+ this.Close();
+ }
+ }
}
}
diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs
index 620c73587..6f223467f 100644
--- a/crypto/src/crypto/tls/TlsProtocolHandler.cs
+++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -21,1165 +21,19 @@ using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <remarks>An implementation of all high level protocols in TLS 1.0.</remarks>
+ [Obsolete("Use 'TlsClientProtocol' instead")]
public class TlsProtocolHandler
- : TlsProtocol
+ : TlsClientProtocol
{
- /*
- * Our Connection states
- */
- private const short CS_CLIENT_HELLO_SEND = 1;
- private const short CS_SERVER_HELLO_RECEIVED = 2;
- private const short CS_SERVER_CERTIFICATE_RECEIVED = 3;
- private const short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4;
- private const short CS_CERTIFICATE_REQUEST_RECEIVED = 5;
- private const short CS_SERVER_HELLO_DONE_RECEIVED = 6;
- private const short CS_CLIENT_KEY_EXCHANGE_SEND = 7;
- private const short CS_CERTIFICATE_VERIFY_SEND = 8;
- private const short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 9;
- private const short CS_CLIENT_FINISHED_SEND = 10;
- private const short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 11;
- private const short CS_DONE = 12;
-
- private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
-
- /*
- * Queues for data from some protocols.
- */
-
- private ByteQueue applicationDataQueue = new ByteQueue();
- private ByteQueue alertQueue = new ByteQueue(2);
- private ByteQueue handshakeQueue = new ByteQueue();
-
- /*
- * The Record Stream we use
- */
- private RecordStream recordStream;
- private SecureRandom random;
-
- private TlsStream tlsStream = null;
-
- private bool closed = false;
- private bool failedWithError = false;
- private bool appDataReady = false;
- private IDictionary clientExtensions;
-
- private SecurityParameters securityParameters = null;
-
- private TlsClientContextImpl tlsClientContext = null;
- private TlsClient tlsClient = null;
- private int[] offeredCipherSuites = null;
- private byte[] offeredCompressionMethods = null;
- private TlsKeyExchange keyExchange = null;
- private TlsAuthentication authentication = null;
- private CertificateRequest certificateRequest = null;
-
- private short connection_state = 0;
-
- private static SecureRandom CreateSecureRandom()
- {
- /*
- * We use our threaded seed generator to generate a good random seed. If the user
- * has a better random seed, he should use the constructor with a SecureRandom.
- *
- * Hopefully, 20 bytes in fast mode are good enough.
- */
- byte[] seed = new ThreadedSeedGenerator().GenerateSeed(20, true);
-
- return new SecureRandom(seed);
- }
-
- public TlsProtocolHandler(
- Stream s)
- : this(s, s)
- {
- }
-
- public TlsProtocolHandler(
- Stream s,
- SecureRandom sr)
- : this(s, s, sr)
+ public TlsProtocolHandler(Stream stream, SecureRandom secureRandom)
+ : base(stream, stream, secureRandom)
{
}
/// <remarks>Both streams can be the same object</remarks>
- public TlsProtocolHandler(
- Stream inStr,
- Stream outStr)
- : this(inStr, outStr, CreateSecureRandom())
- {
- }
-
- /// <remarks>Both streams can be the same object</remarks>
- public TlsProtocolHandler(
- Stream inStr,
- Stream outStr,
- SecureRandom sr)
- {
- this.recordStream = new RecordStream(this, inStr, outStr);
- this.random = sr;
- }
-
- internal void ProcessData(
- byte contentType,
- byte[] buf,
- int offset,
- int len)
- {
- /*
- * Have a look at the protocol type, and add it to the correct queue.
- */
- switch (contentType)
- {
- case ContentType.change_cipher_spec:
- ProcessChangeCipherSpec(buf, offset, len);
- break;
- case ContentType.alert:
- alertQueue.AddData(buf, offset, len);
- ProcessAlert();
- break;
- case ContentType.handshake:
- handshakeQueue.AddData(buf, offset, len);
- ProcessHandshake();
- break;
- case ContentType.application_data:
- if (!appDataReady)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
- applicationDataQueue.AddData(buf, offset, len);
- ProcessApplicationData();
- break;
- default:
- /*
- * Uh, we don't know this protocol.
- *
- * RFC2246 defines on page 13, that we should ignore this.
- */
- break;
- }
- }
-
- private void ProcessHandshake()
- {
- bool read;
- do
- {
- read = false;
-
- /*
- * We need the first 4 bytes, they contain type and length of
- * the message.
- */
- if (handshakeQueue.Available >= 4)
- {
- byte[] beginning = new byte[4];
- handshakeQueue.Read(beginning, 0, 4, 0);
- MemoryStream bis = new MemoryStream(beginning, false);
- byte handshakeType = TlsUtilities.ReadUint8(bis);
- int len = TlsUtilities.ReadUint24(bis);
-
- /*
- * Check if we have enough bytes in the buffer to read
- * the full message.
- */
- if (handshakeQueue.Available >= (len + 4))
- {
- /*
- * Read the message.
- */
- byte[] buf = handshakeQueue.RemoveData(len, 4);
-
- /*
- * RFC 2246 7.4.9. The value handshake_messages includes all
- * handshake messages starting at client hello up to, but not
- * including, this finished message. [..] Note: [Also,] Hello Request
- * messages are omitted from handshake hashes.
- */
- switch (handshakeType)
- {
- case HandshakeType.hello_request:
- case HandshakeType.finished:
- break;
- default:
- recordStream.UpdateHandshakeData(beginning, 0, 4);
- recordStream.UpdateHandshakeData(buf, 0, len);
- break;
- }
-
- /*
- * Now, parse the message.
- */
- ProcessHandshakeMessage(handshakeType, buf);
- read = true;
- }
- }
- }
- while (read);
- }
-
- private void ProcessHandshakeMessage(byte handshakeType, byte[] buf)
- {
- MemoryStream inStr = new MemoryStream(buf, false);
-
- /*
- * Check the type.
- */
- switch (handshakeType)
- {
- case HandshakeType.certificate:
- {
- switch (connection_state)
- {
- case CS_SERVER_HELLO_RECEIVED:
- {
- // Parse the Certificate message and send to cipher suite
-
- Certificate serverCertificate = Certificate.Parse(inStr);
-
- AssertEmpty(inStr);
-
- this.keyExchange.ProcessServerCertificate(serverCertificate);
-
- this.authentication = tlsClient.GetAuthentication();
- this.authentication.NotifyServerCertificate(serverCertificate);
-
- break;
- }
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
-
- connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
- break;
- }
- case HandshakeType.finished:
- switch (connection_state)
- {
- case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED:
- /*
- * Read the checksum from the finished message, it has always 12 bytes.
- */
- byte[] serverVerifyData = new byte[12];
- TlsUtilities.ReadFully(serverVerifyData, inStr);
-
- AssertEmpty(inStr);
-
- /*
- * Calculate our own checksum.
- */
- byte[] expectedServerVerifyData = TlsUtilities.PRF(tlsClientContext,
- securityParameters.masterSecret, ExporterLabel.server_finished, recordStream.GetCurrentHash(), 12);
-
- /*
- * Compare both checksums.
- */
- if (!Arrays.ConstantTimeAreEqual(expectedServerVerifyData, serverVerifyData))
- {
- /*
- * Wrong checksum in the finished message.
- */
- this.FailWithError(AlertLevel.fatal, AlertDescription.decrypt_error);
- }
-
- connection_state = CS_DONE;
-
- /*
- * We are now ready to receive application data.
- */
- this.appDataReady = true;
- break;
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
- break;
- case HandshakeType.server_hello:
- switch (connection_state)
- {
- case CS_CLIENT_HELLO_SEND:
- /*
- * Read the server hello message
- */
- TlsUtilities.CheckVersion(inStr);
-
- /*
- * Read the server random
- */
- securityParameters.serverRandom = new byte[32];
- TlsUtilities.ReadFully(securityParameters.serverRandom, inStr);
-
- byte[] sessionID = TlsUtilities.ReadOpaque8(inStr);
- if (sessionID.Length > 32)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
- }
-
- this.tlsClient.NotifySessionID(sessionID);
-
- /*
- * Find out which CipherSuite the server has chosen and check that
- * it was one of the offered ones.
- */
- int selectedCipherSuite = TlsUtilities.ReadUint16(inStr);
- if (!Arrays.Contains(offeredCipherSuites, selectedCipherSuite)
- || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
- }
-
- this.tlsClient.NotifySelectedCipherSuite(selectedCipherSuite);
-
- /*
- * Find out which CompressionMethod the server has chosen and check that
- * it was one of the offered ones.
- */
- byte selectedCompressionMethod = TlsUtilities.ReadUint8(inStr);
- if (!Arrays.Contains(offeredCompressionMethods, selectedCompressionMethod))
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
- }
-
- this.tlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);
-
- /*
- * RFC3546 2.2 The extended server hello message format MAY be
- * sent in place of the server hello message when the client has
- * requested extended functionality via the extended client hello
- * message specified in Section 2.1.
- * ...
- * Note that the extended server hello message is only sent in response
- * to an extended client hello message. This prevents the possibility
- * that the extended server hello message could "break" existing TLS 1.0
- * clients.
- */
-
- /*
- * TODO RFC 3546 2.3
- * If [...] the older session is resumed, then the server MUST ignore
- * extensions appearing in the client hello, and send a server hello
- * containing no extensions.
- */
-
- // Int32 -> byte[]
- IDictionary serverExtensions = ReadExtensions(inStr);
-
- /*
- * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
- * extended client hello message.
- *
- * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
- * Hello is always allowed.
- */
- if (serverExtensions != null)
- {
- foreach (int extType in serverExtensions.Keys)
- {
- /*
- * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a
- * ClientHello containing only the SCSV is an explicit exception to the prohibition
- * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is
- * only allowed because the client is signaling its willingness to receive the
- * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
- */
- if (ExtensionType.renegotiation_info == extType)
- continue;
-
- // TODO Add session resumption support
- //*
- // * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
- // * extensions appearing in the client hello, and send a server hello containing no
- // * extensions[.]
- // */
- //if (this.resumedSession)
- //{
- // // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats
- // // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats
- // // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats
- // // throw new TlsFatalAlert(AlertDescription.illegal_parameter);
- //}
-
- /*
- * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the
- * same extension type appeared in the corresponding ClientHello. If a client
- * receives an extension type in ServerHello that it did not request in the
- * associated ClientHello, it MUST abort the handshake with an unsupported_extension
- * fatal alert.
- */
- if (null == TlsUtilities.GetExtensionData(clientExtensions, extType))
- throw new TlsFatalAlert(AlertDescription.unsupported_extension);
- }
- }
- else
- {
- // TODO Don't need this eventually...
- serverExtensions = Platform.CreateHashtable();
- }
-
- /*
- * RFC 5746 3.4. When a ServerHello is received, the client MUST check if it
- * includes the "renegotiation_info" extension:
- */
- {
- bool secure_negotiation = serverExtensions.Contains(ExtensionType.renegotiation_info);
-
- /*
- * If the extension is present, set the secure_renegotiation flag
- * to TRUE. The client MUST then verify that the length of the
- * "renegotiated_connection" field is zero, and if it is not, MUST
- * abort the handshake (by sending a fatal handshake_failure
- * alert).
- */
- if (secure_negotiation)
- {
- byte[] renegExtValue = (byte[])serverExtensions[ExtensionType.renegotiation_info];
-
- if (!Arrays.ConstantTimeAreEqual(renegExtValue,
- CreateRenegotiationInfo(TlsUtilities.EmptyBytes)))
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- }
- }
-
- tlsClient.NotifySecureRenegotiation(secure_negotiation);
- }
-
- this.securityParameters.cipherSuite = selectedCipherSuite;
- this.securityParameters.compressionAlgorithm = selectedCompressionMethod;
-
- if (clientExtensions != null)
- {
- tlsClient.ProcessServerExtensions(serverExtensions);
- }
-
- this.keyExchange = tlsClient.GetKeyExchange();
-
- connection_state = CS_SERVER_HELLO_RECEIVED;
-
- // TODO Just a place-holder until other TLS 1.2 changes arrive
- this.securityParameters.prfAlgorithm = PrfAlgorithm.tls_prf_legacy;
-
- /*
- * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
- * verify_data_length has a verify_data_length equal to 12. This includes all
- * existing cipher suites.
- */
- this.securityParameters.verifyDataLength = 12;
-
- break;
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
- break;
- case HandshakeType.server_hello_done:
- switch (connection_state)
- {
- case CS_SERVER_HELLO_RECEIVED:
- case CS_SERVER_CERTIFICATE_RECEIVED:
- case CS_SERVER_KEY_EXCHANGE_RECEIVED:
- case CS_CERTIFICATE_REQUEST_RECEIVED:
-
- // NB: Original code used case label fall-through
-
- if (connection_state == CS_SERVER_HELLO_RECEIVED)
- {
- // There was no server certificate message; check it's OK
- this.keyExchange.SkipServerCredentials();
- this.authentication = null;
-
- // There was no server key exchange message; check it's OK
- this.keyExchange.SkipServerKeyExchange();
- }
- else if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
- {
- // There was no server key exchange message; check it's OK
- this.keyExchange.SkipServerKeyExchange();
- }
-
- AssertEmpty(inStr);
-
- connection_state = CS_SERVER_HELLO_DONE_RECEIVED;
-
- TlsCredentials clientCreds = null;
- if (certificateRequest == null)
- {
- this.keyExchange.SkipClientCredentials();
- }
- else
- {
- clientCreds = this.authentication.GetClientCredentials(certificateRequest);
-
- Certificate clientCert;
- if (clientCreds == null)
- {
- this.keyExchange.SkipClientCredentials();
- clientCert = Certificate.EmptyChain;
- }
- else
- {
- this.keyExchange.ProcessClientCredentials(clientCreds);
- clientCert = clientCreds.Certificate;
- }
-
- SendClientCertificate(clientCert);
- }
-
- /*
- * Send the client key exchange message, depending on the key
- * exchange we are using in our CipherSuite.
- */
- SendClientKeyExchange();
-
- connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;
-
- if (clientCreds != null && clientCreds is TlsSignerCredentials)
- {
- TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds;
- byte[] md5andsha1 = recordStream.GetCurrentHash();
- byte[] clientCertificateSignature = signerCreds.GenerateCertificateSignature(
- md5andsha1);
- SendCertificateVerify(clientCertificateSignature);
-
- connection_state = CS_CERTIFICATE_VERIFY_SEND;
- }
-
- /*
- * Now, we send change cipher state
- */
- byte[] cmessage = new byte[1];
- cmessage[0] = 1;
- recordStream.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length);
-
- connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;
-
- /*
- * Calculate the master_secret
- */
- byte[] pms = this.keyExchange.GeneratePremasterSecret();
-
- securityParameters.masterSecret = TlsUtilities.PRF(tlsClientContext, pms, ExporterLabel.master_secret,
- TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom), 48);
-
- // TODO Is there a way to ensure the data is really overwritten?
- /*
- * RFC 2246 8.1. The pre_master_secret should be deleted from
- * memory once the master_secret has been computed.
- */
- Array.Clear(pms, 0, pms.Length);
-
- /*
- * Initialize our cipher suite
- */
- recordStream.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher());
-
- /*
- * Send our finished message.
- */
- byte[] clientVerifyData = TlsUtilities.PRF(tlsClientContext, securityParameters.masterSecret,
- ExporterLabel.client_finished, recordStream.GetCurrentHash(), 12);
-
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos);
- TlsUtilities.WriteOpaque24(clientVerifyData, bos);
- byte[] message = bos.ToArray();
-
- recordStream.WriteMessage(ContentType.handshake, message, 0, message.Length);
-
- this.connection_state = CS_CLIENT_FINISHED_SEND;
- break;
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- break;
- }
- break;
- case HandshakeType.server_key_exchange:
- {
- switch (connection_state)
- {
- case CS_SERVER_HELLO_RECEIVED:
- case CS_SERVER_CERTIFICATE_RECEIVED:
- {
- // NB: Original code used case label fall-through
- if (connection_state == CS_SERVER_HELLO_RECEIVED)
- {
- // There was no server certificate message; check it's OK
- this.keyExchange.SkipServerCredentials();
- this.authentication = null;
- }
-
- this.keyExchange.ProcessServerKeyExchange(inStr);
-
- AssertEmpty(inStr);
- break;
- }
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
-
- this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
- break;
- }
- case HandshakeType.certificate_request:
- switch (connection_state)
- {
- case CS_SERVER_CERTIFICATE_RECEIVED:
- case CS_SERVER_KEY_EXCHANGE_RECEIVED:
- {
- // NB: Original code used case label fall-through
- if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
- {
- // There was no server key exchange message; check it's OK
- this.keyExchange.SkipServerKeyExchange();
- }
-
- if (this.authentication == null)
- {
- /*
- * RFC 2246 7.4.4. It is a fatal handshake_failure alert
- * for an anonymous server to request client identification.
- */
- this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
- }
-
- this.certificateRequest = CertificateRequest.Parse(//getContext(),
- inStr);
-
- AssertEmpty(inStr);
-
- this.keyExchange.ValidateCertificateRequest(this.certificateRequest);
-
- break;
- }
- default:
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
-
- this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED;
- break;
- case HandshakeType.hello_request:
- /*
- * RFC 2246 7.4.1.1 Hello request
- * This message will be ignored by the client if the client is currently
- * negotiating a session. This message may be ignored by the client if it
- * does not wish to renegotiate a session, or the client may, if it wishes,
- * respond with a no_renegotiation alert.
- */
- if (connection_state == CS_DONE)
- {
- // Renegotiation not supported yet
- SendAlert(AlertLevel.warning, AlertDescription.no_renegotiation);
- }
- break;
- case HandshakeType.client_key_exchange:
- case HandshakeType.certificate_verify:
- case HandshakeType.client_hello:
- default:
- // We do not support this!
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- break;
- }
- }
-
- private void ProcessApplicationData()
- {
- /*
- * There is nothing we need to do here.
- *
- * This function could be used for callbacks when application
- * data arrives in the future.
- */
- }
-
- private void ProcessAlert()
- {
- while (alertQueue.Available >= 2)
- {
- /*
- * An alert is always 2 bytes. Read the alert.
- */
- byte[] tmp = alertQueue.RemoveData(2, 0);
- byte level = tmp[0];
- byte description = tmp[1];
- if (level == (byte)AlertLevel.fatal)
- {
- this.failedWithError = true;
- this.closed = true;
- /*
- * Now try to Close the stream, ignore errors.
- */
- try
- {
- recordStream.Close();
- }
- catch (Exception)
- {
- }
- throw new IOException(TLS_ERROR_MESSAGE);
- }
- else
- {
- if (description == (byte)AlertDescription.close_notify)
- {
- HandleClose(false);
- }
-
- /*
- * If it is just a warning, we continue.
- */
- }
- }
- }
-
- /**
- * This method is called, when a change cipher spec message is received.
- *
- * @throws IOException If the message has an invalid content or the
- * handshake is not in the correct state.
- */
- private void ProcessChangeCipherSpec(byte[] buf, int off, int len)
- {
- for (int i = 0; i < len; ++i)
- {
- if (buf[off + i] != 1)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.decode_error);
- }
-
- /*
- * Check if we are in the correct connection state.
- */
- if (this.connection_state != CS_CLIENT_FINISHED_SEND)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
- }
-
- recordStream.ServerClientSpecReceived();
-
- this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED;
- }
- }
-
- private void SendClientCertificate(Certificate clientCert)
- {
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.certificate, bos);
-
- // Reserve space for length
- TlsUtilities.WriteUint24(0, bos);
-
- clientCert.Encode(bos);
- byte[] message = bos.ToArray();
-
- // Patch actual length back in
- TlsUtilities.WriteUint24(message.Length - 4, message, 1);
-
- recordStream.WriteMessage(ContentType.handshake, message, 0, message.Length);
- }
-
- private void SendClientKeyExchange()
- {
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.client_key_exchange, bos);
-
- // Reserve space for length
- TlsUtilities.WriteUint24(0, bos);
-
- this.keyExchange.GenerateClientKeyExchange(bos);
- byte[] message = bos.ToArray();
-
- // Patch actual length back in
- TlsUtilities.WriteUint24(message.Length - 4, message, 1);
-
- recordStream.WriteMessage(ContentType.handshake, message, 0, message.Length);
- }
-
- private void SendCertificateVerify(byte[] data)
- {
- /*
- * Send signature of handshake messages so far to prove we are the owner of
- * the cert See RFC 2246 sections 4.7, 7.4.3 and 7.4.8
- */
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.certificate_verify, bos);
- TlsUtilities.WriteUint24(data.Length + 2, bos);
- TlsUtilities.WriteOpaque16(data, bos);
- byte[] message = bos.ToArray();
-
- recordStream.WriteMessage(ContentType.handshake, message, 0, message.Length);
- }
-
- public virtual void Connect(TlsClient tlsClient)
- {
- if (tlsClient == null)
- throw new ArgumentNullException("tlsClient");
- if (this.tlsClient != null)
- throw new InvalidOperationException("Connect can only be called once");
-
- this.tlsClient = tlsClient;
-
- this.securityParameters = new SecurityParameters();
- this.securityParameters.entity = ConnectionEnd.client;
-
- this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
-
- this.securityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(),
- tlsClientContext.NonceRandomGenerator);
-
- this.tlsClient.Init(tlsClientContext);
- this.recordStream.Init(tlsClientContext);
-
- /*
- * Send Client hello
- *
- * First, send the client_random data.
- */
- MemoryStream outStr = new MemoryStream();
- TlsUtilities.WriteVersion(outStr);
- outStr.Write(securityParameters.clientRandom, 0, 32);
-
- /*
- * Length of Session id
- */
- TlsUtilities.WriteUint8(0, outStr);
-
- this.offeredCipherSuites = this.tlsClient.GetCipherSuites();
-
- // Int32 -> byte[]
- this.clientExtensions = this.tlsClient.GetClientExtensions();
-
- // Cipher Suites (and SCSV)
- {
- /*
- * RFC 5746 3.4.
- * The client MUST include either an empty "renegotiation_info"
- * extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling
- * cipher suite value in the ClientHello. Including both is NOT
- * RECOMMENDED.
- */
- bool noRenegExt = clientExtensions == null
- || !clientExtensions.Contains(ExtensionType.renegotiation_info);
-
- int count = offeredCipherSuites.Length;
- if (noRenegExt)
- {
- // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
- ++count;
- }
-
- TlsUtilities.WriteUint16(2 * count, outStr);
-
- for (int i = 0; i < offeredCipherSuites.Length; ++i)
- {
- TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr);
- }
-
- if (noRenegExt)
- {
- TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr);
- }
- }
-
- /*
- * Compression methods, just the null method.
- */
- this.offeredCompressionMethods = tlsClient.GetCompressionMethods();
-
- {
- TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr);
- for (int i = 0; i < offeredCompressionMethods.Length; ++i)
- {
- TlsUtilities.WriteUint8(offeredCompressionMethods[i], outStr);
- }
- }
-
- if (clientExtensions != null)
- {
- WriteExtensions(outStr, clientExtensions);
- }
-
- MemoryStream bos = new MemoryStream();
- TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos);
- TlsUtilities.WriteUint24((int)outStr.Length, bos);
- byte[] outBytes = outStr.ToArray();
- bos.Write(outBytes, 0, outBytes.Length);
- byte[] message = bos.ToArray();
- SafeWriteMessage(ContentType.handshake, message, 0, message.Length);
- connection_state = CS_CLIENT_HELLO_SEND;
-
- /*
- * We will now read data, until we have completed the handshake.
- */
- while (connection_state != CS_DONE)
- {
- SafeReadData();
- }
-
- this.tlsStream = new TlsStream(this);
- }
-
- /**
- * Read data from the network. The method will return immediately, if there is
- * still some data left in the buffer, or block until some application
- * data has been read from the network.
- *
- * @param buf The buffer where the data will be copied to.
- * @param offset The position where the data will be placed in the buffer.
- * @param len The maximum number of bytes to read.
- * @return The number of bytes read.
- * @throws IOException If something goes wrong during reading data.
- */
- internal int ReadApplicationData(byte[] buf, int offset, int len)
- {
- while (applicationDataQueue.Available == 0)
- {
- if (this.closed)
- {
- /*
- * We need to read some data.
- */
- if (this.failedWithError)
- {
- /*
- * Something went terribly wrong, we should throw an IOException
- */
- throw new IOException(TLS_ERROR_MESSAGE);
- }
-
- /*
- * Connection has been closed, there is no more data to read.
- */
- return 0;
- }
-
- SafeReadData();
- }
- len = System.Math.Min(len, applicationDataQueue.Available);
- applicationDataQueue.RemoveData(buf, offset, len, 0);
- return len;
- }
-
- private void SafeReadData()
- {
- try
- {
- recordStream.ReadData();
- }
- catch (TlsFatalAlert e)
- {
- if (!this.closed)
- {
- this.FailWithError(AlertLevel.fatal, e.AlertDescription);
- }
- throw e;
- }
- catch (IOException e)
- {
- if (!this.closed)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error);
- }
- throw e;
- }
- catch (Exception e)
- {
- if (!this.closed)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error);
- }
- throw e;
- }
- }
-
- private void SafeWriteMessage(byte type, byte[] buf, int offset, int len)
- {
- try
- {
- recordStream.WriteMessage(type, buf, offset, len);
- }
- catch (TlsFatalAlert e)
- {
- if (!this.closed)
- {
- this.FailWithError(AlertLevel.fatal, e.AlertDescription);
- }
- throw e;
- }
- catch (IOException e)
- {
- if (!closed)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error);
- }
- throw e;
- }
- catch (Exception e)
- {
- if (!closed)
- {
- this.FailWithError(AlertLevel.fatal, AlertDescription.internal_error);
- }
- throw e;
- }
- }
-
- /**
- * Send some application data to the remote system.
- * <p/>
- * The method will handle fragmentation internally.
- *
- * @param buf The buffer with the data.
- * @param offset The position in the buffer where the data is placed.
- * @param len The length of the data.
- * @throws IOException If something goes wrong during sending.
- */
- internal void WriteData(byte[] buf, int offset, int len)
- {
- if (this.closed)
- {
- if (this.failedWithError)
- throw new IOException(TLS_ERROR_MESSAGE);
-
- throw new IOException("Sorry, connection has been closed, you cannot write more data");
- }
-
- while (len > 0)
- {
- /*
- * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are
- * potentially useful as a traffic analysis countermeasure.
- *
- * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting.
- */
-
- //if (this.splitApplicationDataRecords)
- {
- /*
- * Protect against known IV attack!
- *
- * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
- */
- SafeWriteMessage(ContentType.application_data, buf, offset, 1);
- ++offset;
- --len;
- }
-
- if (len > 0)
- {
- // Fragment data according to the current fragment limit.
- //int toWrite = System.Math.Min(len, recordStream.GetPlaintextLimit());
- int toWrite = System.Math.Min(len, 1 << 14);
- SafeWriteMessage(ContentType.application_data, buf, offset, toWrite);
- offset += toWrite;
- len -= toWrite;
- }
- }
- }
-
- /// <summary>The secure bidirectional stream for this connection</summary>
- public virtual Stream Stream
- {
- get { return this.tlsStream; }
- }
-
- /**
- * Terminate this connection with an alert.
- * <p/>
- * Can be used for normal closure too.
- *
- * @param alertLevel The level of the alert, an be AlertLevel.fatal or AL_warning.
- * @param alertDescription The exact alert message.
- * @throws IOException If alert was fatal.
- */
- private void FailWithError(byte alertLevel, byte alertDescription)
- {
- /*
- * Check if the connection is still open.
- */
- if (!closed)
- {
- /*
- * Prepare the message
- */
- this.closed = true;
-
- if (alertLevel == AlertLevel.fatal)
- {
- /*
- * This is a fatal message.
- */
- this.failedWithError = true;
- }
- SendAlert(alertLevel, alertDescription);
- recordStream.Close();
- if (alertLevel == AlertLevel.fatal)
- {
- throw new IOException(TLS_ERROR_MESSAGE);
- }
- }
- else
- {
- throw new IOException(TLS_ERROR_MESSAGE);
- }
- }
-
- internal void SendAlert(byte alertLevel, byte alertDescription)
- {
- byte[] error = new byte[] { alertLevel, alertDescription };
-
- recordStream.WriteMessage(ContentType.alert, error, 0, 2);
- }
-
- /// <summary>Closes this connection</summary>
- /// <exception cref="IOException">If something goes wrong during closing.</exception>
- public virtual void Close()
- {
- HandleClose(true);
- }
-
- protected virtual void HandleClose(bool user_canceled)
- {
- if (!closed)
- {
- if (user_canceled && !appDataReady)
- {
- SendAlert(AlertLevel.warning, AlertDescription.user_canceled);
- }
- this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
- }
- }
-
- protected static byte[] CreateRandomBlock(bool useGMTUnixTime, IRandomGenerator randomGenerator)
- {
- byte[] result = new byte[32];
- randomGenerator.NextBytes(result);
-
- if (useGMTUnixTime)
- {
- TlsUtilities.WriteGmtUnixTime(result, 0);
- }
-
- return result;
- }
-
- internal void Flush()
- {
- recordStream.Flush();
- }
-
- internal bool IsClosed
- {
- get { return closed; }
- }
-
- private static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
+ public TlsProtocolHandler(Stream input, Stream output, SecureRandom secureRandom)
+ : base(input, output, secureRandom)
{
- MemoryStream buf = new MemoryStream();
- TlsUtilities.WriteOpaque8(renegotiated_connection, buf);
- return buf.ToArray();
}
}
}
diff --git a/crypto/src/crypto/tls/TlsStream.cs b/crypto/src/crypto/tls/TlsStream.cs
index 84b901d6e..7ff7184e3 100644
--- a/crypto/src/crypto/tls/TlsStream.cs
+++ b/crypto/src/crypto/tls/TlsStream.cs
@@ -6,10 +6,9 @@ namespace Org.BouncyCastle.Crypto.Tls
internal class TlsStream
: Stream
{
- private readonly TlsProtocolHandler handler;
+ private readonly TlsProtocol handler;
- internal TlsStream(
- TlsProtocolHandler handler)
+ internal TlsStream(TlsProtocol handler)
{
this.handler = handler;
}
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index f530b01a6..f1ea0996d 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -492,26 +492,6 @@ namespace Org.BouncyCastle.Crypto.Tls
return uints;
}
- [Obsolete]
- public static void CheckVersion(byte[] ReadVersion)
- {
- if ((ReadVersion[0] != 3) || (ReadVersion[1] != 1))
- {
- throw new TlsFatalAlert(AlertDescription.protocol_version);
- }
- }
-
- [Obsolete]
- public static void CheckVersion(Stream input)
- {
- int i1 = input.ReadByte();
- int i2 = input.ReadByte();
- if ((i1 != 3) || (i2 != 1))
- {
- throw new TlsFatalAlert(AlertDescription.protocol_version);
- }
- }
-
public static ProtocolVersion ReadVersion(byte[] buf, int offset)
{
return ProtocolVersion.Get(buf[offset], buf[offset + 1]);
@@ -577,20 +557,6 @@ namespace Org.BouncyCastle.Crypto.Tls
buf[offset + 3] = (byte)t;
}
- [Obsolete]
- public static void WriteVersion(Stream output)
- {
- output.WriteByte(3);
- output.WriteByte(1);
- }
-
- [Obsolete]
- public static void WriteVersion(byte[] buf, int offset)
- {
- buf[offset] = 3;
- buf[offset + 1] = 1;
- }
-
public static void WriteVersion(ProtocolVersion version, Stream output)
{
output.WriteByte((byte)version.MajorVersion);
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index 87bc1ba65..27fd18d6d 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -544,6 +544,19 @@ namespace Org.BouncyCastle.Utilities
return rv;
}
+ public static int[] Concatenate(int[] a, int[] b)
+ {
+ if (a == null)
+ return Clone(b);
+ if (b == null)
+ return Clone(a);
+
+ int[] rv = new int[a.Length + b.Length];
+ Array.Copy(a, 0, rv, 0, a.Length);
+ Array.Copy(b, 0, rv, a.Length, b.Length);
+ return rv;
+ }
+
public static byte[] Prepend(byte[] a, byte b)
{
if (a == null)
diff --git a/crypto/test/src/crypto/tls/test/MockTlsClient.cs b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
new file mode 100644
index 000000000..c22e98367
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Tls.Test
+{
+ internal class MockTlsClient
+ : DefaultTlsClient
+ {
+ internal TlsSession mSession;
+
+ internal MockTlsClient(TlsSession session)
+ {
+ this.mSession = session;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return this.mSession;
+ }
+
+ public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client raised alert (AlertLevel." + alertLevel + ", AlertDescription." + alertDescription
+ + ")");
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause.StackTrace);
+ }
+ }
+
+ public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client received alert (AlertLevel." + alertLevel + ", AlertDescription."
+ + alertDescription + ")");
+ }
+
+ public override int[] GetCipherSuites()
+ {
+ return Arrays.Concatenate(base.GetCipherSuites(),
+ new int[]
+ {
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+ CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+ CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+ CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+ });
+ }
+
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+ TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
+ return clientExtensions;
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("TLS client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(mContext);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ TlsSession newSession = mContext.ResumableSession;
+ if (newSession != null)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = Hex.ToHexString(newSessionID);
+
+ if (this.mSession != null && Arrays.AreEqual(this.mSession.SessionID, newSessionID))
+ {
+ Console.WriteLine("Resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Established session: " + hex);
+ }
+
+ this.mSession = newSession;
+ }
+ }
+
+ internal class MyTlsAuthentication
+ : TlsAuthentication
+ {
+ private readonly TlsContext mContext;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.mContext = context;
+ }
+
+ public virtual void NotifyServerCertificate(Certificate serverCertificate)
+ {
+ X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+ Console.WriteLine("TLS client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = chain[i];
+ // TODO Create Fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" Fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+ }
+
+ public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ byte[] certificateTypes = certificateRequest.CertificateTypes;
+ if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+ return null;
+
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ IList sigAlgs = certificateRequest.SupportedSignatureAlgorithms;
+ if (sigAlgs != null)
+ {
+ foreach (SignatureAndHashAlgorithm sigAlg in sigAlgs)
+ {
+ if (sigAlg.Signature == SignatureAlgorithm.rsa)
+ {
+ signatureAndHashAlgorithm = sigAlg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+
+ return TlsTestUtilities.LoadSignerCredentials(mContext, new string[] { "x509-client.pem", "x509-ca.pem" },
+ "x509-client-key.pem", signatureAndHashAlgorithm);
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsClientTest.cs b/crypto/test/src/crypto/tls/test/TlsClientTest.cs
new file mode 100644
index 000000000..e68f1934c
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsClientTest.cs
@@ -0,0 +1,66 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Test
+{
+ /**
+ * A simple test designed to conduct a TLS handshake with an external TLS server.
+ * <p/>
+ * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+ * this package (under 'src/test/resources') for help configuring an external TLS server.
+ */
+ public class TlsClientTest
+ {
+ private static readonly SecureRandom secureRandom = new SecureRandom();
+
+ public static void Main(string[] args)
+ {
+ string hostname = "localhost";
+ int port = 5556;
+
+ long time1 = DateTime.UtcNow.Ticks;
+
+ MockTlsClient client = new MockTlsClient(null);
+ TlsClientProtocol protocol = OpenTlsConnection(hostname, port, client);
+ protocol.Close();
+
+ long time2 = DateTime.UtcNow.Ticks;
+ Console.WriteLine("Elapsed 1: " + (time2 - time1)/TimeSpan.TicksPerMillisecond + "ms");
+
+ client = new MockTlsClient(client.GetSessionToResume());
+ protocol = OpenTlsConnection(hostname, port, client);
+
+ long time3 = DateTime.UtcNow.Ticks;
+ Console.WriteLine("Elapsed 2: " + (time3 - time2)/TimeSpan.TicksPerMillisecond + "ms");
+
+ byte[] req = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\n\r\n");
+
+ Stream tlsStream = protocol.Stream;
+ tlsStream.Write(req, 0, req.Length);
+ tlsStream.Flush();
+
+ StreamReader reader = new StreamReader(tlsStream);
+
+ String line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ Console.WriteLine(">>> " + line);
+ }
+
+ protocol.Close();
+ }
+
+ internal static TlsClientProtocol OpenTlsConnection(string hostname, int port, TlsClient client)
+ {
+ TcpClient tcp = new TcpClient(hostname, port);
+
+ TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream(), secureRandom);
+ protocol.Connect(client);
+ return protocol;
+ }
+ }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs b/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
new file mode 100644
index 000000000..85a47d5e9
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
@@ -0,0 +1,137 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO.Pem;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tls.Test
+{
+ public abstract class TlsTestUtilities
+ {
+ internal static readonly byte[] RsaCertData = Base64
+ .Decode("MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2"
+ + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq"
+ + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA2MDIwNVoXDTEzMDIyNT"
+ + "A2MDM0NVowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw"
+ + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG"
+ + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy"
+ + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCBSAwEgYDVR"
+ + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAHU55Ncz"
+ + "eglREcTg54YLUlGWu2WOYWhit/iM1eeq8Kivro7q98eW52jTuMI3CI5ulqd0hYzshQKQaZ5GDzErMyM=");
+
+ internal static readonly byte[] DudRsaCertData = Base64
+ .Decode("MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2"
+ + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq"
+ + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA1NDcyOFoXDTEzMDIyNT"
+ + "A1NDkwOFowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw"
+ + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG"
+ + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy"
+ + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCAAEwEgYDVR"
+ + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAJg55PBS"
+ + "weg6obRUKF4FF6fCrWFi6oCYSQ99LWcAeupc5BofW5MstFMhCOaEucuGVqunwT5G7/DweazzCIrSzB0=");
+
+ internal static string Fingerprint(X509CertificateStructure c)
+ {
+ byte[] der = c.GetEncoded();
+ byte[] sha1 = Sha256DigestOf(der);
+ byte[] hexBytes = Hex.Encode(sha1);
+ string hex = Platform.ToUpperInvariant(Encoding.ASCII.GetString(hexBytes));
+
+ StringBuilder fp = new StringBuilder();
+ int i = 0;
+ fp.Append(hex.Substring(i, 2));
+ while ((i += 2) < hex.Length)
+ {
+ fp.Append(':');
+ fp.Append(hex.Substring(i, 2));
+ }
+ return fp.ToString();
+ }
+
+ internal static byte[] Sha256DigestOf(byte[] input)
+ {
+ return DigestUtilities.CalculateDigest("SHA256", input);
+ }
+
+ internal static TlsAgreementCredentials LoadAgreementCredentials(TlsContext context,
+ string[] certResources, string keyResource)
+ {
+ Certificate certificate = LoadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsAgreementCredentials(certificate, privateKey);
+ }
+
+ internal static TlsEncryptionCredentials LoadEncryptionCredentials(TlsContext context,
+ string[] certResources, string keyResource)
+ {
+ Certificate certificate = LoadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsEncryptionCredentials(context, certificate, privateKey);
+ }
+
+ internal static TlsSignerCredentials LoadSignerCredentials(TlsContext context, string[] certResources,
+ string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ Certificate certificate = LoadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsSignerCredentials(context, certificate, privateKey, signatureAndHashAlgorithm);
+ }
+
+ internal static Certificate LoadCertificateChain(string[] resources)
+ {
+ X509CertificateStructure[] chain = new X509CertificateStructure[resources.Length];
+ for (int i = 0; i < resources.Length; ++i)
+ {
+ chain[i] = LoadCertificateResource(resources[i]);
+ }
+ return new Certificate(chain);
+ }
+
+ internal static X509CertificateStructure LoadCertificateResource(string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.EndsWith("CERTIFICATE"))
+ {
+ return X509CertificateStructure.GetInstance(pem.Content);
+ }
+ throw new ArgumentException("doesn't specify a valid certificate", "resource");
+ }
+
+ internal static AsymmetricKeyParameter LoadPrivateKeyResource(string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.EndsWith("RSA PRIVATE KEY"))
+ {
+ RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(pem.Content);
+ return new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent,
+ rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1,
+ rsa.Exponent2, rsa.Coefficient);
+ }
+ if (pem.Type.EndsWith("PRIVATE KEY"))
+ {
+ return PrivateKeyFactory.CreateKey(pem.Content);
+ }
+ throw new ArgumentException("doesn't specify a valid private key", "resource");
+ }
+
+ internal static PemObject LoadPemResource(string resource)
+ {
+ Stream s = SimpleTest.GetTestDataAsStream("tls." + resource);
+ PemReader p = new PemReader(new StreamReader(s));
+ PemObject o = p.ReadPemObject();
+ p.Reader.Close();
+ return o;
+ }
+ }
+}
|