1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 using System.Security.Cryptography.X509Certificates;
28 using System.Security.Cryptography;
29 using Mono.Security.Cryptography;
31 namespace Mono.Security.Protocol.Tls.Handshake.Client
33 internal class TlsClientCertificateVerify : HandshakeMessage
37 public TlsClientCertificateVerify(Context context)
38 : base(context, HandshakeType.CertificateVerify)
46 public override void Update()
54 #region Protected Methods
56 protected override void ProcessAsSsl3()
58 AsymmetricAlgorithm privKey = null;
59 ClientContext context = (ClientContext)this.Context;
61 privKey = context.SslStream.RaisePrivateKeySelection(
62 context.ClientSettings.ClientCertificate,
63 context.ClientSettings.TargetHost);
67 throw new TlsException(AlertDescription.UserCancelled, "Client certificate Private Key unavailable.");
71 SslHandshakeHash hash = new SslHandshakeHash(context.MasterSecret);
72 hash.TransformFinalBlock(
73 context.HandshakeMessages.ToArray(),
75 (int)context.HandshakeMessages.Length);
77 // CreateSignature uses ((RSA)privKey).DecryptValue which is not implemented
78 // in RSACryptoServiceProvider. Other implementations likely implement DecryptValue
79 // so we will try the CreateSignature method.
80 byte[] signature = null;
82 if (!(privKey is RSACryptoServiceProvider))
87 signature = hash.CreateSignature((RSA)privKey);
89 catch (NotImplementedException)
92 // If DecryptValue is not implemented, then try to export the private
93 // key and let the RSAManaged class do the DecryptValue
94 if (signature == null)
96 // RSAManaged of the selected ClientCertificate
97 // (at this moment the first one)
98 RSA rsa = this.getClientCertRSA((RSA)privKey);
101 signature = hash.CreateSignature(rsa);
103 this.Write((short)signature.Length);
104 this.Write(signature, 0, signature.Length);
108 protected override void ProcessAsTls1()
110 AsymmetricAlgorithm privKey = null;
111 ClientContext context = (ClientContext)this.Context;
113 privKey = context.SslStream.RaisePrivateKeySelection(
114 context.ClientSettings.ClientCertificate,
115 context.ClientSettings.TargetHost);
119 throw new TlsException(AlertDescription.UserCancelled, "Client certificate Private Key unavailable.");
123 // Compute handshake messages hash
124 MD5SHA1 hash = new MD5SHA1();
126 context.HandshakeMessages.ToArray(),
128 (int)context.HandshakeMessages.Length);
130 // CreateSignature uses ((RSA)privKey).DecryptValue which is not implemented
131 // in RSACryptoServiceProvider. Other implementations likely implement DecryptValue
132 // so we will try the CreateSignature method.
133 byte[] signature = null;
135 if (!(privKey is RSACryptoServiceProvider))
140 signature = hash.CreateSignature((RSA)privKey);
142 catch (NotImplementedException)
145 // If DecryptValue is not implemented, then try to export the private
146 // key and let the RSAManaged class do the DecryptValue
147 if (signature == null)
149 // RSAManaged of the selected ClientCertificate
150 // (at this moment the first one)
151 RSA rsa = this.getClientCertRSA((RSA)privKey);
154 signature = hash.CreateSignature(rsa);
156 this.Write((short)signature.Length);
157 this.Write(signature, 0, signature.Length);
163 #region Private methods
165 private RSA getClientCertRSA(RSA privKey)
167 RSAParameters rsaParams = new RSAParameters();
168 RSAParameters privateParams = privKey.ExportParameters(true);
170 // for RSA m_publickey contains 2 ASN.1 integers
171 // the modulus and the public exponent
172 ASN1 pubkey = new ASN1 (this.Context.ClientSettings.Certificates[0].GetPublicKey());
173 ASN1 modulus = pubkey [0];
174 if ((modulus == null) || (modulus.Tag != 0x02))
178 ASN1 exponent = pubkey [1];
179 if (exponent.Tag != 0x02)
184 rsaParams.Modulus = this.getUnsignedBigInteger(modulus.Value);
185 rsaParams.Exponent = exponent.Value;
187 // Set private key parameters
188 rsaParams.D = privateParams.D;
189 rsaParams.DP = privateParams.DP;
190 rsaParams.DQ = privateParams.DQ;
191 rsaParams.InverseQ = privateParams.InverseQ;
192 rsaParams.P = privateParams.P;
193 rsaParams.Q = privateParams.Q;
195 // BUG: MS BCL 1.0 can't import a key which
196 // isn't the same size as the one present in
198 int keySize = (rsaParams.Modulus.Length << 3);
199 RSAManaged rsa = new RSAManaged(keySize);
200 rsa.ImportParameters (rsaParams);
205 private byte[] getUnsignedBigInteger(byte[] integer)
207 if (integer [0] == 0x00)
209 // this first byte is added so we're sure it's an unsigned integer
210 // however we can't feed it into RSAParameters or DSAParameters
211 int length = integer.Length - 1;
212 byte[] uinteger = new byte [length];
213 Buffer.BlockCopy (integer, 1, uinteger, 0, length);