From ae809eddc05da059ff401787e17fb3e96cd4fe64 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 6 Nov 2018 16:55:58 +0700 Subject: [PATCH 01/22] TLS: Update to RFC 7627 from draft-ietf-tls-session-hash-04 --- crypto/src/crypto/tls/AbstractTlsContext.cs | 20 ++--- crypto/src/crypto/tls/AbstractTlsPeer.cs | 5 ++ crypto/src/crypto/tls/DtlsClientProtocol.cs | 79 ++++++++++--------- crypto/src/crypto/tls/DtlsServerProtocol.cs | 40 +++++++--- crypto/src/crypto/tls/ExporterLabel.cs | 2 +- crypto/src/crypto/tls/SecurityParameters.cs | 7 +- crypto/src/crypto/tls/SessionParameters.cs | 21 ++++- crypto/src/crypto/tls/TlsClientProtocol.cs | 45 ++++++----- crypto/src/crypto/tls/TlsPeer.cs | 15 ++++ crypto/src/crypto/tls/TlsProtocol.cs | 1 + crypto/src/crypto/tls/TlsServerProtocol.cs | 26 +++--- crypto/src/crypto/tls/TlsSessionImpl.cs | 21 +++-- crypto/src/crypto/tls/TlsUtilities.cs | 4 +- .../src/crypto/tls/test/MockDtlsClient.cs | 1 - .../test/src/crypto/tls/test/MockTlsClient.cs | 1 - 15 files changed, 183 insertions(+), 105 deletions(-) diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs index ae7efc64d2..4c484fe642 100644 --- a/crypto/src/crypto/tls/AbstractTlsContext.cs +++ b/crypto/src/crypto/tls/AbstractTlsContext.cs @@ -107,19 +107,21 @@ public virtual object UserObject public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length) { - /* - * TODO[session-hash] - * - * draft-ietf-tls-session-hash-04 5.4. If a client or server chooses to continue with a full - * handshake without the extended master secret extension, [..] the client or server MUST - * NOT export any key material based on the new master secret for any subsequent - * application-level authentication. In particular, it MUST disable [RFC5705] [..]. - */ - if (context_value != null && !TlsUtilities.IsValidUint16(context_value.Length)) throw new ArgumentException("must have length less than 2^16 (or be null)", "context_value"); SecurityParameters sp = SecurityParameters; + if (!sp.IsExtendedMasterSecret) + { + /* + * RFC 7627 5.4. If a client or server chooses to continue with a full handshake without + * the extended master secret extension, [..] the client or server MUST NOT export any + * key material based on the new master secret for any subsequent application-level + * authentication. In particular, it MUST disable [RFC5705] [..]. + */ + throw new InvalidOperationException("cannot export keying material without extended_master_secret"); + } + byte[] cr = sp.ClientRandom, sr = sp.ServerRandom; int seedLength = cr.Length + sr.Length; diff --git a/crypto/src/crypto/tls/AbstractTlsPeer.cs b/crypto/src/crypto/tls/AbstractTlsPeer.cs index 81a53386c5..1bbea68c8d 100644 --- a/crypto/src/crypto/tls/AbstractTlsPeer.cs +++ b/crypto/src/crypto/tls/AbstractTlsPeer.cs @@ -6,6 +6,11 @@ namespace Org.BouncyCastle.Crypto.Tls public abstract class AbstractTlsPeer : TlsPeer { + public virtual bool RequiresExtendedMasterSecret() + { + return false; + } + public virtual bool ShouldUseGmtUnixTime() { /* diff --git a/crypto/src/crypto/tls/DtlsClientProtocol.cs b/crypto/src/crypto/tls/DtlsClientProtocol.cs index ae6e6a5731..ce0c4c7671 100644 --- a/crypto/src/crypto/tls/DtlsClientProtocol.cs +++ b/crypto/src/crypto/tls/DtlsClientProtocol.cs @@ -40,7 +40,7 @@ public virtual DtlsTransport Connect(TlsClient client, DatagramTransport transpo if (sessionToResume != null && sessionToResume.IsResumable) { SessionParameters sessionParameters = sessionToResume.ExportSessionParameters(); - if (sessionParameters != null) + if (sessionParameters != null && sessionParameters.IsExtendedMasterSecret) { state.tlsSession = sessionToResume; state.sessionParameters = sessionParameters; @@ -356,6 +356,7 @@ internal virtual DtlsTransport ClientHandshake(ClientHandshakeState state, DtlsR state.sessionParameters = new SessionParameters.Builder() .SetCipherSuite(securityParameters.CipherSuite) .SetCompressionAlgorithm(securityParameters.CompressionAlgorithm) + .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret) .SetMasterSecret(securityParameters.MasterSecret) .SetPeerCertificate(serverCertificate) .SetPskIdentity(securityParameters.PskIdentity) @@ -383,8 +384,6 @@ protected virtual byte[] GenerateCertificateVerify(ClientHandshakeState state, D protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client) { - MemoryStream buf = new MemoryStream(); - ProtocolVersion client_version = client.ClientVersion; if (!client_version.IsDtls) throw new TlsFatalAlert(AlertDescription.internal_error); @@ -392,10 +391,8 @@ protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClie TlsClientContextImpl context = state.clientContext; context.SetClientVersion(client_version); - TlsUtilities.WriteVersion(client_version, buf); SecurityParameters securityParameters = context.SecurityParameters; - buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length); // Session ID byte[] session_id = TlsUtilities.EmptyBytes; @@ -407,20 +404,35 @@ protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClie session_id = TlsUtilities.EmptyBytes; } } - TlsUtilities.WriteOpaque8(session_id, buf); - - // Cookie - TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); bool fallback = client.IsFallback; - /* - * Cipher suites - */ state.offeredCipherSuites = client.GetCipherSuites(); - // Integer -> byte[] - state.clientExtensions = client.GetClientExtensions(); + if (session_id.Length > 0 && state.sessionParameters != null) + { + if (!state.sessionParameters.IsExtendedMasterSecret + || !Arrays.Contains(state.offeredCipherSuites, state.sessionParameters.CipherSuite) + || CompressionMethod.cls_null != state.sessionParameters.CompressionAlgorithm) + { + session_id = TlsUtilities.EmptyBytes; + } + } + + state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(client.GetClientExtensions()); + + TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.clientExtensions); + + MemoryStream buf = new MemoryStream(); + + TlsUtilities.WriteVersion(client_version, buf); + + buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length); + + TlsUtilities.WriteOpaque8(session_id, buf); + + // Cookie + TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); // Cipher Suites (and SCSV) { @@ -455,18 +467,9 @@ protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClie TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, buf); } - // TODO Add support for compression - // Compression methods - // state.offeredCompressionMethods = client.getCompressionMethods(); - state.offeredCompressionMethods = new byte[]{ CompressionMethod.cls_null }; + TlsUtilities.WriteUint8ArrayWithUint8Length(new byte[]{ CompressionMethod.cls_null }, buf); - TlsUtilities.WriteUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf); - - // Extensions - if (state.clientExtensions != null) - { - TlsProtocol.WriteExtensions(buf, state.clientExtensions); - } + TlsProtocol.WriteExtensions(buf, state.clientExtensions); return buf.ToArray(); } @@ -616,7 +619,7 @@ protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] bod state.client.NotifySelectedCipherSuite(selectedCipherSuite); byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf); - if (!Arrays.Contains(state.offeredCompressionMethods, selectedCompressionMethod)) + if (CompressionMethod.cls_null != selectedCompressionMethod) throw new TlsFatalAlert(AlertDescription.illegal_parameter); state.client.NotifySelectedCompressionMethod(selectedCompressionMethod); @@ -638,6 +641,18 @@ protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] bod // Integer -> byte[] state.serverExtensions = TlsProtocol.ReadExtensions(buf); + /* + * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended + * master secret [..]. (and see 5.2, 5.3) + */ + securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.serverExtensions); + + if (!securityParameters.IsExtendedMasterSecret + && (state.resumedSession || state.client.RequiresExtendedMasterSecret())) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + /* * 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 @@ -725,7 +740,7 @@ protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] bod securityParameters.cipherSuite = selectedCipherSuite; securityParameters.compressionAlgorithm = selectedCompressionMethod; - if (sessionServerExtensions != null) + if (sessionServerExtensions != null && sessionServerExtensions.Count > 0) { { /* @@ -740,8 +755,6 @@ protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] bod securityParameters.encryptThenMac = serverSentEncryptThenMAC; } - securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); - securityParameters.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession, sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter); @@ -760,13 +773,6 @@ protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] bod AlertDescription.illegal_parameter); } - /* - * TODO[session-hash] - * - * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes - * that do not use the extended master secret [..]. (and see 5.2, 5.3) - */ - if (sessionClientExtensions != null) { state.client.ProcessServerExtensions(sessionServerExtensions); @@ -839,7 +845,6 @@ protected internal class ClientHandshakeState internal SessionParameters sessionParameters = null; internal SessionParameters.Builder sessionParametersBuilder = null; internal int[] offeredCipherSuites = null; - internal byte[] offeredCompressionMethods = null; internal IDictionary clientExtensions = null; internal IDictionary serverExtensions = null; internal byte[] selectedSessionID = null; diff --git a/crypto/src/crypto/tls/DtlsServerProtocol.cs b/crypto/src/crypto/tls/DtlsServerProtocol.cs index 3032269d1d..1095014cda 100644 --- a/crypto/src/crypto/tls/DtlsServerProtocol.cs +++ b/crypto/src/crypto/tls/DtlsServerProtocol.cs @@ -268,6 +268,24 @@ internal virtual DtlsTransport ServerHandshake(ServerHandshakeState state, DtlsR handshake.Finish(); + //{ + // state.sessionParameters = new SessionParameters.Builder() + // .SetCipherSuite(securityParameters.CipherSuite) + // .SetCompressionAlgorithm(securityParameters.CompressionAlgorithm) + // .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret) + // .SetMasterSecret(securityParameters.MasterSecret) + // .SetPeerCertificate(state.clientCertificate) + // .SetPskIdentity(securityParameters.PskIdentity) + // .SetSrpIdentity(securityParameters.SrpIdentity) + // // TODO Consider filtering extensions that aren't relevant to resumed sessions + // .SetServerExtensions(state.serverExtensions) + // .Build(); + + // state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters); + + // state.serverContext.SetResumableSession(state.tlsSession); + //} + state.server.NotifyHandshakeComplete(); return new DtlsTransport(recordLayer); @@ -356,7 +374,7 @@ protected virtual byte[] GenerateServerHello(ServerHandshakeState state) TlsUtilities.WriteUint16(selectedCipherSuite, buf); TlsUtilities.WriteUint8(selectedCompressionMethod, buf); - state.serverExtensions = state.server.GetServerExtensions(); + state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.server.GetServerExtensions()); /* * RFC 5746 3.6. Server Behavior: Initial Handshake @@ -380,14 +398,12 @@ protected virtual byte[] GenerateServerHello(ServerHandshakeState state) * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty * "renegotiation_info" extension in the ServerHello message. */ - state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.serverExtensions); state.serverExtensions[ExtensionType.renegotiation_info] = TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes); } } - if (securityParameters.extendedMasterSecret) + if (securityParameters.IsExtendedMasterSecret) { - state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.serverExtensions); TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions); } @@ -397,7 +413,7 @@ protected virtual byte[] GenerateServerHello(ServerHandshakeState state) * extensions. */ - if (state.serverExtensions != null) + if (state.serverExtensions.Count > 0) { securityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(state.serverExtensions); @@ -583,12 +599,18 @@ protected virtual void ProcessClientHello(ServerHandshakeState state, byte[] bod SecurityParameters securityParameters = context.SecurityParameters; /* - * TODO[session-hash] - * - * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes - * that do not use the extended master secret [..]. (and see 5.2, 5.3) + * TODO[resumption] Check RFC 7627 5.4. for required behaviour + */ + + /* + * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended + * master secret [..]. (and see 5.2, 5.3) */ securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions); + if (!securityParameters.IsExtendedMasterSecret && state.server.RequiresExtendedMasterSecret()) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } context.SetClientVersion(client_version); diff --git a/crypto/src/crypto/tls/ExporterLabel.cs b/crypto/src/crypto/tls/ExporterLabel.cs index 5970769d78..12603f3ffe 100644 --- a/crypto/src/crypto/tls/ExporterLabel.cs +++ b/crypto/src/crypto/tls/ExporterLabel.cs @@ -30,7 +30,7 @@ public abstract class ExporterLabel public const string dtls_srtp = "EXTRACTOR-dtls_srtp"; /* - * draft-ietf-tls-session-hash-04 + * RFC 7627 */ public static readonly string extended_master_secret = "extended master secret"; } diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs index 3b851587d5..f3ec7011e6 100644 --- a/crypto/src/crypto/tls/SecurityParameters.cs +++ b/crypto/src/crypto/tls/SecurityParameters.cs @@ -52,7 +52,7 @@ public virtual int CipherSuite /** * @return {@link CompressionMethod} */ - public byte CompressionAlgorithm + public virtual byte CompressionAlgorithm { get { return compressionAlgorithm; } } @@ -99,5 +99,10 @@ public virtual byte[] SrpIdentity { get { return srpIdentity; } } + + public virtual bool IsExtendedMasterSecret + { + get { return extendedMasterSecret; } + } } } diff --git a/crypto/src/crypto/tls/SessionParameters.cs b/crypto/src/crypto/tls/SessionParameters.cs index a1eb5f27c5..e827172ea7 100644 --- a/crypto/src/crypto/tls/SessionParameters.cs +++ b/crypto/src/crypto/tls/SessionParameters.cs @@ -17,6 +17,7 @@ public sealed class Builder private byte[] mPskIdentity = null; private byte[] mSrpIdentity = null; private byte[] mEncodedServerExtensions = null; + private bool mExtendedMasterSecret = false; public Builder() { @@ -28,7 +29,7 @@ public SessionParameters Build() Validate(this.mCompressionAlgorithm >= 0, "compressionAlgorithm"); Validate(this.mMasterSecret != null, "masterSecret"); return new SessionParameters(mCipherSuite, (byte)mCompressionAlgorithm, mMasterSecret, mPeerCertificate, - mPskIdentity, mSrpIdentity, mEncodedServerExtensions); + mPskIdentity, mSrpIdentity, mEncodedServerExtensions, mExtendedMasterSecret); } public Builder SetCipherSuite(int cipherSuite) @@ -43,6 +44,12 @@ public Builder SetCompressionAlgorithm(byte compressionAlgorithm) return this; } + public Builder SetExtendedMasterSecret(bool extendedMasterSecret) + { + this.mExtendedMasterSecret = extendedMasterSecret; + return this; + } + public Builder SetMasterSecret(byte[] masterSecret) { this.mMasterSecret = masterSecret; @@ -96,9 +103,11 @@ private void Validate(bool condition, string parameter) private byte[] mPskIdentity; private byte[] mSrpIdentity; private byte[] mEncodedServerExtensions; + private bool mExtendedMasterSecret; private SessionParameters(int cipherSuite, byte compressionAlgorithm, byte[] masterSecret, - Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions) + Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions, + bool extendedMasterSecret) { this.mCipherSuite = cipherSuite; this.mCompressionAlgorithm = compressionAlgorithm; @@ -107,6 +116,7 @@ private SessionParameters(int cipherSuite, byte compressionAlgorithm, byte[] mas this.mPskIdentity = Arrays.Clone(pskIdentity); this.mSrpIdentity = Arrays.Clone(srpIdentity); this.mEncodedServerExtensions = encodedServerExtensions; + this.mExtendedMasterSecret = extendedMasterSecret; } public void Clear() @@ -120,7 +130,7 @@ public void Clear() public SessionParameters Copy() { return new SessionParameters(mCipherSuite, mCompressionAlgorithm, mMasterSecret, mPeerCertificate, - mPskIdentity, mSrpIdentity, mEncodedServerExtensions); + mPskIdentity, mSrpIdentity, mEncodedServerExtensions, mExtendedMasterSecret); } public int CipherSuite @@ -133,6 +143,11 @@ public byte CompressionAlgorithm get { return mCompressionAlgorithm; } } + public bool IsExtendedMasterSecret + { + get { return mExtendedMasterSecret; } + } + public byte[] MasterSecret { get { return mMasterSecret; } diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs index 8de76c2f8e..17b7566931 100644 --- a/crypto/src/crypto/tls/TlsClientProtocol.cs +++ b/crypto/src/crypto/tls/TlsClientProtocol.cs @@ -96,7 +96,7 @@ public virtual void Connect(TlsClient tlsClient) if (sessionToResume != null && sessionToResume.IsResumable) { SessionParameters sessionParameters = sessionToResume.ExportSessionParameters(); - if (sessionParameters != null) + if (sessionParameters != null && sessionParameters.IsExtendedMasterSecret) { this.mTlsSession = sessionToResume; this.mSessionParameters = sessionParameters; @@ -640,7 +640,7 @@ protected virtual void ReceiveServerHelloMessage(MemoryStream buf) this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod); /* - * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server + * RFC 3546 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 @@ -649,6 +649,19 @@ protected virtual void ReceiveServerHelloMessage(MemoryStream buf) */ this.mServerExtensions = ReadExtensions(buf); + /* + * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended + * master secret [..]. (and see 5.2, 5.3) + */ + this.mSecurityParameters.extendedMasterSecret = !TlsUtilities.IsSsl(mTlsClientContext) + && TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mServerExtensions); + + if (!mSecurityParameters.IsExtendedMasterSecret + && (mResumedSession || mTlsClient.RequiresExtendedMasterSecret())) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + /* * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an * extended client hello message. @@ -738,7 +751,7 @@ protected virtual void ReceiveServerHelloMessage(MemoryStream buf) this.mSecurityParameters.cipherSuite = selectedCipherSuite; this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; - if (sessionServerExtensions != null) + if (sessionServerExtensions != null && sessionServerExtensions.Count > 0) { { /* @@ -754,8 +767,6 @@ protected virtual void ReceiveServerHelloMessage(MemoryStream buf) this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC; } - this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); - this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter); @@ -774,13 +785,6 @@ protected virtual void ReceiveServerHelloMessage(MemoryStream buf) AlertDescription.illegal_parameter); } - /* - * TODO[session-hash] - * - * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes - * that do not use the extended master secret [..]. (and see 5.2, 5.3) - */ - if (sessionClientExtensions != null) { this.mTlsClient.ProcessServerExtensions(sessionServerExtensions); @@ -837,14 +841,20 @@ protected virtual void SendClientHelloMessage() if (session_id.Length > 0 && this.mSessionParameters != null) { - if (!Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite) + if (!mSessionParameters.IsExtendedMasterSecret + || !Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite) || !Arrays.Contains(this.mOfferedCompressionMethods, mSessionParameters.CompressionAlgorithm)) { session_id = TlsUtilities.EmptyBytes; } } - this.mClientExtensions = this.mTlsClient.GetClientExtensions(); + this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mTlsClient.GetClientExtensions()); + + if (!client_version.IsSsl) + { + TlsExtensionsUtilities.AddExtendedMasterSecretExtension(this.mClientExtensions); + } HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello); @@ -869,8 +879,6 @@ protected virtual void SendClientHelloMessage() if (noRenegExt && noRenegScsv) { // 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); } @@ -891,10 +899,7 @@ protected virtual void SendClientHelloMessage() TlsUtilities.WriteUint8ArrayWithUint8Length(mOfferedCompressionMethods, message); - if (mClientExtensions != null) - { - WriteExtensions(message, mClientExtensions); - } + WriteExtensions(message, mClientExtensions); message.WriteToRecordStream(this); } diff --git a/crypto/src/crypto/tls/TlsPeer.cs b/crypto/src/crypto/tls/TlsPeer.cs index 1ae41a41a3..993fdf93f3 100644 --- a/crypto/src/crypto/tls/TlsPeer.cs +++ b/crypto/src/crypto/tls/TlsPeer.cs @@ -5,6 +5,21 @@ namespace Org.BouncyCastle.Crypto.Tls { public interface TlsPeer { + /// + /// This implementation supports RFC 7627 and will always negotiate the extended_master_secret + /// extension where possible. + /// + /// + /// When connecting to a peer that does not offer/accept this extension, it is recommended to + /// abort the handshake. This option is provided for interoperability with legacy peers, + /// although some TLS features will be disabled in that case (see RFC 7627 5.4). + /// + /// + /// true if the handshake should be aborted when the peer does not negotiate the + /// extended_master_secret extension, or false to support legacy interoperability. + /// + bool RequiresExtendedMasterSecret(); + /// /// draft-mathewson-no-gmtunixtime-00 2. "If existing users of a TLS implementation may rely on /// gmt_unix_time containing the current time, we recommend that implementors MAY provide the diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs index bbb76d53ca..394967c37d 100644 --- a/crypto/src/crypto/tls/TlsProtocol.cs +++ b/crypto/src/crypto/tls/TlsProtocol.cs @@ -288,6 +288,7 @@ protected virtual void CompleteHandshake() this.mSessionParameters = new SessionParameters.Builder() .SetCipherSuite(this.mSecurityParameters.CipherSuite) .SetCompressionAlgorithm(this.mSecurityParameters.CompressionAlgorithm) + .SetExtendedMasterSecret(this.mSecurityParameters.IsExtendedMasterSecret) .SetMasterSecret(this.mSecurityParameters.MasterSecret) .SetPeerCertificate(this.mPeerCertificate) .SetPskIdentity(this.mSecurityParameters.PskIdentity) diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs index f5285d80b2..e610b59504 100644 --- a/crypto/src/crypto/tls/TlsServerProtocol.cs +++ b/crypto/src/crypto/tls/TlsServerProtocol.cs @@ -560,12 +560,18 @@ protected virtual void ReceiveClientHelloMessage(MemoryStream buf) this.mClientExtensions = ReadExtensions(buf); /* - * TODO[session-hash] - * - * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes - * that do not use the extended master secret [..]. (and see 5.2, 5.3) + * TODO[resumption] Check RFC 7627 5.4. for required behaviour + */ + + /* + * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended + * master secret [..]. (and see 5.2, 5.3) */ this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions); + if (!mSecurityParameters.IsExtendedMasterSecret && mTlsServer.RequiresExtendedMasterSecret()) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } ContextAdmin.SetClientVersion(client_version); @@ -724,7 +730,7 @@ protected virtual void SendServerHelloMessage() TlsUtilities.WriteUint16(selectedCipherSuite, message); TlsUtilities.WriteUint8(selectedCompressionMethod, message); - this.mServerExtensions = mTlsServer.GetServerExtensions(); + this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mTlsServer.GetServerExtensions()); /* * RFC 5746 3.6. Server Behavior: Initial Handshake @@ -748,14 +754,16 @@ protected virtual void SendServerHelloMessage() * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty * "renegotiation_info" extension in the ServerHello message. */ - this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions); this.mServerExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes); } } - if (mSecurityParameters.extendedMasterSecret) + if (TlsUtilities.IsSsl(mTlsServerContext)) + { + mSecurityParameters.extendedMasterSecret = false; + } + else if (mSecurityParameters.IsExtendedMasterSecret) { - this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mServerExtensions); TlsExtensionsUtilities.AddExtendedMasterSecretExtension(mServerExtensions); } @@ -765,7 +773,7 @@ protected virtual void SendServerHelloMessage() * extensions. */ - if (this.mServerExtensions != null) + if (this.mServerExtensions.Count > 0) { this.mSecurityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions); diff --git a/crypto/src/crypto/tls/TlsSessionImpl.cs b/crypto/src/crypto/tls/TlsSessionImpl.cs index 866392623f..4f0ff819e1 100644 --- a/crypto/src/crypto/tls/TlsSessionImpl.cs +++ b/crypto/src/crypto/tls/TlsSessionImpl.cs @@ -8,17 +8,21 @@ internal class TlsSessionImpl : TlsSession { internal readonly byte[] mSessionID; - internal SessionParameters mSessionParameters; + internal readonly SessionParameters mSessionParameters; + internal bool mResumable; internal TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters) { if (sessionID == null) throw new ArgumentNullException("sessionID"); - if (sessionID.Length < 1 || sessionID.Length > 32) - throw new ArgumentException("must have length between 1 and 32 bytes, inclusive", "sessionID"); + if (sessionID.Length > 32) + throw new ArgumentException("cannot be longer than 32 bytes", "sessionID"); this.mSessionID = Arrays.Clone(sessionID); this.mSessionParameters = sessionParameters; + this.mResumable = sessionID.Length > 0 + && null != sessionParameters + && sessionParameters.IsExtendedMasterSecret; } public virtual SessionParameters ExportSessionParameters() @@ -36,19 +40,12 @@ public virtual byte[] SessionID public virtual void Invalidate() { - lock (this) - { - if (this.mSessionParameters != null) - { - this.mSessionParameters.Clear(); - this.mSessionParameters = null; - } - } + lock (this) this.mResumable = false; } public virtual bool IsResumable { - get { lock (this) return this.mSessionParameters != null; } + get { lock (this) return mResumable; } } } } diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs index 698bf6da6c..e6bd253aa3 100644 --- a/crypto/src/crypto/tls/TlsUtilities.cs +++ b/crypto/src/crypto/tls/TlsUtilities.cs @@ -963,14 +963,14 @@ internal static byte[] CalculateMasterSecret(TlsContext context, byte[] pre_mast { SecurityParameters securityParameters = context.SecurityParameters; - byte[] seed = securityParameters.extendedMasterSecret + byte[] seed = securityParameters.IsExtendedMasterSecret ? securityParameters.SessionHash : Concat(securityParameters.ClientRandom, securityParameters.ServerRandom); if (IsSsl(context)) return CalculateMasterSecret_Ssl(pre_master_secret, seed); - string asciiLabel = securityParameters.extendedMasterSecret + string asciiLabel = securityParameters.IsExtendedMasterSecret ? ExporterLabel.extended_master_secret : ExporterLabel.master_secret; diff --git a/crypto/test/src/crypto/tls/test/MockDtlsClient.cs b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs index 8d76c97b2a..51493fae13 100644 --- a/crypto/test/src/crypto/tls/test/MockDtlsClient.cs +++ b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs @@ -68,7 +68,6 @@ public override IDictionary GetClientExtensions() { IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions()); TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions); - TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions); { /* * NOTE: If you are copying test code, do not blindly set these extensions in your own client. diff --git a/crypto/test/src/crypto/tls/test/MockTlsClient.cs b/crypto/test/src/crypto/tls/test/MockTlsClient.cs index d8deabf960..f28236f0be 100644 --- a/crypto/test/src/crypto/tls/test/MockTlsClient.cs +++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs @@ -58,7 +58,6 @@ public override IDictionary GetClientExtensions() { IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions()); TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions); - TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions); { /* * NOTE: If you are copying test code, do not blindly set these extensions in your own client. From f1659957588d3a883de10237fe2b166a780379cd Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 3 Dec 2018 11:55:06 +0700 Subject: [PATCH 02/22] Fix some comments --- crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs b/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs index 1a9a03e9f9..b1099ed5b7 100644 --- a/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs +++ b/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs @@ -235,10 +235,10 @@ public abstract class PkcsObjectIdentifiers public static readonly DerObjectIdentifier IdAAEtsCertCrlTimestamp = new DerObjectIdentifier(IdAA + ".26"); public static readonly DerObjectIdentifier IdAAEtsArchiveTimestamp = new DerObjectIdentifier(IdAA + ".27"); - /** PKCS#9: 1.2.840.113549.1.9.16.6.2.37 - RFC 4108 */ + /** PKCS#9: 1.2.840.113549.1.9.16.2.37 - RFC 4108 */ public static readonly DerObjectIdentifier IdAADecryptKeyID = IdAAOid.Branch("37"); - /** PKCS#9: 1.2.840.113549.1.9.16.6.2.38 - RFC 4108 */ + /** PKCS#9: 1.2.840.113549.1.9.16.2.38 - RFC 4108 */ public static readonly DerObjectIdentifier IdAAImplCryptoAlgs = IdAAOid.Branch("38"); /** PKCS#9: 1.2.840.113549.1.9.16.2.54 RFC7030*/ From 785d36daf1d125b3fba16e1d92719e2a0f67698e Mon Sep 17 00:00:00 2001 From: Megan Woods Date: Mon, 14 Jan 2019 00:17:24 +1100 Subject: [PATCH 03/22] Added ECGOST3410_2012Signer Updated encoding of SubjectPublicKeyInfo and PrivateKeyInfo --- .../asn1/cryptopro/ECGOST3410NamedCurves.cs | 52 +- .../crypto/parameters/ECGOST3410Parameters.cs | 51 ++ .../parameters/ECNamedDomainParameters.cs | 35 + .../crypto/signers/EcGost3410_2012Signer.cs | 151 +++++ .../crypto/signers/GOST3410DigestSigner.cs | 237 +++---- crypto/src/pkcs/PrivateKeyInfoFactory.cs | 44 +- crypto/src/security/PrivateKeyFactory.cs | 162 ++++- crypto/src/security/PublicKeyFactory.cs | 36 ++ crypto/src/util/BigIntegers.cs | 11 + .../src/x509/SubjectPublicKeyInfoFactory.cs | 66 +- .../src/crypto/test/ECGOST3410_2012Test.cs | 597 ++++++++++++++++++ .../test/EGOST3410_2012SignatureTest.cs | 187 ++++++ 12 files changed, 1462 insertions(+), 167 deletions(-) create mode 100644 crypto/src/crypto/parameters/ECGOST3410Parameters.cs create mode 100644 crypto/src/crypto/parameters/ECNamedDomainParameters.cs create mode 100644 crypto/src/crypto/signers/EcGost3410_2012Signer.cs create mode 100644 crypto/test/src/crypto/test/ECGOST3410_2012Test.cs create mode 100644 crypto/test/src/crypto/test/EGOST3410_2012SignatureTest.cs diff --git a/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs b/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs index b61da6b57d..ccf3155cfe 100644 --- a/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs +++ b/crypto/src/asn1/cryptopro/ECGOST3410NamedCurves.cs @@ -115,70 +115,70 @@ static ECGost3410NamedCurves() parameters[CryptoProObjectIdentifiers.GostR3410x2001CryptoProC] = ecParams; //GOST34.10 2012 - mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319"); //p - mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323"); //q + mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97", 16); //p + mod_q = new BigInteger("400000000000000000000000000000000FD8CDDFC87B6635C115AF556C360C67", 16); //q curve = new FpCurve( mod_p, // p - new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a - new BigInteger("166"), // b + new BigInteger("C2173F1513981673AF4892C23035A27CE25E2013BF95AA33B22C656F277E7335", 16), // a + new BigInteger("295F9BAE7428ED9CCC20E7C359A9D41A22FCCD9108E17BF7BA9337A6F8AE9513", 16), // b mod_q, BigInteger.One); ecParams = new ECDomainParameters( curve, curve.CreatePoint( - new BigInteger("1"), // x - new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y + new BigInteger("91E38443A5E82C0D880923425712B2BB658B9196932E02C78B2582FE742DAA28", 16), // x + new BigInteger("32879423AB1A0375895786C4BB46E9565FDE0B5344766740AF268ADB32322E5C", 16)), // y mod_q, BigInteger.One); parameters[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA] = ecParams; - mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",16); //p - mod_q = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275",16); //q + mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7", 16); //p + mod_q = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275", 16); //q curve = new FpCurve( mod_p, // p - new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4",16), // a - new BigInteger("E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760",16), // b + new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4", 16), // a + new BigInteger("E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760", 16), // b mod_q, BigInteger.One); ecParams = new ECDomainParameters( curve, curve.CreatePoint( new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"), // x - new BigInteger("7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4",16)), // y + new BigInteger("7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4", 16)), // y mod_q, BigInteger.One); parameters[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA] = ecParams; - mod_p = new BigInteger("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F",16); //p - mod_q = new BigInteger("800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD",16); //q + mod_p = new BigInteger("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F", 16); //p + mod_q = new BigInteger("800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD", 16); //q curve = new FpCurve( mod_p, // p - new BigInteger("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C",16), // a - new BigInteger("687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116",16), // b + new BigInteger("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C", 16), // a + new BigInteger("687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116", 16), // b mod_q, BigInteger.One); ecParams = new ECDomainParameters( curve, curve.CreatePoint( new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"), // x - new BigInteger("1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD",16)), // y + new BigInteger("1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD", 16)), // y mod_q, BigInteger.One); parameters[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetB] = ecParams; - mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",16); //p - mod_q = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC98CDBA46506AB004C33A9FF5147502CC8EDA9E7A769A12694623CEF47F023ED",16); //q + mod_p = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7", 16); //p + mod_q = new BigInteger("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC98CDBA46506AB004C33A9FF5147502CC8EDA9E7A769A12694623CEF47F023ED", 16); //q curve = new FpCurve( mod_p, // p - new BigInteger("DC9203E514A721875485A529D2C722FB187BC8980EB866644DE41C68E143064546E861C0E2C9EDD92ADE71F46FCF50FF2AD97F951FDA9F2A2EB6546F39689BD3",16), // a - new BigInteger("B4C4EE28CEBC6C2C8AC12952CF37F16AC7EFB6A9F69F4B57FFDA2E4F0DE5ADE038CBC2FFF719D2C18DE0284B8BFEF3B52B8CC7A5F5BF0A3C8D2319A5312557E1",16), // b + new BigInteger("DC9203E514A721875485A529D2C722FB187BC8980EB866644DE41C68E143064546E861C0E2C9EDD92ADE71F46FCF50FF2AD97F951FDA9F2A2EB6546F39689BD3", 16), // a + new BigInteger("B4C4EE28CEBC6C2C8AC12952CF37F16AC7EFB6A9F69F4B57FFDA2E4F0DE5ADE038CBC2FFF719D2C18DE0284B8BFEF3B52B8CC7A5F5BF0A3C8D2319A5312557E1", 16), // b mod_q, BigInteger.One); ecParams = new ECDomainParameters( curve, curve.CreatePoint( new BigInteger("E2E31EDFC23DE7BDEBE241CE593EF5DE2295B7A9CBAEF021D385F7074CEA043AA27272A7AE602BF2A7B9033DB9ED3610C6FB85487EAE97AAC5BC7928C1950148", 16), // x - new BigInteger("F5CE40D95B5EB899ABBCCFF5911CB8577939804D6527378B8C108C3D2090FF9BE18E2D33E3021ED2EF32D85822423B6304F726AA854BAE07D0396E9A9ADDC40F",16)), // y + new BigInteger("F5CE40D95B5EB899ABBCCFF5911CB8577939804D6527378B8C108C3D2090FF9BE18E2D33E3021ED2EF32D85822423B6304F726AA854BAE07D0396E9A9ADDC40F", 16)), // y mod_q, BigInteger.One); parameters[RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetC] = ecParams; @@ -213,7 +213,7 @@ static ECGost3410NamedCurves() public static ECDomainParameters GetByOid( DerObjectIdentifier oid) { - return (ECDomainParameters) parameters[oid]; + return (ECDomainParameters)parameters[oid]; } /** @@ -228,11 +228,11 @@ public static IEnumerable Names public static ECDomainParameters GetByName( string name) { - DerObjectIdentifier oid = (DerObjectIdentifier) objIds[name]; + DerObjectIdentifier oid = (DerObjectIdentifier)objIds[name]; if (oid != null) { - return (ECDomainParameters) parameters[oid]; + return (ECDomainParameters)parameters[oid]; } return null; @@ -244,13 +244,13 @@ public static ECDomainParameters GetByName( public static string GetName( DerObjectIdentifier oid) { - return (string) names[oid]; + return (string)names[oid]; } public static DerObjectIdentifier GetOid( string name) { - return (DerObjectIdentifier) objIds[name]; + return (DerObjectIdentifier)objIds[name]; } } } diff --git a/crypto/src/crypto/parameters/ECGOST3410Parameters.cs b/crypto/src/crypto/parameters/ECGOST3410Parameters.cs new file mode 100644 index 0000000000..ede7433d6b --- /dev/null +++ b/crypto/src/crypto/parameters/ECGOST3410Parameters.cs @@ -0,0 +1,51 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECGOST3410Parameters : ECNamedDomainParameters + { + + private readonly DerObjectIdentifier _publicKeyParamSet; + private readonly DerObjectIdentifier _digestParamSet; + private readonly DerObjectIdentifier _encryptionParamSet; + + public DerObjectIdentifier PublicKeyParamSet + { + get { return _publicKeyParamSet; } + } + + public DerObjectIdentifier DigestParamSet + { + get { return _digestParamSet; } + } + + public DerObjectIdentifier EncryptionParamSet + { + get { return _encryptionParamSet; } + } + + public ECGOST3410Parameters( + ECNamedDomainParameters dp, + DerObjectIdentifier publicKeyParamSet, + DerObjectIdentifier digestParamSet, + DerObjectIdentifier encryptionParamSet) : base(dp.Name, dp.Curve, dp.G, dp.N, dp.H, dp.GetSeed()) + { + this._publicKeyParamSet = publicKeyParamSet; + this._digestParamSet = digestParamSet; + this._encryptionParamSet = encryptionParamSet; + } + + + public ECGOST3410Parameters(ECDomainParameters dp, DerObjectIdentifier publicKeyParamSet, + DerObjectIdentifier digestParamSet, + DerObjectIdentifier encryptionParamSet) : base(publicKeyParamSet, dp.Curve, dp.G, dp.N, dp.H, dp.GetSeed()) + { + this._publicKeyParamSet = publicKeyParamSet; + this._digestParamSet = digestParamSet; + this._encryptionParamSet = encryptionParamSet; + } + + } +} \ No newline at end of file diff --git a/crypto/src/crypto/parameters/ECNamedDomainParameters.cs b/crypto/src/crypto/parameters/ECNamedDomainParameters.cs new file mode 100644 index 0000000000..34e390a8f8 --- /dev/null +++ b/crypto/src/crypto/parameters/ECNamedDomainParameters.cs @@ -0,0 +1,35 @@ +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; + +namespace Org.BouncyCastle.Crypto.Parameters +{ + public class ECNamedDomainParameters : ECDomainParameters + { + private DerObjectIdentifier name; + + public DerObjectIdentifier Name + { + get { return name; } + } + + public ECNamedDomainParameters(DerObjectIdentifier name, ECDomainParameters dp) : this(name, dp.curve, dp.g, dp.n, dp.h, dp.seed) + { } + + + public ECNamedDomainParameters(DerObjectIdentifier name, ECCurve curve, ECPoint g, BigInteger n) : base(curve, g, n) + { + this.name = name; + } + + public ECNamedDomainParameters(DerObjectIdentifier name, ECCurve curve, ECPoint g, BigInteger n, BigInteger h) : base(curve, g, n, h) + { + this.name = name; + } + + public ECNamedDomainParameters(DerObjectIdentifier name, ECCurve curve, ECPoint g, BigInteger n, BigInteger h, byte[] seed) : base(curve, g, n, h, seed) + { + this.name = name; + } + } +} \ No newline at end of file diff --git a/crypto/src/crypto/signers/EcGost3410_2012Signer.cs b/crypto/src/crypto/signers/EcGost3410_2012Signer.cs new file mode 100644 index 0000000000..cd50916dd2 --- /dev/null +++ b/crypto/src/crypto/signers/EcGost3410_2012Signer.cs @@ -0,0 +1,151 @@ +using Org.BouncyCastle.Math; +using System; +using System.IO; +using NUnit.Core; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Math.EC.Multiplier; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Crypto.Signers +{ + public class ECGOST3410_2012Signer : IDsaExt + { + private ECKeyParameters key; + private SecureRandom secureRandom; + private bool forSigning; + + public BigInteger Order + { + get { return key.Parameters.N; } + } + + public string AlgorithmName + { + get { return key.AlgorithmName; } + } + + public virtual void Init(bool forSigning, ICipherParameters parameters) + { + this.forSigning = forSigning; + if (forSigning) + { + if (parameters is ParametersWithRandom) + { + ParametersWithRandom rParam = (ParametersWithRandom)parameters; + this.secureRandom = rParam.Random; + this.key = (ECPrivateKeyParameters)rParam.Parameters; + } + else + { + this.secureRandom = new SecureRandom(); + this.key = (ECPrivateKeyParameters)parameters; + } + } + else + { + this.key = (ECPublicKeyParameters)parameters; + } + } + + public BigInteger[] GenerateSignature(byte[] message) + { + if (!forSigning) + { + throw new InvalidOperationException("not initialised for signing."); + } + + byte[] mRev = new byte[message.Length]; // conversion is little-endian + for (int i = 0; i != mRev.Length; i++) + { + mRev[i] = message[mRev.Length - 1 - i]; + } + BigInteger e = new BigInteger(1, mRev); + + ECDomainParameters ec = key.Parameters; + BigInteger n = ec.N; + BigInteger d = ((ECPrivateKeyParameters)key).D; + + BigInteger r, s; + + ECMultiplier basePointMultiplier = CreateBasePointMultiplier(); + + do // generate s + { + BigInteger k; + do // generate r + { + do + { + k = BigIntegers.CreateRandomBigInteger(n.BitLength, secureRandom); + } while (k.Equals(BigInteger.Zero)); // ECConstants.ZERO)); + + ECPoint p = basePointMultiplier.Multiply(ec.G, k).Normalize(); + + r = p.AffineXCoord.ToBigInteger().Mod(n); + } while (r.Equals(BigInteger.Zero)); // ECConstants.ZERO)); + + s = (k.Multiply(e)).Add(d.Multiply(r)).Mod(n); + } while (s.Equals(BigInteger.Zero)); // ECConstants.ZERO)); + + return new BigInteger[] { r, s }; + } + + + public bool VerifySignature(byte[] message, BigInteger r, BigInteger s) + { + if (forSigning) + { + throw new InvalidOperationException("not initialised for verification."); + } + + + byte[] mRev = new byte[message.Length]; // conversion is little-endian + for (int i = 0; i != mRev.Length; i++) + { + mRev[i] = message[mRev.Length - 1 - i]; + } + BigInteger e = new BigInteger(1, mRev); + BigInteger n = key.Parameters.N; + + // r in the range [1,n-1] + if (r.CompareTo(BigInteger.One) < 0 || r.CompareTo(n) >= 0) + { + return false; + } + + // s in the range [1,n-1] + if (s.CompareTo(BigInteger.One) < 0 || s.CompareTo(n) >= 0) + { + return false; + } + + BigInteger v = e.ModInverse(n); + + BigInteger z1 = s.Multiply(v).Mod(n); + BigInteger z2 = (n.Subtract(r)).Multiply(v).Mod(n); + + ECPoint G = key.Parameters.G; // P + ECPoint Q = ((ECPublicKeyParameters)key).Q; + + ECPoint point = ECAlgorithms.SumOfTwoMultiplies(G, z1, Q, z2).Normalize(); + + // components must be bogus. + if (point.IsInfinity) + { + return false; + } + + BigInteger R = point.AffineXCoord.ToBigInteger().Mod(n); + + return R.Equals(r); + } + + protected virtual ECMultiplier CreateBasePointMultiplier() + { + return new FixedPointCombMultiplier(); + } + + } +} \ No newline at end of file diff --git a/crypto/src/crypto/signers/GOST3410DigestSigner.cs b/crypto/src/crypto/signers/GOST3410DigestSigner.cs index bc32808df5..bff35869b7 100644 --- a/crypto/src/crypto/signers/GOST3410DigestSigner.cs +++ b/crypto/src/crypto/signers/GOST3410DigestSigner.cs @@ -11,135 +11,144 @@ namespace Org.BouncyCastle.Crypto.Signers { - public class Gost3410DigestSigner - : ISigner - { - private readonly IDigest digest; - private readonly IDsa dsaSigner; - private bool forSigning; - - public Gost3410DigestSigner( - IDsa signer, - IDigest digest) - { - this.dsaSigner = signer; - this.digest = digest; - } + public class Gost3410DigestSigner + : ISigner + { + private readonly IDigest digest; + private readonly IDsa dsaSigner; + private readonly int size; + private int halfSize; + private bool forSigning; + + + + public Gost3410DigestSigner( + IDsa signer, + IDigest digest) + { + this.dsaSigner = signer; + this.digest = digest; + + halfSize = digest.GetDigestSize(); + this.size = halfSize * 2; + + } public virtual string AlgorithmName - { - get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } - } + { + get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; } + } public virtual void Init( - bool forSigning, - ICipherParameters parameters) - { - this.forSigning = forSigning; - - AsymmetricKeyParameter k; - if (parameters is ParametersWithRandom) - { - k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; - } - else - { - k = (AsymmetricKeyParameter)parameters; - } - - if (forSigning && !k.IsPrivate) - { - throw new InvalidKeyException("Signing Requires Private Key."); - } - - if (!forSigning && k.IsPrivate) - { - throw new InvalidKeyException("Verification Requires Public Key."); - } - - Reset(); - - dsaSigner.Init(forSigning, parameters); - } - - /** + bool forSigning, + ICipherParameters parameters) + { + this.forSigning = forSigning; + + AsymmetricKeyParameter k; + if (parameters is ParametersWithRandom) + { + k = (AsymmetricKeyParameter)((ParametersWithRandom)parameters).Parameters; + } + else + { + k = (AsymmetricKeyParameter)parameters; + } + + if (forSigning && !k.IsPrivate) + { + throw new InvalidKeyException("Signing Requires Private Key."); + } + + if (!forSigning && k.IsPrivate) + { + throw new InvalidKeyException("Verification Requires Public Key."); + } + + + Reset(); + + dsaSigner.Init(forSigning, parameters); + } + + /** * update the internal digest with the byte b */ public virtual void Update( - byte input) - { - digest.Update(input); - } + byte input) + { + digest.Update(input); + } - /** + /** * update the internal digest with the byte array in */ public virtual void BlockUpdate( - byte[] input, - int inOff, - int length) - { - digest.BlockUpdate(input, inOff, length); - } - - /** + byte[] input, + int inOff, + int length) + { + digest.BlockUpdate(input, inOff, length); + } + + /** * Generate a signature for the message we've been loaded with using * the key we were initialised with. */ public virtual byte[] GenerateSignature() - { - if (!forSigning) - throw new InvalidOperationException("GOST3410DigestSigner not initialised for signature generation."); - - byte[] hash = new byte[digest.GetDigestSize()]; - digest.DoFinal(hash, 0); - - try - { - BigInteger[] sig = dsaSigner.GenerateSignature(hash); - byte[] sigBytes = new byte[64]; - - // TODO Add methods to allow writing BigInteger to existing byte array? - byte[] r = sig[0].ToByteArrayUnsigned(); - byte[] s = sig[1].ToByteArrayUnsigned(); - s.CopyTo(sigBytes, 32 - s.Length); - r.CopyTo(sigBytes, 64 - r.Length); - return sigBytes; - } - catch (Exception e) - { - throw new SignatureException(e.Message, e); - } - } - - /// true if the internal state represents the signature described in the passed in array. + { + if (!forSigning) + throw new InvalidOperationException("GOST3410DigestSigner not initialised for signature generation."); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + try + { + BigInteger[] sig = dsaSigner.GenerateSignature(hash); + byte[] sigBytes = new byte[size]; + + // TODO Add methods to allow writing BigInteger to existing byte array? + byte[] r = sig[0].ToByteArrayUnsigned(); + byte[] s = sig[1].ToByteArrayUnsigned(); + s.CopyTo(sigBytes, halfSize - s.Length); + r.CopyTo(sigBytes, size - r.Length); + return sigBytes; + } + catch (Exception e) + { + throw new SignatureException(e.Message, e); + } + } + + /// true if the internal state represents the signature described in the passed in array. public virtual bool VerifySignature( - byte[] signature) - { - if (forSigning) - throw new InvalidOperationException("DSADigestSigner not initialised for verification"); - - byte[] hash = new byte[digest.GetDigestSize()]; - digest.DoFinal(hash, 0); - - BigInteger R, S; - try - { - R = new BigInteger(1, signature, 32, 32); - S = new BigInteger(1, signature, 0, 32); - } - catch (Exception e) - { - throw new SignatureException("error decoding signature bytes.", e); - } - - return dsaSigner.VerifySignature(hash, R, S); - } - - /// Reset the internal state + byte[] signature) + { + if (forSigning) + throw new InvalidOperationException("DSADigestSigner not initialised for verification"); + + byte[] hash = new byte[digest.GetDigestSize()]; + digest.DoFinal(hash, 0); + + BigInteger R, S; + try + { + R = new BigInteger(1, signature, halfSize, halfSize); + S = new BigInteger(1, signature, 0, halfSize); + } + catch (Exception e) + { + throw new SignatureException("error decoding signature bytes.", e); + } + + return dsaSigner.VerifySignature(hash, R, S); + } + + /// Reset the internal state public virtual void Reset() - { - digest.Reset(); - } - } + { + digest.Reset(); + } + } } diff --git a/crypto/src/pkcs/PrivateKeyInfoFactory.cs b/crypto/src/pkcs/PrivateKeyInfoFactory.cs index 3036dc8b6b..75a56983ad 100644 --- a/crypto/src/pkcs/PrivateKeyInfoFactory.cs +++ b/crypto/src/pkcs/PrivateKeyInfoFactory.cs @@ -5,6 +5,7 @@ using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Rosstandart; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; @@ -117,10 +118,35 @@ public static PrivateKeyInfo CreatePrivateKeyInfo(AsymmetricKeyParameter private if (privateKey is ECPrivateKeyParameters) { - ECPrivateKeyParameters priv = (ECPrivateKeyParameters)privateKey; + ECPrivateKeyParameters priv = (ECPrivateKeyParameters) privateKey; DerBitString publicKey = new DerBitString(ECKeyPairGenerator.GetCorrespondingPublicKey(priv).Q.GetEncoded(false)); ECDomainParameters dp = priv.Parameters; + + // ECGOST3410 + if (dp is ECGOST3410Parameters) + { + ECGOST3410Parameters domainParameters = (ECGOST3410Parameters) dp; + + Gost3410PublicKeyAlgParameters gostParams = new Gost3410PublicKeyAlgParameters( + (domainParameters).PublicKeyParamSet, + (domainParameters).DigestParamSet, + (domainParameters).EncryptionParamSet); + + bool is512 = priv.D.BitLength > 256; + DerObjectIdentifier identifier = (is512) ? + RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512 : + RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256; + int size = (is512) ? 64 : 32; + + byte[] encKey = new byte[size]; + + ExtractBytes(encKey, size, 0, priv.D); + + return new PrivateKeyInfo(new AlgorithmIdentifier(identifier, gostParams), new DerOctetString(encKey)); + } + + int orderBitLength = dp.N.BitLength; AlgorithmIdentifier algID; @@ -245,5 +271,21 @@ public static PrivateKeyInfo CreatePrivateKeyInfo( return PrivateKeyInfo.GetInstance(keyBytes); } + + private static void ExtractBytes(byte[] encKey, int size, int offSet, BigInteger bI) + { + byte[] val = bI.ToByteArray(); + if (val.Length < size) + { + byte[] tmp = new byte[size]; + Array.Copy(val, 0, tmp, tmp.Length - val.Length, val.Length); + val = tmp; + } + + for (int i = 0; i != size; i++) + { + encKey[offSet + i] = val[val.Length - 1 - i]; + } + } } } diff --git a/crypto/src/security/PrivateKeyFactory.cs b/crypto/src/security/PrivateKeyFactory.cs index 0b07d0659f..9f2d2e9c1a 100644 --- a/crypto/src/security/PrivateKeyFactory.cs +++ b/crypto/src/security/PrivateKeyFactory.cs @@ -8,6 +8,7 @@ using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Rosstandart; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; @@ -67,7 +68,7 @@ public static AsymmetricKeyParameter CreateKey( keyStructure.Coefficient); } // TODO? -// else if (algOid.Equals(X9ObjectIdentifiers.DHPublicNumber)) + // else if (algOid.Equals(X9ObjectIdentifiers.DHPublicNumber)) else if (algOid.Equals(PkcsObjectIdentifiers.DhKeyAgreement)) { DHParameter para = new DHParameter( @@ -82,7 +83,7 @@ public static AsymmetricKeyParameter CreateKey( } else if (algOid.Equals(OiwObjectIdentifiers.ElGamalAlgorithm)) { - ElGamalParameter para = new ElGamalParameter( + ElGamalParameter para = new ElGamalParameter( Asn1Sequence.GetInstance(algID.Parameters.ToAsn1Object())); DerInteger derX = (DerInteger)keyInfo.ParsePrivateKey(); @@ -126,7 +127,7 @@ public static AsymmetricKeyParameter CreateKey( return new ECPrivateKeyParameters("EC", d, (DerObjectIdentifier)para.Parameters); } - ECDomainParameters dParams = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed()); + ECDomainParameters dParams = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed()); return new ECPrivateKeyParameters(d, dParams); } else if (algOid.Equals(CryptoProObjectIdentifiers.GostR3410x2001)) @@ -163,14 +164,14 @@ public static AsymmetricKeyParameter CreateKey( if (privKey is DerInteger) { x = DerInteger.GetInstance(privKey).PositiveValue; - } + } else { x = new BigInteger(1, Arrays.Reverse(Asn1OctetString.GetInstance(privKey).GetOctets())); - } + } - return new Gost3410PrivateKeyParameters(x, gostParams.PublicKeyParamSet); - } + return new Gost3410PrivateKeyParameters(x, gostParams.PublicKeyParamSet); + } else if (algOid.Equals(EdECObjectIdentifiers.id_X25519)) { return new X25519PrivateKeyParameters(GetRawKey(keyInfo, X25519PrivateKeyParameters.KeySize), 0); @@ -187,6 +188,117 @@ public static AsymmetricKeyParameter CreateKey( { return new Ed448PrivateKeyParameters(GetRawKey(keyInfo, Ed448PrivateKeyParameters.KeySize), 0); } + else if (algOid.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512) + || algOid.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256)) + { + Gost3410PublicKeyAlgParameters gostParams = Gost3410PublicKeyAlgParameters.GetInstance(keyInfo.PrivateKeyAlgorithm.Parameters); + ECGOST3410Parameters ecSpec = null; + BigInteger d = null; + Asn1Object p = keyInfo.PrivateKeyAlgorithm.Parameters.ToAsn1Object(); + if (p is Asn1Sequence && (Asn1Sequence.GetInstance(p).Count == 2 || Asn1Sequence.GetInstance(p).Count == 3)) + { + + ECDomainParameters ecP = ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet); + + ecSpec = new ECGOST3410Parameters( + new ECNamedDomainParameters( + gostParams.PublicKeyParamSet, ecP), + gostParams.PublicKeyParamSet, + gostParams.DigestParamSet, + gostParams.EncryptionParamSet); + Asn1Encodable privKey = keyInfo.ParsePrivateKey(); + if (privKey is DerInteger) + { + d = DerInteger.GetInstance(privKey).PositiveValue; + } + else + { + byte[] encVal = Asn1OctetString.GetInstance(privKey).GetOctets(); + byte[] dVal = new byte[encVal.Length]; + + for (int i = 0; i != encVal.Length; i++) + { + dVal[i] = encVal[encVal.Length - 1 - i]; + } + + d = new BigInteger(1, dVal); + } + + + } + else + { + X962Parameters parameters = X962Parameters.GetInstance(keyInfo.PrivateKeyAlgorithm.Parameters); + + if (parameters.IsNamedCurve) + { + DerObjectIdentifier oid = DerObjectIdentifier.GetInstance(parameters.Parameters); + X9ECParameters ecP = ECNamedCurveTable.GetByOid(oid); + if (ecP == null) + { + ECDomainParameters gParam = ECGost3410NamedCurves.GetByOid(oid); + ecSpec = new ECGOST3410Parameters(new ECNamedDomainParameters( + oid, + gParam.Curve, + gParam.G, + gParam.N, + gParam.H, + gParam.GetSeed()), gostParams.PublicKeyParamSet, gostParams.DigestParamSet, + gostParams.EncryptionParamSet); + } + else + { + ecSpec = new ECGOST3410Parameters(new ECNamedDomainParameters( + oid, + ecP.Curve, + ecP.G, + ecP.N, + ecP.H, + ecP.GetSeed()), gostParams.PublicKeyParamSet, gostParams.DigestParamSet, + gostParams.EncryptionParamSet); + } + } + else if (parameters.IsImplicitlyCA) + { + ecSpec = null; + } + else + { + X9ECParameters ecP = X9ECParameters.GetInstance(parameters.Parameters); + ecSpec = new ECGOST3410Parameters(new ECNamedDomainParameters( + algOid, + ecP.Curve, + ecP.G, + ecP.N, + ecP.H, + ecP.GetSeed()), + gostParams.PublicKeyParamSet, + gostParams.DigestParamSet, + gostParams.EncryptionParamSet); + } + + Asn1Encodable privKey = keyInfo.ParsePrivateKey(); + if (privKey is DerInteger) + { + DerInteger derD = DerInteger.GetInstance(privKey); + d = derD.Value; + } + else + { + ECPrivateKeyStructure ec = ECPrivateKeyStructure.GetInstance(privKey); + d = ec.GetKey(); + } + } + + return new ECPrivateKeyParameters( + d, + new ECGOST3410Parameters( + ecSpec, + gostParams.PublicKeyParamSet, + gostParams.DigestParamSet, + gostParams.EncryptionParamSet)); + + } else { throw new SecurityUtilityException("algorithm identifier in private key not recognised"); @@ -203,50 +315,50 @@ private static byte[] GetRawKey(PrivateKeyInfo keyInfo, int expectedSize) } public static AsymmetricKeyParameter DecryptKey( - char[] passPhrase, - EncryptedPrivateKeyInfo encInfo) + char[] passPhrase, + EncryptedPrivateKeyInfo encInfo) { return CreateKey(PrivateKeyInfoFactory.CreatePrivateKeyInfo(passPhrase, encInfo)); } public static AsymmetricKeyParameter DecryptKey( - char[] passPhrase, - byte[] encryptedPrivateKeyInfoData) + char[] passPhrase, + byte[] encryptedPrivateKeyInfoData) { return DecryptKey(passPhrase, Asn1Object.FromByteArray(encryptedPrivateKeyInfoData)); } public static AsymmetricKeyParameter DecryptKey( - char[] passPhrase, - Stream encryptedPrivateKeyInfoStream) + char[] passPhrase, + Stream encryptedPrivateKeyInfoStream) { return DecryptKey(passPhrase, Asn1Object.FromStream(encryptedPrivateKeyInfoStream)); } private static AsymmetricKeyParameter DecryptKey( - char[] passPhrase, - Asn1Object asn1Object) + char[] passPhrase, + Asn1Object asn1Object) { return DecryptKey(passPhrase, EncryptedPrivateKeyInfo.GetInstance(asn1Object)); } public static byte[] EncryptKey( - DerObjectIdentifier algorithm, - char[] passPhrase, - byte[] salt, - int iterationCount, - AsymmetricKeyParameter key) + DerObjectIdentifier algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + AsymmetricKeyParameter key) { return EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( algorithm, passPhrase, salt, iterationCount, key).GetEncoded(); } public static byte[] EncryptKey( - string algorithm, - char[] passPhrase, - byte[] salt, - int iterationCount, - AsymmetricKeyParameter key) + string algorithm, + char[] passPhrase, + byte[] salt, + int iterationCount, + AsymmetricKeyParameter key) { return EncryptedPrivateKeyInfoFactory.CreateEncryptedPrivateKeyInfo( algorithm, passPhrase, salt, iterationCount, key).GetEncoded(); diff --git a/crypto/src/security/PublicKeyFactory.cs b/crypto/src/security/PublicKeyFactory.cs index e39748e45a..3623c3ee23 100644 --- a/crypto/src/security/PublicKeyFactory.cs +++ b/crypto/src/security/PublicKeyFactory.cs @@ -8,6 +8,7 @@ using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Rosstandart; using Org.BouncyCastle.Asn1.Sec; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; @@ -16,6 +17,7 @@ using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.Security { @@ -234,6 +236,40 @@ public static AsymmetricKeyParameter CreateKey( else if (algOid.Equals(EdECObjectIdentifiers.id_Ed448)) { return new Ed448PublicKeyParameters(GetRawKey(keyInfo, Ed448PublicKeyParameters.KeySize), 0); + } else if ( + algOid.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256) || + algOid.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512)) + { + + byte[] keyEnc = ((DerOctetString)Asn1Object.FromByteArray(keyInfo.PublicKeyData.GetOctets())).str; + + int fieldSize = 32; + if (algOid.Equals(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512)) + { + fieldSize = 64; + } + + int keySize = 2 * fieldSize; + + byte[] x9Encoding = new byte[1 + keySize]; + x9Encoding[0] = 0x04; + for (int i = 1; i <= fieldSize; ++i) + { + x9Encoding[i] = keyEnc[fieldSize - i]; + x9Encoding[i + fieldSize] = keyEnc[keySize - i]; + } + + Gost3410PublicKeyAlgParameters gostParams = Gost3410PublicKeyAlgParameters.GetInstance(keyInfo.AlgorithmID.Parameters); + + ECGOST3410Parameters ecDomainParameters = + new ECGOST3410Parameters( + new ECNamedDomainParameters(gostParams.PublicKeyParamSet, ECGost3410NamedCurves.GetByOid(gostParams.PublicKeyParamSet)), + gostParams.PublicKeyParamSet, + gostParams.DigestParamSet, + gostParams.EncryptionParamSet); + + return new ECPublicKeyParameters(ecDomainParameters.Curve.DecodePoint(x9Encoding), ecDomainParameters); + } else { diff --git a/crypto/src/util/BigIntegers.cs b/crypto/src/util/BigIntegers.cs index df78d1d86c..6674750bb9 100644 --- a/crypto/src/util/BigIntegers.cs +++ b/crypto/src/util/BigIntegers.cs @@ -46,6 +46,17 @@ public static byte[] AsUnsignedByteArray(int length, BigInteger n) return tmp; } + /// + /// Creates a Random BigInteger from the secure random of a given bit length. + /// + /// + /// + /// + public static BigInteger CreateRandomBigInteger(int bitLength, SecureRandom secureRandom) + { + return new BigInteger(bitLength, secureRandom); + } + /** * Return a random BigInteger not less than 'min' and not greater than 'max' * diff --git a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs index 2fa8b7a28d..234bcff343 100644 --- a/crypto/src/x509/SubjectPublicKeyInfoFactory.cs +++ b/crypto/src/x509/SubjectPublicKeyInfoFactory.cs @@ -6,12 +6,14 @@ using Org.BouncyCastle.Asn1.EdEC; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Rosstandart; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Math; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; namespace Org.BouncyCastle.X509 { @@ -91,9 +93,54 @@ public static SubjectPublicKeyInfo CreateSubjectPublicKeyInfo( } // End of RSA. if (publicKey is ECPublicKeyParameters) - { + { + ECPublicKeyParameters _key = (ECPublicKeyParameters) publicKey; + + if (_key.Parameters is ECGOST3410Parameters) + { + ECGOST3410Parameters gostParams = (ECGOST3410Parameters)_key.Parameters; + + BigInteger bX = _key.Q.AffineXCoord.ToBigInteger(); + BigInteger bY = _key.Q.AffineYCoord.ToBigInteger(); + bool is512 = (bX.BitLength > 256); + + Gost3410PublicKeyAlgParameters parameters = new Gost3410PublicKeyAlgParameters( + gostParams.PublicKeyParamSet, + gostParams.DigestParamSet, + gostParams.EncryptionParamSet); + + int encKeySize; + int offset; + DerObjectIdentifier algIdentifier; + if (is512) + { + encKeySize = 128; + offset = 64; + algIdentifier = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512; + } + else + { + encKeySize = 64; + offset = 32; + algIdentifier = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256; + } + + byte[] encKey = new byte[encKeySize]; + + ExtractBytes(encKey, encKeySize / 2, 0, bX); + ExtractBytes(encKey, encKeySize / 2, offset, bY); + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(algIdentifier, parameters), new DerOctetString(encKey)); + + + } // End of ECGOST3410_2012 + + + + + if (_key.AlgorithmName == "ECGOST3410") { if (_key.PublicKeyParamSet == null) @@ -209,5 +256,22 @@ private static void ExtractBytes( encKey[offset + i] = val[val.Length - 1 - i]; } } + + + private static void ExtractBytes(byte[] encKey, int size, int offSet, BigInteger bI) + { + byte[] val = bI.ToByteArray(); + if (val.Length < size) + { + byte[] tmp = new byte[size]; + Array.Copy(val, 0, tmp, tmp.Length - val.Length, val.Length); + val = tmp; + } + + for (int i = 0; i != size; i++) + { + encKey[offSet + i] = val[val.Length - 1 - i]; + } + } } } diff --git a/crypto/test/src/crypto/test/ECGOST3410_2012Test.cs b/crypto/test/src/crypto/test/ECGOST3410_2012Test.cs new file mode 100644 index 0000000000..94726bc92d --- /dev/null +++ b/crypto/test/src/crypto/test/ECGOST3410_2012Test.cs @@ -0,0 +1,597 @@ +using System; +using NUnit.Framework; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Rosstandart; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.Test; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Crypto.Tests +{ + [TestFixture] + public class ECGOST3410_2012Test:SimpleTest + { + public override string Name + { + get { return "ECGOST3410-2012-Test"; } + } + + public SimpleTestResult EncodeRecodePublicKey() + { + + DerObjectIdentifier oid = ECGost3410NamedCurves.GetOid("Tc26-Gost-3410-12-512-paramSetA"); + ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGost3410NamedCurves.GetByOid(oid)); + ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512,null); + ECKeyGenerationParameters paramameters = new ECKeyGenerationParameters(gostParams, new SecureRandom()); + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + engine.Init(paramameters); + AsymmetricCipherKeyPair pair = engine.GenerateKeyPair(); + + ECPublicKeyParameters generatedKeyParameters = (ECPublicKeyParameters)pair.Public; + ECPublicKeyParameters keyParameters = generatedKeyParameters; + + + // + // Continuously encode/decode the key and check for loss of information. + // + for (int t = 0; t < 3; t++) + { + + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyParameters); + keyParameters = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(info); + + { // Specifically cast and test gost parameters. + ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.Parameters; + ECGOST3410Parameters rParam = (ECGOST3410Parameters)keyParameters.Parameters; + + + bool ok = SafeEquals(gParam.DigestParamSet, rParam.DigestParamSet) && + SafeEquals(gParam.EncryptionParamSet, rParam.EncryptionParamSet) && + SafeEquals(gParam.PublicKeyParamSet, rParam.PublicKeyParamSet); + + if (!ok) + { + return new SimpleTestResult(false, "GOST parameters does not match"); + } + + } + + if (!((ECGOST3410Parameters)keyParameters.Parameters).Name.Equals( + ((ECGOST3410Parameters)generatedKeyParameters.Parameters).Name)) + { + return new SimpleTestResult(false, "Name does not match"); + } + + + if (keyParameters.IsPrivate != generatedKeyParameters.IsPrivate) + { + return new SimpleTestResult(false, "isPrivate does not match"); + } + + if (!Arrays.AreEqual(keyParameters.Q.GetEncoded(true), generatedKeyParameters.Q.GetEncoded(true))) + { + return new SimpleTestResult(false, "Q does not match"); + } + + if (!keyParameters.Parameters.Curve.Equals(generatedKeyParameters.Parameters.Curve)) + { + return new SimpleTestResult(false, "Curve does not match"); + } + + if (!Arrays.AreEqual( + keyParameters.Parameters.G.GetEncoded(true), + generatedKeyParameters.Parameters.G.GetEncoded(true))) + { + return new SimpleTestResult(false, "G does not match"); + } + + if (!keyParameters.Parameters.H.Equals(generatedKeyParameters.Parameters.H)) + { + return new SimpleTestResult(false, "H does not match"); + } + + if (!keyParameters.Parameters.HInv.Equals(generatedKeyParameters.Parameters.HInv)) + { + return new SimpleTestResult(false, "Hinv does not match"); + } + + if (!keyParameters.Parameters.N.Equals(generatedKeyParameters.Parameters.N)) + { + return new SimpleTestResult(false, "N does not match"); + } + + if (!Arrays.AreEqual(keyParameters.Parameters.GetSeed(), generatedKeyParameters.Parameters.GetSeed())) + { + return new SimpleTestResult(false, "Seed does not match"); + } + } + return new SimpleTestResult(true, null); + + + } + + + private SimpleTestResult EncodeRecodePrivateKey() + { + + DerObjectIdentifier oid = ECGost3410NamedCurves.GetOid("Tc26-Gost-3410-12-512-paramSetA"); + ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGost3410NamedCurves.GetByOid(oid)); + ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512,null); + ECKeyGenerationParameters parameters = new ECKeyGenerationParameters(gostParams, new SecureRandom()); + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + engine.Init(parameters); + AsymmetricCipherKeyPair pair = engine.GenerateKeyPair(); + + ECPrivateKeyParameters generatedKeyParameters = (ECPrivateKeyParameters)pair.Private; + ECPrivateKeyParameters keyParameters = generatedKeyParameters; + + + // + // Continuously encode/decode the key and check for loss of information. + // + + + for (int t = 0; t < 3; t++) + { + PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyParameters); + keyParameters = (ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(info); + + { // Specifically cast and test gost parameters. + ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.Parameters; + ECGOST3410Parameters rParam = (ECGOST3410Parameters)keyParameters.Parameters; + + bool ok = SafeEquals(gParam.DigestParamSet, rParam.DigestParamSet) && + SafeEquals(gParam.EncryptionParamSet, rParam.EncryptionParamSet) && + SafeEquals(gParam.PublicKeyParamSet, rParam.PublicKeyParamSet); + + if (!ok) + { + return new SimpleTestResult(false, "GOST parameters does not match"); + } + + } + + if (keyParameters.IsPrivate != generatedKeyParameters.IsPrivate) + { + return new SimpleTestResult(false, "isPrivate does not match"); + } + + if (!keyParameters.D.Equals(generatedKeyParameters.D)) + { + return new SimpleTestResult(false, "D does not match"); + } + + if (!((ECGOST3410Parameters)keyParameters.Parameters).Name.Equals( + ((ECGOST3410Parameters)generatedKeyParameters.Parameters).Name)) + { + return new SimpleTestResult(false, "Name does not match"); + } + + if (!keyParameters.Parameters.Curve.Equals(generatedKeyParameters.Parameters.Curve)) + { + return new SimpleTestResult(false, "Curve does not match"); + } + + if (!Arrays.AreEqual( + keyParameters.Parameters.G.GetEncoded(true), + generatedKeyParameters.Parameters.G.GetEncoded(true))) + { + return new SimpleTestResult(false, "G does not match"); + } + + if (!keyParameters.Parameters.H.Equals(generatedKeyParameters.Parameters.H)) + { + return new SimpleTestResult(false, "H does not match"); + } + + if (!keyParameters.Parameters.HInv.Equals(generatedKeyParameters.Parameters.HInv)) + { + return new SimpleTestResult(false, "Hinv does not match"); + } + + if (!keyParameters.Parameters.N.Equals(generatedKeyParameters.Parameters.N)) + { + return new SimpleTestResult(false, "N does not match"); + } + + if (!Arrays.AreEqual(keyParameters.Parameters.GetSeed(), generatedKeyParameters.Parameters.GetSeed())) + { + return new SimpleTestResult(false, "Seed does not match"); + } + } + + + + return new SimpleTestResult(true, null); + } + + private SimpleTestResult DecodeJCEPublic() + { + byte[] pub256 = Hex.Decode("3068302106082a85030701010101301506092a850307010201010106082a850307010102020343000440292335c87d892510c35a033819a13e2b0dc606d911676af2bad8872d74a4b7bae6c729e98ace04c3dee626343f794731e1489edb7bc26f1c8c56e1448c96501a"); + + ECPublicKeyParameters pkInfo = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(pub256); + + if (pkInfo.IsPrivate) + { + return new SimpleTestResult(false, "isPrivate should be false"); + } + + if ( + !Arrays.AreEqual( + pkInfo.Q.GetEncoded(true), + Hex.Decode("02bab7a4742d87d8baf26a6711d906c60d2b3ea11938035ac31025897dc8352329"))) + { + return new SimpleTestResult(false, "Q does not match"); + } + + if (!((ECGOST3410Parameters)pkInfo.Parameters).PublicKeyParamSet.ToString().Equals("1.2.643.7.1.2.1.1.1")) + { + return new SimpleTestResult(false, "PublicKeyParamSet does not match"); + } + + if (!((ECGOST3410Parameters)pkInfo.Parameters).DigestParamSet.ToString().Equals("1.2.643.7.1.1.2.2")) + { + return new SimpleTestResult(false, "DigestParamSet does not match"); + } + + if (((ECGOST3410Parameters)pkInfo.Parameters).EncryptionParamSet != null) + { + return new SimpleTestResult(false, "EncryptionParamSet is not null"); + } + + + byte[] pub512 = Hex.Decode("3081aa302106082a85030701010102301506092a850307010201020106082a850307010102030381840004818043ccc22692ee8a1870c7c9de0566d7e3a494cf0e3c80f9e8852a3d1ec10d2a829d357253e0864aee2eaacd5e2d327578dee771f62f24decfd6358e06199efe540e7912db43c4c80fe0fd31f7f67a862f9d44fd0075cfee6e3d638c7520063d26311ef962547e8129fb8c5b194e129370cd30313884b4a60872254a10772fe595"); + + pkInfo = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(pub512); + + if (pkInfo.IsPrivate) + { + return new SimpleTestResult(false, "isPrivate should be true"); + } + + if ( + !Arrays.AreEqual( + pkInfo.Q.GetEncoded(true), + Hex.Decode("0254fe9e19068e35d6cfde242ff671e7de7875322d5ecdaa2eee4a86e05372359d822a0dc11e3d2a85e8f9803c0ecf94a4e3d76605dec9c770188aee9226c2cc43"))) + { + return new SimpleTestResult(false, "Q does not match"); + } + + + if (!((ECGOST3410Parameters)pkInfo.Parameters).PublicKeyParamSet.ToString().Equals("1.2.643.7.1.2.1.2.1")) + { + return new SimpleTestResult(false, "PublicKeyParamSet does not match"); + } + + if (!((ECGOST3410Parameters)pkInfo.Parameters).DigestParamSet.ToString().Equals("1.2.643.7.1.1.2.3")) + { + return new SimpleTestResult(false, "DigestParamSet does not match"); + } + + if (((ECGOST3410Parameters)pkInfo.Parameters).EncryptionParamSet != null) + { + return new SimpleTestResult(false, "EncryptionParamSet is not null"); + } + + + + return new SimpleTestResult(true, null); + } + + private SimpleTestResult DecodeJCEPrivate() + { + byte[] priv256 = Hex.Decode("304a020100302106082a85030701010101301506092a850307010201010106082a8503070101020204220420fe75ba328d5439ed4859e6dc7e6ca2e9aab0818f094eddeb0d57d1c16a90762b"); + ECPrivateKeyParameters pkInfo = (ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(priv256); + + if (!pkInfo.IsPrivate) + { + return new SimpleTestResult(false, "isPrivate should be true"); + } + + if ( + !Arrays.AreEqual( + Hex.Decode("2b76906ac1d1570debdd4e098f81b0aae9a26c7edce65948ed39548d32ba75fe"), + pkInfo.D.ToByteArray())) + { + return new SimpleTestResult(false, "D does not match"); + } + + if (!((ECGOST3410Parameters)pkInfo.Parameters).PublicKeyParamSet.ToString().Equals("1.2.643.7.1.2.1.1.1")) + { + return new SimpleTestResult(false, "PublicKeyParamSet does not match"); + } + + if (!((ECGOST3410Parameters)pkInfo.Parameters).DigestParamSet.ToString().Equals("1.2.643.7.1.1.2.2")) + { + return new SimpleTestResult(false, "DigestParamSet does not match"); + } + + if (((ECGOST3410Parameters)pkInfo.Parameters).EncryptionParamSet != null) + { + return new SimpleTestResult(false, "EncryptionParamSet is not null"); + } + + + byte[] priv512 = Hex.Decode("306a020100302106082a85030701010102301506092a850307010201020106082a85030701010203044204402fc35576152f6e873236608b592b4b98d0793bf5184f8dc4a99512be703716991a96061ef46aceeae5319b5c69e6fcbfa7e339207878597ce50f9b7cbf857ff1"); + + pkInfo = (ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(priv512); + + if (!pkInfo.IsPrivate) + { + return new SimpleTestResult(false, "isPrivate should be true"); + } + + if ( + !Arrays.AreEqual( + Hex.Decode("00f17f85bf7c9b0fe57c5978782039e3a7bffce6695c9b31e5eace6af41e06961a99163770be1295a9c48d4f18f53b79d0984b2b598b603632876e2f157655c32f"), + pkInfo.D.ToByteArray())) + { + return new SimpleTestResult(false, "D does not match"); + } + + if (!((ECGOST3410Parameters)pkInfo.Parameters).PublicKeyParamSet.ToString().Equals("1.2.643.7.1.2.1.2.1")) + { + return new SimpleTestResult(false, "PublicKeyParamSet does not match"); + } + + if (!((ECGOST3410Parameters)pkInfo.Parameters).DigestParamSet.ToString().Equals("1.2.643.7.1.1.2.3")) + { + return new SimpleTestResult(false, "DigestParamSet does not match"); + } + + if (((ECGOST3410Parameters)pkInfo.Parameters).EncryptionParamSet != null) + { + return new SimpleTestResult(false, "EncryptionParamSet is not null"); + } + + + + return new SimpleTestResult(true, null); + } + + + + public SimpleTestResult EncodeDecodePrivateLW(String oidStr, DerObjectIdentifier digest) + { + DerObjectIdentifier oid = ECGost3410NamedCurves.GetOid(oidStr); + ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGost3410NamedCurves.GetByOid(oid)); + ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, digest, null); + ECKeyGenerationParameters parameters = new ECKeyGenerationParameters(gostParams, new SecureRandom()); + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + engine.Init(parameters); + AsymmetricCipherKeyPair pair = engine.GenerateKeyPair(); + + + ECPrivateKeyParameters generatedKeyParameters = (ECPrivateKeyParameters) pair.Private; + + PrivateKeyInfo info = PrivateKeyInfoFactory.CreatePrivateKeyInfo(generatedKeyParameters); + + ECPrivateKeyParameters recoveredKeyParameters = (ECPrivateKeyParameters)PrivateKeyFactory.CreateKey(info); + + + { // Specifically cast and test gost parameters. + ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.Parameters; + ECGOST3410Parameters rParam = (ECGOST3410Parameters)recoveredKeyParameters.Parameters; + + bool ok = SafeEquals(gParam.DigestParamSet, rParam.DigestParamSet) && + SafeEquals(gParam.EncryptionParamSet, rParam.EncryptionParamSet) && + SafeEquals(gParam.PublicKeyParamSet, rParam.PublicKeyParamSet); + + if (!ok) + { + return new SimpleTestResult(false, "GOST parameters does not match"); + } + + } + + + if (recoveredKeyParameters.IsPrivate != generatedKeyParameters.IsPrivate) + { + return new SimpleTestResult(false, "isPrivate does not match"); + } + + if (!((ECGOST3410Parameters)recoveredKeyParameters.Parameters).Name.Equals( + ((ECGOST3410Parameters)generatedKeyParameters.Parameters).Name)) + { + return new SimpleTestResult(false, "Name does not match"); + } + + + if (!recoveredKeyParameters.D.Equals(generatedKeyParameters.D)) + { + return new SimpleTestResult(false, "D does not match"); + } + + if (!recoveredKeyParameters.Parameters.Curve.Equals(generatedKeyParameters.Parameters.Curve)) + { + return new SimpleTestResult(false, "Curve does not match"); + } + + if (!Arrays.AreEqual( + recoveredKeyParameters.Parameters.G.GetEncoded(true), + generatedKeyParameters.Parameters.G.GetEncoded(true))) + { + return new SimpleTestResult(false, "G does not match"); + } + + if (!recoveredKeyParameters.Parameters.H.Equals(generatedKeyParameters.Parameters.H)) + { + return new SimpleTestResult(false, "H does not match"); + } + + if (!recoveredKeyParameters.Parameters.HInv.Equals(generatedKeyParameters.Parameters.HInv)) + { + return new SimpleTestResult(false, "Hinv does not match"); + } + + if (!recoveredKeyParameters.Parameters.N.Equals(generatedKeyParameters.Parameters.N)) + { + return new SimpleTestResult(false, "N does not match"); + } + + if (!Arrays.AreEqual(recoveredKeyParameters.Parameters.GetSeed(), generatedKeyParameters.Parameters.GetSeed())) + { + return new SimpleTestResult(false, "Seed does not match"); + } + + return new SimpleTestResult(true, null); + } + + public SimpleTestResult EncodeDecodePublicLW(string oidStr, DerObjectIdentifier digest) + { + DerObjectIdentifier oid = ECGost3410NamedCurves.GetOid(oidStr); + ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGost3410NamedCurves.GetByOid(oid)); + ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp,oid,digest,null); + ECKeyGenerationParameters parameters = new ECKeyGenerationParameters(gostParams, new SecureRandom()); + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + engine.Init(parameters); + AsymmetricCipherKeyPair pair = engine.GenerateKeyPair(); + ECPublicKeyParameters generatedKeyParameters = (ECPublicKeyParameters)pair.Public; + + SubjectPublicKeyInfo info = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(generatedKeyParameters); + + ECPublicKeyParameters recoveredKeyParameters = (ECPublicKeyParameters)PublicKeyFactory.CreateKey(info); + + { // Specifically cast and test gost parameters. + ECGOST3410Parameters gParam = (ECGOST3410Parameters)generatedKeyParameters.Parameters; + ECGOST3410Parameters rParam = (ECGOST3410Parameters)recoveredKeyParameters.Parameters; + + + bool ok = SafeEquals(gParam.DigestParamSet, rParam.DigestParamSet) && + SafeEquals(gParam.EncryptionParamSet, rParam.EncryptionParamSet) && + SafeEquals(gParam.PublicKeyParamSet, rParam.PublicKeyParamSet); + + if (!ok) + { + return new SimpleTestResult(false, "GOST parameters does not match"); + } + + } + + if (!((ECGOST3410Parameters)recoveredKeyParameters.Parameters).Name.Equals( + ((ECGOST3410Parameters)generatedKeyParameters.Parameters).Name)) + { + return new SimpleTestResult(false, "Name does not match"); + } + + + if (recoveredKeyParameters.IsPrivate != generatedKeyParameters.IsPrivate) + { + return new SimpleTestResult(false, "isPrivate does not match"); + } + + if (!Arrays.AreEqual(recoveredKeyParameters.Q.GetEncoded(true), generatedKeyParameters.Q.GetEncoded(true))) + { + return new SimpleTestResult(false, "Q does not match"); + } + + if (!recoveredKeyParameters.Parameters.Curve.Equals(generatedKeyParameters.Parameters.Curve)) + { + return new SimpleTestResult(false, "Curve does not match"); + } + + if (!Arrays.AreEqual( + recoveredKeyParameters.Parameters.G.GetEncoded(true), + generatedKeyParameters.Parameters.G.GetEncoded(true))) + { + return new SimpleTestResult(false, "G does not match"); + } + + if (!recoveredKeyParameters.Parameters.H.Equals(generatedKeyParameters.Parameters.H)) + { + return new SimpleTestResult(false, "H does not match"); + } + + if (!recoveredKeyParameters.Parameters.HInv.Equals(generatedKeyParameters.Parameters.HInv)) + { + return new SimpleTestResult(false, "Hinv does not match"); + } + + if (!recoveredKeyParameters.Parameters.N.Equals(generatedKeyParameters.Parameters.N)) + { + return new SimpleTestResult(false, "N does not match"); + } + + if (!Arrays.AreEqual(recoveredKeyParameters.Parameters.GetSeed(), generatedKeyParameters.Parameters.GetSeed())) + { + return new SimpleTestResult(false, "Seed does not match"); + } + + return new SimpleTestResult(true, null); + } + + [Test] + public override void PerformTest() + { + + SimpleTestResult str = EncodeDecodePublicLW("Tc26-Gost-3410-12-512-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512); + if (!str.IsSuccessful()) + { + Fail(str.ToString(), str.GetException()); + } + + str = EncodeDecodePrivateLW("Tc26-Gost-3410-12-512-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512); + if (!str.IsSuccessful()) + { + Fail(str.ToString(), str.GetException()); + } + + + str = EncodeDecodePublicLW("Tc26-Gost-3410-12-256-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256); + if (!str.IsSuccessful()) + { + Fail(str.ToString(), str.GetException()); + } + + str = EncodeDecodePrivateLW("Tc26-Gost-3410-12-256-paramSetA", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256); + if (!str.IsSuccessful()) + { + Fail(str.ToString(), str.GetException()); + } + + + str = DecodeJCEPrivate(); + if (!str.IsSuccessful()) + { + Fail(str.ToString(), str.GetException()); + } + + str = DecodeJCEPublic(); + if (!str.IsSuccessful()) + { + Fail(str.ToString(), str.GetException()); + } + + str = EncodeRecodePrivateKey(); + if (!str.IsSuccessful()) + { + Fail(str.ToString(), str.GetException()); + } + + str = EncodeRecodePublicKey(); + if (!str.IsSuccessful()) + { + Fail(str.ToString(), str.GetException()); + } + + } + + private bool SafeEquals(object left, object right) + { + if (left == null || right == null) + { + return left == null && right == null; + } + + return left.Equals(right); + } + } +} \ No newline at end of file diff --git a/crypto/test/src/crypto/test/EGOST3410_2012SignatureTest.cs b/crypto/test/src/crypto/test/EGOST3410_2012SignatureTest.cs new file mode 100644 index 0000000000..0065174edb --- /dev/null +++ b/crypto/test/src/crypto/test/EGOST3410_2012SignatureTest.cs @@ -0,0 +1,187 @@ +using System; +using System.Security.Cryptography.X509Certificates; +using NUnit.Framework; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Rosstandart; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Math.EC; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.Test; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Crypto.Tests +{ + [TestFixture] + public class EGOST3410_2012SignatureTest : SimpleTest + { + public override string Name { get; } + + [Test] + public override void PerformTest() + { + EcGOST34102012256Test(); + } + + + public void EcGOST34102012256Test() + { + BigInteger r = new BigInteger("29700980915817952874371204983938256990422752107994319651632687982059210933395"); + BigInteger s = new BigInteger("574973400270084654178925310019147038455227042649098563933718999175515839552"); + + BigInteger e = new BigInteger("20798893674476452017134061561508270130637142515379653289952617252661468872421"); + + byte[] kData = BigIntegers.AsUnsignedByteArray(new BigInteger("53854137677348463731403841147996619241504003434302020712960838528893196233395")); + SecureRandom k = new TestRandomBigInteger(kData); + + BigInteger mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564821041"); + BigInteger mod_q = new BigInteger("57896044618658097711785492504343953927082934583725450622380973592137631069619"); + + + ECCurve curve = new FpCurve( + mod_p, + new BigInteger("7"), // a + new BigInteger("43308876546767276905765904595650931995942111794451039583252968842033849580414"), // b + mod_q, BigInteger.One); + + ECDomainParameters spec = new ECDomainParameters(curve, + curve.CreatePoint( + new BigInteger("2"), // x + new BigInteger("4018974056539037503335449422937059775635739389905545080690979365213431566280")), // y + mod_q, BigInteger.One); + + ECPrivateKeyParameters privateKey = new ECPrivateKeyParameters( + new BigInteger("55441196065363246126355624130324183196576709222340016572108097750006097525544"), // d + spec); + + ECPublicKeyParameters publicKey = new ECPublicKeyParameters(curve.CreatePoint( + new BigInteger("57520216126176808443631405023338071176630104906313632182896741342206604859403"), // x + new BigInteger("17614944419213781543809391949654080031942662045363639260709847859438286763994")), // y + spec); + + ECGOST3410_2012Signer signer = new ECGOST3410_2012Signer(); + signer.Init(true, new ParametersWithRandom(privateKey, k)); + + byte[] rev = e.ToByteArray(); + byte[] message = new byte[rev.Length]; + for (int i = 0; i != rev.Length; i++) + { + message[i] = rev[rev.Length - 1 - i]; + } + BigInteger[] sig = signer.GenerateSignature(message); + + signer.Init(false, publicKey); + + if (!signer.VerifySignature(message, sig[0], sig[1])) + { + Fail("ECGOST3410 2012 verification failed"); + } + + if (!r.Equals(sig[0])) + { + Fail( + ": r component wrong." + Environment.NewLine + + " expecting: " + r + Environment.NewLine + + " got : " + sig[0]); + } + + if (!s.Equals(sig[1])) + { + Fail( + ": s component wrong." + Environment.NewLine + + " expecting: " + s + Environment.NewLine + + " got : " + sig[1]); + } + + + // 256Bit + { + DerObjectIdentifier oid = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256_paramSetA; + ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGost3410NamedCurves.GetByOid(oid)); + ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, + RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256, null); + ECKeyGenerationParameters parameters = new ECKeyGenerationParameters(gostParams, new SecureRandom()); + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + engine.Init(parameters); + AsymmetricCipherKeyPair pair = engine.GenerateKeyPair(); + SignatureGost12Test("ECGOST3410-2012-256", 64, pair); + } + + // 512Bit + + + { + DerObjectIdentifier oid = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512_paramSetA; + ECNamedDomainParameters ecp = new ECNamedDomainParameters(oid, ECGost3410NamedCurves.GetByOid(oid)); + ECGOST3410Parameters gostParams = new ECGOST3410Parameters(ecp, oid, + RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512, null); + ECKeyGenerationParameters parameters = new ECKeyGenerationParameters(gostParams, new SecureRandom()); + ECKeyPairGenerator engine = new ECKeyPairGenerator(); + engine.Init(parameters); + AsymmetricCipherKeyPair pair = engine.GenerateKeyPair(); + + SignatureGost12Test("ECGOST3410-2012-512", 128, pair); + + } + } + + + + private void SignatureGost12Test(String signatureAlg, int expectedSignLen, AsymmetricCipherKeyPair p) + + { + byte[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; + + ECPrivateKeyParameters sKey = (ECPrivateKeyParameters)p.Private; + ECPublicKeyParameters vKey = (ECPublicKeyParameters)p.Public; + + ECGOST3410_2012Signer s = new ECGOST3410_2012Signer(); + + s.Init(true, sKey); + BigInteger[] sig = s.GenerateSignature(data); + + + s = new ECGOST3410_2012Signer(); + s.Init(false, vKey); + + if (!s.VerifySignature(data, sig[0], sig[1])) + { + Fail("Signature " + signatureAlg + " did not verify"); + } + + // + // Test with Digest signer. + // + Gost3410DigestSigner digestSigner = new Gost3410DigestSigner( + new ECGOST3410_2012Signer(), + DigestUtilities.GetDigest(((ECGOST3410Parameters)vKey.Parameters).DigestParamSet)); + digestSigner.Init(true, sKey); + digestSigner.BlockUpdate(data, 0, data.Length); + byte[] sigBytes = digestSigner.GenerateSignature(); + + if (sigBytes.Length != expectedSignLen) + { + Fail(signatureAlg + " signature failed at expected length"); + } + + digestSigner = new Gost3410DigestSigner( + new ECGOST3410_2012Signer(), + DigestUtilities.GetDigest(((ECGOST3410Parameters)vKey.Parameters).DigestParamSet)); + digestSigner.Init(false, vKey); + digestSigner.BlockUpdate(data, 0, data.Length); + + if (!digestSigner.VerifySignature(sigBytes)) + { + Fail("Signature " + signatureAlg + " did not verify"); + } + } + + + } +} \ No newline at end of file From 839879ee7ad82df31adc2a12cf237760d3fdfda5 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 14 Jan 2019 16:43:22 +1100 Subject: [PATCH 04/22] minor tweaks --- .../crypto/signers/EcGost3410_2012Signer.cs | 16 +++++++++------- crypto/test/src/crypto/test/GOST3410Test.cs | 18 +++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/crypto/src/crypto/signers/EcGost3410_2012Signer.cs b/crypto/src/crypto/signers/EcGost3410_2012Signer.cs index cd50916dd2..e7174ace61 100644 --- a/crypto/src/crypto/signers/EcGost3410_2012Signer.cs +++ b/crypto/src/crypto/signers/EcGost3410_2012Signer.cs @@ -1,7 +1,7 @@ using Org.BouncyCastle.Math; using System; using System.IO; -using NUnit.Core; + using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math.EC; using Org.BouncyCastle.Math.EC.Multiplier; @@ -53,7 +53,7 @@ public BigInteger[] GenerateSignature(byte[] message) { if (!forSigning) { - throw new InvalidOperationException("not initialised for signing."); + throw new InvalidOperationException("not initialized for signing"); } byte[] mRev = new byte[message.Length]; // conversion is little-endian @@ -79,15 +79,18 @@ public BigInteger[] GenerateSignature(byte[] message) do { k = BigIntegers.CreateRandomBigInteger(n.BitLength, secureRandom); - } while (k.Equals(BigInteger.Zero)); // ECConstants.ZERO)); + } + while (k.Equals(BigInteger.Zero)); // ECConstants.ZERO)); ECPoint p = basePointMultiplier.Multiply(ec.G, k).Normalize(); r = p.AffineXCoord.ToBigInteger().Mod(n); - } while (r.Equals(BigInteger.Zero)); // ECConstants.ZERO)); + } + while (r.Equals(BigInteger.Zero)); // ECConstants.ZERO)); s = (k.Multiply(e)).Add(d.Multiply(r)).Mod(n); - } while (s.Equals(BigInteger.Zero)); // ECConstants.ZERO)); + } + while (s.Equals(BigInteger.Zero)); // ECConstants.ZERO)); return new BigInteger[] { r, s }; } @@ -97,7 +100,7 @@ public bool VerifySignature(byte[] message, BigInteger r, BigInteger s) { if (forSigning) { - throw new InvalidOperationException("not initialised for verification."); + throw new InvalidOperationException("not initialized for verification"); } @@ -146,6 +149,5 @@ protected virtual ECMultiplier CreateBasePointMultiplier() { return new FixedPointCombMultiplier(); } - } } \ No newline at end of file diff --git a/crypto/test/src/crypto/test/GOST3410Test.cs b/crypto/test/src/crypto/test/GOST3410Test.cs index f54662770b..1369d35061 100644 --- a/crypto/test/src/crypto/test/GOST3410Test.cs +++ b/crypto/test/src/crypto/test/GOST3410Test.cs @@ -1553,15 +1553,15 @@ public ITestResult Perform() { new Gost3410_TEST1_512(), new Gost3410_TEST2_512(), -// new Gost3410_TEST1_1024(), -// new Gost3410_TEST2_1024(), -// new Gost3410_AParam(), -// new Gost3410_BParam(), -// new Gost3410_CParam(), -// new Gost3410_DParam(), -// new Gost3410_AExParam(), -// new Gost3410_BExParam(), -// new Gost3410_CExParam() + new Gost3410_TEST1_1024(), + new Gost3410_TEST2_1024(), + new Gost3410_AParam(), + new Gost3410_BParam(), + new Gost3410_CParam(), + new Gost3410_DParam(), + new Gost3410_AExParam(), + new Gost3410_BExParam(), + new Gost3410_CExParam() }; public string Name From ecc8edb622f0f42d43f72ae388fa4c4274e51c5f Mon Sep 17 00:00:00 2001 From: Megan Woods Date: Mon, 14 Jan 2019 17:07:22 +1100 Subject: [PATCH 05/22] Initial CMP --- .../cmp/CertificateConfirmationContent.cs | 44 ++ .../CertificateConfirmationContentBuilder.cs | 71 +++ crypto/src/asn1/cmp/CertificateStatus.cs | 54 +++ crypto/src/asn1/cmp/CmpException.cs | 26 ++ crypto/src/asn1/cmp/GeneralPKIMessage.cs | 49 +++ crypto/src/asn1/cmp/ProtectedPkiMessage.cs | 107 +++++ .../asn1/cmp/ProtectedPkiMessageBuilder.cs | 174 ++++++++ crypto/src/asn1/crmf/AuthenticatorControl.cs | 33 ++ .../asn1/crmf/CertificateRequestMessage.cs | 182 ++++++++ .../crmf/CertificateRequestMessageBuilder.cs | 261 +++++++++++ crypto/src/asn1/crmf/Controls.cs | 9 + crypto/src/asn1/crmf/CrmfException.cs | 26 ++ crypto/src/asn1/crmf/EncryptedValueBuilder.cs | 53 +++ crypto/src/asn1/crmf/OptionalValidity.cs | 6 + crypto/src/asn1/crmf/PkiArchiveControl.cs | 68 +++ .../ProofOfPossessionSigningKeyBuilder.cs | 90 ++++ crypto/src/asn1/crmf/RegTokenControl.cs | 32 ++ crypto/src/cms/CMSSignedGenerator.cs | 337 ++++++++++++++ crypto/src/crypto/IMacFactory.cs | 20 + crypto/src/crypto/operators/Asn1Mac.cs | 410 ++++++++++++++++++ crypto/src/crypto/operators/Asn1Signature.cs | 2 + crypto/src/crypto/operators/GenericKey.cs | 41 ++ crypto/src/util/Strings.cs | 25 ++ crypto/test/src/cms/test/AllTests.cs | 3 +- 24 files changed, 2122 insertions(+), 1 deletion(-) create mode 100644 crypto/src/asn1/cmp/CertificateConfirmationContent.cs create mode 100644 crypto/src/asn1/cmp/CertificateConfirmationContentBuilder.cs create mode 100644 crypto/src/asn1/cmp/CertificateStatus.cs create mode 100644 crypto/src/asn1/cmp/CmpException.cs create mode 100644 crypto/src/asn1/cmp/GeneralPKIMessage.cs create mode 100644 crypto/src/asn1/cmp/ProtectedPkiMessage.cs create mode 100644 crypto/src/asn1/cmp/ProtectedPkiMessageBuilder.cs create mode 100644 crypto/src/asn1/crmf/AuthenticatorControl.cs create mode 100644 crypto/src/asn1/crmf/CertificateRequestMessage.cs create mode 100644 crypto/src/asn1/crmf/CertificateRequestMessageBuilder.cs create mode 100644 crypto/src/asn1/crmf/CrmfException.cs create mode 100644 crypto/src/asn1/crmf/EncryptedValueBuilder.cs create mode 100644 crypto/src/asn1/crmf/PkiArchiveControl.cs create mode 100644 crypto/src/asn1/crmf/ProofOfPossessionSigningKeyBuilder.cs create mode 100644 crypto/src/asn1/crmf/RegTokenControl.cs create mode 100644 crypto/src/crypto/IMacFactory.cs create mode 100644 crypto/src/crypto/operators/Asn1Mac.cs create mode 100644 crypto/src/crypto/operators/GenericKey.cs diff --git a/crypto/src/asn1/cmp/CertificateConfirmationContent.cs b/crypto/src/asn1/cmp/CertificateConfirmationContent.cs new file mode 100644 index 0000000000..9f2f3e0381 --- /dev/null +++ b/crypto/src/asn1/cmp/CertificateConfirmationContent.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Cms; + + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertificateConfirmationContent + { + private DefaultDigestAlgorithmIdentifierFinder digestAlgFinder; + private CertConfirmContent content; + + + public CertificateConfirmationContent(CertConfirmContent content) + { + this.content = content; + } + + public CertificateConfirmationContent(CertConfirmContent content, + DefaultDigestAlgorithmIdentifierFinder digestAlgFinder) + { + this.content = content; + this.digestAlgFinder = digestAlgFinder; + } + + public CertConfirmContent ToAsn1Structure() + { + return content; + } + + public CertificateStatus[] GetStatusMessages() + { + CertStatus[] statusArray = content.ToCertStatusArray(); + CertificateStatus[] ret = new CertificateStatus[statusArray.Length]; + for (int i = 0; i != ret.Length; i++) + { + ret[i] = new CertificateStatus(digestAlgFinder, statusArray[i]); + } + + return ret; + } + } +} diff --git a/crypto/src/asn1/cmp/CertificateConfirmationContentBuilder.cs b/crypto/src/asn1/cmp/CertificateConfirmationContentBuilder.cs new file mode 100644 index 0000000000..b8c306f816 --- /dev/null +++ b/crypto/src/asn1/cmp/CertificateConfirmationContentBuilder.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertificateConfirmationContentBuilder + { + DefaultSignatureAlgorithmIdentifierFinder sigAlgFinder = new DefaultSignatureAlgorithmIdentifierFinder(); + private DefaultDigestAlgorithmIdentifierFinder digestAlgFinder; + private ArrayList acceptedCerts = new ArrayList(); + private ArrayList acceptedReqIds = new ArrayList(); + + public CertificateConfirmationContentBuilder() : this(new DefaultDigestAlgorithmIdentifierFinder()) + { + + } + + public CertificateConfirmationContentBuilder(DefaultDigestAlgorithmIdentifierFinder digestAlgFinder) + { + this.digestAlgFinder = digestAlgFinder; + } + + public CertificateConfirmationContentBuilder AddAcceptedCertificate(X509Certificate certHolder, + BigInteger certReqId) + { + acceptedCerts.Add(certHolder); + acceptedReqIds.Add(certReqId); + return this; + } + + public CertificateConfirmationContent Build() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + for (int i = 0; i != acceptedCerts.Count; i++) + { + X509Certificate cert = (X509Certificate) acceptedCerts[i]; + BigInteger reqId = (BigInteger) acceptedReqIds[i]; + + + + AlgorithmIdentifier algorithmIdentifier = sigAlgFinder.Find(cert.SigAlgName); + + AlgorithmIdentifier digAlg = digestAlgFinder.find(algorithmIdentifier); + if (digAlg == null) + { + throw new CmpException("cannot find algorithm for digest from signature"); + } + + DigestSink sink = new DigestSink(DigestUtilities.GetDigest(digAlg.Algorithm)); + + sink.Write(cert.GetEncoded()); + + byte[] dig = new byte[sink.Digest.GetDigestSize()]; + sink.Digest.DoFinal(dig, 0); + + v.Add(new CertStatus(dig,reqId)); + } + + return new CertificateConfirmationContent(CertConfirmContent.GetInstance(new DerSequence(v)), + digestAlgFinder); + } + } +} diff --git a/crypto/src/asn1/cmp/CertificateStatus.cs b/crypto/src/asn1/cmp/CertificateStatus.cs new file mode 100644 index 0000000000..d16c8e0069 --- /dev/null +++ b/crypto/src/asn1/cmp/CertificateStatus.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CertificateStatus + { + private DefaultSignatureAlgorithmIdentifierFinder sigAlgFinder = new DefaultSignatureAlgorithmIdentifierFinder(); + private DefaultDigestAlgorithmIdentifierFinder digestAlgFinder; + private CertStatus certStatus; + + public CertificateStatus(DefaultDigestAlgorithmIdentifierFinder digestAlgFinder, CertStatus certStatus) + { + this.digestAlgFinder = digestAlgFinder; + this.certStatus = certStatus; + } + + public PkiStatusInfo PkiStatusInfo + { + get { return certStatus.StatusInfo; } + } + + public BigInteger CertRequestId + { + get { return certStatus.CertReqID.Value; } + } + + public bool IsVerified(X509Certificate cert) + { + + AlgorithmIdentifier digAlg = digestAlgFinder.find( sigAlgFinder.Find(cert.SigAlgName)); + if (digAlg == null) + { + throw new CmpException("cannot find algorithm for digest from signature "+cert.SigAlgName); + } + + DigestSink digestSink = new DigestSink(DigestUtilities.GetDigest(digAlg.Algorithm)); + + digestSink.Write(cert.GetEncoded()); + + byte[] digest = new byte[digestSink.Digest.GetDigestSize()]; + digestSink.Digest.DoFinal(digest, 0); + return Arrays.ConstantTimeAreEqual(certStatus.CertHash.GetOctets(), digest); + } + } +} diff --git a/crypto/src/asn1/cmp/CmpException.cs b/crypto/src/asn1/cmp/CmpException.cs new file mode 100644 index 0000000000..0500b7d3e7 --- /dev/null +++ b/crypto/src/asn1/cmp/CmpException.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class CmpException : Exception + { + public CmpException() + { + } + + public CmpException(string message) : base(message) + { + } + + public CmpException(string message, Exception innerException) : base(message, innerException) + { + } + + protected CmpException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/crypto/src/asn1/cmp/GeneralPKIMessage.cs b/crypto/src/asn1/cmp/GeneralPKIMessage.cs new file mode 100644 index 0000000000..d91b8ef7e0 --- /dev/null +++ b/crypto/src/asn1/cmp/GeneralPKIMessage.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + public class GeneralPKIMessage + { + private readonly PkiMessage pkiMessage; + + private static PkiMessage parseBytes(byte[] encoding) + { + return PkiMessage.GetInstance(Asn1Object.FromByteArray(encoding)); + } + + public GeneralPKIMessage(PkiMessage pkiMessage) + { + this.pkiMessage = pkiMessage; + } + + public GeneralPKIMessage(byte[] encoding) : this(parseBytes(encoding)) + { + } + + public PkiHeader Header { + get { + return pkiMessage.Header; + } + } + + public PkiBody Body + { + get + { + return pkiMessage.Body; + } + } + + public bool HasProtection + { + get { return pkiMessage.Protection != null; } + } + + public PkiMessage ToAsn1Structure() + { + return pkiMessage; + } + } +} diff --git a/crypto/src/asn1/cmp/ProtectedPkiMessage.cs b/crypto/src/asn1/cmp/ProtectedPkiMessage.cs new file mode 100644 index 0000000000..c39f06ad04 --- /dev/null +++ b/crypto/src/asn1/cmp/ProtectedPkiMessage.cs @@ -0,0 +1,107 @@ +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509; +using System; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Asn1.Cmp +{ + + public class ProtectedPkiMessage + { + private PkiMessage pkiMessage; + + + public ProtectedPkiMessage(GeneralPKIMessage pkiMessage) + { + + if (!pkiMessage.HasProtection) + { + throw new ArgumentException("pki message not protected"); + } + + this.pkiMessage = pkiMessage.ToAsn1Structure(); + } + + public ProtectedPkiMessage(PkiMessage pkiMessage) + { + if (pkiMessage.Header.ProtectionAlg == null) + { + throw new ArgumentException("pki message not protected"); + } + + this.pkiMessage = pkiMessage; + } + + public PkiHeader Header { get { return pkiMessage.Header; } } + public PkiBody Body { get { return pkiMessage.Body; } } + + public PkiMessage ToAsn1Message() { return pkiMessage; } + + public bool HasPasswordBasedMacProtected { get { return Header.ProtectionAlg.Algorithm.Equals(CmpObjectIdentifiers.passwordBasedMac); } } + + public X509Certificate[] GetCertificates() + { + CmpCertificate[] certs = pkiMessage.GetExtraCerts(); + + if (certs == null) + { + return new X509Certificate[0]; + } + + X509Certificate[] res = new X509Certificate[certs.Length]; + for (int t=0; t 0) + { + InfoTypeAndValue[] genInfos = new InfoTypeAndValue[generalInfos.Count]; + for (int t = 0; t < genInfos.Length; t++) + { + genInfos[t] = (InfoTypeAndValue) generalInfos[t]; + } + + hdrBuilBuilder.SetGeneralInfo(genInfos); + } + } + + private ProtectedPkiMessage FinalizeMessage(PkiHeader header, DerBitString protection) + { + if (extraCerts.Count > 0) + { + CmpCertificate[] cmpCertificates = new CmpCertificate[extraCerts.Count]; + for (int i = 0; i < cmpCertificates.Length; i++) + { + byte[] cert = ((X509Certificate) extraCerts[i]).GetEncoded(); + cmpCertificates[i] = CmpCertificate.GetInstance((Asn1Sequence.FromByteArray(cert))); + } + + return new ProtectedPkiMessage(new PkiMessage(header, body, protection, cmpCertificates)); + } + + return new ProtectedPkiMessage(new PkiMessage(header, body, protection)); + } + + private byte[] CalculateSignature(IStreamCalculator signer, PkiHeader header, PkiBody body) + { + Asn1EncodableVector avec = new Asn1EncodableVector(); + avec.Add(header); + avec.Add(body); + byte[] encoded = new DerSequence(avec).GetEncoded(); + signer.Stream.Write(encoded, 0, encoded.Length); + Object result = signer.GetResult(); + + + if (result is DefaultSignatureResult) + { + return ((DefaultSignatureResult) result).Collect(); + } + else if (result is DefaultMacAndDigestResult) + { + return ((DefaultMacAndDigestResult) result).MacResult; + } + else if (result is byte[]) + { + return (byte[]) result; + } + + throw new InvalidOperationException("result is not byte[] or DefaultSignatureResult"); + } + } +} \ No newline at end of file diff --git a/crypto/src/asn1/crmf/AuthenticatorControl.cs b/crypto/src/asn1/crmf/AuthenticatorControl.cs new file mode 100644 index 0000000000..060088b1fc --- /dev/null +++ b/crypto/src/asn1/crmf/AuthenticatorControl.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class AuthenticatorControl:IControl + { + + private static readonly DerObjectIdentifier type = CrmfObjectIdentifiers.id_regCtrl_authenticator; + + private readonly DerUtf8String token; + + public AuthenticatorControl(DerUtf8String token) + { + this.token = token; + } + + public AuthenticatorControl(String token) + { + this.token = new DerUtf8String(token); + } + + public DerObjectIdentifier Type + { + get { return type; } + } + + public Asn1Encodable Value { + get { return token; } + } + } +} diff --git a/crypto/src/asn1/crmf/CertificateRequestMessage.cs b/crypto/src/asn1/crmf/CertificateRequestMessage.cs new file mode 100644 index 0000000000..b80c2a3fda --- /dev/null +++ b/crypto/src/asn1/crmf/CertificateRequestMessage.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Operators; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CertificateRequestMessage + { + public static readonly int popRaVerified = Org.BouncyCastle.Asn1.Crmf.ProofOfPossession.TYPE_RA_VERIFIED; + public static readonly int popSigningKey = Org.BouncyCastle.Asn1.Crmf.ProofOfPossession.TYPE_SIGNING_KEY; + public static readonly int popKeyEncipherment = Org.BouncyCastle.Asn1.Crmf.ProofOfPossession.TYPE_KEY_ENCIPHERMENT; + public static readonly int popKeyAgreement = Org.BouncyCastle.Asn1.Crmf.ProofOfPossession.TYPE_KEY_AGREEMENT; + + private readonly CertReqMsg certReqMsg; + private readonly Controls controls; + + private static CertReqMsg ParseBytes(byte[] encoding) + + { + return CertReqMsg.GetInstance(encoding); + } + + public CertificateRequestMessage(CertReqMsg certReqMsg) + { + this.certReqMsg = certReqMsg; + this.controls = certReqMsg.CertReq.Controls; + } + + public CertReqMsg ToAsn1Structure() + { + return certReqMsg; + } + + public CertTemplate GetCertTemplate() + { + return this.certReqMsg.CertReq.CertTemplate; + } + + public bool HasControls + { + get { return controls != null; } + } + + + public bool HasControl(DerObjectIdentifier objectIdentifier) + { + return findControl(objectIdentifier) != null; + } + + public IControl GetControl(DerObjectIdentifier type) + { + AttributeTypeAndValue found = findControl(type); + if (found != null) + { + if (found.Type.Equals(CrmfObjectIdentifiers.id_regCtrl_pkiArchiveOptions)) + { + return new PkiArchiveControl(PkiArchiveOptions.GetInstance(found.Value)); + } + + if (found.Type.Equals(CrmfObjectIdentifiers.id_regCtrl_regToken)) + { + return new RegTokenControl(DerUtf8String.GetInstance(found.Value)); + } + + if (found.Type.Equals(CrmfObjectIdentifiers.id_regCtrl_authenticator)) + { + return new AuthenticatorControl(DerUtf8String.GetInstance(found.Value)); + } + } + return null; + } + + + + + public AttributeTypeAndValue findControl(DerObjectIdentifier type) + { + if (controls == null) + { + return null; + } + + AttributeTypeAndValue[] tAndV = controls.ToAttributeTypeAndValueArray(); + AttributeTypeAndValue found = null; + + for (int i = 0; i < tAndV.Length; i++) + { + if (tAndV[i].Type.Equals(type)) + { + found = tAndV[i]; + break; + } + } + + return found; + } + + public bool HasProofOfPossession + { + get { return certReqMsg.Popo != null; } + } + + public int ProofOfPossession + { + get { return certReqMsg.Popo.Type; } + } + + public bool HasSigningKeyProofOfPossessionWithPkMac + { + get + { + ProofOfPossession pop = certReqMsg.Popo; + + if (pop.Type == popSigningKey) + { + PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object); + + return popoSign.PoposkInput.PublicKeyMac != null; + } + + return false; + + } + } + + public bool IsValidSigningKeyPop(IVerifierFactoryProvider verifierProvider) + { + ProofOfPossession pop = certReqMsg.Popo; + if (pop.Type == popSigningKey) + { + PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object); + if (popoSign.PoposkInput != null && popoSign.PoposkInput.PublicKeyMac != null) + { + throw new InvalidOperationException("verification requires password check"); + } + return verifySignature(verifierProvider, popoSign); + } + + throw new InvalidOperationException("not Signing Key type of proof of possession"); + } + + + + private bool verifySignature(IVerifierFactoryProvider verifierFactoryProvider, PopoSigningKey signKey) + { + IVerifierFactory verifer; + IStreamCalculator calculator; + try + { + verifer = verifierFactoryProvider.CreateVerifierFactory(signKey.AlgorithmIdentifier); + calculator = verifer.CreateCalculator(); + } + catch (Exception ex) + { + throw new CrmfException("unable to create verifier: "+ex.Message, ex); + } + + if (signKey.PoposkInput != null) + { + byte[] b = signKey.GetDerEncoded(); + calculator.Stream.Write(b,0,b.Length); + } + else + { + byte[] b = certReqMsg.GetDerEncoded(); + calculator.Stream.Write(b,0,b.Length); + } + + DefaultVerifierResult result = (DefaultVerifierResult) calculator.GetResult(); + + return result.IsVerified(signKey.Signature.GetBytes()); + } + + public byte[] GetEncoded() + { + return certReqMsg.GetEncoded(); + } + } +} diff --git a/crypto/src/asn1/crmf/CertificateRequestMessageBuilder.cs b/crypto/src/asn1/crmf/CertificateRequestMessageBuilder.cs new file mode 100644 index 0000000000..df604f4f56 --- /dev/null +++ b/crypto/src/asn1/crmf/CertificateRequestMessageBuilder.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Math; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CertificateRequestMessageBuilder + { + private readonly BigInteger _certReqId; + private X509ExtensionsGenerator _extGenerator; + private CertTemplateBuilder _templateBuilder; + private ArrayList _controls= new ArrayList(); + private ISignatureFactory _popSigner; + private PkMacFactory _pkMacBuilder; + private char[] _password; + private GeneralName _sender; + private int _popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT; + private PopoPrivKey _popoPrivKey; + private Asn1Null _popRaVerified; + private PKMacValue _agreeMac; + + public CertificateRequestMessageBuilder(BigInteger certReqId) + { + this._certReqId = certReqId; + this._extGenerator = new X509ExtensionsGenerator(); + this._templateBuilder = new CertTemplateBuilder(); + } + + public CertificateRequestMessageBuilder SetPublicKey(SubjectPublicKeyInfo publicKeyInfo) + { + if (publicKeyInfo != null) + { + _templateBuilder.SetPublicKey(publicKeyInfo); + } + + return this; + } + + + public CertificateRequestMessageBuilder SetIssuer(X509Name issuer) + { + if (issuer != null) + { + _templateBuilder.SetIssuer(issuer); + } + + return this; + } + + public CertificateRequestMessageBuilder SetSubject(X509Name subject) + { + if (subject != null) + { + _templateBuilder.SetSubject(subject); + } + + return this; + } + + public CertificateRequestMessageBuilder SetSerialNumber(BigInteger serialNumber) + { + if (serialNumber != null) + { + _templateBuilder.SetSerialNumber(new DerInteger(serialNumber)); + } + + return this; + } + + public CertificateRequestMessageBuilder SetValidity(Time notBefore, Time notAfter) + { + _templateBuilder.SetValidity(new OptionalValidity(notBefore, notAfter)); + return this; + } + + public CertificateRequestMessageBuilder AddExtension(DerObjectIdentifier oid, bool critical, + Asn1Encodable value) + { + _extGenerator.AddExtension(oid,critical, value); + return this; + } + + public CertificateRequestMessageBuilder AddExtension(DerObjectIdentifier oid, bool critical, + byte[] value) + { + _extGenerator.AddExtension(oid, critical, value); + return this; + } + + public CertificateRequestMessageBuilder SetProofOfPossessionSignKeySigner(ISignatureFactory popoSignatureFactory) + { + if (_popoPrivKey != null || _popRaVerified != null || _agreeMac != null) + { + throw new InvalidOperationException("only one proof of possession is allowed."); + } + + this._popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT; + return this; + } + + public CertificateRequestMessageBuilder SetProofOfPossessionSubsequentMessage(SubsequentMessage msg) + { + if (_popoPrivKey != null || _popRaVerified != null || _agreeMac != null) + { + throw new InvalidOperationException("only one proof of possession is allowed."); + } + + this._popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT; + this._popoPrivKey = new PopoPrivKey(msg); + + + return this; + } + + + public CertificateRequestMessageBuilder SetProofOfPossessionSubsequentMessage(int type, SubsequentMessage msg) + { + if (_popoPrivKey != null || _popRaVerified != null || _agreeMac != null) + { + throw new InvalidOperationException("only one proof of possession is allowed."); + } + + if (type != ProofOfPossession.TYPE_KEY_ENCIPHERMENT && type != ProofOfPossession.TYPE_KEY_AGREEMENT) + { + throw new ArgumentException("type must be ProofOfPossession.TYPE_KEY_ENCIPHERMENT || ProofOfPossession.TYPE_KEY_AGREEMENT"); + } + + this._popoType = type; + this._popoPrivKey = new PopoPrivKey(msg); + return this; + } + + public CertificateRequestMessageBuilder SetProofOfPossessionAgreeMac(PKMacValue macValue) + { + if (_popSigner != null || _popRaVerified != null || _popoPrivKey != null) + { + throw new InvalidOperationException("only one proof of possession allowed"); + } + + this._agreeMac = macValue; + return this; + } + + public CertificateRequestMessageBuilder SetProofOfPossessionRaVerified() + { + if (_popSigner != null || _popoPrivKey != null) + { + throw new InvalidOperationException("only one proof of possession allowed"); + } + + this._popRaVerified = DerNull.Instance; + + return this; + } + + public CertificateRequestMessageBuilder SetAuthInfoPKMAC(PkMacFactory pkmacFactory, char[] password) + { + this._pkMacBuilder = pkmacFactory; + this._password = password; + + return this; + } + + public CertificateRequestMessageBuilder SetAuthInfoSender(X509Name sender) + { + return SetAuthInfoSender(new GeneralName(sender)); + } + + public CertificateRequestMessageBuilder SetAuthInfoSender(GeneralName sender) + { + this._sender = sender; + return this; + } + + public CertificateRequestMessage Build() + { + Asn1EncodableVector v = new Asn1EncodableVector(); + + v.Add(new DerInteger(this._certReqId)); + + if (!this._extGenerator.IsEmpty) + { + this._templateBuilder.SetExtensions(_extGenerator.Generate()); + } + + v.Add(_templateBuilder.Build()); + + if (_controls.Count>0) + { + Asn1EncodableVector controlV = new Asn1EncodableVector(); + + foreach (Object item in _controls) + { + IControl control = (IControl) item; + controlV.Add(new AttributeTypeAndValue(control.Type, control.Value)); + } + + v.Add(new DerSequence(controlV)); + } + + CertRequest request = CertRequest.GetInstance(new DerSequence(v)); + + v = new Asn1EncodableVector(); + + v.Add(request); + + if (_popSigner != null) + { + CertTemplate template = request.CertTemplate; + + if (template.Subject == null || template.PublicKey == null) + { + SubjectPublicKeyInfo pubKeyInfo = request.CertTemplate.PublicKey; + + ProofOfPossessionSigningKeyBuilder builder = new ProofOfPossessionSigningKeyBuilder(pubKeyInfo); + + if (_sender != null) + { + builder.setSender(_sender); + } + else + { + // PkMa pkmacGenerator = new PKMACValueGenerator(_pkmacBuilder); + + builder.setPublicKeyMac(_pkMacBuilder, _password); + } + + v.Add(new ProofOfPossession(builder.build(_popSigner))); + } + else + { + ProofOfPossessionSigningKeyBuilder builder = new ProofOfPossessionSigningKeyBuilder(request); + + v.Add(new ProofOfPossession(builder.build(_popSigner))); + } + } + else if (_popoPrivKey != null) + { + v.Add(new ProofOfPossession(_popoType, _popoPrivKey)); + } + else if (_agreeMac != null) + { + v.Add(new ProofOfPossession(ProofOfPossession.TYPE_KEY_AGREEMENT, + PopoPrivKey.GetInstance(new DerTaggedObject(false, PopoPrivKey.agreeMAC, _agreeMac),true ))); + + } + else if (_popRaVerified != null) + { + v.Add(new ProofOfPossession()); + } + + return new CertificateRequestMessage(CertReqMsg.GetInstance(new DerSequence(v))); + } + } +} diff --git a/crypto/src/asn1/crmf/Controls.cs b/crypto/src/asn1/crmf/Controls.cs index e8b9f3db0a..8f986168c9 100644 --- a/crypto/src/asn1/crmf/Controls.cs +++ b/crypto/src/asn1/crmf/Controls.cs @@ -4,6 +4,15 @@ namespace Org.BouncyCastle.Asn1.Crmf { + + public interface IControl + { + DerObjectIdentifier Type { get; } + + Asn1Encodable Value { get; } + } + + public class Controls : Asn1Encodable { diff --git a/crypto/src/asn1/crmf/CrmfException.cs b/crypto/src/asn1/crmf/CrmfException.cs new file mode 100644 index 0000000000..7043ccd735 --- /dev/null +++ b/crypto/src/asn1/crmf/CrmfException.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class CrmfException : Exception + { + public CrmfException() + { + } + + public CrmfException(string message) : base(message) + { + } + + public CrmfException(string message, Exception innerException) : base(message, innerException) + { + } + + protected CrmfException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/crypto/src/asn1/crmf/EncryptedValueBuilder.cs b/crypto/src/asn1/crmf/EncryptedValueBuilder.cs new file mode 100644 index 0000000000..4b57156d4b --- /dev/null +++ b/crypto/src/asn1/crmf/EncryptedValueBuilder.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + +// public delegate IBlockCipher BlockCipherCreator(ICipherParameters); +// +// public class EncryptedValueBuilder +// { +// private readonly IBlockCipher _cipher; +// private static readonly IDictionary algToDelegate = Platform.CreateHashtable(); +// static EncryptedValueBuilder() +// { +// algToDelegate[NistObjectIdentifiers.IdAes128Cbc] = new CipherCreator() +// {Creator = delegate(ICipherParameters param) { return new AesEngine(); }}; +// +// } +// +// +// public EncryptedValueBuilder(DerObjectIdentifier alg) +// { +// +// } +// +// +// private static IBlockCipher AesCBC(ICipherParameters param) +// { +// if (param is ParametersWithIV ivParam) { +// return new +// } +// else +// { +// throw new ArgumentException("expecting param to be ParametersWithIv"); +// } +// } +// +// +// +// private class CipherCreator +// { +// public BlockCipherCreator Creator { get; set; } +// } +// +// } +} diff --git a/crypto/src/asn1/crmf/OptionalValidity.cs b/crypto/src/asn1/crmf/OptionalValidity.cs index d1a0f7ffbb..46fd1f860e 100644 --- a/crypto/src/asn1/crmf/OptionalValidity.cs +++ b/crypto/src/asn1/crmf/OptionalValidity.cs @@ -33,6 +33,12 @@ public static OptionalValidity GetInstance(object obj) return new OptionalValidity(Asn1Sequence.GetInstance(obj)); } + public OptionalValidity(Time notBefore, Time notAfter) + { + this.notBefore = notBefore; + this.notAfter = notAfter; + } + public virtual Time NotBefore { get { return notBefore; } diff --git a/crypto/src/asn1/crmf/PkiArchiveControl.cs b/crypto/src/asn1/crmf/PkiArchiveControl.cs new file mode 100644 index 0000000000..0290fdfe67 --- /dev/null +++ b/crypto/src/asn1/crmf/PkiArchiveControl.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Cms; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class PkiArchiveControl:IControl + { + public static readonly int encryptedPrivKey = PkiArchiveOptions.encryptedPrivKey; + public static readonly int keyGenParameters = PkiArchiveOptions.keyGenParameters; + public static readonly int archiveRemGenPrivKey = PkiArchiveOptions.archiveRemGenPrivKey; + + private static readonly DerObjectIdentifier type = CrmfObjectIdentifiers.id_regCtrl_pkiArchiveOptions; + + private readonly PkiArchiveOptions pkiArchiveOptions; + + public PkiArchiveControl(PkiArchiveOptions pkiArchiveOptions) + { + this.pkiArchiveOptions = pkiArchiveOptions; + } + + public DerObjectIdentifier Type + { + get { return type; } + } + + public Asn1Encodable Value + { + get { return pkiArchiveOptions; } + } + + public int ArchiveType + { + get { return pkiArchiveOptions.Type; } + } + + public bool EnvelopedData + { + get + { + EncryptedKey encKey = EncryptedKey.GetInstance(pkiArchiveOptions.Value); + return !encKey.IsEncryptedValue; + } + } + + public CmsEnvelopedData GetEnvelopedData() + { + try + { + EncryptedKey encKey = EncryptedKey.GetInstance(pkiArchiveOptions.Value); + EnvelopedData data = Org.BouncyCastle.Asn1.Cms.EnvelopedData.GetInstance(encKey.Value); + + return new CmsEnvelopedData(new ContentInfo(CmsObjectIdentifiers.EnvelopedData, data)); + } + catch (CmsException e) + { + throw new CrmfException("CMS parsing error: " + e.Message, e); + } + catch (Exception e) + { + throw new CrmfException("CRMF parsing error: "+e.Message, e); + } + } + + } +} diff --git a/crypto/src/asn1/crmf/ProofOfPossessionSigningKeyBuilder.cs b/crypto/src/asn1/crmf/ProofOfPossessionSigningKeyBuilder.cs new file mode 100644 index 0000000000..cbaf834a12 --- /dev/null +++ b/crypto/src/asn1/crmf/ProofOfPossessionSigningKeyBuilder.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class ProofOfPossessionSigningKeyBuilder + { + private CertRequest _certRequest; + private SubjectPublicKeyInfo _pubKeyInfo; + private GeneralName _name; + private PKMacValue _publicKeyMAC; + + public ProofOfPossessionSigningKeyBuilder(CertRequest certRequest) + { + this._certRequest = certRequest; + } + + + public ProofOfPossessionSigningKeyBuilder(SubjectPublicKeyInfo pubKeyInfo) + { + this._pubKeyInfo = pubKeyInfo; + } + + public ProofOfPossessionSigningKeyBuilder setSender(GeneralName name) + { + this._name = name; + + return this; + } + + public ProofOfPossessionSigningKeyBuilder setPublicKeyMac(PkMacFactory generator, char[] password) + { + IStreamCalculator calc = generator.CreateCalculator(); + byte[] d = _pubKeyInfo.GetDerEncoded(); + calc.Stream.Write(d, 0, d.Length); + calc.Stream.Flush(); + calc.Stream.Close(); + + + this._publicKeyMAC = new PKMacValue( + (AlgorithmIdentifier)generator.AlgorithmDetails, + new DerBitString(((DefaultMacAndDigestResult)calc.GetResult()).MacResult)); + + return this; + } + + public PopoSigningKey build(ISignatureFactory signer) + { + if (_name != null && _publicKeyMAC != null) + { + throw new InvalidOperationException("name and publicKeyMAC cannot both be set."); + } + + PopoSigningKeyInput popo; + byte[] b; + IStreamCalculator calc = signer.CreateCalculator(); + if (_certRequest != null) + { + popo = null; + b = _certRequest.GetDerEncoded(); + calc.Stream.Write(b, 0, b.Length); + + } + else if (_name != null) + { + popo = new PopoSigningKeyInput(_name, _pubKeyInfo); + b = popo.GetDerEncoded(); + calc.Stream.Write(b, 0, b.Length); + } + else + { + popo = new PopoSigningKeyInput(_publicKeyMAC, _pubKeyInfo); + b = popo.GetDerEncoded(); + calc.Stream.Write(b, 0, b.Length); + } + + calc.Stream.Flush(); + calc.Stream.Close(); + DefaultSignatureResult res = (DefaultSignatureResult)calc.GetResult(); + return new PopoSigningKey(popo, (AlgorithmIdentifier)signer.AlgorithmDetails, new DerBitString(res.Collect())); + } + + + } +} diff --git a/crypto/src/asn1/crmf/RegTokenControl.cs b/crypto/src/asn1/crmf/RegTokenControl.cs new file mode 100644 index 0000000000..77bf32557e --- /dev/null +++ b/crypto/src/asn1/crmf/RegTokenControl.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class RegTokenControl:IControl + { + private static readonly DerObjectIdentifier type = CrmfObjectIdentifiers.id_regCtrl_regToken; + + private readonly DerUtf8String token; + + public RegTokenControl(DerUtf8String token) + { + this.token = token; + } + + public RegTokenControl(String token) + { + this.token = new DerUtf8String(token); + } + + public DerObjectIdentifier Type + { + get { return type; } + } + public Asn1Encodable Value + { + get { return token; } + } + } +} diff --git a/crypto/src/cms/CMSSignedGenerator.cs b/crypto/src/cms/CMSSignedGenerator.cs index 249d704993..1ac9f39b71 100644 --- a/crypto/src/cms/CMSSignedGenerator.cs +++ b/crypto/src/cms/CMSSignedGenerator.cs @@ -3,11 +3,16 @@ using System.IO; using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.BC; +using Org.BouncyCastle.Asn1.Bsi; using Org.BouncyCastle.Asn1.Cms; using Org.BouncyCastle.Asn1.CryptoPro; +using Org.BouncyCastle.Asn1.Eac; +using Org.BouncyCastle.Asn1.GM; using Org.BouncyCastle.Asn1.Nist; using Org.BouncyCastle.Asn1.Oiw; using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Rosstandart; using Org.BouncyCastle.Asn1.TeleTrust; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X9; @@ -21,6 +26,338 @@ namespace Org.BouncyCastle.Cms { + + public class DefaultSignatureAlgorithmIdentifierFinder + { + private static readonly IDictionary algorithms = Platform.CreateHashtable(); + private static readonly ISet noParams = new HashSet(); + private static readonly IDictionary _params = Platform.CreateHashtable(); + private static readonly ISet pkcs15RsaEncryption = new HashSet(); + private static readonly IDictionary digestOids = Platform.CreateHashtable(); + + private static readonly IDictionary digestBuilders = Platform.CreateHashtable(); + + + private static readonly DerObjectIdentifier ENCRYPTION_RSA = PkcsObjectIdentifiers.RsaEncryption; + private static readonly DerObjectIdentifier ENCRYPTION_DSA = X9ObjectIdentifiers.IdDsaWithSha1; + private static readonly DerObjectIdentifier ENCRYPTION_ECDSA = X9ObjectIdentifiers.ECDsaWithSha1; + private static readonly DerObjectIdentifier ENCRYPTION_RSA_PSS = PkcsObjectIdentifiers.IdRsassaPss; + private static readonly DerObjectIdentifier ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.GostR3410x94; + private static readonly DerObjectIdentifier ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.GostR3410x2001; + private static readonly DerObjectIdentifier ENCRYPTION_ECGOST3410_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256; + private static readonly DerObjectIdentifier ENCRYPTION_ECGOST3410_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512; + + + static DefaultSignatureAlgorithmIdentifierFinder() + { + algorithms["MD2WITHRSAENCRYPTION"] = PkcsObjectIdentifiers.MD2WithRsaEncryption; + algorithms["MD2WITHRSA"] = PkcsObjectIdentifiers.MD2WithRsaEncryption; + algorithms["MD5WITHRSAENCRYPTION"] = PkcsObjectIdentifiers.MD5WithRsaEncryption; + algorithms["MD5WITHRSA"] = PkcsObjectIdentifiers.MD5WithRsaEncryption; + algorithms["SHA1WITHRSAENCRYPTION"] = PkcsObjectIdentifiers.Sha1WithRsaEncryption; + algorithms["SHA1WITHRSA"] = PkcsObjectIdentifiers.Sha1WithRsaEncryption; + algorithms["SHA-1WITHRSA"] = PkcsObjectIdentifiers.Sha1WithRsaEncryption; + algorithms["SHA224WITHRSAENCRYPTION"] = PkcsObjectIdentifiers.Sha224WithRsaEncryption; + algorithms["SHA224WITHRSA"] = PkcsObjectIdentifiers.Sha224WithRsaEncryption; + algorithms["SHA256WITHRSAENCRYPTION"] = PkcsObjectIdentifiers.Sha256WithRsaEncryption; + algorithms["SHA256WITHRSA"] = PkcsObjectIdentifiers.Sha256WithRsaEncryption; + algorithms["SHA384WITHRSAENCRYPTION"] = PkcsObjectIdentifiers.Sha384WithRsaEncryption; + algorithms["SHA384WITHRSA"] = PkcsObjectIdentifiers.Sha384WithRsaEncryption; + algorithms["SHA512WITHRSAENCRYPTION"] = PkcsObjectIdentifiers.Sha512WithRsaEncryption; + algorithms["SHA512WITHRSA"] = PkcsObjectIdentifiers.Sha512WithRsaEncryption; + algorithms["SHA1WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["SHA224WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["SHA256WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["SHA384WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["SHA512WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["SHA3-224WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["SHA3-256WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["SHA3-384WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["SHA3-512WITHRSAANDMGF1"] = PkcsObjectIdentifiers.IdRsassaPss; + algorithms["RIPEMD160WITHRSAENCRYPTION"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160; + algorithms["RIPEMD160WITHRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160; + algorithms["RIPEMD128WITHRSAENCRYPTION"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128; + algorithms["RIPEMD128WITHRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128; + algorithms["RIPEMD256WITHRSAENCRYPTION"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256; + algorithms["RIPEMD256WITHRSA"] = TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256; + algorithms["SHA1WITHDSA"] = X9ObjectIdentifiers.IdDsaWithSha1; + algorithms["SHA-1WITHDSA"] = X9ObjectIdentifiers.IdDsaWithSha1; + algorithms["DSAWITHSHA1"] = X9ObjectIdentifiers.IdDsaWithSha1; + algorithms["SHA224WITHDSA"] = NistObjectIdentifiers.DsaWithSha224; + algorithms["SHA256WITHDSA"] = NistObjectIdentifiers.DsaWithSha256; + algorithms["SHA384WITHDSA"] = NistObjectIdentifiers.DsaWithSha384; + algorithms["SHA512WITHDSA"] = NistObjectIdentifiers.DsaWithSha512; + algorithms["SHA3-224WITHDSA"] = NistObjectIdentifiers.IdDsaWithSha3_224; // id_dsa_with_sha3_224; + algorithms["SHA3-256WITHDSA"] = NistObjectIdentifiers.IdDsaWithSha3_256; //id_dsa_with_sha3_256; + algorithms["SHA3-384WITHDSA"] = NistObjectIdentifiers.IdDsaWithSha3_384; //id_dsa_with_sha3_384; + algorithms["SHA3-512WITHDSA"] = NistObjectIdentifiers.IdDsaWithSha3_512; //id_dsa_with_sha3_512; + algorithms["SHA3-224WITHECDSA"] = NistObjectIdentifiers.IdEcdsaWithSha3_224;// id_ecdsa_with_sha3_224; + algorithms["SHA3-256WITHECDSA"] = NistObjectIdentifiers.IdEcdsaWithSha3_256;//id_ecdsa_with_sha3_256; + algorithms["SHA3-384WITHECDSA"] = NistObjectIdentifiers.IdEcdsaWithSha3_384;//id_ecdsa_with_sha3_384; + algorithms["SHA3-512WITHECDSA"] = NistObjectIdentifiers.IdEcdsaWithSha3_512;//id_ecdsa_with_sha3_512; + algorithms["SHA3-224WITHRSA"] = NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_224;// id_rsassa_pkcs1_v1_5_with_sha3_224; + algorithms["SHA3-256WITHRSA"] = NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_256;// id_rsassa_pkcs1_v1_5_with_sha3_256; + algorithms["SHA3-384WITHRSA"] = NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_384;// id_rsassa_pkcs1_v1_5_with_sha3_384; + algorithms["SHA3-512WITHRSA"] = NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_512;// id_rsassa_pkcs1_v1_5_with_sha3_512; + algorithms["SHA3-224WITHRSAENCRYPTION"] = NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_224;// id_rsassa_pkcs1_v1_5_with_sha3_224; + algorithms["SHA3-256WITHRSAENCRYPTION"] = NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_256;// id_rsassa_pkcs1_v1_5_with_sha3_256; + algorithms["SHA3-384WITHRSAENCRYPTION"] = NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_384; //id_rsassa_pkcs1_v1_5_with_sha3_384; + algorithms["SHA3-512WITHRSAENCRYPTION"] = NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_512; // id_rsassa_pkcs1_v1_5_with_sha3_512; + algorithms["SHA1WITHECDSA"] = X9ObjectIdentifiers.ECDsaWithSha1; + algorithms["ECDSAWITHSHA1"] = X9ObjectIdentifiers.ECDsaWithSha1; + algorithms["SHA224WITHECDSA"] = X9ObjectIdentifiers.ECDsaWithSha224; + algorithms["SHA256WITHECDSA"] = X9ObjectIdentifiers.ECDsaWithSha224; + algorithms["SHA384WITHECDSA"] = X9ObjectIdentifiers.ECDsaWithSha384; + algorithms["SHA512WITHECDSA"] = X9ObjectIdentifiers.ECDsaWithSha256; + + + algorithms["GOST3411WITHGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94; + algorithms["GOST3411WITHGOST3410-94"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94; + algorithms["GOST3411WITHECGOST3410"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001; + algorithms["GOST3411WITHECGOST3410-2001"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001; + algorithms["GOST3411WITHGOST3410-2001"] = CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001; + algorithms["GOST3411WITHECGOST3410-2012-256"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256; + algorithms["GOST3411WITHECGOST3410-2012-512"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512; + algorithms["GOST3411WITHGOST3410-2012-256"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256; + algorithms["GOST3411WITHGOST3410-2012-512"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512; + algorithms["GOST3411-2012-256WITHECGOST3410-2012-256"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256; + algorithms["GOST3411-2012-512WITHECGOST3410-2012-512"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512; + algorithms["GOST3411-2012-256WITHGOST3410-2012-256"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256; + algorithms["GOST3411-2012-512WITHGOST3410-2012-512"] = RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512; + algorithms["SHA1WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_SHA1; + algorithms["SHA224WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_SHA224; + algorithms["SHA256WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_SHA256; + algorithms["SHA384WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_SHA384; + algorithms["SHA512WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_SHA512; + algorithms["RIPEMD160WITHPLAIN-ECDSA"] = BsiObjectIdentifiers.ecdsa_plain_RIPEMD160; + algorithms["SHA1WITHCVC-ECDSA"] = EacObjectIdentifiers.id_TA_ECDSA_SHA_1; + algorithms["SHA224WITHCVC-ECDSA"] = EacObjectIdentifiers.id_TA_ECDSA_SHA_224; + algorithms["SHA256WITHCVC-ECDSA"] = EacObjectIdentifiers.id_TA_ECDSA_SHA_256; + algorithms["SHA384WITHCVC-ECDSA"] = EacObjectIdentifiers.id_TA_ECDSA_SHA_384; + algorithms["SHA512WITHCVC-ECDSA"] = EacObjectIdentifiers.id_TA_ECDSA_SHA_512; + algorithms["SHA3-512WITHSPHINCS256"] = BCObjectIdentifiers.sphincs256_with_SHA3_512; + algorithms["SHA512WITHSPHINCS256"] = BCObjectIdentifiers.sphincs256_with_SHA512; + algorithms["SM3WITHSM2"] = GMObjectIdentifiers.sm2sign_with_sm3; + + algorithms["SHA256WITHXMSS"] = BCObjectIdentifiers.xmss_with_SHA256; + algorithms["SHA512WITHXMSS"] = BCObjectIdentifiers.xmss_with_SHA512; + algorithms["SHAKE128WITHXMSS"] = BCObjectIdentifiers.xmss_with_SHAKE128; + algorithms["SHAKE256WITHXMSS"] = BCObjectIdentifiers.xmss_with_SHAKE256; + + algorithms["SHA256WITHXMSSMT"] = BCObjectIdentifiers.xmss_mt_with_SHA256; + algorithms["SHA512WITHXMSSMT"] = BCObjectIdentifiers.xmss_mt_with_SHA512; + algorithms["SHAKE128WITHXMSSMT"] = BCObjectIdentifiers.xmss_mt_with_SHAKE128; + algorithms["SHAKE256WITHXMSSMT"] = BCObjectIdentifiers.xmss_mt_with_SHAKE256; + + + // + // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field. + // The parameters field SHALL be NULL for RSA based signature algorithms. + // + noParams.Add((object)X9ObjectIdentifiers.ECDsaWithSha1); + noParams.Add((object)X9ObjectIdentifiers.ECDsaWithSha224); + noParams.Add((object)X9ObjectIdentifiers.ECDsaWithSha256); + noParams.Add((object)X9ObjectIdentifiers.ECDsaWithSha384); + noParams.Add((object)X9ObjectIdentifiers.ECDsaWithSha512); + noParams.Add((object)X9ObjectIdentifiers.IdDsaWithSha1); + noParams.Add((object)NistObjectIdentifiers.DsaWithSha224); + noParams.Add((object)NistObjectIdentifiers.DsaWithSha256); + noParams.Add((object)NistObjectIdentifiers.DsaWithSha384); + noParams.Add((object)NistObjectIdentifiers.DsaWithSha512); + noParams.Add((object)NistObjectIdentifiers.IdDsaWithSha3_224); + noParams.Add((object)NistObjectIdentifiers.IdDsaWithSha3_256); + noParams.Add((object)NistObjectIdentifiers.IdDsaWithSha3_384); + noParams.Add((object)NistObjectIdentifiers.IdDsaWithSha3_512); + noParams.Add((object)NistObjectIdentifiers.IdEcdsaWithSha3_224); + noParams.Add((object)NistObjectIdentifiers.IdEcdsaWithSha3_256); + noParams.Add((object)NistObjectIdentifiers.IdEcdsaWithSha3_384); + noParams.Add((object)NistObjectIdentifiers.IdEcdsaWithSha3_512); + + + // + // RFC 4491 + // + noParams.Add((object)CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94); + noParams.Add((object)CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001); + noParams.Add((object)RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256); + noParams.Add((object)RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512); + + // + // SPHINCS-256 + // + noParams.Add((object)BCObjectIdentifiers.sphincs256_with_SHA512); + noParams.Add((object)BCObjectIdentifiers.sphincs256_with_SHA3_512); + + // + // XMSS + // + noParams.Add((object)BCObjectIdentifiers.xmss_with_SHA256); + noParams.Add((object)BCObjectIdentifiers.xmss_with_SHA512); + noParams.Add((object)BCObjectIdentifiers.xmss_with_SHAKE128); + noParams.Add((object)BCObjectIdentifiers.xmss_with_SHAKE256); + noParams.Add((object)BCObjectIdentifiers.xmss_mt_with_SHA256); + noParams.Add((object)BCObjectIdentifiers.xmss_mt_with_SHA512); + noParams.Add((object)BCObjectIdentifiers.xmss_mt_with_SHAKE128); + noParams.Add((object)BCObjectIdentifiers.xmss_mt_with_SHAKE256); + + // + // SM2 + // + noParams.Add((object)GMObjectIdentifiers.sm2sign_with_sm3); + + // + // PKCS 1.5 encrypted algorithms + // + pkcs15RsaEncryption.Add((object)PkcsObjectIdentifiers.Sha1WithRsaEncryption); + pkcs15RsaEncryption.Add((object)PkcsObjectIdentifiers.Sha224WithRsaEncryption); + pkcs15RsaEncryption.Add((object)PkcsObjectIdentifiers.Sha256WithRsaEncryption); + pkcs15RsaEncryption.Add((object)PkcsObjectIdentifiers.Sha384WithRsaEncryption); + pkcs15RsaEncryption.Add((object)PkcsObjectIdentifiers.Sha512WithRsaEncryption); + pkcs15RsaEncryption.Add((object)TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128); + pkcs15RsaEncryption.Add((object)TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160); + pkcs15RsaEncryption.Add((object)TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256); + pkcs15RsaEncryption.Add((object)NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_224); + pkcs15RsaEncryption.Add((object)NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_256); + pkcs15RsaEncryption.Add((object)NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_384); + pkcs15RsaEncryption.Add((object)NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_512); + + // + // explicit params + // + AlgorithmIdentifier sha1AlgId = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + _params["SHA1WITHRSAANDMGF1"] = CreatePssParams(sha1AlgId, 20); + + AlgorithmIdentifier sha224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha224, DerNull.Instance); + _params["SHA224WITHRSAANDMGF1"] = CreatePssParams(sha224AlgId, 28); + + AlgorithmIdentifier sha256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha256, DerNull.Instance); + _params["SHA256WITHRSAANDMGF1"] = CreatePssParams(sha256AlgId, 32); + + AlgorithmIdentifier sha384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha384, DerNull.Instance); + _params["SHA384WITHRSAANDMGF1"] = CreatePssParams(sha384AlgId, 48); + + AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha512, DerNull.Instance); + _params["SHA512WITHRSAANDMGF1"] = CreatePssParams(sha512AlgId, 64); + + AlgorithmIdentifier sha3_224AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha3_224, DerNull.Instance); + _params["SHA3-224WITHRSAANDMGF1"] = CreatePssParams(sha3_224AlgId, 28); + + AlgorithmIdentifier sha3_256AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha3_256, DerNull.Instance); + _params["SHA3-256WITHRSAANDMGF1"] = CreatePssParams(sha3_256AlgId, 32); + + AlgorithmIdentifier sha3_384AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha3_384, DerNull.Instance); + _params["SHA3-384WITHRSAANDMGF1"] = CreatePssParams(sha3_384AlgId, 48); + + AlgorithmIdentifier sha3_512AlgId = new AlgorithmIdentifier(NistObjectIdentifiers.IdSha3_512, DerNull.Instance); + _params["SHA3-512WITHRSAANDMGF1"] = CreatePssParams(sha3_512AlgId, 64); + + // + // digests + // + digestOids[PkcsObjectIdentifiers.Sha224WithRsaEncryption] = NistObjectIdentifiers.IdSha224; + digestOids[PkcsObjectIdentifiers.Sha256WithRsaEncryption] = NistObjectIdentifiers.IdSha256; + digestOids[PkcsObjectIdentifiers.Sha384WithRsaEncryption] = NistObjectIdentifiers.IdSha384; + digestOids[PkcsObjectIdentifiers.Sha512WithRsaEncryption] = NistObjectIdentifiers.IdSha512; + digestOids[NistObjectIdentifiers.DsaWithSha224] = NistObjectIdentifiers.IdSha224; + digestOids[NistObjectIdentifiers.DsaWithSha224] = NistObjectIdentifiers.IdSha256; + digestOids[NistObjectIdentifiers.DsaWithSha224] = NistObjectIdentifiers.IdSha384; + digestOids[NistObjectIdentifiers.DsaWithSha224] = NistObjectIdentifiers.IdSha512; + digestOids[NistObjectIdentifiers.IdDsaWithSha3_224] = NistObjectIdentifiers.IdSha3_224; + digestOids[NistObjectIdentifiers.IdDsaWithSha3_256] = NistObjectIdentifiers.IdSha3_256; + digestOids[NistObjectIdentifiers.IdDsaWithSha3_384] = NistObjectIdentifiers.IdSha3_384; + digestOids[NistObjectIdentifiers.IdDsaWithSha3_512] = NistObjectIdentifiers.IdSha3_512; + digestOids[NistObjectIdentifiers.IdEcdsaWithSha3_224] = NistObjectIdentifiers.IdSha3_224; + digestOids[NistObjectIdentifiers.IdEcdsaWithSha3_256] = NistObjectIdentifiers.IdSha3_256; + digestOids[NistObjectIdentifiers.IdEcdsaWithSha3_384] = NistObjectIdentifiers.IdSha3_384; + digestOids[NistObjectIdentifiers.IdEcdsaWithSha3_512] = NistObjectIdentifiers.IdSha3_512; + digestOids[NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_224] = NistObjectIdentifiers.IdSha3_224; + digestOids[NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_256] = NistObjectIdentifiers.IdSha3_256; + digestOids[NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_384] = NistObjectIdentifiers.IdSha3_384; + digestOids[NistObjectIdentifiers.IdRsassaPkcs1V15WithSha3_512] = NistObjectIdentifiers.IdSha3_512; + + digestOids[PkcsObjectIdentifiers.MD2WithRsaEncryption] = PkcsObjectIdentifiers.MD2; + digestOids[PkcsObjectIdentifiers.MD4WithRsaEncryption] = PkcsObjectIdentifiers.MD4; + digestOids[PkcsObjectIdentifiers.MD5WithRsaEncryption] = PkcsObjectIdentifiers.MD5; + digestOids[PkcsObjectIdentifiers.Sha1WithRsaEncryption] = OiwObjectIdentifiers.IdSha1; + digestOids[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD128] = TeleTrusTObjectIdentifiers.RipeMD128; + digestOids[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD160] = TeleTrusTObjectIdentifiers.RipeMD160; + digestOids[TeleTrusTObjectIdentifiers.RsaSignatureWithRipeMD256] = TeleTrusTObjectIdentifiers.RipeMD256; + digestOids[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x94] = CryptoProObjectIdentifiers.GostR3411; + digestOids[CryptoProObjectIdentifiers.GostR3411x94WithGostR3410x2001] = CryptoProObjectIdentifiers.GostR3411; + digestOids[RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256] = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256; + digestOids[RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512] = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512; + digestOids[GMObjectIdentifiers.sm2sign_with_sm3] = GMObjectIdentifiers.sm3; + + } + + private static AlgorithmIdentifier Generate(string signatureAlgorithm) + { + AlgorithmIdentifier sigAlgId; + AlgorithmIdentifier encAlgId; + AlgorithmIdentifier digAlgId; + + string algorithmName = Strings.ToUpperCase(signatureAlgorithm); + DerObjectIdentifier sigOID = (DerObjectIdentifier)algorithms[algorithmName]; + if (sigOID == null) + { + throw new ArgumentException("Unknown signature type requested: " + algorithmName); + } + + if (noParams.Contains(sigOID)) + { + sigAlgId = new AlgorithmIdentifier(sigOID); + } + else if (_params.Contains(algorithmName)) + { + sigAlgId = new AlgorithmIdentifier(sigOID, (Asn1Encodable)_params[algorithmName]); + } + else + { + sigAlgId = new AlgorithmIdentifier(sigOID, DerNull.Instance); + } + + if (pkcs15RsaEncryption.Contains(sigOID)) + { + encAlgId = new AlgorithmIdentifier(PkcsObjectIdentifiers.RsaEncryption, DerNull.Instance); + } + else + { + encAlgId = sigAlgId; + } + + if (sigAlgId.Algorithm.Equals(PkcsObjectIdentifiers.IdRsassaPss)) + { + digAlgId = ((RsassaPssParameters)sigAlgId.Parameters).HashAlgorithm; + } + else + { + digAlgId = new AlgorithmIdentifier((DerObjectIdentifier)digestOids[sigOID], DerNull.Instance); + } + + return sigAlgId; + } + + private static RsassaPssParameters CreatePssParams(AlgorithmIdentifier hashAlgId, int saltSize) + { + return new RsassaPssParameters( + hashAlgId, + new AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, hashAlgId), + new DerInteger(saltSize), + new DerInteger(1)); + } + + public AlgorithmIdentifier Find(string sigAlgName) + { + + return Generate(sigAlgName); + } + } + + + + + + public class DefaultDigestAlgorithmIdentifierFinder { private static readonly IDictionary digestOids = Platform.CreateHashtable(); diff --git a/crypto/src/crypto/IMacFactory.cs b/crypto/src/crypto/IMacFactory.cs new file mode 100644 index 0000000000..d6b7ddfa70 --- /dev/null +++ b/crypto/src/crypto/IMacFactory.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Crypto +{ + public interface IMacFactory + { + /// The algorithm details object for this calculator. + Object AlgorithmDetails { get; } + + /// + /// Create a stream calculator for this signature calculator. The stream + /// calculator is used for the actual operation of entering the data to be signed + /// and producing the signature block. + /// + /// A calculator producing an IBlockResult with a signature in it. + IStreamCalculator CreateCalculator(); + } +} diff --git a/crypto/src/crypto/operators/Asn1Mac.cs b/crypto/src/crypto/operators/Asn1Mac.cs new file mode 100644 index 0000000000..ff70e58497 --- /dev/null +++ b/crypto/src/crypto/operators/Asn1Mac.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Iana; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Org.BouncyCastle.Crypto.Operators +{ + + public class DefaultMacStreamCalculator : IStreamCalculator + { + private readonly MacSink _stream; + + public DefaultMacStreamCalculator(IMac mac) + { + _stream = new MacSink(mac); + } + + public void Init(KeyParameter key) + { + _stream.Mac.Init(key); + } + + public Stream Stream + { + get { return _stream; } + } + public object GetResult() + { + byte[] res = new byte[_stream.Mac.GetMacSize()]; + _stream.Mac.DoFinal(res, 0); + return res; + } + } + + + public class DefaultMacAndDigestStreamCalculator : IStreamCalculator + { + + private readonly MacSink macSink; + private readonly DigestSink digestSink; + private readonly Stream _stream; + + + public DefaultMacAndDigestStreamCalculator(IMac imac, IDigest idigest) + { + this.macSink = new MacSink(imac); + this.digestSink = new DigestSink(idigest); + _stream = new MergedStream(macSink,digestSink); + } + + + public void Init(KeyParameter macKey) + { + this.macSink.Mac.Init(macKey); + } + + public void Init(PbmParameter parameter, byte[] password) + { + + byte[] pw = password; + byte[] salt = parameter.Salt.GetOctets(); + byte[] K = new byte[pw.Length + salt.Length]; + + System.Array.Copy(pw,K,pw.Length); + System.Array.Copy(salt,0,K,pw.Length,salt.Length); + int iter = parameter.IterationCount.Value.IntValue; + this.digestSink.Digest.Reset(); + + IDigest dig = DigestUtilities.GetDigest(digestSink.Digest.AlgorithmName); + + + + dig.BlockUpdate(K,0,K.Length); + K = new byte[dig.GetDigestSize()]; + dig.DoFinal(K, 0); + iter--; + + do + { + dig.BlockUpdate(K,0,K.Length); + dig.DoFinal(K, 0); + } while (--iter > 0); + + macSink.Mac.Init(new KeyParameter(K)); + } + + + + public Stream Stream + { + get { return _stream; } + } + + + public object GetResult() + { + byte[] macResult = new byte[macSink.Mac.GetMacSize()]; + macSink.Mac.DoFinal(macResult, 0); + byte[] digestResult = new byte[digestSink.Digest.GetByteLength()]; + digestSink.Digest.DoFinal(digestResult, 0); + return new DefaultMacAndDigestResult(digestResult, macResult); + } + + private class MergedStream : Stream + { + + private Stream aStream; + private Stream bStream; + + public MergedStream(Stream aStream, Stream bStream) + { + this.aStream = aStream; + this.bStream = bStream; + } + + public override void Flush() + { + aStream.Flush(); + bStream.Flush(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + aStream.Seek(offset, origin); + return bStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + aStream.SetLength(value); + bStream.SetLength(value); + } + + public override int Read(byte[] buffer, int offset, int count) + { + aStream.Read(buffer, offset, count); + return bStream.Read(buffer, offset, count); + } + + public override void Write(byte[] buffer, int offset, int count) + { + aStream.Write(buffer, offset, count); + bStream.Write(buffer, offset, count); + } + + public override bool CanRead + { + get { return bStream.CanRead && aStream.CanRead; } + } + public override bool CanSeek + { + get { return bStream.CanSeek && aStream.CanSeek; } + + } + public override bool CanWrite { + get { return bStream.CanWrite && aStream.CanWrite; } + + } + public override long Length { + get + { + return aStream.Length; + } + } + public override long Position + { + get { return aStream.Position; } + + set { aStream.Position = value; } + } + } + } + + public struct DefaultMacAndDigestResult + { + public DefaultMacAndDigestResult(byte[] digestResult, byte[] macResult) + { + DigestResult = digestResult; + MacResult = macResult; + } + + public byte[] DigestResult { get; } + + public byte[] MacResult { get; } + } + + public class Asn1MacFactory : IMacFactory + { + protected readonly AlgorithmIdentifier MacAlgorithmIdentifier; + + + + public Asn1MacFactory(AlgorithmIdentifier macAlgorithmIdentifier) + { + MacAlgorithmIdentifier = macAlgorithmIdentifier; + } + + + + public virtual object AlgorithmDetails + { + get { return MacAlgorithmIdentifier; } + } + + public virtual IStreamCalculator CreateCalculator() + { + IMac mac = MacUtilities.GetMac(MacAlgorithmIdentifier.Algorithm); + return new DefaultMacStreamCalculator(mac); + } + } + + + public interface IMacFactoryProvider + { + IMacFactory CreateMacFactory(AlgorithmIdentifier algorithmIdentifier); + } + + public class Asn1MacFactoryProvider : IMacFactoryProvider + { + public IMacFactory CreateMacFactory(AlgorithmIdentifier algorithmIdentifier) + { + return new Asn1MacFactory(algorithmIdentifier); + } + + public IMacFactory CreateMacFactory(AlgorithmIdentifier digestAlgorithmIdentifier, AlgorithmIdentifier macAlgorithmIdentifier) + { + return new PkMacFactory(digestAlgorithmIdentifier,macAlgorithmIdentifier); + } + + public IMacFactory CreateMacFactory(PbmParameter parameter) + { + return new PkMacFactory(parameter); + } + + } + + + + public class PkMacFactory:Asn1MacFactory + { + private readonly AlgorithmIdentifier _digestAlgorithmIdentifier; + private byte[] password; + private int iterationCount; + private byte[] salt; + + + + public PkMacFactory(SecureRandom random) : base(new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1)) + { + this._digestAlgorithmIdentifier = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); + this.iterationCount = 1000; + this.salt = new byte[20]; + random.NextBytes(salt); + } + + public PkMacFactory(AlgorithmIdentifier digestAlgorithmIdentifier, AlgorithmIdentifier macAlgorithmIdentifier) : base(macAlgorithmIdentifier) + { + this._digestAlgorithmIdentifier = digestAlgorithmIdentifier; + } + + public PkMacFactory(PbmParameter parameter):base(parameter.Mac) + { + this._digestAlgorithmIdentifier = parameter.Owf; + this.salt = parameter.Salt.GetOctets(); + this.iterationCount = parameter.IterationCount.Value.IntValue; + } + + public override object AlgorithmDetails + { + get + { + return new AlgorithmIdentifier(CmpObjectIdentifiers.passwordBasedMac, + new PbmParameter(salt, _digestAlgorithmIdentifier, iterationCount, MacAlgorithmIdentifier)); + } + } + + + public int IterationCount + { + set { this.iterationCount = value; } + } + public byte[] Salt + { + set { this.salt = value;} + } + public byte[] Password { + set { this.password = value; } + } + + + public override IStreamCalculator CreateCalculator() + { + + DefaultMacAndDigestStreamCalculator calc = new DefaultMacAndDigestStreamCalculator( + MacUtilities.GetMac(this.MacAlgorithmIdentifier.Algorithm), + DigestUtilities.GetDigest(_digestAlgorithmIdentifier.Algorithm)); + + PbmParameter parameter = new PbmParameter(salt, _digestAlgorithmIdentifier,iterationCount,MacAlgorithmIdentifier); + calc.Init(parameter, password); + + + return calc; + } + + } + + + public class MacVerifierFactory : IVerifierFactory + { + private readonly IMacFactory _macFactory; + + + public MacVerifierFactory(IMacFactory macFactory) + { + this._macFactory = macFactory; + } + + public object AlgorithmDetails + { + get { return _macFactory.AlgorithmDetails; } + } + public IStreamCalculator CreateCalculator() + { + return new MacVerifier(_macFactory.CreateCalculator()); + } + + private class MacVerifier : IStreamCalculator + { + public IStreamCalculator _calculator; + + public MacVerifier(IStreamCalculator calculator) + { + _calculator = calculator; + } + + public Stream Stream + { + get { return _calculator.Stream; } + } + + public object GetResult() + { + object result = _calculator.GetResult(); + if (result is byte[]) + { + return new DefaultMacVerifierResult((byte[])result); + } else if (result is DefaultMacAndDigestResult) + { + return new DefaultMacVerifierResult(((DefaultMacAndDigestResult)result).MacResult); + + } + + throw new InvalidOperationException("calculator did not return byte[] or DefaultMacVerifierResult"); + } + } + + } + + + public class DefaultMacVerifierResult:IVerifier + { + private readonly byte[] _calculatedResult; + + public DefaultMacVerifierResult(byte[] calculatedResult) + { + this._calculatedResult = calculatedResult; + } + + + public bool IsVerified(byte[] data) + { + return Arrays.ConstantTimeAreEqual(_calculatedResult, data); + } + + public bool IsVerified(byte[] source, int off, int length) + { + if (_calculatedResult.Length != length) + { + return false; + } + + // + // Must be constant time. + // + int j = 0; + int nonEqual = 0; + for (int i = off; i < off + length; i++) + { + nonEqual |= (_calculatedResult[j++] ^ source[i]); + } + + return nonEqual == 0; + } + } + + +} diff --git a/crypto/src/crypto/operators/Asn1Signature.cs b/crypto/src/crypto/operators/Asn1Signature.cs index 3fa1932739..3962a4a15a 100644 --- a/crypto/src/crypto/operators/Asn1Signature.cs +++ b/crypto/src/crypto/operators/Asn1Signature.cs @@ -236,6 +236,8 @@ internal static IEnumerable GetAlgNames() } } + + /// /// Calculator factory class for signature generation in ASN.1 based profiles that use an AlgorithmIdentifier to preserve /// signature algorithm details. diff --git a/crypto/src/crypto/operators/GenericKey.cs b/crypto/src/crypto/operators/GenericKey.cs new file mode 100644 index 0000000000..b2df74661f --- /dev/null +++ b/crypto/src/crypto/operators/GenericKey.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.X509; + +namespace Org.BouncyCastle.Crypto.Operators +{ + public class GenericKey + { + private AlgorithmIdentifier algorithmIdentifier; + private object representation; + + public GenericKey(object representation) + { + algorithmIdentifier = null; + this.representation = representation; + } + + public GenericKey(AlgorithmIdentifier algorithmIdentifier, byte[] representation) + { + this.algorithmIdentifier = algorithmIdentifier; + this.representation = representation; + } + + public GenericKey(AlgorithmIdentifier algorithmIdentifier, object representation) + { + this.algorithmIdentifier = algorithmIdentifier; + this.representation = representation; + } + + public AlgorithmIdentifier AlgorithmIdentifier + { + get { return algorithmIdentifier; } + } + + public object Representation + { + get { return representation; } + } + } +} diff --git a/crypto/src/util/Strings.cs b/crypto/src/util/Strings.cs index 3937a087fd..73a15ea73b 100644 --- a/crypto/src/util/Strings.cs +++ b/crypto/src/util/Strings.cs @@ -6,6 +6,31 @@ namespace Org.BouncyCastle.Utilities /// General string utilities. public abstract class Strings { + + public static string ToUpperCase(string original) + { + bool changed = false; + char[] chars = original.ToCharArray(); + + for (int i = 0; i != chars.Length; i++) + { + char ch = chars[i]; + if ('a' <= ch && 'z' >= ch) + { + changed = true; + chars[i] = (char)(ch - 'a' + 'A'); + } + } + + if (changed) + { + return new String(chars); + } + + return original; + } + + internal static bool IsOneOf(string s, params string[] candidates) { foreach (string candidate in candidates) diff --git a/crypto/test/src/cms/test/AllTests.cs b/crypto/test/src/cms/test/AllTests.cs index b473749142..7ab5721ac8 100644 --- a/crypto/test/src/cms/test/AllTests.cs +++ b/crypto/test/src/cms/test/AllTests.cs @@ -3,7 +3,7 @@ using NUnit.Core; using NUnit.Framework; - +using Org.BouncyCastle.Asn1.Tests; using Org.BouncyCastle.Utilities.Test; namespace Org.BouncyCastle.Cms.Tests @@ -21,6 +21,7 @@ public static TestSuite Suite get { TestSuite suite = new TestSuite("CMS Tests"); + suite.Add(new ProtectedMessageTest()); suite.Add(new CompressedDataTest()); suite.Add(new CompressedDataStreamTest()); suite.Add(new EnvelopedDataTest()); From c15ffb6ed0df553670fb1da7826c54a166fa3d5f Mon Sep 17 00:00:00 2001 From: Megan Woods Date: Mon, 14 Jan 2019 17:13:41 +1100 Subject: [PATCH 06/22] Nist algs and ProtectedMessageTests --- crypto/src/asn1/nist/NISTObjectIdentifiers.cs | 31 +- .../src/asn1/test/ProtectedMessageTest.cs | 429 ++++++++++++++++++ 2 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 crypto/test/src/asn1/test/ProtectedMessageTest.cs diff --git a/crypto/src/asn1/nist/NISTObjectIdentifiers.cs b/crypto/src/asn1/nist/NISTObjectIdentifiers.cs index b5002d28c7..840718ccd1 100644 --- a/crypto/src/asn1/nist/NISTObjectIdentifiers.cs +++ b/crypto/src/asn1/nist/NISTObjectIdentifiers.cs @@ -70,6 +70,35 @@ private NistObjectIdentifiers() public static readonly DerObjectIdentifier DsaWithSha224 = new DerObjectIdentifier(IdDsaWithSha2 + ".1"); public static readonly DerObjectIdentifier DsaWithSha256 = new DerObjectIdentifier(IdDsaWithSha2 + ".2"); public static readonly DerObjectIdentifier DsaWithSha384 = new DerObjectIdentifier(IdDsaWithSha2 + ".3"); - public static readonly DerObjectIdentifier DsaWithSha512 = new DerObjectIdentifier(IdDsaWithSha2 + ".4"); + public static readonly DerObjectIdentifier DsaWithSha512 = new DerObjectIdentifier(IdDsaWithSha2 + ".4"); + + /** 2.16.840.1.101.3.4.3.5 */ + public static readonly DerObjectIdentifier IdDsaWithSha3_224 = new DerObjectIdentifier(IdDsaWithSha2 + ".5"); + /** 2.16.840.1.101.3.4.3.6 */ + public static readonly DerObjectIdentifier IdDsaWithSha3_256 = new DerObjectIdentifier(IdDsaWithSha2 + ".6"); + /** 2.16.840.1.101.3.4.3.7 */ + public static readonly DerObjectIdentifier IdDsaWithSha3_384 = new DerObjectIdentifier(IdDsaWithSha2 + ".7"); + /** 2.16.840.1.101.3.4.3.8 */ + public static readonly DerObjectIdentifier IdDsaWithSha3_512 = new DerObjectIdentifier(IdDsaWithSha2 + ".8"); + + // ECDSA with SHA-3 + /** 2.16.840.1.101.3.4.3.9 */ + public static readonly DerObjectIdentifier IdEcdsaWithSha3_224 = new DerObjectIdentifier(IdDsaWithSha2 + ".9"); + /** 2.16.840.1.101.3.4.3.10 */ + public static readonly DerObjectIdentifier IdEcdsaWithSha3_256 = new DerObjectIdentifier(IdDsaWithSha2 + ".10"); + /** 2.16.840.1.101.3.4.3.11 */ + public static readonly DerObjectIdentifier IdEcdsaWithSha3_384 = new DerObjectIdentifier(IdDsaWithSha2 + ".11"); + /** 2.16.840.1.101.3.4.3.12 */ + public static readonly DerObjectIdentifier IdEcdsaWithSha3_512 = new DerObjectIdentifier(IdDsaWithSha2 + ".12"); + + // RSA PKCS #1 v1.5 Signature with SHA-3 family. + /** 2.16.840.1.101.3.4.3.9 */ + public static readonly DerObjectIdentifier IdRsassaPkcs1V15WithSha3_224 = new DerObjectIdentifier(IdDsaWithSha2 + ".13"); + /** 2.16.840.1.101.3.4.3.10 */ + public static readonly DerObjectIdentifier IdRsassaPkcs1V15WithSha3_256 = new DerObjectIdentifier(IdDsaWithSha2 + ".14"); + /** 2.16.840.1.101.3.4.3.11 */ + public static readonly DerObjectIdentifier IdRsassaPkcs1V15WithSha3_384 = new DerObjectIdentifier(IdDsaWithSha2 + ".15"); + /** 2.16.840.1.101.3.4.3.12 */ + public static readonly DerObjectIdentifier IdRsassaPkcs1V15WithSha3_512 = new DerObjectIdentifier(IdDsaWithSha2 + ".16"); } } diff --git a/crypto/test/src/asn1/test/ProtectedMessageTest.cs b/crypto/test/src/asn1/test/ProtectedMessageTest.cs new file mode 100644 index 0000000000..5ff51ba1a6 --- /dev/null +++ b/crypto/test/src/asn1/test/ProtectedMessageTest.cs @@ -0,0 +1,429 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using System.Text; +using NUnit.Framework; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.Test; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Asn1.Tests +{ + [TestFixture] + public class ProtectedMessageTest : SimpleTest + { + public override string Name + { + get { return "ProtectedMessageTest"; } + } + + public override void PerformTest() + { + TestVerifyBCJavaGeneratedMessage(); + TestSubsequentMessage(); + TestMacProtectedMessage(); + TestProtectedMessage(); + TestConfirmationMessage(); + TestSampleCr(); + } + +// [Test] +// public void TestServerSideKey() +// { +// RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); +// rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); +// AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); +// +// TestCertBuilder builder = new TestCertBuilder() +// { +// Issuer = new X509Name("CN=Test"), +// Subject = new X509Name("CN=Test"), +// NotBefore = DateTime.UtcNow.AddDays(-1), +// NotAfter = DateTime.UtcNow.AddDays(1), +// PublicKey = rsaKeyPair.Public, +// SignatureAlgorithm = "MD5WithRSAEncryption" +// }; +// +// builder.AddAttribute(X509Name.C, "Foo"); +// X509Certificate cert = builder.Build(rsaKeyPair.Private); +// +// GeneralName sender = new GeneralName(new X509Name("CN=Sender")); +// GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); +// +// +// +// } + + [Test] + public void TestNotBeforeNotAfter() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + doNotBeforeNotAfterTest(rsaKeyPair, new DateTime(1,1,1,0,0,1), new DateTime(1,1,1,0,0,10)); + doNotBeforeNotAfterTest(rsaKeyPair, DateTime.MinValue, new DateTime(1, 1, 1, 0, 0, 10)); + doNotBeforeNotAfterTest(rsaKeyPair, new DateTime(1, 1, 1, 0, 0, 1), DateTime.MinValue); + } + + + private void doNotBeforeNotAfterTest(AsymmetricCipherKeyPair kp, DateTime notBefore, DateTime notAfter) + { + CertificateRequestMessageBuilder builder = new CertificateRequestMessageBuilder(BigInteger.One) + .SetPublicKey(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public)) + .SetProofOfPossessionSubsequentMessage(SubsequentMessage.encrCert); + + builder.SetValidity(new Time(notBefore), new Time(notAfter)); + CertificateRequestMessage msg = builder.Build(); + + if (!notBefore.Equals(DateTime.MinValue)) + { + IsTrue("NotBefore did not match",(notBefore.Equals(msg.GetCertTemplate().Validity.NotBefore.ToDateTime()))); + } + else + { + IsTrue("Expected NotBefore to empty.",DateTime.MinValue == msg.GetCertTemplate().Validity.NotBefore.ToDateTime()); + } + + if (!notAfter.Equals(DateTime.MinValue)) + { + IsTrue("NotAfter did not match", (notAfter.Equals(msg.GetCertTemplate().Validity.NotAfter.ToDateTime()))); + } + else + { + IsTrue("Expected NotAfter to be empty.", DateTime.MinValue == msg.GetCertTemplate().Validity.NotAfter.ToDateTime()); + } + + } + + + [Test] + public void TestSubsequentMessage() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + TestCertBuilder builder = new TestCertBuilder() + { + NotBefore = DateTime.UtcNow.AddDays(-1), + NotAfter = DateTime.UtcNow.AddDays(1), + PublicKey = rsaKeyPair.Public, + SignatureAlgorithm = "Sha1WithRSAEncryption" + + }; + + X509Certificate cert = builder.Build(rsaKeyPair.Private); + + GeneralName user = new GeneralName(new X509Name("CN=Test")); + + CertificateRequestMessageBuilder crmBuiler = new CertificateRequestMessageBuilder(BigInteger.One) + .SetPublicKey(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(rsaKeyPair.Public)) + .SetProofOfPossessionSubsequentMessage(SubsequentMessage.encrCert); + + ISignatureFactory sigFact = new Asn1SignatureFactory("SHA256WithRSA", rsaKeyPair.Private); + + ProtectedPkiMessage certRequestMsg = new ProtectedPkiMessageBuilder(user,user) + .SetTransactionId(new byte[]{1,2,3,4,5}) + .SetBody(new PkiBody(PkiBody.TYPE_KEY_RECOVERY_REQ, new CertReqMessages(new CertReqMsg[]{crmBuiler.Build().ToAsn1Structure()}))) + .AddCmpCertificate(cert) + .Build(sigFact); + + ProtectedPkiMessage msg = new ProtectedPkiMessage(new GeneralPKIMessage(certRequestMsg.ToAsn1Message().GetDerEncoded())); + CertReqMessages reqMsgs = CertReqMessages.GetInstance(msg.Body.Content); + CertReqMsg reqMsg = reqMsgs.ToCertReqMsgArray()[0]; + IsEquals(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, reqMsg.Popo.Type); + + } + + + + [Test] + public void TestSampleCr() + { + byte[] raw = Base64.Decode( + "MIIB5TCB3AIBAqQdMBsxDDAKBgNVBAMMA0FSUDELMAkGA1UEBhMCQ0ikOTA3MREwDwYDVQQDDAhBZG1pbkNBM" + + "TEVMBMGA1UECgwMRUpCQ0EgU2FtcGxlMQswCQYDVQQGEwJTRaFGMEQGCSqGSIb2fQdCDTA3BBxzYWx0Tm9NYX" + + "R0ZXJXaGF0VGhpc1N0cmluZ0lzMAcGBSsOAwIaAgIEADAKBggrBgEFBQgBAqIQBA5TZW5kZXJLSUQtMjAwOKQ" + + "PBA0xMjAzNjA3MDE1OTQ0pRIEEOPfE1DMncRUdrBj8KelgsCigeowgecwgeQwgd0CAQAwgcGlHTAbMQwwCgYD" + + "VQQDDANBUlAxCzAJBgNVBAYTAkNIpoGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrrv4e42olM2YJqSbCN" + + "d19EtW7d6T8HYvcSU5wsm5icKFkxyD5jrO/2xYh3zqUFYwZap0pA7qbhxk5sEne2ywVpt2lGSmpAU8M7hC9oh" + + "Ep9wvv+3+td5MEO+qMuWWxF8OZBlYIFBZ/k+pGlU+4XlBP5Ai6pu/EI/0A+1/bcGs0sQIDAQABMBQwEgYJKwY" + + "BBQUHBQEBDAVEVU1NWaACBQCgFwMVAO73HUPF//mY5+E714Cv5oprt0kO\r\n"); + + ProtectedPkiMessage msg = new ProtectedPkiMessage(new GeneralPKIMessage(raw)); + + + + IsTrue(msg.Verify(new Asn1MacFactoryProvider(),Strings.ToAsciiByteArray("TopSecret1234"))); + + } + + + [Test] + public void TestConfirmationMessage() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + TestCertBuilder builder = new TestCertBuilder() + { + NotBefore = DateTime.UtcNow.AddDays(-1), + NotAfter = DateTime.UtcNow.AddDays(1), + PublicKey = rsaKeyPair.Public, + SignatureAlgorithm = "Sha1WithRSAEncryption" + + }; + + builder.AddAttribute(X509Name.C, "Foo"); + X509Certificate cert = builder.Build(rsaKeyPair.Private); + + GeneralName sender = new GeneralName(new X509Name("CN=Sender")); + GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); + + CertificateConfirmationContent content = new CertificateConfirmationContentBuilder() + .AddAcceptedCertificate(cert, BigInteger.One) + .Build(); + + ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender, recipient); + msgBuilder.SetBody(new PkiBody(PkiBody.TYPE_CERT_CONFIRM, content.ToAsn1Structure())); + msgBuilder.AddCmpCertificate(cert); + + ISignatureFactory sigFact = new Asn1SignatureFactory("MD5WithRSA", rsaKeyPair.Private); + ProtectedPkiMessage msg = msgBuilder.Build(sigFact); + + IVerifierFactory verifierFactory = new Asn1VerifierFactory("MD5WithRSA", rsaKeyPair.Public); + + IsTrue("PKIMessage must verify (MD5withRSA)", msg.Verify(verifierFactory)); + + IsEquals(sender,msg.Header.Sender); + IsEquals(recipient,msg.Header.Recipient); + + content = new CertificateConfirmationContent(CertConfirmContent.GetInstance(msg.Body.Content), new DefaultDigestAlgorithmIdentifierFinder()); + CertificateStatus[] statusList = content.GetStatusMessages(); + IsEquals(1,statusList.Length); + IsTrue(statusList[0].IsVerified(cert)); + } + + + + [Test] + public void TestProtectedMessage() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537),new SecureRandom(),2048,100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + TestCertBuilder builder = new TestCertBuilder() + { + NotBefore = DateTime.UtcNow.AddDays(-1), + NotAfter = DateTime.UtcNow.AddDays(1), + PublicKey = rsaKeyPair.Public, + SignatureAlgorithm = "Sha1WithRSAEncryption" + + }; + + builder.AddAttribute(X509Name.C, "Foo"); + X509Certificate cert = builder.Build(rsaKeyPair.Private); + + GeneralName sender = new GeneralName(new X509Name("CN=Sender")); + GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); + + ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender,recipient); + msgBuilder.AddCmpCertificate(cert); + + ISignatureFactory sigFact = new Asn1SignatureFactory("MD5WithRSA",rsaKeyPair.Private); + + ProtectedPkiMessage msg = msgBuilder.Build(sigFact); + + X509Certificate certificate = msg.GetCertificates()[0]; + + IVerifierFactory verifierFactory = new Asn1VerifierFactory("MD5WithRSA", rsaKeyPair.Public); + + IsTrue("PKIMessage must verify (MD5withRSA)",msg.Verify(verifierFactory)); + } + + [Test] + public void TestMacProtectedMessage() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, + 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + TestCertBuilder builder = new TestCertBuilder() + { + NotBefore = DateTime.UtcNow.AddDays(-1), + NotAfter = DateTime.UtcNow.AddDays(1), + PublicKey = rsaKeyPair.Public, + SignatureAlgorithm = "Sha1WithRSAEncryption" + + }; + + builder.AddAttribute(X509Name.C, "Foo"); + X509Certificate cert = builder.Build(rsaKeyPair.Private); + + GeneralName sender = new GeneralName(new X509Name("CN=Sender")); + GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); + + ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender, recipient); + msgBuilder.AddCmpCertificate(cert); + + // + // Default instance. + // + + + PkMacFactory macFactory = new PkMacFactory(new SecureRandom()); + macFactory.Password = Strings.ToAsciiByteArray("testpass"); + ProtectedPkiMessage msg = msgBuilder.Build(macFactory); + + + MacVerifierFactory verifierFactory = new MacVerifierFactory( + new PkMacFactory((PbmParameter) msg.Header.ProtectionAlg.Parameters) + {Password = Strings.ToAsciiByteArray("testpass")} + ); + IsTrue(msg.Verify(verifierFactory)); + } + + + + + [Test] + public void TestVerifyBCJavaGeneratedMessage() + { + // + // Test with content generated by BC-JAVA version. + // + + ICipherParameters publicKey = PublicKeyFactory.CreateKey(Hex.Decode( + "305c300d06092a864886f70d0101010500034b003048024100ac1e59ba5f96" + + "ba86c86e6d8bbfd43ece04265fa29e6ebdb320388b58af365d05b26970cbd2" + + "6e5b0fa7df2074b90b42a1d16ab270cdb851b53e464b87f683774502030100" + + "01")); + ICipherParameters privateKey = PrivateKeyFactory.CreateKey(Hex.Decode( + "30820155020100300d06092a864886f70d01010105000482013f3082013b02" + + "0100024100ac1e59ba5f96ba86c86e6d8bbfd43ece04265fa29e6ebdb32038" + + "8b58af365d05b26970cbd26e5b0fa7df2074b90b42a1d16ab270cdb851b53e" + + "464b87f68377450203010001024046f3f208570c735349bfe00fdaa1fbcc00" + + "c0f2eebe42279876a168ac43fa74a8cdf9a1bb49066c07cfcfa7196f69f2b9" + + "419d378109db967891428c50273dcc37022100d488dc3fb86f404d726a8166" + + "b2a9aba9bee12fdbf38470a62403a2a20bad0977022100cf51874e479b141f" + + "9915533bf54d68f1940f84d7fe6130538ff01a23e3493423022100986f94f1" + + "0afa9837341219bfabf32fd16ebb9a94fa630a5ccf45e036b383275f02201b" + + "6dff07f563684b31f6e757548254733a12bf91d05f4d8490d3c4b1a0ddcb9f" + + "02210087c3b2049e9a3edfc4cb40a3a275dabf7ffff80b467157e384603042" + + "3fe91d68")); + + byte[] ind = Hex.Decode( + "308201ac306e020102a4133011310f300d06035504030c0653656e646572a4" + + "123010310e300c06035504030c055265636970a140303e06092a864886f67d" + + "07420d30310414fdccb4ffd7848e6a697bee36cbe0f3722ed7fe2f30070605" + + "2b0e03021a020203e8300c06082b060105050801020500a10430023000a017" + + "031500c131c357441daa78eb538bfd9c24870e220fdafaa182011930820115" + + "308201113081bca003020102020601684a515d5b300d06092a864886f70d01" + + "01050500300f310d300b06035504030c0454657374301e170d313930313134" + + "3033303433325a170d3139303432343033303433325a300f310d300b060355" + + "04030c0454657374305c300d06092a864886f70d0101010500034b00304802" + + "4100ac1e59ba5f96ba86c86e6d8bbfd43ece04265fa29e6ebdb320388b58af" + + "365d05b26970cbd26e5b0fa7df2074b90b42a1d16ab270cdb851b53e464b87" + + "f68377450203010001300d06092a864886f70d0101050500034100264b5b76" + + "f268e2a992f05ad83783b091ce806a6726912c6200d06b33375ae58fe3c474" + + "c3a42ad6e572a2c48ae3bf914a7510bb995c3474829cfe71ab679a3db0"); + + + ProtectedPkiMessage pkiMsg = new ProtectedPkiMessage(PkiMessage.GetInstance(ind)); + + PbmParameter pbmParameters = PbmParameter.GetInstance(pkiMsg.Header.ProtectionAlg.Parameters); + + MacVerifierFactory verifierFactory = new MacVerifierFactory(new PkMacFactory(pbmParameters) + { + Password = Strings.ToAsciiByteArray("secret") + }); + + IsTrue(pkiMsg.Verify(verifierFactory)); + } + + + +} + + public class TestCertBuilder + { + IDictionary attrs = new Hashtable(); + IList ord = new ArrayList(); + IList values = new ArrayList(); + + public DateTime NotBefore { get; set; } + + public DateTime NotAfter { get; set; } + + public AsymmetricKeyParameter PublicKey { get; set; } + + public String SignatureAlgorithm { get; set; } + + public X509Name Issuer { get; set; } + public X509Name Subject { get; set; } + + public TestCertBuilder AddAttribute(DerObjectIdentifier name, Object value) + { + attrs[name] = value; + ord.Add(name); + values.Add(value); + return this; + } + + public X509Certificate Build(AsymmetricKeyParameter privateKey) + { + X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); + + certGen.SetSerialNumber(BigInteger.One); + + if (Issuer != null) + { + certGen.SetIssuerDN(Issuer); + } + else + { + certGen.SetIssuerDN(new X509Name(ord, attrs)); + } + + + certGen.SetNotBefore(NotBefore); + certGen.SetNotAfter(NotAfter); + + if (Subject != null) + { + certGen.SetSubjectDN(Subject); + } + else + { + certGen.SetSubjectDN(new X509Name(ord, attrs)); + } + + + certGen.SetPublicKey(PublicKey); + certGen.SetSignatureAlgorithm(SignatureAlgorithm); + + return certGen.Generate(privateKey); + } + } +} From a723aca1e07f57af70d7596a4fe3961045cdb0d9 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 14 Jan 2019 18:10:49 +1100 Subject: [PATCH 07/22] packaging fix --- crypto/src/{asn1 => }/cmp/CertificateConfirmationContent.cs | 3 ++- .../{asn1 => }/cmp/CertificateConfirmationContentBuilder.cs | 4 +++- crypto/src/{asn1 => }/cmp/CertificateStatus.cs | 3 ++- crypto/src/{asn1 => }/cmp/CmpException.cs | 2 +- crypto/src/{asn1 => }/crmf/AuthenticatorControl.cs | 5 ++++- crypto/src/{asn1 => }/crmf/CertificateRequestMessage.cs | 4 +++- .../src/{asn1 => }/crmf/CertificateRequestMessageBuilder.cs | 2 +- crypto/src/{asn1 => }/crmf/CrmfException.cs | 2 +- crypto/src/{asn1/crmf/Controls.cs => crmf/IControl.cs} | 0 crypto/src/{asn1 => }/crmf/PkiArchiveControl.cs | 4 +++- crypto/src/{asn1 => }/crmf/RegTokenControl.cs | 6 +++++- 11 files changed, 25 insertions(+), 10 deletions(-) rename crypto/src/{asn1 => }/cmp/CertificateConfirmationContent.cs (94%) rename crypto/src/{asn1 => }/cmp/CertificateConfirmationContentBuilder.cs (96%) rename crypto/src/{asn1 => }/cmp/CertificateStatus.cs (96%) rename crypto/src/{asn1 => }/cmp/CmpException.cs (93%) rename crypto/src/{asn1 => }/crmf/AuthenticatorControl.cs (87%) rename crypto/src/{asn1 => }/crmf/CertificateRequestMessage.cs (98%) rename crypto/src/{asn1 => }/crmf/CertificateRequestMessageBuilder.cs (99%) rename crypto/src/{asn1 => }/crmf/CrmfException.cs (93%) rename crypto/src/{asn1/crmf/Controls.cs => crmf/IControl.cs} (100%) rename crypto/src/{asn1 => }/crmf/PkiArchiveControl.cs (95%) rename crypto/src/{asn1 => }/crmf/RegTokenControl.cs (84%) diff --git a/crypto/src/asn1/cmp/CertificateConfirmationContent.cs b/crypto/src/cmp/CertificateConfirmationContent.cs similarity index 94% rename from crypto/src/asn1/cmp/CertificateConfirmationContent.cs rename to crypto/src/cmp/CertificateConfirmationContent.cs index 9f2f3e0381..882bd2091c 100644 --- a/crypto/src/asn1/cmp/CertificateConfirmationContent.cs +++ b/crypto/src/cmp/CertificateConfirmationContent.cs @@ -2,9 +2,10 @@ using System.Collections.Generic; using System.Text; using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Asn1.Cmp; -namespace Org.BouncyCastle.Asn1.Cmp +namespace Org.BouncyCastle.Cmp { public class CertificateConfirmationContent { diff --git a/crypto/src/asn1/cmp/CertificateConfirmationContentBuilder.cs b/crypto/src/cmp/CertificateConfirmationContentBuilder.cs similarity index 96% rename from crypto/src/asn1/cmp/CertificateConfirmationContentBuilder.cs rename to crypto/src/cmp/CertificateConfirmationContentBuilder.cs index b8c306f816..126484917b 100644 --- a/crypto/src/asn1/cmp/CertificateConfirmationContentBuilder.cs +++ b/crypto/src/cmp/CertificateConfirmationContentBuilder.cs @@ -2,6 +2,8 @@ using System.Collections; using System.Collections.Generic; using System.Text; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Cms; using Org.BouncyCastle.Crypto.IO; @@ -9,7 +11,7 @@ using Org.BouncyCastle.Security; using Org.BouncyCastle.X509; -namespace Org.BouncyCastle.Asn1.Cmp +namespace Org.BouncyCastle.Cmp { public class CertificateConfirmationContentBuilder { diff --git a/crypto/src/asn1/cmp/CertificateStatus.cs b/crypto/src/cmp/CertificateStatus.cs similarity index 96% rename from crypto/src/asn1/cmp/CertificateStatus.cs rename to crypto/src/cmp/CertificateStatus.cs index d16c8e0069..e8c3546dd2 100644 --- a/crypto/src/asn1/cmp/CertificateStatus.cs +++ b/crypto/src/cmp/CertificateStatus.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Cms; using Org.BouncyCastle.Crypto.IO; @@ -9,7 +10,7 @@ using Org.BouncyCastle.Utilities; using Org.BouncyCastle.X509; -namespace Org.BouncyCastle.Asn1.Cmp +namespace Org.BouncyCastle.Cmp { public class CertificateStatus { diff --git a/crypto/src/asn1/cmp/CmpException.cs b/crypto/src/cmp/CmpException.cs similarity index 93% rename from crypto/src/asn1/cmp/CmpException.cs rename to crypto/src/cmp/CmpException.cs index 0500b7d3e7..7ecdf5af80 100644 --- a/crypto/src/asn1/cmp/CmpException.cs +++ b/crypto/src/cmp/CmpException.cs @@ -3,7 +3,7 @@ using System.Runtime.Serialization; using System.Text; -namespace Org.BouncyCastle.Asn1.Cmp +namespace Org.BouncyCastle.Cmp { public class CmpException : Exception { diff --git a/crypto/src/asn1/crmf/AuthenticatorControl.cs b/crypto/src/crmf/AuthenticatorControl.cs similarity index 87% rename from crypto/src/asn1/crmf/AuthenticatorControl.cs rename to crypto/src/crmf/AuthenticatorControl.cs index 060088b1fc..7803c44180 100644 --- a/crypto/src/asn1/crmf/AuthenticatorControl.cs +++ b/crypto/src/crmf/AuthenticatorControl.cs @@ -2,7 +2,10 @@ using System.Collections.Generic; using System.Text; -namespace Org.BouncyCastle.Asn1.Crmf +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Crmf; + +namespace Org.BouncyCastle.Crmf { public class AuthenticatorControl:IControl { diff --git a/crypto/src/asn1/crmf/CertificateRequestMessage.cs b/crypto/src/crmf/CertificateRequestMessage.cs similarity index 98% rename from crypto/src/asn1/crmf/CertificateRequestMessage.cs rename to crypto/src/crmf/CertificateRequestMessage.cs index b80c2a3fda..818facade6 100644 --- a/crypto/src/asn1/crmf/CertificateRequestMessage.cs +++ b/crypto/src/crmf/CertificateRequestMessage.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Operators; -namespace Org.BouncyCastle.Asn1.Crmf +namespace Org.BouncyCastle.Crmf { public class CertificateRequestMessage { diff --git a/crypto/src/asn1/crmf/CertificateRequestMessageBuilder.cs b/crypto/src/crmf/CertificateRequestMessageBuilder.cs similarity index 99% rename from crypto/src/asn1/crmf/CertificateRequestMessageBuilder.cs rename to crypto/src/crmf/CertificateRequestMessageBuilder.cs index df604f4f56..53ebdf3f50 100644 --- a/crypto/src/asn1/crmf/CertificateRequestMessageBuilder.cs +++ b/crypto/src/crmf/CertificateRequestMessageBuilder.cs @@ -8,7 +8,7 @@ using Org.BouncyCastle.Crypto.Operators; using Org.BouncyCastle.Math; -namespace Org.BouncyCastle.Asn1.Crmf +namespace Org.BouncyCastle.Crmf { public class CertificateRequestMessageBuilder { diff --git a/crypto/src/asn1/crmf/CrmfException.cs b/crypto/src/crmf/CrmfException.cs similarity index 93% rename from crypto/src/asn1/crmf/CrmfException.cs rename to crypto/src/crmf/CrmfException.cs index 7043ccd735..c80f480b71 100644 --- a/crypto/src/asn1/crmf/CrmfException.cs +++ b/crypto/src/crmf/CrmfException.cs @@ -3,7 +3,7 @@ using System.Runtime.Serialization; using System.Text; -namespace Org.BouncyCastle.Asn1.Crmf +namespace Org.BouncyCastle.Crmf { public class CrmfException : Exception { diff --git a/crypto/src/asn1/crmf/Controls.cs b/crypto/src/crmf/IControl.cs similarity index 100% rename from crypto/src/asn1/crmf/Controls.cs rename to crypto/src/crmf/IControl.cs diff --git a/crypto/src/asn1/crmf/PkiArchiveControl.cs b/crypto/src/crmf/PkiArchiveControl.cs similarity index 95% rename from crypto/src/asn1/crmf/PkiArchiveControl.cs rename to crypto/src/crmf/PkiArchiveControl.cs index 0290fdfe67..ec8fb76715 100644 --- a/crypto/src/asn1/crmf/PkiArchiveControl.cs +++ b/crypto/src/crmf/PkiArchiveControl.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; using System.Text; +using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cms; +using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Cms; -namespace Org.BouncyCastle.Asn1.Crmf +namespace Org.BouncyCastle.Crmf { public class PkiArchiveControl:IControl { diff --git a/crypto/src/asn1/crmf/RegTokenControl.cs b/crypto/src/crmf/RegTokenControl.cs similarity index 84% rename from crypto/src/asn1/crmf/RegTokenControl.cs rename to crypto/src/crmf/RegTokenControl.cs index 77bf32557e..b53ce19229 100644 --- a/crypto/src/asn1/crmf/RegTokenControl.cs +++ b/crypto/src/crmf/RegTokenControl.cs @@ -2,7 +2,11 @@ using System.Collections.Generic; using System.Text; -namespace Org.BouncyCastle.Asn1.Crmf +using Org.BouncyCastle.Crmf; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Crmf; + +namespace Org.BouncyCastle.Crmf { public class RegTokenControl:IControl { From ed9339d61d5241df5bcf7a99cec3ea25e1dbf427 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 14 Jan 2019 18:12:38 +1100 Subject: [PATCH 08/22] update --- crypto/src/crmf/IControl.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crypto/src/crmf/IControl.cs b/crypto/src/crmf/IControl.cs index 8f986168c9..bf29fafddc 100644 --- a/crypto/src/crmf/IControl.cs +++ b/crypto/src/crmf/IControl.cs @@ -1,8 +1,10 @@ using System; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Utilities; -namespace Org.BouncyCastle.Asn1.Crmf +namespace Org.BouncyCastle.Crmf { public interface IControl From 5eafeb2618a4a0d93bc71de8dd93b94389e9d306 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 14 Jan 2019 18:16:10 +1100 Subject: [PATCH 09/22] move --- .../test/src/cmp/test/ProtectedMessageTest.cs | 430 ++++++++++++++++++ 1 file changed, 430 insertions(+) create mode 100644 crypto/test/src/cmp/test/ProtectedMessageTest.cs diff --git a/crypto/test/src/cmp/test/ProtectedMessageTest.cs b/crypto/test/src/cmp/test/ProtectedMessageTest.cs new file mode 100644 index 0000000000..ef19197792 --- /dev/null +++ b/crypto/test/src/cmp/test/ProtectedMessageTest.cs @@ -0,0 +1,430 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using System.Text; +using NUnit.Framework; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Crmf; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Signers; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Utilities.Test; +using Org.BouncyCastle.X509; + +namespace Org.BouncyCastle.Cmp.Tests +{ + [TestFixture] + public class ProtectedMessageTest : SimpleTest + { + public override string Name + { + get { return "ProtectedMessageTest"; } + } + + public override void PerformTest() + { + TestVerifyBCJavaGeneratedMessage(); + TestSubsequentMessage(); + TestMacProtectedMessage(); + TestProtectedMessage(); + TestConfirmationMessage(); + TestSampleCr(); + } + +// [Test] +// public void TestServerSideKey() +// { +// RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); +// rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); +// AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); +// +// TestCertBuilder builder = new TestCertBuilder() +// { +// Issuer = new X509Name("CN=Test"), +// Subject = new X509Name("CN=Test"), +// NotBefore = DateTime.UtcNow.AddDays(-1), +// NotAfter = DateTime.UtcNow.AddDays(1), +// PublicKey = rsaKeyPair.Public, +// SignatureAlgorithm = "MD5WithRSAEncryption" +// }; +// +// builder.AddAttribute(X509Name.C, "Foo"); +// X509Certificate cert = builder.Build(rsaKeyPair.Private); +// +// GeneralName sender = new GeneralName(new X509Name("CN=Sender")); +// GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); +// +// +// +// } + + [Test] + public void TestNotBeforeNotAfter() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + doNotBeforeNotAfterTest(rsaKeyPair, new DateTime(1,1,1,0,0,1), new DateTime(1,1,1,0,0,10)); + doNotBeforeNotAfterTest(rsaKeyPair, DateTime.MinValue, new DateTime(1, 1, 1, 0, 0, 10)); + doNotBeforeNotAfterTest(rsaKeyPair, new DateTime(1, 1, 1, 0, 0, 1), DateTime.MinValue); + } + + + private void doNotBeforeNotAfterTest(AsymmetricCipherKeyPair kp, DateTime notBefore, DateTime notAfter) + { + CertificateRequestMessageBuilder builder = new CertificateRequestMessageBuilder(BigInteger.One) + .SetPublicKey(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public)) + .SetProofOfPossessionSubsequentMessage(SubsequentMessage.encrCert); + + builder.SetValidity(new Time(notBefore), new Time(notAfter)); + CertificateRequestMessage msg = builder.Build(); + + if (!notBefore.Equals(DateTime.MinValue)) + { + IsTrue("NotBefore did not match",(notBefore.Equals(msg.GetCertTemplate().Validity.NotBefore.ToDateTime()))); + } + else + { + IsTrue("Expected NotBefore to empty.",DateTime.MinValue == msg.GetCertTemplate().Validity.NotBefore.ToDateTime()); + } + + if (!notAfter.Equals(DateTime.MinValue)) + { + IsTrue("NotAfter did not match", (notAfter.Equals(msg.GetCertTemplate().Validity.NotAfter.ToDateTime()))); + } + else + { + IsTrue("Expected NotAfter to be empty.", DateTime.MinValue == msg.GetCertTemplate().Validity.NotAfter.ToDateTime()); + } + + } + + + [Test] + public void TestSubsequentMessage() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + TestCertBuilder builder = new TestCertBuilder() + { + NotBefore = DateTime.UtcNow.AddDays(-1), + NotAfter = DateTime.UtcNow.AddDays(1), + PublicKey = rsaKeyPair.Public, + SignatureAlgorithm = "Sha1WithRSAEncryption" + + }; + + X509Certificate cert = builder.Build(rsaKeyPair.Private); + + GeneralName user = new GeneralName(new X509Name("CN=Test")); + + CertificateRequestMessageBuilder crmBuiler = new CertificateRequestMessageBuilder(BigInteger.One) + .SetPublicKey(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(rsaKeyPair.Public)) + .SetProofOfPossessionSubsequentMessage(SubsequentMessage.encrCert); + + ISignatureFactory sigFact = new Asn1SignatureFactory("SHA256WithRSA", rsaKeyPair.Private); + + ProtectedPkiMessage certRequestMsg = new ProtectedPkiMessageBuilder(user,user) + .SetTransactionId(new byte[]{1,2,3,4,5}) + .SetBody(new PkiBody(PkiBody.TYPE_KEY_RECOVERY_REQ, new CertReqMessages(new CertReqMsg[]{crmBuiler.Build().ToAsn1Structure()}))) + .AddCmpCertificate(cert) + .Build(sigFact); + + ProtectedPkiMessage msg = new ProtectedPkiMessage(new GeneralPKIMessage(certRequestMsg.ToAsn1Message().GetDerEncoded())); + CertReqMessages reqMsgs = CertReqMessages.GetInstance(msg.Body.Content); + CertReqMsg reqMsg = reqMsgs.ToCertReqMsgArray()[0]; + IsEquals(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, reqMsg.Popo.Type); + + } + + + + [Test] + public void TestSampleCr() + { + byte[] raw = Base64.Decode( + "MIIB5TCB3AIBAqQdMBsxDDAKBgNVBAMMA0FSUDELMAkGA1UEBhMCQ0ikOTA3MREwDwYDVQQDDAhBZG1pbkNBM" + + "TEVMBMGA1UECgwMRUpCQ0EgU2FtcGxlMQswCQYDVQQGEwJTRaFGMEQGCSqGSIb2fQdCDTA3BBxzYWx0Tm9NYX" + + "R0ZXJXaGF0VGhpc1N0cmluZ0lzMAcGBSsOAwIaAgIEADAKBggrBgEFBQgBAqIQBA5TZW5kZXJLSUQtMjAwOKQ" + + "PBA0xMjAzNjA3MDE1OTQ0pRIEEOPfE1DMncRUdrBj8KelgsCigeowgecwgeQwgd0CAQAwgcGlHTAbMQwwCgYD" + + "VQQDDANBUlAxCzAJBgNVBAYTAkNIpoGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrrv4e42olM2YJqSbCN" + + "d19EtW7d6T8HYvcSU5wsm5icKFkxyD5jrO/2xYh3zqUFYwZap0pA7qbhxk5sEne2ywVpt2lGSmpAU8M7hC9oh" + + "Ep9wvv+3+td5MEO+qMuWWxF8OZBlYIFBZ/k+pGlU+4XlBP5Ai6pu/EI/0A+1/bcGs0sQIDAQABMBQwEgYJKwY" + + "BBQUHBQEBDAVEVU1NWaACBQCgFwMVAO73HUPF//mY5+E714Cv5oprt0kO\r\n"); + + ProtectedPkiMessage msg = new ProtectedPkiMessage(new GeneralPKIMessage(raw)); + + + + IsTrue(msg.Verify(new Asn1MacFactoryProvider(),Strings.ToAsciiByteArray("TopSecret1234"))); + + } + + + [Test] + public void TestConfirmationMessage() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + TestCertBuilder builder = new TestCertBuilder() + { + NotBefore = DateTime.UtcNow.AddDays(-1), + NotAfter = DateTime.UtcNow.AddDays(1), + PublicKey = rsaKeyPair.Public, + SignatureAlgorithm = "Sha1WithRSAEncryption" + + }; + + builder.AddAttribute(X509Name.C, "Foo"); + X509Certificate cert = builder.Build(rsaKeyPair.Private); + + GeneralName sender = new GeneralName(new X509Name("CN=Sender")); + GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); + + CertificateConfirmationContent content = new CertificateConfirmationContentBuilder() + .AddAcceptedCertificate(cert, BigInteger.One) + .Build(); + + ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender, recipient); + msgBuilder.SetBody(new PkiBody(PkiBody.TYPE_CERT_CONFIRM, content.ToAsn1Structure())); + msgBuilder.AddCmpCertificate(cert); + + ISignatureFactory sigFact = new Asn1SignatureFactory("MD5WithRSA", rsaKeyPair.Private); + ProtectedPkiMessage msg = msgBuilder.Build(sigFact); + + IVerifierFactory verifierFactory = new Asn1VerifierFactory("MD5WithRSA", rsaKeyPair.Public); + + IsTrue("PKIMessage must verify (MD5withRSA)", msg.Verify(verifierFactory)); + + IsEquals(sender,msg.Header.Sender); + IsEquals(recipient,msg.Header.Recipient); + + content = new CertificateConfirmationContent(CertConfirmContent.GetInstance(msg.Body.Content), new DefaultDigestAlgorithmIdentifierFinder()); + CertificateStatus[] statusList = content.GetStatusMessages(); + IsEquals(1,statusList.Length); + IsTrue(statusList[0].IsVerified(cert)); + } + + + + [Test] + public void TestProtectedMessage() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537),new SecureRandom(),2048,100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + TestCertBuilder builder = new TestCertBuilder() + { + NotBefore = DateTime.UtcNow.AddDays(-1), + NotAfter = DateTime.UtcNow.AddDays(1), + PublicKey = rsaKeyPair.Public, + SignatureAlgorithm = "Sha1WithRSAEncryption" + + }; + + builder.AddAttribute(X509Name.C, "Foo"); + X509Certificate cert = builder.Build(rsaKeyPair.Private); + + GeneralName sender = new GeneralName(new X509Name("CN=Sender")); + GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); + + ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender,recipient); + msgBuilder.AddCmpCertificate(cert); + + ISignatureFactory sigFact = new Asn1SignatureFactory("MD5WithRSA",rsaKeyPair.Private); + + ProtectedPkiMessage msg = msgBuilder.Build(sigFact); + + X509Certificate certificate = msg.GetCertificates()[0]; + + IVerifierFactory verifierFactory = new Asn1VerifierFactory("MD5WithRSA", rsaKeyPair.Public); + + IsTrue("PKIMessage must verify (MD5withRSA)",msg.Verify(verifierFactory)); + } + + [Test] + public void TestMacProtectedMessage() + { + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, + 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + TestCertBuilder builder = new TestCertBuilder() + { + NotBefore = DateTime.UtcNow.AddDays(-1), + NotAfter = DateTime.UtcNow.AddDays(1), + PublicKey = rsaKeyPair.Public, + SignatureAlgorithm = "Sha1WithRSAEncryption" + + }; + + builder.AddAttribute(X509Name.C, "Foo"); + X509Certificate cert = builder.Build(rsaKeyPair.Private); + + GeneralName sender = new GeneralName(new X509Name("CN=Sender")); + GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); + + ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender, recipient); + msgBuilder.AddCmpCertificate(cert); + + // + // Default instance. + // + + + PkMacFactory macFactory = new PkMacFactory(new SecureRandom()); + macFactory.Password = Strings.ToAsciiByteArray("testpass"); + ProtectedPkiMessage msg = msgBuilder.Build(macFactory); + + + MacVerifierFactory verifierFactory = new MacVerifierFactory( + new PkMacFactory((PbmParameter) msg.Header.ProtectionAlg.Parameters) + {Password = Strings.ToAsciiByteArray("testpass")} + ); + IsTrue(msg.Verify(verifierFactory)); + } + + + + + [Test] + public void TestVerifyBCJavaGeneratedMessage() + { + // + // Test with content generated by BC-JAVA version. + // + + ICipherParameters publicKey = PublicKeyFactory.CreateKey(Hex.Decode( + "305c300d06092a864886f70d0101010500034b003048024100ac1e59ba5f96" + + "ba86c86e6d8bbfd43ece04265fa29e6ebdb320388b58af365d05b26970cbd2" + + "6e5b0fa7df2074b90b42a1d16ab270cdb851b53e464b87f683774502030100" + + "01")); + ICipherParameters privateKey = PrivateKeyFactory.CreateKey(Hex.Decode( + "30820155020100300d06092a864886f70d01010105000482013f3082013b02" + + "0100024100ac1e59ba5f96ba86c86e6d8bbfd43ece04265fa29e6ebdb32038" + + "8b58af365d05b26970cbd26e5b0fa7df2074b90b42a1d16ab270cdb851b53e" + + "464b87f68377450203010001024046f3f208570c735349bfe00fdaa1fbcc00" + + "c0f2eebe42279876a168ac43fa74a8cdf9a1bb49066c07cfcfa7196f69f2b9" + + "419d378109db967891428c50273dcc37022100d488dc3fb86f404d726a8166" + + "b2a9aba9bee12fdbf38470a62403a2a20bad0977022100cf51874e479b141f" + + "9915533bf54d68f1940f84d7fe6130538ff01a23e3493423022100986f94f1" + + "0afa9837341219bfabf32fd16ebb9a94fa630a5ccf45e036b383275f02201b" + + "6dff07f563684b31f6e757548254733a12bf91d05f4d8490d3c4b1a0ddcb9f" + + "02210087c3b2049e9a3edfc4cb40a3a275dabf7ffff80b467157e384603042" + + "3fe91d68")); + + byte[] ind = Hex.Decode( + "308201ac306e020102a4133011310f300d06035504030c0653656e646572a4" + + "123010310e300c06035504030c055265636970a140303e06092a864886f67d" + + "07420d30310414fdccb4ffd7848e6a697bee36cbe0f3722ed7fe2f30070605" + + "2b0e03021a020203e8300c06082b060105050801020500a10430023000a017" + + "031500c131c357441daa78eb538bfd9c24870e220fdafaa182011930820115" + + "308201113081bca003020102020601684a515d5b300d06092a864886f70d01" + + "01050500300f310d300b06035504030c0454657374301e170d313930313134" + + "3033303433325a170d3139303432343033303433325a300f310d300b060355" + + "04030c0454657374305c300d06092a864886f70d0101010500034b00304802" + + "4100ac1e59ba5f96ba86c86e6d8bbfd43ece04265fa29e6ebdb320388b58af" + + "365d05b26970cbd26e5b0fa7df2074b90b42a1d16ab270cdb851b53e464b87" + + "f68377450203010001300d06092a864886f70d0101050500034100264b5b76" + + "f268e2a992f05ad83783b091ce806a6726912c6200d06b33375ae58fe3c474" + + "c3a42ad6e572a2c48ae3bf914a7510bb995c3474829cfe71ab679a3db0"); + + + ProtectedPkiMessage pkiMsg = new ProtectedPkiMessage(PkiMessage.GetInstance(ind)); + + PbmParameter pbmParameters = PbmParameter.GetInstance(pkiMsg.Header.ProtectionAlg.Parameters); + + MacVerifierFactory verifierFactory = new MacVerifierFactory(new PkMacFactory(pbmParameters) + { + Password = Strings.ToAsciiByteArray("secret") + }); + + IsTrue(pkiMsg.Verify(verifierFactory)); + } + + + +} + + public class TestCertBuilder + { + IDictionary attrs = new Hashtable(); + IList ord = new ArrayList(); + IList values = new ArrayList(); + + public DateTime NotBefore { get; set; } + + public DateTime NotAfter { get; set; } + + public AsymmetricKeyParameter PublicKey { get; set; } + + public String SignatureAlgorithm { get; set; } + + public X509Name Issuer { get; set; } + public X509Name Subject { get; set; } + + public TestCertBuilder AddAttribute(DerObjectIdentifier name, Object value) + { + attrs[name] = value; + ord.Add(name); + values.Add(value); + return this; + } + + public X509Certificate Build(AsymmetricKeyParameter privateKey) + { + X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); + + certGen.SetSerialNumber(BigInteger.One); + + if (Issuer != null) + { + certGen.SetIssuerDN(Issuer); + } + else + { + certGen.SetIssuerDN(new X509Name(ord, attrs)); + } + + + certGen.SetNotBefore(NotBefore); + certGen.SetNotAfter(NotAfter); + + if (Subject != null) + { + certGen.SetSubjectDN(Subject); + } + else + { + certGen.SetSubjectDN(new X509Name(ord, attrs)); + } + + + certGen.SetPublicKey(PublicKey); + certGen.SetSignatureAlgorithm(SignatureAlgorithm); + + return certGen.Generate(privateKey); + } + } +} From b7d7b6c6abb66a343ac9722fd0f80a4de203cc25 Mon Sep 17 00:00:00 2001 From: Megan Woods Date: Mon, 14 Jan 2019 19:19:55 +1100 Subject: [PATCH 10/22] Updated locations added initial example for EJBCA --- .../cmp/ProtectedPkiMessageBuilder.cs | 19 +- .../crmf/CertificateRequestMessageBuilder.cs | 2 + .../src/asn1/test/ProtectedMessageTest.cs | 429 ------------------ .../test/src/cmp/test/ProtectedMessageTest.cs | 1 + .../src/ejbca/test/EnrollmentExampleTest.cs | 84 ++++ 5 files changed, 96 insertions(+), 439 deletions(-) rename crypto/src/{asn1 => }/cmp/ProtectedPkiMessageBuilder.cs (94%) delete mode 100644 crypto/test/src/asn1/test/ProtectedMessageTest.cs create mode 100644 crypto/test/src/ejbca/test/EnrollmentExampleTest.cs diff --git a/crypto/src/asn1/cmp/ProtectedPkiMessageBuilder.cs b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs similarity index 94% rename from crypto/src/asn1/cmp/ProtectedPkiMessageBuilder.cs rename to crypto/src/cmp/ProtectedPkiMessageBuilder.cs index a6a98d7530..22a004669c 100644 --- a/crypto/src/asn1/cmp/ProtectedPkiMessageBuilder.cs +++ b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs @@ -1,20 +1,13 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Text; -using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.IO; -using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Operators; -using Org.BouncyCastle.Crypto.Paddings; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.X509; -namespace Org.BouncyCastle.Asn1.Cmp +namespace Org.BouncyCastle.Cmp { public class ProtectedPkiMessageBuilder { @@ -52,6 +45,12 @@ public ProtectedPkiMessageBuilder AddGeneralInfo(InfoTypeAndValue genInfo) return this; } + public ProtectedPkiMessageBuilder SetMessageTime(DerGeneralizedTime generalizedTime) + { + hdrBuilBuilder.SetMessageTime(generalizedTime); + return this; + } + public ProtectedPkiMessageBuilder SetRecipKID(byte[] id) { hdrBuilBuilder.SetRecipKID(id); diff --git a/crypto/src/crmf/CertificateRequestMessageBuilder.cs b/crypto/src/crmf/CertificateRequestMessageBuilder.cs index 53ebdf3f50..10a575abef 100644 --- a/crypto/src/crmf/CertificateRequestMessageBuilder.cs +++ b/crypto/src/crmf/CertificateRequestMessageBuilder.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Text; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Operators; diff --git a/crypto/test/src/asn1/test/ProtectedMessageTest.cs b/crypto/test/src/asn1/test/ProtectedMessageTest.cs deleted file mode 100644 index 5ff51ba1a6..0000000000 --- a/crypto/test/src/asn1/test/ProtectedMessageTest.cs +++ /dev/null @@ -1,429 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -using System.Text; -using NUnit.Framework; -using Org.BouncyCastle.Asn1.Cmp; -using Org.BouncyCastle.Asn1.Crmf; -using Org.BouncyCastle.Asn1.Nist; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Cms; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Generators; -using Org.BouncyCastle.Crypto.Operators; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Crypto.Signers; -using Org.BouncyCastle.Math; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.Utilities.Encoders; -using Org.BouncyCastle.Utilities.Test; -using Org.BouncyCastle.X509; - -namespace Org.BouncyCastle.Asn1.Tests -{ - [TestFixture] - public class ProtectedMessageTest : SimpleTest - { - public override string Name - { - get { return "ProtectedMessageTest"; } - } - - public override void PerformTest() - { - TestVerifyBCJavaGeneratedMessage(); - TestSubsequentMessage(); - TestMacProtectedMessage(); - TestProtectedMessage(); - TestConfirmationMessage(); - TestSampleCr(); - } - -// [Test] -// public void TestServerSideKey() -// { -// RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); -// rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); -// AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); -// -// TestCertBuilder builder = new TestCertBuilder() -// { -// Issuer = new X509Name("CN=Test"), -// Subject = new X509Name("CN=Test"), -// NotBefore = DateTime.UtcNow.AddDays(-1), -// NotAfter = DateTime.UtcNow.AddDays(1), -// PublicKey = rsaKeyPair.Public, -// SignatureAlgorithm = "MD5WithRSAEncryption" -// }; -// -// builder.AddAttribute(X509Name.C, "Foo"); -// X509Certificate cert = builder.Build(rsaKeyPair.Private); -// -// GeneralName sender = new GeneralName(new X509Name("CN=Sender")); -// GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); -// -// -// -// } - - [Test] - public void TestNotBeforeNotAfter() - { - RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); - rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); - AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); - - doNotBeforeNotAfterTest(rsaKeyPair, new DateTime(1,1,1,0,0,1), new DateTime(1,1,1,0,0,10)); - doNotBeforeNotAfterTest(rsaKeyPair, DateTime.MinValue, new DateTime(1, 1, 1, 0, 0, 10)); - doNotBeforeNotAfterTest(rsaKeyPair, new DateTime(1, 1, 1, 0, 0, 1), DateTime.MinValue); - } - - - private void doNotBeforeNotAfterTest(AsymmetricCipherKeyPair kp, DateTime notBefore, DateTime notAfter) - { - CertificateRequestMessageBuilder builder = new CertificateRequestMessageBuilder(BigInteger.One) - .SetPublicKey(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public)) - .SetProofOfPossessionSubsequentMessage(SubsequentMessage.encrCert); - - builder.SetValidity(new Time(notBefore), new Time(notAfter)); - CertificateRequestMessage msg = builder.Build(); - - if (!notBefore.Equals(DateTime.MinValue)) - { - IsTrue("NotBefore did not match",(notBefore.Equals(msg.GetCertTemplate().Validity.NotBefore.ToDateTime()))); - } - else - { - IsTrue("Expected NotBefore to empty.",DateTime.MinValue == msg.GetCertTemplate().Validity.NotBefore.ToDateTime()); - } - - if (!notAfter.Equals(DateTime.MinValue)) - { - IsTrue("NotAfter did not match", (notAfter.Equals(msg.GetCertTemplate().Validity.NotAfter.ToDateTime()))); - } - else - { - IsTrue("Expected NotAfter to be empty.", DateTime.MinValue == msg.GetCertTemplate().Validity.NotAfter.ToDateTime()); - } - - } - - - [Test] - public void TestSubsequentMessage() - { - RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); - rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); - AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); - - TestCertBuilder builder = new TestCertBuilder() - { - NotBefore = DateTime.UtcNow.AddDays(-1), - NotAfter = DateTime.UtcNow.AddDays(1), - PublicKey = rsaKeyPair.Public, - SignatureAlgorithm = "Sha1WithRSAEncryption" - - }; - - X509Certificate cert = builder.Build(rsaKeyPair.Private); - - GeneralName user = new GeneralName(new X509Name("CN=Test")); - - CertificateRequestMessageBuilder crmBuiler = new CertificateRequestMessageBuilder(BigInteger.One) - .SetPublicKey(SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(rsaKeyPair.Public)) - .SetProofOfPossessionSubsequentMessage(SubsequentMessage.encrCert); - - ISignatureFactory sigFact = new Asn1SignatureFactory("SHA256WithRSA", rsaKeyPair.Private); - - ProtectedPkiMessage certRequestMsg = new ProtectedPkiMessageBuilder(user,user) - .SetTransactionId(new byte[]{1,2,3,4,5}) - .SetBody(new PkiBody(PkiBody.TYPE_KEY_RECOVERY_REQ, new CertReqMessages(new CertReqMsg[]{crmBuiler.Build().ToAsn1Structure()}))) - .AddCmpCertificate(cert) - .Build(sigFact); - - ProtectedPkiMessage msg = new ProtectedPkiMessage(new GeneralPKIMessage(certRequestMsg.ToAsn1Message().GetDerEncoded())); - CertReqMessages reqMsgs = CertReqMessages.GetInstance(msg.Body.Content); - CertReqMsg reqMsg = reqMsgs.ToCertReqMsgArray()[0]; - IsEquals(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, reqMsg.Popo.Type); - - } - - - - [Test] - public void TestSampleCr() - { - byte[] raw = Base64.Decode( - "MIIB5TCB3AIBAqQdMBsxDDAKBgNVBAMMA0FSUDELMAkGA1UEBhMCQ0ikOTA3MREwDwYDVQQDDAhBZG1pbkNBM" + - "TEVMBMGA1UECgwMRUpCQ0EgU2FtcGxlMQswCQYDVQQGEwJTRaFGMEQGCSqGSIb2fQdCDTA3BBxzYWx0Tm9NYX" + - "R0ZXJXaGF0VGhpc1N0cmluZ0lzMAcGBSsOAwIaAgIEADAKBggrBgEFBQgBAqIQBA5TZW5kZXJLSUQtMjAwOKQ" + - "PBA0xMjAzNjA3MDE1OTQ0pRIEEOPfE1DMncRUdrBj8KelgsCigeowgecwgeQwgd0CAQAwgcGlHTAbMQwwCgYD" + - "VQQDDANBUlAxCzAJBgNVBAYTAkNIpoGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrrv4e42olM2YJqSbCN" + - "d19EtW7d6T8HYvcSU5wsm5icKFkxyD5jrO/2xYh3zqUFYwZap0pA7qbhxk5sEne2ywVpt2lGSmpAU8M7hC9oh" + - "Ep9wvv+3+td5MEO+qMuWWxF8OZBlYIFBZ/k+pGlU+4XlBP5Ai6pu/EI/0A+1/bcGs0sQIDAQABMBQwEgYJKwY" + - "BBQUHBQEBDAVEVU1NWaACBQCgFwMVAO73HUPF//mY5+E714Cv5oprt0kO\r\n"); - - ProtectedPkiMessage msg = new ProtectedPkiMessage(new GeneralPKIMessage(raw)); - - - - IsTrue(msg.Verify(new Asn1MacFactoryProvider(),Strings.ToAsciiByteArray("TopSecret1234"))); - - } - - - [Test] - public void TestConfirmationMessage() - { - RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); - rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); - AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); - - TestCertBuilder builder = new TestCertBuilder() - { - NotBefore = DateTime.UtcNow.AddDays(-1), - NotAfter = DateTime.UtcNow.AddDays(1), - PublicKey = rsaKeyPair.Public, - SignatureAlgorithm = "Sha1WithRSAEncryption" - - }; - - builder.AddAttribute(X509Name.C, "Foo"); - X509Certificate cert = builder.Build(rsaKeyPair.Private); - - GeneralName sender = new GeneralName(new X509Name("CN=Sender")); - GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); - - CertificateConfirmationContent content = new CertificateConfirmationContentBuilder() - .AddAcceptedCertificate(cert, BigInteger.One) - .Build(); - - ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender, recipient); - msgBuilder.SetBody(new PkiBody(PkiBody.TYPE_CERT_CONFIRM, content.ToAsn1Structure())); - msgBuilder.AddCmpCertificate(cert); - - ISignatureFactory sigFact = new Asn1SignatureFactory("MD5WithRSA", rsaKeyPair.Private); - ProtectedPkiMessage msg = msgBuilder.Build(sigFact); - - IVerifierFactory verifierFactory = new Asn1VerifierFactory("MD5WithRSA", rsaKeyPair.Public); - - IsTrue("PKIMessage must verify (MD5withRSA)", msg.Verify(verifierFactory)); - - IsEquals(sender,msg.Header.Sender); - IsEquals(recipient,msg.Header.Recipient); - - content = new CertificateConfirmationContent(CertConfirmContent.GetInstance(msg.Body.Content), new DefaultDigestAlgorithmIdentifierFinder()); - CertificateStatus[] statusList = content.GetStatusMessages(); - IsEquals(1,statusList.Length); - IsTrue(statusList[0].IsVerified(cert)); - } - - - - [Test] - public void TestProtectedMessage() - { - RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); - rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537),new SecureRandom(),2048,100)); - AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); - - TestCertBuilder builder = new TestCertBuilder() - { - NotBefore = DateTime.UtcNow.AddDays(-1), - NotAfter = DateTime.UtcNow.AddDays(1), - PublicKey = rsaKeyPair.Public, - SignatureAlgorithm = "Sha1WithRSAEncryption" - - }; - - builder.AddAttribute(X509Name.C, "Foo"); - X509Certificate cert = builder.Build(rsaKeyPair.Private); - - GeneralName sender = new GeneralName(new X509Name("CN=Sender")); - GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); - - ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender,recipient); - msgBuilder.AddCmpCertificate(cert); - - ISignatureFactory sigFact = new Asn1SignatureFactory("MD5WithRSA",rsaKeyPair.Private); - - ProtectedPkiMessage msg = msgBuilder.Build(sigFact); - - X509Certificate certificate = msg.GetCertificates()[0]; - - IVerifierFactory verifierFactory = new Asn1VerifierFactory("MD5WithRSA", rsaKeyPair.Public); - - IsTrue("PKIMessage must verify (MD5withRSA)",msg.Verify(verifierFactory)); - } - - [Test] - public void TestMacProtectedMessage() - { - RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); - rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, - 100)); - AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); - - TestCertBuilder builder = new TestCertBuilder() - { - NotBefore = DateTime.UtcNow.AddDays(-1), - NotAfter = DateTime.UtcNow.AddDays(1), - PublicKey = rsaKeyPair.Public, - SignatureAlgorithm = "Sha1WithRSAEncryption" - - }; - - builder.AddAttribute(X509Name.C, "Foo"); - X509Certificate cert = builder.Build(rsaKeyPair.Private); - - GeneralName sender = new GeneralName(new X509Name("CN=Sender")); - GeneralName recipient = new GeneralName(new X509Name("CN=Recip")); - - ProtectedPkiMessageBuilder msgBuilder = new ProtectedPkiMessageBuilder(sender, recipient); - msgBuilder.AddCmpCertificate(cert); - - // - // Default instance. - // - - - PkMacFactory macFactory = new PkMacFactory(new SecureRandom()); - macFactory.Password = Strings.ToAsciiByteArray("testpass"); - ProtectedPkiMessage msg = msgBuilder.Build(macFactory); - - - MacVerifierFactory verifierFactory = new MacVerifierFactory( - new PkMacFactory((PbmParameter) msg.Header.ProtectionAlg.Parameters) - {Password = Strings.ToAsciiByteArray("testpass")} - ); - IsTrue(msg.Verify(verifierFactory)); - } - - - - - [Test] - public void TestVerifyBCJavaGeneratedMessage() - { - // - // Test with content generated by BC-JAVA version. - // - - ICipherParameters publicKey = PublicKeyFactory.CreateKey(Hex.Decode( - "305c300d06092a864886f70d0101010500034b003048024100ac1e59ba5f96" + - "ba86c86e6d8bbfd43ece04265fa29e6ebdb320388b58af365d05b26970cbd2" + - "6e5b0fa7df2074b90b42a1d16ab270cdb851b53e464b87f683774502030100" + - "01")); - ICipherParameters privateKey = PrivateKeyFactory.CreateKey(Hex.Decode( - "30820155020100300d06092a864886f70d01010105000482013f3082013b02" + - "0100024100ac1e59ba5f96ba86c86e6d8bbfd43ece04265fa29e6ebdb32038" + - "8b58af365d05b26970cbd26e5b0fa7df2074b90b42a1d16ab270cdb851b53e" + - "464b87f68377450203010001024046f3f208570c735349bfe00fdaa1fbcc00" + - "c0f2eebe42279876a168ac43fa74a8cdf9a1bb49066c07cfcfa7196f69f2b9" + - "419d378109db967891428c50273dcc37022100d488dc3fb86f404d726a8166" + - "b2a9aba9bee12fdbf38470a62403a2a20bad0977022100cf51874e479b141f" + - "9915533bf54d68f1940f84d7fe6130538ff01a23e3493423022100986f94f1" + - "0afa9837341219bfabf32fd16ebb9a94fa630a5ccf45e036b383275f02201b" + - "6dff07f563684b31f6e757548254733a12bf91d05f4d8490d3c4b1a0ddcb9f" + - "02210087c3b2049e9a3edfc4cb40a3a275dabf7ffff80b467157e384603042" + - "3fe91d68")); - - byte[] ind = Hex.Decode( - "308201ac306e020102a4133011310f300d06035504030c0653656e646572a4" + - "123010310e300c06035504030c055265636970a140303e06092a864886f67d" + - "07420d30310414fdccb4ffd7848e6a697bee36cbe0f3722ed7fe2f30070605" + - "2b0e03021a020203e8300c06082b060105050801020500a10430023000a017" + - "031500c131c357441daa78eb538bfd9c24870e220fdafaa182011930820115" + - "308201113081bca003020102020601684a515d5b300d06092a864886f70d01" + - "01050500300f310d300b06035504030c0454657374301e170d313930313134" + - "3033303433325a170d3139303432343033303433325a300f310d300b060355" + - "04030c0454657374305c300d06092a864886f70d0101010500034b00304802" + - "4100ac1e59ba5f96ba86c86e6d8bbfd43ece04265fa29e6ebdb320388b58af" + - "365d05b26970cbd26e5b0fa7df2074b90b42a1d16ab270cdb851b53e464b87" + - "f68377450203010001300d06092a864886f70d0101050500034100264b5b76" + - "f268e2a992f05ad83783b091ce806a6726912c6200d06b33375ae58fe3c474" + - "c3a42ad6e572a2c48ae3bf914a7510bb995c3474829cfe71ab679a3db0"); - - - ProtectedPkiMessage pkiMsg = new ProtectedPkiMessage(PkiMessage.GetInstance(ind)); - - PbmParameter pbmParameters = PbmParameter.GetInstance(pkiMsg.Header.ProtectionAlg.Parameters); - - MacVerifierFactory verifierFactory = new MacVerifierFactory(new PkMacFactory(pbmParameters) - { - Password = Strings.ToAsciiByteArray("secret") - }); - - IsTrue(pkiMsg.Verify(verifierFactory)); - } - - - -} - - public class TestCertBuilder - { - IDictionary attrs = new Hashtable(); - IList ord = new ArrayList(); - IList values = new ArrayList(); - - public DateTime NotBefore { get; set; } - - public DateTime NotAfter { get; set; } - - public AsymmetricKeyParameter PublicKey { get; set; } - - public String SignatureAlgorithm { get; set; } - - public X509Name Issuer { get; set; } - public X509Name Subject { get; set; } - - public TestCertBuilder AddAttribute(DerObjectIdentifier name, Object value) - { - attrs[name] = value; - ord.Add(name); - values.Add(value); - return this; - } - - public X509Certificate Build(AsymmetricKeyParameter privateKey) - { - X509V3CertificateGenerator certGen = new X509V3CertificateGenerator(); - - certGen.SetSerialNumber(BigInteger.One); - - if (Issuer != null) - { - certGen.SetIssuerDN(Issuer); - } - else - { - certGen.SetIssuerDN(new X509Name(ord, attrs)); - } - - - certGen.SetNotBefore(NotBefore); - certGen.SetNotAfter(NotAfter); - - if (Subject != null) - { - certGen.SetSubjectDN(Subject); - } - else - { - certGen.SetSubjectDN(new X509Name(ord, attrs)); - } - - - certGen.SetPublicKey(PublicKey); - certGen.SetSignatureAlgorithm(SignatureAlgorithm); - - return certGen.Generate(privateKey); - } - } -} diff --git a/crypto/test/src/cmp/test/ProtectedMessageTest.cs b/crypto/test/src/cmp/test/ProtectedMessageTest.cs index ef19197792..c22ba32975 100644 --- a/crypto/test/src/cmp/test/ProtectedMessageTest.cs +++ b/crypto/test/src/cmp/test/ProtectedMessageTest.cs @@ -4,6 +4,7 @@ using System.Text; using NUnit.Framework; +using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Crmf; diff --git a/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs b/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs new file mode 100644 index 0000000000..ce023549ec --- /dev/null +++ b/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs @@ -0,0 +1,84 @@ +using NUnit.Framework; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cmp; +using Org.BouncyCastle.Crmf; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; + +namespace crypto.test.src.ejbca.test +{ + [TestFixture] + public class EnrollmentExampleTest + { + + [Test] + public void TestEnrollmentRAWithSharedSecret() + { + long certReqId = 1; + SecureRandom secureRandom = new SecureRandom(); + + byte[] senderNonce = new byte[20]; + secureRandom.NextBytes(senderNonce); + + byte[] transactionId = Strings.ToAsciiByteArray("MyTransactionId"); + + + RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); + rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); + AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); + + + CertificateRequestMessageBuilder msgbuilder = new CertificateRequestMessageBuilder(BigInteger.ValueOf(certReqId)); + X509NameEntryConverter dnconverter = new X509DefaultEntryConverter(); + + X509Name issuerDN = X509Name.GetInstance(new X509Name("CN=AdminCA1").ToAsn1Object()); + X509Name subjectDN = X509Name.GetInstance(new X509Name("CN=user", dnconverter).ToAsn1Object()); + msgbuilder.SetIssuer(issuerDN); + msgbuilder.SetSubject(subjectDN); + SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(rsaKeyPair.Public); + + msgbuilder.SetPublicKey(keyInfo); + GeneralName sender = new GeneralName(subjectDN); + msgbuilder.SetAuthInfoSender(sender); + // RAVerified POP + msgbuilder.SetProofOfPossessionRaVerified(); + CertificateRequestMessage msg = msgbuilder.Build(); + GeneralName recipient = new GeneralName(issuerDN); + + ProtectedPkiMessageBuilder pbuilder = new ProtectedPkiMessageBuilder(sender, recipient); + // pbuilder. SetMessageTime(new Date()); + // senderNonce + pbuilder.SetSenderNonce(senderNonce); + // TransactionId + pbuilder.SetTransactionId(transactionId); + // Key Id used (required) by the recipient to do a lot of stuff + pbuilder.SetSenderKID(Strings.ToAsciiByteArray("KeyId")); + + + CertReqMessages msgs = new CertReqMessages(msg.ToAsn1Structure()); + PkiBody pkibody = new PkiBody(PkiBody.TYPE_INIT_REQ, msgs); + pbuilder.SetBody(pkibody); + + + + AlgorithmIdentifier digAlg = new AlgorithmIdentifier("1.3.14.3.2.26"); // SHA1 + AlgorithmIdentifier macAlg = new AlgorithmIdentifier("1.2.840.113549.2.7"); // HMAC/SHA1 + + PkMacFactory macFactory = new PkMacFactory(digAlg,macAlg); + macFactory.Password = Strings.ToAsciiByteArray("password"); + + ProtectedPkiMessage message = pbuilder.Build(macFactory); + + + } + + } +} \ No newline at end of file From 11c4aef6426adcd663f51fdd8b37fbc8fdddef73 Mon Sep 17 00:00:00 2001 From: Megan Woods Date: Mon, 14 Jan 2019 19:21:53 +1100 Subject: [PATCH 11/22] Updated EJBCA Example --- crypto/test/src/ejbca/test/EnrollmentExampleTest.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs b/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs index ce023549ec..3a636d8ea8 100644 --- a/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs +++ b/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs @@ -67,8 +67,7 @@ public void TestEnrollmentRAWithSharedSecret() PkiBody pkibody = new PkiBody(PkiBody.TYPE_INIT_REQ, msgs); pbuilder.SetBody(pkibody); - - + AlgorithmIdentifier digAlg = new AlgorithmIdentifier("1.3.14.3.2.26"); // SHA1 AlgorithmIdentifier macAlg = new AlgorithmIdentifier("1.2.840.113549.2.7"); // HMAC/SHA1 From e62f859d5d77d465efb9bb30f35fe33da61c2409 Mon Sep 17 00:00:00 2001 From: Megan Woods Date: Mon, 14 Jan 2019 19:28:29 +1100 Subject: [PATCH 12/22] Removed reference to old test. Added call to set time in EJBCA example. --- crypto/test/src/cms/test/AllTests.cs | 3 +-- crypto/test/src/ejbca/test/EnrollmentExampleTest.cs | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crypto/test/src/cms/test/AllTests.cs b/crypto/test/src/cms/test/AllTests.cs index 7ab5721ac8..52ca95f2b5 100644 --- a/crypto/test/src/cms/test/AllTests.cs +++ b/crypto/test/src/cms/test/AllTests.cs @@ -20,8 +20,7 @@ public static TestSuite Suite { get { - TestSuite suite = new TestSuite("CMS Tests"); - suite.Add(new ProtectedMessageTest()); + TestSuite suite = new TestSuite("CMS Tests"); suite.Add(new CompressedDataTest()); suite.Add(new CompressedDataStreamTest()); suite.Add(new EnvelopedDataTest()); diff --git a/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs b/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs index 3a636d8ea8..4ee0ae2c3c 100644 --- a/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs +++ b/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs @@ -1,4 +1,6 @@ -using NUnit.Framework; +using System; +using NUnit.Framework; +using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Asn1.X509; @@ -54,7 +56,7 @@ public void TestEnrollmentRAWithSharedSecret() GeneralName recipient = new GeneralName(issuerDN); ProtectedPkiMessageBuilder pbuilder = new ProtectedPkiMessageBuilder(sender, recipient); - // pbuilder. SetMessageTime(new Date()); + pbuilder.SetMessageTime(new DerGeneralizedTime(DateTime.Now)); // senderNonce pbuilder.SetSenderNonce(senderNonce); // TransactionId From 56256b133d57fada6dd8997bd58c4d60ef97db3e Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 15 Jan 2019 05:05:09 +1100 Subject: [PATCH 13/22] moved ASN.1 class back --- crypto/src/asn1/crmf/Controls.cs | 55 ++++++++++++++++++++++++++++++++ crypto/src/crmf/IControl.cs | 49 ---------------------------- 2 files changed, 55 insertions(+), 49 deletions(-) create mode 100644 crypto/src/asn1/crmf/Controls.cs diff --git a/crypto/src/asn1/crmf/Controls.cs b/crypto/src/asn1/crmf/Controls.cs new file mode 100644 index 0000000000..5f132155a4 --- /dev/null +++ b/crypto/src/asn1/crmf/Controls.cs @@ -0,0 +1,55 @@ +using Org.BouncyCastle.Utilities; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Asn1.Crmf +{ + public class Controls + : Asn1Encodable + { + private readonly Asn1Sequence content; + + private Controls(Asn1Sequence seq) + { + content = seq; + } + + public static Controls GetInstance(object obj) + { + if (obj is Controls) + return (Controls)obj; + + if (obj is Asn1Sequence) + return new Controls((Asn1Sequence)obj); + + throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj"); + } + + public Controls(params AttributeTypeAndValue[] atvs) + { + content = new DerSequence(atvs); + } + + public virtual AttributeTypeAndValue[] ToAttributeTypeAndValueArray() + { + AttributeTypeAndValue[] result = new AttributeTypeAndValue[content.Count]; + for (int i = 0; i != result.Length; ++i) + { + result[i] = AttributeTypeAndValue.GetInstance(content[i]); + } + return result; + } + + /** + *
+         * Controls  ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue
+         * 
+ * @return a basic ASN.1 object representation. + */ + public override Asn1Object ToAsn1Object() + { + return content; + } + } +} diff --git a/crypto/src/crmf/IControl.cs b/crypto/src/crmf/IControl.cs index bf29fafddc..3601633a4c 100644 --- a/crypto/src/crmf/IControl.cs +++ b/crypto/src/crmf/IControl.cs @@ -13,53 +13,4 @@ public interface IControl Asn1Encodable Value { get; } } - - - public class Controls - : Asn1Encodable - { - private readonly Asn1Sequence content; - - private Controls(Asn1Sequence seq) - { - content = seq; - } - - public static Controls GetInstance(object obj) - { - if (obj is Controls) - return (Controls)obj; - - if (obj is Asn1Sequence) - return new Controls((Asn1Sequence)obj); - - throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj"); - } - - public Controls(params AttributeTypeAndValue[] atvs) - { - content = new DerSequence(atvs); - } - - public virtual AttributeTypeAndValue[] ToAttributeTypeAndValueArray() - { - AttributeTypeAndValue[] result = new AttributeTypeAndValue[content.Count]; - for (int i = 0; i != result.Length; ++i) - { - result[i] = AttributeTypeAndValue.GetInstance(content[i]); - } - return result; - } - - /** - *
-         * Controls  ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue
-         * 
- * @return a basic ASN.1 object representation. - */ - public override Asn1Object ToAsn1Object() - { - return content; - } - } } From 6ca2f2f9b941289f42d0ef0d2ef8f0cfa1e4ac86 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 15 Jan 2019 08:05:41 +1100 Subject: [PATCH 14/22] refactor of PKMacBuilder --- .../GeneralPkiMessage.cs} | 0 .../src/{asn1 => }/cmp/ProtectedPkiMessage.cs | 35 +- crypto/src/cmp/ProtectedPkiMessageBuilder.cs | 9 +- .../crmf/CertificateRequestMessageBuilder.cs | 4 +- .../crmf/DefaultPKMacPrimitivesProvider.cs | 23 + crypto/src/crmf/IPKMacPrimitivesProvider.cs | 28 ++ crypto/src/crmf/PKMacBuilder.cs | 256 +++++++++++ .../ProofOfPossessionSigningKeyBuilder.cs | 90 ++++ crypto/src/crypto/operators/Asn1Mac.cs | 410 ------------------ 9 files changed, 423 insertions(+), 432 deletions(-) rename crypto/src/{asn1/cmp/GeneralPKIMessage.cs => cmp/GeneralPkiMessage.cs} (100%) rename crypto/src/{asn1 => }/cmp/ProtectedPkiMessage.cs (75%) create mode 100644 crypto/src/crmf/DefaultPKMacPrimitivesProvider.cs create mode 100644 crypto/src/crmf/IPKMacPrimitivesProvider.cs create mode 100644 crypto/src/crmf/PKMacBuilder.cs create mode 100644 crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs delete mode 100644 crypto/src/crypto/operators/Asn1Mac.cs diff --git a/crypto/src/asn1/cmp/GeneralPKIMessage.cs b/crypto/src/cmp/GeneralPkiMessage.cs similarity index 100% rename from crypto/src/asn1/cmp/GeneralPKIMessage.cs rename to crypto/src/cmp/GeneralPkiMessage.cs diff --git a/crypto/src/asn1/cmp/ProtectedPkiMessage.cs b/crypto/src/cmp/ProtectedPkiMessage.cs similarity index 75% rename from crypto/src/asn1/cmp/ProtectedPkiMessage.cs rename to crypto/src/cmp/ProtectedPkiMessage.cs index c39f06ad04..159f087225 100644 --- a/crypto/src/asn1/cmp/ProtectedPkiMessage.cs +++ b/crypto/src/cmp/ProtectedPkiMessage.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; using System.Text; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Crypto; @@ -10,8 +12,9 @@ using Org.BouncyCastle.Crypto.Paddings; using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Crmf; -namespace Org.BouncyCastle.Asn1.Cmp +namespace Org.BouncyCastle.Cmp { public class ProtectedPkiMessage @@ -64,29 +67,31 @@ public X509Certificate[] GetCertificates() return res; } - - + public bool Verify(IVerifierFactory verifierFactory) + { + IStreamCalculator streamCalculator = verifierFactory.CreateCalculator(); + + IVerifier result = (IVerifier)Process(streamCalculator); + + return result.IsVerified(pkiMessage.Protection.GetBytes()); + } - public bool Verify(IVerifierFactory verifier) + private Object Process(IStreamCalculator streamCalculator) { Asn1EncodableVector avec = new Asn1EncodableVector(); avec.Add(pkiMessage.Header); avec.Add(pkiMessage.Body); byte[] enc = new DerSequence(avec).GetDerEncoded(); - IStreamCalculator streamCalculator = verifier.CreateCalculator(); - streamCalculator.Stream.Write(enc,0,enc.Length); streamCalculator.Stream.Flush(); streamCalculator.Stream.Close(); - IVerifier result = (IVerifier) streamCalculator.GetResult(); - return result.IsVerified(pkiMessage.Protection.GetBytes()); + return streamCalculator.GetResult(); } - - public bool Verify(Asn1MacFactoryProvider asn1Factory, byte[] password) + public bool Verify(PKMacBuilder pkMacBuilder, char[] password) { if (!CmpObjectIdentifiers.passwordBasedMac.Equals(pkiMessage.Header.ProtectionAlg.Algorithm)) { @@ -95,13 +100,11 @@ public bool Verify(Asn1MacFactoryProvider asn1Factory, byte[] password) PbmParameter parameter = PbmParameter.GetInstance(pkiMessage.Header.ProtectionAlg.Parameters); - PkMacFactory macFactory = (PkMacFactory)asn1Factory.CreateMacFactory(parameter); - - macFactory.Password = password; - MacVerifierFactory macVerifierFactory = new MacVerifierFactory(macFactory); + pkMacBuilder.SetParameters(parameter); - return Verify(macVerifierFactory); - } + IBlockResult result = (IBlockResult)Process(pkMacBuilder.Build(password).CreateCalculator()); + return Arrays.ConstantTimeAreEqual(result.Collect(), this.pkiMessage.Protection.GetBytes()); + } } } diff --git a/crypto/src/cmp/ProtectedPkiMessageBuilder.cs b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs index 22a004669c..e660f844a9 100644 --- a/crypto/src/cmp/ProtectedPkiMessageBuilder.cs +++ b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs @@ -1,11 +1,13 @@ using System; using System.Collections; +using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Operators; using Org.BouncyCastle.X509; +using Org.BouncyCastle.Crmf; namespace Org.BouncyCastle.Cmp { @@ -153,14 +155,13 @@ private byte[] CalculateSignature(IStreamCalculator signer, PkiHeader header, Pk signer.Stream.Write(encoded, 0, encoded.Length); Object result = signer.GetResult(); - if (result is DefaultSignatureResult) { - return ((DefaultSignatureResult) result).Collect(); + return ((DefaultSignatureResult)result).Collect(); } - else if (result is DefaultMacAndDigestResult) + else if (result is IBlockResult) { - return ((DefaultMacAndDigestResult) result).MacResult; + return ((IBlockResult)result).Collect(); } else if (result is byte[]) { diff --git a/crypto/src/crmf/CertificateRequestMessageBuilder.cs b/crypto/src/crmf/CertificateRequestMessageBuilder.cs index 10a575abef..384f6a965d 100644 --- a/crypto/src/crmf/CertificateRequestMessageBuilder.cs +++ b/crypto/src/crmf/CertificateRequestMessageBuilder.cs @@ -19,7 +19,7 @@ public class CertificateRequestMessageBuilder private CertTemplateBuilder _templateBuilder; private ArrayList _controls= new ArrayList(); private ISignatureFactory _popSigner; - private PkMacFactory _pkMacBuilder; + private PKMacBuilder _pkMacBuilder; private char[] _password; private GeneralName _sender; private int _popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT; @@ -161,7 +161,7 @@ public CertificateRequestMessageBuilder SetProofOfPossessionRaVerified() return this; } - public CertificateRequestMessageBuilder SetAuthInfoPKMAC(PkMacFactory pkmacFactory, char[] password) + public CertificateRequestMessageBuilder SetAuthInfoPKMAC(PKMacBuilder pkmacFactory, char[] password) { this._pkMacBuilder = pkmacFactory; this._password = password; diff --git a/crypto/src/crmf/DefaultPKMacPrimitivesProvider.cs b/crypto/src/crmf/DefaultPKMacPrimitivesProvider.cs new file mode 100644 index 0000000000..1757d6a92c --- /dev/null +++ b/crypto/src/crmf/DefaultPKMacPrimitivesProvider.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crmf +{ + public class DefaultPKMacPrimitivesProvider : IPKMacPrimitivesProvider + { + public IDigest CreateDigest(AlgorithmIdentifier digestAlg) + { + return DigestUtilities.GetDigest(digestAlg.Algorithm); + } + + public IMac CreateMac(AlgorithmIdentifier macAlg) + { + return MacUtilities.GetMac(macAlg.Algorithm); + } + } +} diff --git a/crypto/src/crmf/IPKMacPrimitivesProvider.cs b/crypto/src/crmf/IPKMacPrimitivesProvider.cs new file mode 100644 index 0000000000..8b90be5153 --- /dev/null +++ b/crypto/src/crmf/IPKMacPrimitivesProvider.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Iana; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; +using Org.BouncyCastle.Crypto; + +namespace Org.BouncyCastle.Crmf +{ + public interface IPKMacPrimitivesProvider + { + IDigest CreateDigest(AlgorithmIdentifier digestAlg); + + IMac CreateMac(AlgorithmIdentifier macAlg); + } +} diff --git a/crypto/src/crmf/PKMacBuilder.cs b/crypto/src/crmf/PKMacBuilder.cs new file mode 100644 index 0000000000..5e3a5844d3 --- /dev/null +++ b/crypto/src/crmf/PKMacBuilder.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Iana; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Asn1.Oiw; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.IO; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Utilities.Encoders; + + +namespace Org.BouncyCastle.Crmf +{ + + class PKMacStreamCalculator : IStreamCalculator + { + private readonly MacSink _stream; + + public PKMacStreamCalculator(IMac mac) + { + _stream = new MacSink(mac); + } + + public Stream Stream + { + get { return _stream; } + } + + public object GetResult() + { + return new DefaultPKMacResult(_stream.Mac); + } + } + + class PKMacFactory : IMacFactory + { + protected readonly PbmParameter parameters; + private byte[] key; + + + public PKMacFactory(byte[] key, PbmParameter parameters) + { + this.key = Arrays.Clone(key); + + this.parameters = parameters; + } + + public virtual object AlgorithmDetails + { + get { return new AlgorithmIdentifier(CmpObjectIdentifiers.passwordBasedMac, parameters); } + } + + public virtual IStreamCalculator CreateCalculator() + { + IMac mac = MacUtilities.GetMac(parameters.Mac.Algorithm); + + mac.Init(new KeyParameter(key)); + + return new PKMacStreamCalculator(mac); + } + } + + class DefaultPKMacResult: IBlockResult + { + private readonly IMac mac; + + public DefaultPKMacResult(IMac mac) + { + this.mac = mac; + } + + public byte[] Collect() + { + byte[] res = new byte[mac.GetMacSize()]; + + mac.DoFinal(res, 0); + + return res; + } + + public int Collect(byte[] sig, int sigOff) + { + byte[] signature = Collect(); + signature.CopyTo(sig, sigOff); + return signature.Length; + } + } + + public class PKMacBuilder + { + private AlgorithmIdentifier owf; + private AlgorithmIdentifier mac; + private IPKMacPrimitivesProvider provider; + private SecureRandom random; + private PbmParameter parameters; + private int iterationCount; + private int saltLength; + private byte[] salt; + private int maxIterations; + + public PKMacBuilder() : + this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000, new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), new DefaultPKMacPrimitivesProvider()) + { + } + + public PKMacBuilder(IPKMacPrimitivesProvider provider) : + this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000, new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), provider) + { + } + + public PKMacBuilder(IPKMacPrimitivesProvider provider, int maxIterations) + { + this.provider = provider; + this.maxIterations = maxIterations; + } + + private PKMacBuilder(AlgorithmIdentifier digestAlgorithmIdentifier, int iterationCount, AlgorithmIdentifier macAlgorithmIdentifier, IPKMacPrimitivesProvider provider) + { + this.iterationCount = iterationCount; + this.mac = macAlgorithmIdentifier; + this.owf = digestAlgorithmIdentifier; + this.provider = provider; + } + + /** + * Set the salt length in octets. + * + * @param saltLength length in octets of the salt to be generated. + * @return the generator + */ + public PKMacBuilder SetSaltLength(int saltLength) + { + if (saltLength < 8) + { + throw new ArgumentException("salt length must be at least 8 bytes"); + } + + this.saltLength = saltLength; + + return this; + } + + public PKMacBuilder SetIterationCount(int iterationCount) + { + if (iterationCount < 100) + { + throw new ArgumentException("iteration count must be at least 100"); + } + checkIterationCountCeiling(iterationCount); + + this.iterationCount = iterationCount; + + return this; + } + + public PKMacBuilder SetParameters(PbmParameter parameters) + { + checkIterationCountCeiling(parameters.IterationCount.Value.IntValue); + + this.parameters = parameters; + + return this; + } + + public PKMacBuilder SetSecureRandom(SecureRandom random) + { + this.random = random; + + return this; + } + + public IMacFactory Build(char[] password) + { + if (parameters != null) + { + return genCalculator(parameters, password); + } + else + { + byte[] salt = new byte[saltLength]; + + if (random == null) + { + this.random = new SecureRandom(); + } + + random.NextBytes(salt); + + return genCalculator(new PbmParameter(salt, owf, iterationCount, mac), password); + } + } + + private void checkIterationCountCeiling(int iterationCount) + { + if (maxIterations > 0 && iterationCount > maxIterations) + { + throw new ArgumentException("iteration count exceeds limit (" + iterationCount + " > " + maxIterations + ")"); + } + } + + private IMacFactory genCalculator(PbmParameter parameters, char[] password) + { + // From RFC 4211 + // + // 1. Generate a random salt value S + // + // 2. Append the salt to the pw. K = pw || salt. + // + // 3. Hash the value of K. K = HASH(K) + // + // 4. Iter = Iter - 1. If Iter is greater than zero. Goto step 3. + // + // 5. Compute an HMAC as documented in [HMAC]. + // + // MAC = HASH( K XOR opad, HASH( K XOR ipad, data) ) + // + // Where opad and ipad are defined in [HMAC]. + byte[] pw = Strings.ToUtf8ByteArray(password); + byte[] salt = parameters.Salt.GetOctets(); + byte[] K = new byte[pw.Length + salt.Length]; + + System.Array.Copy(pw, 0, K, 0, pw.Length); + System.Array.Copy(salt, 0, K, pw.Length, salt.Length); + + IDigest digest = provider.CreateDigest(parameters.Owf); + + int iter = parameters.IterationCount.Value.IntValue; + + digest.BlockUpdate(K, 0, K.Length); + + K = new byte[digest.GetDigestSize()]; + + digest.DoFinal(K, 0); + + while (--iter > 0) + { + digest.BlockUpdate(K, 0, K.Length); + + digest.DoFinal(K, 0); + } + + byte[] key = K; + + return new PKMacFactory(key, parameters); + } + } +} diff --git a/crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs b/crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs new file mode 100644 index 0000000000..8457585ff1 --- /dev/null +++ b/crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Crmf; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Paddings; + +namespace Org.BouncyCastle.Crmf +{ + public class ProofOfPossessionSigningKeyBuilder + { + private CertRequest _certRequest; + private SubjectPublicKeyInfo _pubKeyInfo; + private GeneralName _name; + private PKMacValue _publicKeyMAC; + + public ProofOfPossessionSigningKeyBuilder(CertRequest certRequest) + { + this._certRequest = certRequest; + } + + public ProofOfPossessionSigningKeyBuilder(SubjectPublicKeyInfo pubKeyInfo) + { + this._pubKeyInfo = pubKeyInfo; + } + + public ProofOfPossessionSigningKeyBuilder setSender(GeneralName name) + { + this._name = name; + + return this; + } + + public ProofOfPossessionSigningKeyBuilder setPublicKeyMac(PKMacBuilder generator, char[] password) + { + IMacFactory fact = generator.Build(password); + + IStreamCalculator calc = fact.CreateCalculator(); + byte[] d = _pubKeyInfo.GetDerEncoded(); + calc.Stream.Write(d, 0, d.Length); + calc.Stream.Flush(); + calc.Stream.Close(); + + this._publicKeyMAC = new PKMacValue( + (AlgorithmIdentifier)fact.AlgorithmDetails, + new DerBitString(((IBlockResult)calc.GetResult()).Collect())); + + return this; + } + + public PopoSigningKey build(ISignatureFactory signer) + { + if (_name != null && _publicKeyMAC != null) + { + throw new InvalidOperationException("name and publicKeyMAC cannot both be set."); + } + + PopoSigningKeyInput popo; + byte[] b; + IStreamCalculator calc = signer.CreateCalculator(); + if (_certRequest != null) + { + popo = null; + b = _certRequest.GetDerEncoded(); + calc.Stream.Write(b, 0, b.Length); + + } + else if (_name != null) + { + popo = new PopoSigningKeyInput(_name, _pubKeyInfo); + b = popo.GetDerEncoded(); + calc.Stream.Write(b, 0, b.Length); + } + else + { + popo = new PopoSigningKeyInput(_publicKeyMAC, _pubKeyInfo); + b = popo.GetDerEncoded(); + calc.Stream.Write(b, 0, b.Length); + } + + calc.Stream.Flush(); + calc.Stream.Close(); + DefaultSignatureResult res = (DefaultSignatureResult)calc.GetResult(); + return new PopoSigningKey(popo, (AlgorithmIdentifier)signer.AlgorithmDetails, new DerBitString(res.Collect())); + } + } +} diff --git a/crypto/src/crypto/operators/Asn1Mac.cs b/crypto/src/crypto/operators/Asn1Mac.cs deleted file mode 100644 index ff70e58497..0000000000 --- a/crypto/src/crypto/operators/Asn1Mac.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Cmp; -using Org.BouncyCastle.Asn1.Iana; -using Org.BouncyCastle.Asn1.Nist; -using Org.BouncyCastle.Asn1.Oiw; -using Org.BouncyCastle.Asn1.Pkcs; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Cms; -using Org.BouncyCastle.Crypto.IO; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.Utilities.Encoders; - -namespace Org.BouncyCastle.Crypto.Operators -{ - - public class DefaultMacStreamCalculator : IStreamCalculator - { - private readonly MacSink _stream; - - public DefaultMacStreamCalculator(IMac mac) - { - _stream = new MacSink(mac); - } - - public void Init(KeyParameter key) - { - _stream.Mac.Init(key); - } - - public Stream Stream - { - get { return _stream; } - } - public object GetResult() - { - byte[] res = new byte[_stream.Mac.GetMacSize()]; - _stream.Mac.DoFinal(res, 0); - return res; - } - } - - - public class DefaultMacAndDigestStreamCalculator : IStreamCalculator - { - - private readonly MacSink macSink; - private readonly DigestSink digestSink; - private readonly Stream _stream; - - - public DefaultMacAndDigestStreamCalculator(IMac imac, IDigest idigest) - { - this.macSink = new MacSink(imac); - this.digestSink = new DigestSink(idigest); - _stream = new MergedStream(macSink,digestSink); - } - - - public void Init(KeyParameter macKey) - { - this.macSink.Mac.Init(macKey); - } - - public void Init(PbmParameter parameter, byte[] password) - { - - byte[] pw = password; - byte[] salt = parameter.Salt.GetOctets(); - byte[] K = new byte[pw.Length + salt.Length]; - - System.Array.Copy(pw,K,pw.Length); - System.Array.Copy(salt,0,K,pw.Length,salt.Length); - int iter = parameter.IterationCount.Value.IntValue; - this.digestSink.Digest.Reset(); - - IDigest dig = DigestUtilities.GetDigest(digestSink.Digest.AlgorithmName); - - - - dig.BlockUpdate(K,0,K.Length); - K = new byte[dig.GetDigestSize()]; - dig.DoFinal(K, 0); - iter--; - - do - { - dig.BlockUpdate(K,0,K.Length); - dig.DoFinal(K, 0); - } while (--iter > 0); - - macSink.Mac.Init(new KeyParameter(K)); - } - - - - public Stream Stream - { - get { return _stream; } - } - - - public object GetResult() - { - byte[] macResult = new byte[macSink.Mac.GetMacSize()]; - macSink.Mac.DoFinal(macResult, 0); - byte[] digestResult = new byte[digestSink.Digest.GetByteLength()]; - digestSink.Digest.DoFinal(digestResult, 0); - return new DefaultMacAndDigestResult(digestResult, macResult); - } - - private class MergedStream : Stream - { - - private Stream aStream; - private Stream bStream; - - public MergedStream(Stream aStream, Stream bStream) - { - this.aStream = aStream; - this.bStream = bStream; - } - - public override void Flush() - { - aStream.Flush(); - bStream.Flush(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - aStream.Seek(offset, origin); - return bStream.Seek(offset, origin); - } - - public override void SetLength(long value) - { - aStream.SetLength(value); - bStream.SetLength(value); - } - - public override int Read(byte[] buffer, int offset, int count) - { - aStream.Read(buffer, offset, count); - return bStream.Read(buffer, offset, count); - } - - public override void Write(byte[] buffer, int offset, int count) - { - aStream.Write(buffer, offset, count); - bStream.Write(buffer, offset, count); - } - - public override bool CanRead - { - get { return bStream.CanRead && aStream.CanRead; } - } - public override bool CanSeek - { - get { return bStream.CanSeek && aStream.CanSeek; } - - } - public override bool CanWrite { - get { return bStream.CanWrite && aStream.CanWrite; } - - } - public override long Length { - get - { - return aStream.Length; - } - } - public override long Position - { - get { return aStream.Position; } - - set { aStream.Position = value; } - } - } - } - - public struct DefaultMacAndDigestResult - { - public DefaultMacAndDigestResult(byte[] digestResult, byte[] macResult) - { - DigestResult = digestResult; - MacResult = macResult; - } - - public byte[] DigestResult { get; } - - public byte[] MacResult { get; } - } - - public class Asn1MacFactory : IMacFactory - { - protected readonly AlgorithmIdentifier MacAlgorithmIdentifier; - - - - public Asn1MacFactory(AlgorithmIdentifier macAlgorithmIdentifier) - { - MacAlgorithmIdentifier = macAlgorithmIdentifier; - } - - - - public virtual object AlgorithmDetails - { - get { return MacAlgorithmIdentifier; } - } - - public virtual IStreamCalculator CreateCalculator() - { - IMac mac = MacUtilities.GetMac(MacAlgorithmIdentifier.Algorithm); - return new DefaultMacStreamCalculator(mac); - } - } - - - public interface IMacFactoryProvider - { - IMacFactory CreateMacFactory(AlgorithmIdentifier algorithmIdentifier); - } - - public class Asn1MacFactoryProvider : IMacFactoryProvider - { - public IMacFactory CreateMacFactory(AlgorithmIdentifier algorithmIdentifier) - { - return new Asn1MacFactory(algorithmIdentifier); - } - - public IMacFactory CreateMacFactory(AlgorithmIdentifier digestAlgorithmIdentifier, AlgorithmIdentifier macAlgorithmIdentifier) - { - return new PkMacFactory(digestAlgorithmIdentifier,macAlgorithmIdentifier); - } - - public IMacFactory CreateMacFactory(PbmParameter parameter) - { - return new PkMacFactory(parameter); - } - - } - - - - public class PkMacFactory:Asn1MacFactory - { - private readonly AlgorithmIdentifier _digestAlgorithmIdentifier; - private byte[] password; - private int iterationCount; - private byte[] salt; - - - - public PkMacFactory(SecureRandom random) : base(new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1)) - { - this._digestAlgorithmIdentifier = new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1, DerNull.Instance); - this.iterationCount = 1000; - this.salt = new byte[20]; - random.NextBytes(salt); - } - - public PkMacFactory(AlgorithmIdentifier digestAlgorithmIdentifier, AlgorithmIdentifier macAlgorithmIdentifier) : base(macAlgorithmIdentifier) - { - this._digestAlgorithmIdentifier = digestAlgorithmIdentifier; - } - - public PkMacFactory(PbmParameter parameter):base(parameter.Mac) - { - this._digestAlgorithmIdentifier = parameter.Owf; - this.salt = parameter.Salt.GetOctets(); - this.iterationCount = parameter.IterationCount.Value.IntValue; - } - - public override object AlgorithmDetails - { - get - { - return new AlgorithmIdentifier(CmpObjectIdentifiers.passwordBasedMac, - new PbmParameter(salt, _digestAlgorithmIdentifier, iterationCount, MacAlgorithmIdentifier)); - } - } - - - public int IterationCount - { - set { this.iterationCount = value; } - } - public byte[] Salt - { - set { this.salt = value;} - } - public byte[] Password { - set { this.password = value; } - } - - - public override IStreamCalculator CreateCalculator() - { - - DefaultMacAndDigestStreamCalculator calc = new DefaultMacAndDigestStreamCalculator( - MacUtilities.GetMac(this.MacAlgorithmIdentifier.Algorithm), - DigestUtilities.GetDigest(_digestAlgorithmIdentifier.Algorithm)); - - PbmParameter parameter = new PbmParameter(salt, _digestAlgorithmIdentifier,iterationCount,MacAlgorithmIdentifier); - calc.Init(parameter, password); - - - return calc; - } - - } - - - public class MacVerifierFactory : IVerifierFactory - { - private readonly IMacFactory _macFactory; - - - public MacVerifierFactory(IMacFactory macFactory) - { - this._macFactory = macFactory; - } - - public object AlgorithmDetails - { - get { return _macFactory.AlgorithmDetails; } - } - public IStreamCalculator CreateCalculator() - { - return new MacVerifier(_macFactory.CreateCalculator()); - } - - private class MacVerifier : IStreamCalculator - { - public IStreamCalculator _calculator; - - public MacVerifier(IStreamCalculator calculator) - { - _calculator = calculator; - } - - public Stream Stream - { - get { return _calculator.Stream; } - } - - public object GetResult() - { - object result = _calculator.GetResult(); - if (result is byte[]) - { - return new DefaultMacVerifierResult((byte[])result); - } else if (result is DefaultMacAndDigestResult) - { - return new DefaultMacVerifierResult(((DefaultMacAndDigestResult)result).MacResult); - - } - - throw new InvalidOperationException("calculator did not return byte[] or DefaultMacVerifierResult"); - } - } - - } - - - public class DefaultMacVerifierResult:IVerifier - { - private readonly byte[] _calculatedResult; - - public DefaultMacVerifierResult(byte[] calculatedResult) - { - this._calculatedResult = calculatedResult; - } - - - public bool IsVerified(byte[] data) - { - return Arrays.ConstantTimeAreEqual(_calculatedResult, data); - } - - public bool IsVerified(byte[] source, int off, int length) - { - if (_calculatedResult.Length != length) - { - return false; - } - - // - // Must be constant time. - // - int j = 0; - int nonEqual = 0; - for (int i = off; i < off + length; i++) - { - nonEqual |= (_calculatedResult[j++] ^ source[i]); - } - - return nonEqual == 0; - } - } - - -} From a4dfc8f5b5db53ac21d7ad5f5c7851429e902b54 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 15 Jan 2019 09:18:28 +1100 Subject: [PATCH 15/22] added missing interface --- crypto/src/crypto/IKeyUnwrapper.cs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 crypto/src/crypto/IKeyUnwrapper.cs diff --git a/crypto/src/crypto/IKeyUnwrapper.cs b/crypto/src/crypto/IKeyUnwrapper.cs new file mode 100644 index 0000000000..764ead2ce3 --- /dev/null +++ b/crypto/src/crypto/IKeyUnwrapper.cs @@ -0,0 +1,24 @@ +namespace Org.BouncyCastle.Crypto +{ + /// + /// Base interface for a key unwrapper. + /// + /// The algorithm details/parameter type for the key unwrapper. + public interface IKeyUnwrapper + { + /// + /// The parameter set used to configure this key unwrapper. + /// + A AlgorithmDetails { get; } + + /// + /// Unwrap the passed in data. + /// + /// The array containing the data to be unwrapped. + /// The offset into cipherText at which the unwrapped data starts. + /// The length of the data to be unwrapped. + /// an IBlockResult containing the unwrapped key data. + IBlockResult Unwrap(byte[] cipherText, int offset, int length); + } +} + From fcb62761a47c1010fd4dc5412493ef372a630310 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 15 Jan 2019 09:35:28 +1100 Subject: [PATCH 16/22] updated --- .../test/src/cmp/test/ProtectedMessageTest.cs | 22 +++++-------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/crypto/test/src/cmp/test/ProtectedMessageTest.cs b/crypto/test/src/cmp/test/ProtectedMessageTest.cs index c22ba32975..569ba6c7b7 100644 --- a/crypto/test/src/cmp/test/ProtectedMessageTest.cs +++ b/crypto/test/src/cmp/test/ProtectedMessageTest.cs @@ -174,7 +174,7 @@ public void TestSampleCr() - IsTrue(msg.Verify(new Asn1MacFactoryProvider(),Strings.ToAsciiByteArray("TopSecret1234"))); + IsTrue(msg.Verify(new PKMacBuilder(), "TopSecret1234".ToCharArray())); } @@ -293,17 +293,10 @@ public void TestMacProtectedMessage() // Default instance. // + PKMacBuilder macFactory = new PKMacBuilder(); + ProtectedPkiMessage msg = msgBuilder.Build(macFactory.Build("testpass".ToCharArray())); - PkMacFactory macFactory = new PkMacFactory(new SecureRandom()); - macFactory.Password = Strings.ToAsciiByteArray("testpass"); - ProtectedPkiMessage msg = msgBuilder.Build(macFactory); - - - MacVerifierFactory verifierFactory = new MacVerifierFactory( - new PkMacFactory((PbmParameter) msg.Header.ProtectionAlg.Parameters) - {Password = Strings.ToAsciiByteArray("testpass")} - ); - IsTrue(msg.Verify(verifierFactory)); + IsTrue(msg.Verify(macFactory, "testpass".ToCharArray())); } @@ -356,12 +349,7 @@ public void TestVerifyBCJavaGeneratedMessage() PbmParameter pbmParameters = PbmParameter.GetInstance(pkiMsg.Header.ProtectionAlg.Parameters); - MacVerifierFactory verifierFactory = new MacVerifierFactory(new PkMacFactory(pbmParameters) - { - Password = Strings.ToAsciiByteArray("secret") - }); - - IsTrue(pkiMsg.Verify(verifierFactory)); + IsTrue(pkiMsg.Verify(new PKMacBuilder().SetParameters(pbmParameters), "secret".ToCharArray())); } From e1cd803b9802dfc2d4f35a4b29aab2d728f70b20 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 15 Jan 2019 09:36:13 +1100 Subject: [PATCH 17/22] update --- crypto/src/asn1/crmf/EncryptedValueBuilder.cs | 53 ----------- .../ProofOfPossessionSigningKeyBuilder.cs | 90 ------------------- 2 files changed, 143 deletions(-) delete mode 100644 crypto/src/asn1/crmf/EncryptedValueBuilder.cs delete mode 100644 crypto/src/asn1/crmf/ProofOfPossessionSigningKeyBuilder.cs diff --git a/crypto/src/asn1/crmf/EncryptedValueBuilder.cs b/crypto/src/asn1/crmf/EncryptedValueBuilder.cs deleted file mode 100644 index 4b57156d4b..0000000000 --- a/crypto/src/asn1/crmf/EncryptedValueBuilder.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; -using Org.BouncyCastle.Asn1.Nist; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Utilities; - -namespace Org.BouncyCastle.Asn1.Crmf -{ - -// public delegate IBlockCipher BlockCipherCreator(ICipherParameters); -// -// public class EncryptedValueBuilder -// { -// private readonly IBlockCipher _cipher; -// private static readonly IDictionary algToDelegate = Platform.CreateHashtable(); -// static EncryptedValueBuilder() -// { -// algToDelegate[NistObjectIdentifiers.IdAes128Cbc] = new CipherCreator() -// {Creator = delegate(ICipherParameters param) { return new AesEngine(); }}; -// -// } -// -// -// public EncryptedValueBuilder(DerObjectIdentifier alg) -// { -// -// } -// -// -// private static IBlockCipher AesCBC(ICipherParameters param) -// { -// if (param is ParametersWithIV ivParam) { -// return new -// } -// else -// { -// throw new ArgumentException("expecting param to be ParametersWithIv"); -// } -// } -// -// -// -// private class CipherCreator -// { -// public BlockCipherCreator Creator { get; set; } -// } -// -// } -} diff --git a/crypto/src/asn1/crmf/ProofOfPossessionSigningKeyBuilder.cs b/crypto/src/asn1/crmf/ProofOfPossessionSigningKeyBuilder.cs deleted file mode 100644 index cbaf834a12..0000000000 --- a/crypto/src/asn1/crmf/ProofOfPossessionSigningKeyBuilder.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Operators; -using Org.BouncyCastle.Crypto.Paddings; - -namespace Org.BouncyCastle.Asn1.Crmf -{ - public class ProofOfPossessionSigningKeyBuilder - { - private CertRequest _certRequest; - private SubjectPublicKeyInfo _pubKeyInfo; - private GeneralName _name; - private PKMacValue _publicKeyMAC; - - public ProofOfPossessionSigningKeyBuilder(CertRequest certRequest) - { - this._certRequest = certRequest; - } - - - public ProofOfPossessionSigningKeyBuilder(SubjectPublicKeyInfo pubKeyInfo) - { - this._pubKeyInfo = pubKeyInfo; - } - - public ProofOfPossessionSigningKeyBuilder setSender(GeneralName name) - { - this._name = name; - - return this; - } - - public ProofOfPossessionSigningKeyBuilder setPublicKeyMac(PkMacFactory generator, char[] password) - { - IStreamCalculator calc = generator.CreateCalculator(); - byte[] d = _pubKeyInfo.GetDerEncoded(); - calc.Stream.Write(d, 0, d.Length); - calc.Stream.Flush(); - calc.Stream.Close(); - - - this._publicKeyMAC = new PKMacValue( - (AlgorithmIdentifier)generator.AlgorithmDetails, - new DerBitString(((DefaultMacAndDigestResult)calc.GetResult()).MacResult)); - - return this; - } - - public PopoSigningKey build(ISignatureFactory signer) - { - if (_name != null && _publicKeyMAC != null) - { - throw new InvalidOperationException("name and publicKeyMAC cannot both be set."); - } - - PopoSigningKeyInput popo; - byte[] b; - IStreamCalculator calc = signer.CreateCalculator(); - if (_certRequest != null) - { - popo = null; - b = _certRequest.GetDerEncoded(); - calc.Stream.Write(b, 0, b.Length); - - } - else if (_name != null) - { - popo = new PopoSigningKeyInput(_name, _pubKeyInfo); - b = popo.GetDerEncoded(); - calc.Stream.Write(b, 0, b.Length); - } - else - { - popo = new PopoSigningKeyInput(_publicKeyMAC, _pubKeyInfo); - b = popo.GetDerEncoded(); - calc.Stream.Write(b, 0, b.Length); - } - - calc.Stream.Flush(); - calc.Stream.Close(); - DefaultSignatureResult res = (DefaultSignatureResult)calc.GetResult(); - return new PopoSigningKey(popo, (AlgorithmIdentifier)signer.AlgorithmDetails, new DerBitString(res.Collect())); - } - - - } -} From ac16dcfaca0b2475b185eadc8694bc033000b095 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 15 Jan 2019 09:42:30 +1100 Subject: [PATCH 18/22] removed generic --- crypto/src/crypto/IKeyUnwrapper.cs | 7 ++++--- crypto/src/crypto/IKeyWrapper.cs | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 crypto/src/crypto/IKeyWrapper.cs diff --git a/crypto/src/crypto/IKeyUnwrapper.cs b/crypto/src/crypto/IKeyUnwrapper.cs index 764ead2ce3..2e280d9120 100644 --- a/crypto/src/crypto/IKeyUnwrapper.cs +++ b/crypto/src/crypto/IKeyUnwrapper.cs @@ -1,15 +1,16 @@ +using System; + namespace Org.BouncyCastle.Crypto { /// /// Base interface for a key unwrapper. /// - /// The algorithm details/parameter type for the key unwrapper. - public interface IKeyUnwrapper + public interface IKeyUnwrapper { /// /// The parameter set used to configure this key unwrapper. /// - A AlgorithmDetails { get; } + Object AlgorithmDetails { get; } /// /// Unwrap the passed in data. diff --git a/crypto/src/crypto/IKeyWrapper.cs b/crypto/src/crypto/IKeyWrapper.cs new file mode 100644 index 0000000000..d3ece2de2f --- /dev/null +++ b/crypto/src/crypto/IKeyWrapper.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Crypto +{ + /// + /// Base interface for a key wrapper. + /// + public interface IKeyWrapper + { + /// + /// The parameter set used to configure this key wrapper. + /// + Object AlgorithmDetails { get; } + + /// + /// Wrap the passed in key data. + /// + /// The key data to be wrapped. + /// an IBlockResult containing the wrapped key data. + IBlockResult Wrap(byte[] keyData); + } +} From 9be761f2c1c7a6c89c7d390c5ce4ee078c04583c Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 15 Jan 2019 09:47:25 +1100 Subject: [PATCH 19/22] added alg constructor --- crypto/src/crmf/PKMacBuilder.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crypto/src/crmf/PKMacBuilder.cs b/crypto/src/crmf/PKMacBuilder.cs index 5e3a5844d3..3dec4e86f4 100644 --- a/crypto/src/crmf/PKMacBuilder.cs +++ b/crypto/src/crmf/PKMacBuilder.cs @@ -117,6 +117,11 @@ public PKMacBuilder(IPKMacPrimitivesProvider provider) : { } + public PKMacBuilder(IPKMacPrimitivesProvider provider, AlgorithmIdentifier digestAlgorithmIdentifier, AlgorithmIdentifier macAlgorithmIdentifier) : + this(digestAlgorithmIdentifier, 1000, macAlgorithmIdentifier, provider) + { + } + public PKMacBuilder(IPKMacPrimitivesProvider provider, int maxIterations) { this.provider = provider; From f25f7bed6807096d9a67d31f547398c1f6f213e4 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 15 Jan 2019 11:01:18 +1100 Subject: [PATCH 20/22] first cut on Pkcs8 --- crypto/src/crmf/EncryptedValueBuilder.cs | 163 ++++++++++++++++++ crypto/src/crmf/IEncryptedValuePadder.cs | 29 ++++ crypto/src/crypto/ICipher.cs | 43 +++++ crypto/src/crypto/ICipherBuilder.cs | 31 ++++ crypto/src/crypto/ICipherBuilderWithKey.cs | 14 ++ .../src/crypto/IDecryptorBuilderProvider.cs | 18 ++ crypto/src/crypto/Security.cs | 108 ++++++++++++ .../src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs | 106 ++++++++++++ .../Pkcs8EncryptedPrivateKeyInfoBuilder.cs | 52 ++++++ crypto/src/pkcs/PkcsException.cs | 18 ++ crypto/src/pkcs/PkcsIOException.cs | 19 ++ crypto/src/util/io/MemoryInputStream.cs | 13 ++ crypto/src/util/io/MemoryOutputStream.cs | 10 ++ 13 files changed, 624 insertions(+) create mode 100644 crypto/src/crmf/EncryptedValueBuilder.cs create mode 100644 crypto/src/crmf/IEncryptedValuePadder.cs create mode 100644 crypto/src/crypto/ICipher.cs create mode 100644 crypto/src/crypto/ICipherBuilder.cs create mode 100644 crypto/src/crypto/ICipherBuilderWithKey.cs create mode 100644 crypto/src/crypto/IDecryptorBuilderProvider.cs create mode 100644 crypto/src/crypto/Security.cs create mode 100644 crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs create mode 100644 crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfoBuilder.cs create mode 100644 crypto/src/pkcs/PkcsException.cs create mode 100644 crypto/src/pkcs/PkcsIOException.cs create mode 100644 crypto/src/util/io/MemoryInputStream.cs create mode 100644 crypto/src/util/io/MemoryOutputStream.cs diff --git a/crypto/src/crmf/EncryptedValueBuilder.cs b/crypto/src/crmf/EncryptedValueBuilder.cs new file mode 100644 index 0000000000..f9279bd53f --- /dev/null +++ b/crypto/src/crmf/EncryptedValueBuilder.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Org.BouncyCastle.Asn1.Nist; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.Crmf; +using System.IO; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.X509; +using Org.BouncyCastle.Utilities.IO; + +namespace Org.BouncyCastle.Crmf +{ + public class EncryptedValueBuilder + { + private IKeyWrapper wrapper; + private ICipherBuilderWithKey encryptor; + private EncryptedValuePadder padder; + + /** + * Create a builder that makes EncryptedValue structures. + * + * @param wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue. + * @param encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue. + */ + public EncryptedValueBuilder(IKeyWrapper wrapper, ICipherBuilderWithKey encryptor) : this(wrapper, encryptor, null) + { + } + + /** + * Create a builder that makes EncryptedValue structures with fixed length blocks padded using the passed in padder. + * + * @param wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue. + * @param encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue. + * @param padder a padder to ensure that the EncryptedValue created will always be a constant length. + */ + public EncryptedValueBuilder(IKeyWrapper wrapper, ICipherBuilderWithKey encryptor, EncryptedValuePadder padder) + { + this.wrapper = wrapper; + this.encryptor = encryptor; + this.padder = padder; + } + + /** + * Build an EncryptedValue structure containing the passed in pass phrase. + * + * @param revocationPassphrase a revocation pass phrase. + * @return an EncryptedValue containing the encrypted pass phrase. + * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. + */ + public EncryptedValue Build(char[] revocationPassphrase) + { + return encryptData(padData(Strings.ToUtf8ByteArray(revocationPassphrase))); + } + + /** + * Build an EncryptedValue structure containing the certificate contained in + * the passed in holder. + * + * @param holder a holder containing a certificate. + * @return an EncryptedValue containing the encrypted certificate. + * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. + */ + public EncryptedValue Build(X509Certificate holder) + { + try + { + return encryptData(padData(holder.GetEncoded())); + } + catch (IOException e) + { + throw new CrmfException("cannot encode certificate: " + e.Message, e); + } + } + + /** + * Build an EncryptedValue structure containing the private key contained in + * the passed info structure. + * + * @param privateKeyInfo a PKCS#8 private key info structure. + * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure. + * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. + */ + public EncryptedValue Build(PrivateKeyInfo privateKeyInfo) + { + Pkcs8EncryptedPrivateKeyInfoBuilder encInfoBldr = new Pkcs8EncryptedPrivateKeyInfoBuilder(privateKeyInfo); + + AlgorithmIdentifier intendedAlg = privateKeyInfo.PrivateKeyAlgorithm; + AlgorithmIdentifier symmAlg = (AlgorithmIdentifier)encryptor.AlgorithmDetails; + DerBitString encSymmKey; + + try + { + Pkcs8EncryptedPrivateKeyInfo encInfo = encInfoBldr.Build(encryptor); + + encSymmKey = new DerBitString(wrapper.Wrap(((KeyParameter)encryptor.Key).GetKey()).Collect()); + + AlgorithmIdentifier keyAlg = (AlgorithmIdentifier)wrapper.AlgorithmDetails; + Asn1OctetString valueHint = null; + + return new EncryptedValue(intendedAlg, symmAlg, encSymmKey, keyAlg, valueHint, new DerBitString(encInfo.GetEncryptedData())); + } + catch (Exception e) + { + throw new CrmfException("cannot wrap key: " + e.Message, e); + } + } + + private EncryptedValue encryptData(byte[] data) + { + MemoryOutputStream bOut = new MemoryOutputStream(); + + Stream eOut = encryptor.BuildCipher(bOut).Stream; + + try + { + eOut.Write(data, 0, data.Length); + + eOut.Close(); + } + catch (IOException e) + { + throw new CrmfException("cannot process data: " + e.Message, e); + } + + AlgorithmIdentifier intendedAlg = null; + AlgorithmIdentifier symmAlg = (AlgorithmIdentifier)encryptor.AlgorithmDetails; + DerBitString encSymmKey; + + try + { + encSymmKey = new DerBitString(wrapper.Wrap(((KeyParameter)encryptor.Key).GetKey()).Collect()); + } + catch (Exception e) + { + throw new CrmfException("cannot wrap key: " + e.Message, e); + } + + AlgorithmIdentifier keyAlg = (AlgorithmIdentifier)wrapper.AlgorithmDetails; + Asn1OctetString valueHint = null; + DerBitString encValue = new DerBitString(bOut.ToArray()); + + return new EncryptedValue(intendedAlg, symmAlg, encSymmKey, keyAlg, valueHint, encValue); + } + + private byte[] padData(byte[] data) + { + if (padder != null) + { + return padder.GetPaddedData(data); + } + + return data; + } + } +} diff --git a/crypto/src/crmf/IEncryptedValuePadder.cs b/crypto/src/crmf/IEncryptedValuePadder.cs new file mode 100644 index 0000000000..b620186dca --- /dev/null +++ b/crypto/src/crmf/IEncryptedValuePadder.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Org.BouncyCastle.Crmf +{ + /** + * An encrypted value padder is used to make sure that prior to a value been + * encrypted the data is padded to a standard length. + */ + public interface EncryptedValuePadder + { + /** + * Return a byte array of padded data. + * + * @param data the data to be padded. + * @return a padded byte array containing data. + */ + byte[] GetPaddedData(byte[] data); + + /** + * Return a byte array of with padding removed. + * + * @param paddedData the data to be padded. + * @return an array containing the original unpadded data. + */ + byte[] GetUnpaddedData(byte[] paddedData); + } +} diff --git a/crypto/src/crypto/ICipher.cs b/crypto/src/crypto/ICipher.cs new file mode 100644 index 0000000000..9041e61ad7 --- /dev/null +++ b/crypto/src/crypto/ICipher.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Crypto +{ + /// + /// Base interface for a ciphers that do not require data to be block aligned. + /// + /// Note: In cases where the underlying algorithm is block based, these ciphers may add or remove padding as needed. + /// + /// + public interface ICipher + { + /// + /// Return the size of the output buffer required for a Write() plus a + /// close() with the write() being passed inputLen bytes. + /// + /// The returned size may be dependent on the initialisation of this cipher + /// and may not be accurate once subsequent input data is processed as the cipher may + /// add, add or remove padding, as it sees fit. + /// + /// + /// The space required to accommodate a call to processBytes and doFinal with inputLen bytes of input. + /// The length of the expected input. + int GetMaxOutputSize(int inputLen); + + /// + /// Return the size of the output buffer required for a write() with the write() being + /// passed inputLen bytes and just updating the cipher output. + /// + /// The space required to accommodate a call to processBytes with inputLen bytes of input. + /// The length of the expected input. + int GetUpdateOutputSize(int inputLen); + + /// + /// Gets the stream for reading/writing data processed/to be processed. + /// + /// The stream associated with this cipher. + Stream Stream { get; } + } +} diff --git a/crypto/src/crypto/ICipherBuilder.cs b/crypto/src/crypto/ICipherBuilder.cs new file mode 100644 index 0000000000..5d4d1279c1 --- /dev/null +++ b/crypto/src/crypto/ICipherBuilder.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Crypto +{ + /// + /// Base interface for cipher builders. + /// + public interface ICipherBuilder + { + /// + /// Return the algorithm and parameter details associated with any cipher built. + /// + Object AlgorithmDetails { get ; } + + /// + /// Return the maximum output size that a given input will produce. + /// + /// the length of the expected input. + /// The maximum possible output size that can produced for the expected input length. + int GetMaxOutputSize (int inputLen); + + /// + /// Build a cipher that operates on the passed in stream. + /// + /// The stream to write/read any encrypted/decrypted data. + /// A cipher based around the given stream. + ICipher BuildCipher(Stream stream); + } +} + diff --git a/crypto/src/crypto/ICipherBuilderWithKey.cs b/crypto/src/crypto/ICipherBuilderWithKey.cs new file mode 100644 index 0000000000..01a7a2cafc --- /dev/null +++ b/crypto/src/crypto/ICipherBuilderWithKey.cs @@ -0,0 +1,14 @@ + +namespace Org.BouncyCastle.Crypto +{ + /// + /// A cipher builder that can also return the key it was initialized with. + /// + public interface ICipherBuilderWithKey: ICipherBuilder + { + /// + /// Return the key we were initialized with. + /// + ICipherParameters Key { get; } + } +} diff --git a/crypto/src/crypto/IDecryptorBuilderProvider.cs b/crypto/src/crypto/IDecryptorBuilderProvider.cs new file mode 100644 index 0000000000..7f151e3aeb --- /dev/null +++ b/crypto/src/crypto/IDecryptorBuilderProvider.cs @@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Crypto +{ + /// + /// Interface describing a provider of cipher builders for creating decrypting ciphers. + /// + public interface IDecryptorBuilderProvider + { + /// + /// Return a cipher builder for creating decrypting ciphers. + /// + /// The algorithm details/parameters to use to create the final cipher. + /// A new cipher builder. + ICipherBuilder CreateDecryptorBuilder (Object algorithmDetails); + } +} + diff --git a/crypto/src/crypto/Security.cs b/crypto/src/crypto/Security.cs new file mode 100644 index 0000000000..716679044f --- /dev/null +++ b/crypto/src/crypto/Security.cs @@ -0,0 +1,108 @@ +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Paddings; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using System; +using System.Text; + +namespace crypto +{ + public class Security + { + // USAGE + + //var key = Security.GenerateText(32); + + //var iv = Security.GenerateText(16); + + //var encrypted = Security.Encrypt("MY SECRET", key, iv); + + //var dencrypted = Security.Decrypt(encrypted, key, iv); + + + /// + /// Return a salted hash based on PBKDF2 for the UTF-8 encoding of the argument text. + /// + /// Provided key text + /// Base64 encoded string representing the salt + /// + public static String ComputeHash(string text, string salt) + { + var data = Encoding.UTF8.GetBytes(text); + var sha = new Sha512Digest(); + var gen = new Pkcs5S2ParametersGenerator(sha); + + gen.Init(data, Convert.FromBase64String(salt), 2048); + + return Convert.ToBase64String(((KeyParameter)gen.GenerateDerivedParameters(sha.GetDigestSize() * 8)).GetKey()); + } + + public static String Decrypt(String cipherText, String key, String iv) + + { + + var cipher = CreateCipher(false, key, iv); + + var textAsBytes = cipher.DoFinal(Convert.FromBase64String(cipherText)); + + + + return Encoding.UTF8.GetString(textAsBytes, 0, textAsBytes.Length); + + } + + + + public static String Encrypt(String plainText, String key, String iv) + + { + + var cipher = CreateCipher(true, key, iv); + + + + return Convert.ToBase64String(cipher.DoFinal(Encoding.UTF8.GetBytes(plainText))); + + } + + + + public static String GenerateText(int size) + + { + + var textAsBytes = new Byte[size]; + + var secureRandom = SecureRandom.GetInstance("SHA256PRNG", true); + + + + secureRandom.NextBytes(textAsBytes); + + return Convert.ToBase64String(textAsBytes); + + } + + + + private static PaddedBufferedBlockCipher CreateCipher(Boolean isEncryption, String key, String iv) + + { + + var cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new RijndaelEngine()), new ISO10126d2Padding()); + + var keyParam = new KeyParameter(Convert.FromBase64String(key)); + + ICipherParameters cipherParams = String.IsNullOrEmpty(iv) ? (ICipherParameters)keyParam : new ParametersWithIV(keyParam, Convert.FromBase64String(iv)); + + cipher.Init(isEncryption, cipherParams); + + return cipher; + + } + } +} diff --git a/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs b/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs new file mode 100644 index 0000000000..4c4ae83eba --- /dev/null +++ b/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfo.cs @@ -0,0 +1,106 @@ + +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities.IO; +using System; +using System.IO; + +namespace Org.BouncyCastle.Pkcs +{ + /// + /// A holding class for a PKCS#8 encrypted private key info object that allows for its decryption. + /// + public class Pkcs8EncryptedPrivateKeyInfo + { + private EncryptedPrivateKeyInfo encryptedPrivateKeyInfo; + + private static EncryptedPrivateKeyInfo parseBytes(byte[] pkcs8Encoding) + { + try + { + return EncryptedPrivateKeyInfo.GetInstance(pkcs8Encoding); + } + + catch (ArgumentException e) + { + throw new PkcsIOException("malformed data: " + e.Message, e); + } + catch (Exception e) + { + throw new PkcsIOException("malformed data: " + e.Message, e); + } + } + + /// + /// Base constructor from a PKCS#8 EncryptedPrivateKeyInfo object. + /// + /// A PKCS#8 EncryptedPrivateKeyInfo object. + public Pkcs8EncryptedPrivateKeyInfo(EncryptedPrivateKeyInfo encryptedPrivateKeyInfo) + { + this.encryptedPrivateKeyInfo = encryptedPrivateKeyInfo; + } + + /// + /// Base constructor from a BER encoding of a PKCS#8 EncryptedPrivateKeyInfo object. + /// + /// A BER encoding of a PKCS#8 EncryptedPrivateKeyInfo objects. + public Pkcs8EncryptedPrivateKeyInfo(byte[] encryptedPrivateKeyInfo) : this(parseBytes(encryptedPrivateKeyInfo)) + { + + } + + /// + /// Returns the underlying ASN.1 structure inside this object. + /// + /// Return the EncryptedPrivateKeyInfo structure in this object. + public EncryptedPrivateKeyInfo ToAsn1Structure() + { + return encryptedPrivateKeyInfo; + } + + /// + /// Returns a copy of the encrypted data in this structure. + /// + /// Return a copy of the encrypted data in this object. + public byte[] GetEncryptedData() + { + return encryptedPrivateKeyInfo.GetEncryptedData(); + } + + /// + /// Return a binary ASN.1 encoding of the EncryptedPrivateKeyInfo structure in this object. + /// + /// A byte array containing the encoded object. + public byte[] GetEncoded() + { + return encryptedPrivateKeyInfo.GetEncoded(); + } + + /// + /// Get a decryptor from the passed in provider and decrypt the encrypted private key info, returning the result. + /// + /// A provider to query for decryptors for the object. + /// The decrypted private key info structure. + public PrivateKeyInfo DecryptPrivateKeyInfo(IDecryptorBuilderProvider inputDecryptorProvider) + { + try + { + ICipherBuilder decryptorBuilder = inputDecryptorProvider.CreateDecryptorBuilder(encryptedPrivateKeyInfo.EncryptionAlgorithm); + + ICipher encIn = decryptorBuilder.BuildCipher(new MemoryInputStream(encryptedPrivateKeyInfo.GetEncryptedData())); + + using (Stream strm = encIn.Stream) + { + byte[] data = Streams.ReadAll(encIn.Stream); + + return PrivateKeyInfo.GetInstance(data); + } + } + catch (Exception e) + { + throw new PkcsException("unable to read encrypted data: " + e.Message, e); + } + } + } +} diff --git a/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfoBuilder.cs b/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfoBuilder.cs new file mode 100644 index 0000000000..3b05deea76 --- /dev/null +++ b/crypto/src/pkcs/Pkcs8EncryptedPrivateKeyInfoBuilder.cs @@ -0,0 +1,52 @@ +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Utilities.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Org.BouncyCastle.Pkcs +{ + public class Pkcs8EncryptedPrivateKeyInfoBuilder + { + private PrivateKeyInfo privateKeyInfo; + + public Pkcs8EncryptedPrivateKeyInfoBuilder(byte[] privateKeyInfo): this(PrivateKeyInfo.GetInstance(privateKeyInfo)) + { + } + + public Pkcs8EncryptedPrivateKeyInfoBuilder(PrivateKeyInfo privateKeyInfo) + { + this.privateKeyInfo = privateKeyInfo; + } + + /// + /// Create the encrypted private key info using the passed in encryptor. + /// + /// The encryptor to use. + /// An encrypted private key info containing the original private key info. + public Pkcs8EncryptedPrivateKeyInfo Build( + ICipherBuilder encryptor) + { + try + { + MemoryStream bOut = new MemoryOutputStream(); + ICipher cOut = encryptor.BuildCipher(bOut); + byte[] keyData = privateKeyInfo.GetEncoded(); + + using (var str = cOut.Stream) + { + str.Write(keyData, 0, keyData.Length); + } + + return new Pkcs8EncryptedPrivateKeyInfo(new EncryptedPrivateKeyInfo((AlgorithmIdentifier)encryptor.AlgorithmDetails, bOut.ToArray())); + } + catch (IOException) + { + throw new InvalidOperationException("cannot encode privateKeyInfo"); + } + } + } +} diff --git a/crypto/src/pkcs/PkcsException.cs b/crypto/src/pkcs/PkcsException.cs new file mode 100644 index 0000000000..f82d367242 --- /dev/null +++ b/crypto/src/pkcs/PkcsException.cs @@ -0,0 +1,18 @@ +using System; + +namespace Org.BouncyCastle.Pkcs +{ + /// + /// Base exception for PKCS related issues. + /// + public class PkcsException : Exception + { + public PkcsException(String message) : base(message) + { + } + + public PkcsException(String message, Exception underlying) : base(message, underlying) + { + } + } +} diff --git a/crypto/src/pkcs/PkcsIOException.cs b/crypto/src/pkcs/PkcsIOException.cs new file mode 100644 index 0000000000..19f17a3942 --- /dev/null +++ b/crypto/src/pkcs/PkcsIOException.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Pkcs +{ + /// + /// Base exception for parsing related issues in the PKCS namespace. + /// + public class PkcsIOException: IOException + { + public PkcsIOException(String message) : base(message) + { + } + + public PkcsIOException(String message, Exception underlying) : base(message, underlying) + { + } + } +} diff --git a/crypto/src/util/io/MemoryInputStream.cs b/crypto/src/util/io/MemoryInputStream.cs new file mode 100644 index 0000000000..d353314ee9 --- /dev/null +++ b/crypto/src/util/io/MemoryInputStream.cs @@ -0,0 +1,13 @@ +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class MemoryInputStream : MemoryStream + { + public MemoryInputStream(byte[] buffer) : base(buffer, false) + { + } + + public sealed override bool CanWrite { get { return false; } } + } +} diff --git a/crypto/src/util/io/MemoryOutputStream.cs b/crypto/src/util/io/MemoryOutputStream.cs new file mode 100644 index 0000000000..a6de64680c --- /dev/null +++ b/crypto/src/util/io/MemoryOutputStream.cs @@ -0,0 +1,10 @@ + +using System.IO; + +namespace Org.BouncyCastle.Utilities.IO +{ + public class MemoryOutputStream: MemoryStream + { + public sealed override bool CanRead { get { return false; } } + } +} From 6614f7fda643ebb09a1c21979a067fab17c3ab6c Mon Sep 17 00:00:00 2001 From: Megan Woods Date: Tue, 15 Jan 2019 14:55:39 +1100 Subject: [PATCH 21/22] Updated C# doc. Removed EJBCA EnrollmentTest as it s not viable to produce an example on .Net Framework 2.0 --- crypto/src/asn1/crmf/CertRequest.cs | 1 + .../src/cmp/CertificateConfirmationContent.cs | 4 +- .../CertificateConfirmationContentBuilder.cs | 5 +- crypto/src/cmp/CertificateStatus.cs | 5 +- crypto/src/cmp/CmpException.cs | 3 +- crypto/src/cmp/GeneralPkiMessage.cs | 25 ++++-- crypto/src/cmp/ProtectedPkiMessage.cs | 81 +++++++++++++----- crypto/src/cmp/ProtectedPkiMessageBuilder.cs | 2 - crypto/src/crmf/AuthenticatorControl.cs | 20 ++++- crypto/src/crmf/CertificateRequestMessage.cs | 63 ++++++++++++-- crypto/src/crmf/EncryptedValueBuilder.cs | 71 ++++++++-------- crypto/src/crmf/IEncryptedValuePadder.cs | 33 +++---- crypto/src/crmf/PKMacBuilder.cs | 40 +++++++++ crypto/src/crmf/PkiArchiveControl.cs | 25 ++++++ crypto/src/crmf/RegTokenControl.cs | 18 +++- .../test/src/cmp/test/ProtectedMessageTest.cs | 8 -- .../src/ejbca/test/EnrollmentExampleTest.cs | 85 ------------------- 17 files changed, 289 insertions(+), 200 deletions(-) delete mode 100644 crypto/test/src/ejbca/test/EnrollmentExampleTest.cs diff --git a/crypto/src/asn1/crmf/CertRequest.cs b/crypto/src/asn1/crmf/CertRequest.cs index 625a9b5192..bf6182f25d 100644 --- a/crypto/src/asn1/crmf/CertRequest.cs +++ b/crypto/src/asn1/crmf/CertRequest.cs @@ -1,4 +1,5 @@ using System; +using Org.BouncyCastle.Crmf; namespace Org.BouncyCastle.Asn1.Crmf { diff --git a/crypto/src/cmp/CertificateConfirmationContent.cs b/crypto/src/cmp/CertificateConfirmationContent.cs index 882bd2091c..13d1dab8ed 100644 --- a/crypto/src/cmp/CertificateConfirmationContent.cs +++ b/crypto/src/cmp/CertificateConfirmationContent.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; + using Org.BouncyCastle.Cms; using Org.BouncyCastle.Asn1.Cmp; diff --git a/crypto/src/cmp/CertificateConfirmationContentBuilder.cs b/crypto/src/cmp/CertificateConfirmationContentBuilder.cs index 126484917b..56f5d5ccb8 100644 --- a/crypto/src/cmp/CertificateConfirmationContentBuilder.cs +++ b/crypto/src/cmp/CertificateConfirmationContentBuilder.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; +using System.Collections; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.X509; diff --git a/crypto/src/cmp/CertificateStatus.cs b/crypto/src/cmp/CertificateStatus.cs index e8c3546dd2..92a94ea05e 100644 --- a/crypto/src/cmp/CertificateStatus.cs +++ b/crypto/src/cmp/CertificateStatus.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Org.BouncyCastle.Asn1.Cmp; +using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Cms; using Org.BouncyCastle.Crypto.IO; diff --git a/crypto/src/cmp/CmpException.cs b/crypto/src/cmp/CmpException.cs index 7ecdf5af80..2f50f7780c 100644 --- a/crypto/src/cmp/CmpException.cs +++ b/crypto/src/cmp/CmpException.cs @@ -1,7 +1,6 @@ using System; -using System.Collections.Generic; using System.Runtime.Serialization; -using System.Text; + namespace Org.BouncyCastle.Cmp { diff --git a/crypto/src/cmp/GeneralPkiMessage.cs b/crypto/src/cmp/GeneralPkiMessage.cs index d91b8ef7e0..ad55a8005e 100644 --- a/crypto/src/cmp/GeneralPkiMessage.cs +++ b/crypto/src/cmp/GeneralPkiMessage.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Org.BouncyCastle.Asn1.Cmp +namespace Org.BouncyCastle.Asn1.Cmp { public class GeneralPKIMessage { @@ -13,17 +9,28 @@ private static PkiMessage parseBytes(byte[] encoding) return PkiMessage.GetInstance(Asn1Object.FromByteArray(encoding)); } + + /// + /// Wrap a PKIMessage ASN.1 structure. + /// + /// PKI message. public GeneralPKIMessage(PkiMessage pkiMessage) { this.pkiMessage = pkiMessage; } + /// + /// Create a PKIMessage from the passed in bytes. + /// + /// BER/DER encoding of the PKIMessage public GeneralPKIMessage(byte[] encoding) : this(parseBytes(encoding)) { } - public PkiHeader Header { - get { + public PkiHeader Header + { + get + { return pkiMessage.Header; } } @@ -36,6 +43,10 @@ public PkiBody Body } } + /// + /// Return true if this message has protection bits on it. A return value of true + /// indicates the message can be used to construct a ProtectedPKIMessage. + /// public bool HasProtection { get { return pkiMessage.Protection != null; } diff --git a/crypto/src/cmp/ProtectedPkiMessage.cs b/crypto/src/cmp/ProtectedPkiMessage.cs index 159f087225..d3cf4a5249 100644 --- a/crypto/src/cmp/ProtectedPkiMessage.cs +++ b/crypto/src/cmp/ProtectedPkiMessage.cs @@ -1,30 +1,30 @@ using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.X509; using System; -using System.Collections.Generic; -using System.Text; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cmp; -using Org.BouncyCastle.Asn1.Crmf; -using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Operators; -using Org.BouncyCastle.Crypto.Paddings; using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.Utilities.Encoders; + using Org.BouncyCastle.Crmf; namespace Org.BouncyCastle.Cmp { - + /// + /// Wrapper for a PKIMessage with protection attached to it. + /// public class ProtectedPkiMessage { private PkiMessage pkiMessage; - + /// + /// Wrap a general message. + /// + /// If the general message does not have protection. + /// The General message public ProtectedPkiMessage(GeneralPKIMessage pkiMessage) { - + if (!pkiMessage.HasProtection) { throw new ArgumentException("pki message not protected"); @@ -32,7 +32,12 @@ public ProtectedPkiMessage(GeneralPKIMessage pkiMessage) this.pkiMessage = pkiMessage.ToAsn1Structure(); } - + + /// + /// Wrap a PKI message. + /// + /// If the PKI message does not have protection. + /// The PKI message public ProtectedPkiMessage(PkiMessage pkiMessage) { if (pkiMessage.Header.ProtectionAlg == null) @@ -43,13 +48,33 @@ public ProtectedPkiMessage(PkiMessage pkiMessage) this.pkiMessage = pkiMessage; } + /// + /// Message header + /// public PkiHeader Header { get { return pkiMessage.Header; } } + + /// + /// Message Body + /// public PkiBody Body { get { return pkiMessage.Body; } } + /// + /// Return the underlying ASN.1 structure contained in this object. + /// + /// PKI Message structure public PkiMessage ToAsn1Message() { return pkiMessage; } + /// + /// Determine whether the message is protected by a password based MAC. Use verify(PKMACBuilder, char[]) + /// to verify the message if this method returns true. + /// + /// true if protection MAC PBE based, false otherwise. public bool HasPasswordBasedMacProtected { get { return Header.ProtectionAlg.Algorithm.Equals(CmpObjectIdentifiers.passwordBasedMac); } } + /// + /// Return the extra certificates associated with this message. + /// + /// an array of extra certificates, zero length if none present. public X509Certificate[] GetCertificates() { CmpCertificate[] certs = pkiMessage.GetExtraCerts(); @@ -60,7 +85,7 @@ public X509Certificate[] GetCertificates() } X509Certificate[] res = new X509Certificate[certs.Length]; - for (int t=0; t + /// Verify a message with a public key based signature attached. + /// + /// a factory of signature verifiers. + /// true if the provider is able to create a verifier that validates the signature, false otherwise. public bool Verify(IVerifierFactory verifierFactory) { IStreamCalculator streamCalculator = verifierFactory.CreateCalculator(); @@ -79,18 +109,25 @@ public bool Verify(IVerifierFactory verifierFactory) private Object Process(IStreamCalculator streamCalculator) { - Asn1EncodableVector avec = new Asn1EncodableVector(); - avec.Add(pkiMessage.Header); - avec.Add(pkiMessage.Body); - byte[] enc = new DerSequence(avec).GetDerEncoded(); - - streamCalculator.Stream.Write(enc,0,enc.Length); - streamCalculator.Stream.Flush(); - streamCalculator.Stream.Close(); - - return streamCalculator.GetResult(); + Asn1EncodableVector avec = new Asn1EncodableVector(); + avec.Add(pkiMessage.Header); + avec.Add(pkiMessage.Body); + byte[] enc = new DerSequence(avec).GetDerEncoded(); + + streamCalculator.Stream.Write(enc, 0, enc.Length); + streamCalculator.Stream.Flush(); + streamCalculator.Stream.Close(); + + return streamCalculator.GetResult(); } + /// + /// Verify a message with password based MAC protection. + /// + /// MAC builder that can be used to construct the appropriate MacCalculator + /// the MAC password + /// true if the passed in password and MAC builder verify the message, false otherwise. + /// if algorithm not MAC based, or an exception is thrown verifying the MAC. public bool Verify(PKMacBuilder pkMacBuilder, char[] password) { if (!CmpObjectIdentifiers.passwordBasedMac.Equals(pkiMessage.Header.ProtectionAlg.Algorithm)) diff --git a/crypto/src/cmp/ProtectedPkiMessageBuilder.cs b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs index e660f844a9..3ee223ba87 100644 --- a/crypto/src/cmp/ProtectedPkiMessageBuilder.cs +++ b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs @@ -1,13 +1,11 @@ using System; using System.Collections; -using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Operators; using Org.BouncyCastle.X509; -using Org.BouncyCastle.Crmf; namespace Org.BouncyCastle.Cmp { diff --git a/crypto/src/crmf/AuthenticatorControl.cs b/crypto/src/crmf/AuthenticatorControl.cs index 7803c44180..976135ed8d 100644 --- a/crypto/src/crmf/AuthenticatorControl.cs +++ b/crypto/src/crmf/AuthenticatorControl.cs @@ -1,12 +1,12 @@ using System; -using System.Collections.Generic; -using System.Text; - using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Crmf; namespace Org.BouncyCastle.Crmf { + /// + /// Carrier for an authenticator control. + /// public class AuthenticatorControl:IControl { @@ -14,21 +14,35 @@ public class AuthenticatorControl:IControl private readonly DerUtf8String token; + /// + /// Basic constructor - build from a UTF-8 string representing the token. + /// + /// UTF-8 string representing the token. public AuthenticatorControl(DerUtf8String token) { this.token = token; } + /// + /// Basic constructor - build from a string representing the token. + /// + /// string representing the token. public AuthenticatorControl(String token) { this.token = new DerUtf8String(token); } + /// + /// Return the type of this control. + /// public DerObjectIdentifier Type { get { return type; } } + /// + /// Return the token associated with this control (a UTF8String). + /// public Asn1Encodable Value { get { return token; } } diff --git a/crypto/src/crmf/CertificateRequestMessage.cs b/crypto/src/crmf/CertificateRequestMessage.cs index 818facade6..087e8a9332 100644 --- a/crypto/src/crmf/CertificateRequestMessage.cs +++ b/crypto/src/crmf/CertificateRequestMessage.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Crypto; @@ -19,39 +16,68 @@ public class CertificateRequestMessage private readonly CertReqMsg certReqMsg; private readonly Controls controls; - private static CertReqMsg ParseBytes(byte[] encoding) - + private static CertReqMsg ParseBytes(byte[] encoding) { return CertReqMsg.GetInstance(encoding); } + /// + /// Create a CertificateRequestMessage from the passed in bytes. + /// + /// BER/DER encoding of the CertReqMsg structure. + public CertificateRequestMessage(byte[] encoded):this(CertReqMsg.GetInstance(encoded)) + { + + } + public CertificateRequestMessage(CertReqMsg certReqMsg) { this.certReqMsg = certReqMsg; this.controls = certReqMsg.CertReq.Controls; } + /// + /// Return the underlying ASN.1 object defining this CertificateRequestMessage object. + /// + /// A CertReqMsg public CertReqMsg ToAsn1Structure() { return certReqMsg; } + /// + /// Return the certificate template contained in this message. + /// + /// a CertTemplate structure. public CertTemplate GetCertTemplate() { return this.certReqMsg.CertReq.CertTemplate; } + /// + /// Return whether or not this request has control values associated with it. + /// + /// true if there are control values present, false otherwise. public bool HasControls { get { return controls != null; } } - + /// + /// Return whether or not this request has a specific type of control value. + /// + /// the type OID for the control value we are checking for. + /// true if a control value of type is present, false otherwise. public bool HasControl(DerObjectIdentifier objectIdentifier) { return findControl(objectIdentifier) != null; } + /// + /// Return a control value of the specified type. + /// + /// the type OID for the control value we are checking for. + /// the control value if present, null otherwise. public IControl GetControl(DerObjectIdentifier type) { AttributeTypeAndValue found = findControl(type); @@ -100,16 +126,29 @@ public AttributeTypeAndValue findControl(DerObjectIdentifier type) return found; } + /// + /// Return whether or not this request message has a proof-of-possession field in it. + /// + /// true if proof-of-possession is present, false otherwise. public bool HasProofOfPossession { get { return certReqMsg.Popo != null; } } + /// + /// Return the type of the proof-of-possession this request message provides. + /// + /// one of: popRaVerified, popSigningKey, popKeyEncipherment, popKeyAgreement public int ProofOfPossession { get { return certReqMsg.Popo.Type; } } + /// + /// Return whether or not the proof-of-possession (POP) is of the type popSigningKey and + /// it has a public key MAC associated with it. + /// + /// true if POP is popSigningKey and a PKMAC is present, false otherwise. public bool HasSigningKeyProofOfPossessionWithPkMac { get @@ -127,7 +166,13 @@ public bool HasSigningKeyProofOfPossessionWithPkMac } } - + /// + /// Return whether or not a signing key proof-of-possession (POP) is valid. + /// + /// a provider that can produce content verifiers for the signature contained in this POP. + /// true if the POP is valid, false otherwise. + /// if there is a problem in verification or content verifier creation. + /// if POP not appropriate. public bool IsValidSigningKeyPop(IVerifierFactoryProvider verifierProvider) { ProofOfPossession pop = certReqMsg.Popo; @@ -176,6 +221,10 @@ private bool verifySignature(IVerifierFactoryProvider verifierFactoryProvider, P return result.IsVerified(signKey.Signature.GetBytes()); } + /// + /// Return the ASN.1 encoding of the certReqMsg we wrap. + /// + /// a byte array containing the binary encoding of the certReqMsg. public byte[] GetEncoded() { return certReqMsg.GetEncoded(); diff --git a/crypto/src/crmf/EncryptedValueBuilder.cs b/crypto/src/crmf/EncryptedValueBuilder.cs index f9279bd53f..28d5b52b49 100644 --- a/crypto/src/crmf/EncryptedValueBuilder.cs +++ b/crypto/src/crmf/EncryptedValueBuilder.cs @@ -24,23 +24,23 @@ public class EncryptedValueBuilder private ICipherBuilderWithKey encryptor; private EncryptedValuePadder padder; - /** - * Create a builder that makes EncryptedValue structures. - * - * @param wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue. - * @param encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue. - */ + /// + /// Create a builder that makes EncryptedValue structures. + /// + /// wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue. + /// encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue. + /// public EncryptedValueBuilder(IKeyWrapper wrapper, ICipherBuilderWithKey encryptor) : this(wrapper, encryptor, null) { } - /** - * Create a builder that makes EncryptedValue structures with fixed length blocks padded using the passed in padder. - * - * @param wrapper a wrapper for key used to encrypt the actual data contained in the EncryptedValue. - * @param encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue. - * @param padder a padder to ensure that the EncryptedValue created will always be a constant length. - */ + /// + /// Create a builder that makes EncryptedValue structures with fixed length blocks padded using the passed in padder. + /// + /// a wrapper for key used to encrypt the actual data contained in the EncryptedValue. + /// encryptor an output encryptor to encrypt the actual data contained in the EncryptedValue. + /// padder a padder to ensure that the EncryptedValue created will always be a constant length. + /// public EncryptedValueBuilder(IKeyWrapper wrapper, ICipherBuilderWithKey encryptor, EncryptedValuePadder padder) { this.wrapper = wrapper; @@ -48,26 +48,25 @@ public EncryptedValueBuilder(IKeyWrapper wrapper, ICipherBuilderWithKey encrypto this.padder = padder; } - /** - * Build an EncryptedValue structure containing the passed in pass phrase. - * - * @param revocationPassphrase a revocation pass phrase. - * @return an EncryptedValue containing the encrypted pass phrase. - * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. - */ + /// + /// Build an EncryptedValue structure containing the passed in pass phrase. + /// + /// a revocation pass phrase. + ///an EncryptedValue containing the encrypted pass phrase. + /// public EncryptedValue Build(char[] revocationPassphrase) { return encryptData(padData(Strings.ToUtf8ByteArray(revocationPassphrase))); } - /** - * Build an EncryptedValue structure containing the certificate contained in - * the passed in holder. - * - * @param holder a holder containing a certificate. - * @return an EncryptedValue containing the encrypted certificate. - * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. - */ + /// + /// Build an EncryptedValue structure containing the certificate contained in + /// the passed in holder. + /// + /// a holder containing a certificate. + /// an EncryptedValue containing the encrypted certificate. + /// on a failure to encrypt the data, or wrap the symmetric key for this value. + /// public EncryptedValue Build(X509Certificate holder) { try @@ -80,14 +79,14 @@ public EncryptedValue Build(X509Certificate holder) } } - /** - * Build an EncryptedValue structure containing the private key contained in - * the passed info structure. - * - * @param privateKeyInfo a PKCS#8 private key info structure. - * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure. - * @throws CrmfException on a failure to encrypt the data, or wrap the symmetric key for this value. - */ + /// + /// Build an EncryptedValue structure containing the private key contained in + /// the passed info structure. + /// + /// a PKCS#8 private key info structure. + /// an EncryptedValue containing an EncryptedPrivateKeyInfo structure. + /// on a failure to encrypt the data, or wrap the symmetric key for this value. + /// public EncryptedValue Build(PrivateKeyInfo privateKeyInfo) { Pkcs8EncryptedPrivateKeyInfoBuilder encInfoBldr = new Pkcs8EncryptedPrivateKeyInfoBuilder(privateKeyInfo); diff --git a/crypto/src/crmf/IEncryptedValuePadder.cs b/crypto/src/crmf/IEncryptedValuePadder.cs index b620186dca..b12993e1f2 100644 --- a/crypto/src/crmf/IEncryptedValuePadder.cs +++ b/crypto/src/crmf/IEncryptedValuePadder.cs @@ -4,26 +4,27 @@ namespace Org.BouncyCastle.Crmf { - /** - * An encrypted value padder is used to make sure that prior to a value been - * encrypted the data is padded to a standard length. - */ + + /// + /// An encrypted value padder is used to make sure that prior to a value been + /// encrypted the data is padded to a standard length. + /// public interface EncryptedValuePadder { - /** - * Return a byte array of padded data. - * - * @param data the data to be padded. - * @return a padded byte array containing data. - */ + /// + /// Return a byte array of padded data. + /// + /// the data to be padded. + /// a padded byte array containing data. + /// byte[] GetPaddedData(byte[] data); - /** - * Return a byte array of with padding removed. - * - * @param paddedData the data to be padded. - * @return an array containing the original unpadded data. - */ + /// + /// Return a byte array of with padding removed. + /// + /// the data to be padded. + /// an array containing the original unpadded data. + /// byte[] GetUnpaddedData(byte[] paddedData); } } diff --git a/crypto/src/crmf/PKMacBuilder.cs b/crypto/src/crmf/PKMacBuilder.cs index 3dec4e86f4..00bec9f8bf 100644 --- a/crypto/src/crmf/PKMacBuilder.cs +++ b/crypto/src/crmf/PKMacBuilder.cs @@ -107,27 +107,46 @@ public class PKMacBuilder private byte[] salt; private int maxIterations; + /// + /// Default, IterationCount = 1000, OIW=IdSha1, Mac=HmacSHA1 + /// public PKMacBuilder() : this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000, new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), new DefaultPKMacPrimitivesProvider()) { } + /// + /// Defaults with IPKMacPrimitivesProvider + /// + /// public PKMacBuilder(IPKMacPrimitivesProvider provider) : this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000, new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), provider) { } + /// + /// Create. + /// + /// The Mac provider + /// Digest Algorithm Id + /// Mac Algorithm Id public PKMacBuilder(IPKMacPrimitivesProvider provider, AlgorithmIdentifier digestAlgorithmIdentifier, AlgorithmIdentifier macAlgorithmIdentifier) : this(digestAlgorithmIdentifier, 1000, macAlgorithmIdentifier, provider) { } + /// + /// Create a PKMAC builder enforcing a ceiling on the maximum iteration count. + /// + /// supporting calculator + /// max allowable value for iteration count. public PKMacBuilder(IPKMacPrimitivesProvider provider, int maxIterations) { this.provider = provider; this.maxIterations = maxIterations; } + private PKMacBuilder(AlgorithmIdentifier digestAlgorithmIdentifier, int iterationCount, AlgorithmIdentifier macAlgorithmIdentifier, IPKMacPrimitivesProvider provider) { this.iterationCount = iterationCount; @@ -154,6 +173,12 @@ public PKMacBuilder SetSaltLength(int saltLength) return this; } + /// + /// Set the iteration count. + /// + /// the iteration count. + /// this + /// if iteration count is less than 100 public PKMacBuilder SetIterationCount(int iterationCount) { if (iterationCount < 100) @@ -167,6 +192,11 @@ public PKMacBuilder SetIterationCount(int iterationCount) return this; } + /// + /// Set PbmParameters + /// + /// The parameters. + /// this public PKMacBuilder SetParameters(PbmParameter parameters) { checkIterationCountCeiling(parameters.IterationCount.Value.IntValue); @@ -176,6 +206,11 @@ public PKMacBuilder SetParameters(PbmParameter parameters) return this; } + /// + /// The Secure random + /// + /// The random. + /// this public PKMacBuilder SetSecureRandom(SecureRandom random) { this.random = random; @@ -183,6 +218,11 @@ public PKMacBuilder SetSecureRandom(SecureRandom random) return this; } + /// + /// Build an IMacFactory. + /// + /// The password. + /// IMacFactory public IMacFactory Build(char[] password) { if (parameters != null) diff --git a/crypto/src/crmf/PkiArchiveControl.cs b/crypto/src/crmf/PkiArchiveControl.cs index ec8fb76715..d533e6c52d 100644 --- a/crypto/src/crmf/PkiArchiveControl.cs +++ b/crypto/src/crmf/PkiArchiveControl.cs @@ -18,26 +18,47 @@ public class PkiArchiveControl:IControl private readonly PkiArchiveOptions pkiArchiveOptions; + /// + /// Basic constructor - build from an PKIArchiveOptions structure. + /// + /// the ASN.1 structure that will underlie this control. public PkiArchiveControl(PkiArchiveOptions pkiArchiveOptions) { this.pkiArchiveOptions = pkiArchiveOptions; } + /// + /// Return the type of this control. + /// + /// CRMFObjectIdentifiers.id_regCtrl_pkiArchiveOptions public DerObjectIdentifier Type { + get { return type; } } + /// + /// Return the underlying ASN.1 object. + /// + /// a PKIArchiveOptions structure. public Asn1Encodable Value { get { return pkiArchiveOptions; } } + /// + /// Return the archive control type, one of: encryptedPrivKey,keyGenParameters,or archiveRemGenPrivKey. + /// + /// the archive control type. public int ArchiveType { get { return pkiArchiveOptions.Type; } } + /// + /// Return whether this control contains enveloped data. + /// + /// true if the control contains enveloped data, false otherwise. public bool EnvelopedData { get @@ -47,6 +68,10 @@ public bool EnvelopedData } } + /// + /// Return the enveloped data structure contained in this control. + /// + /// a CMSEnvelopedData object. public CmsEnvelopedData GetEnvelopedData() { try diff --git a/crypto/src/crmf/RegTokenControl.cs b/crypto/src/crmf/RegTokenControl.cs index b53ce19229..90e956f679 100644 --- a/crypto/src/crmf/RegTokenControl.cs +++ b/crypto/src/crmf/RegTokenControl.cs @@ -14,20 +14,36 @@ public class RegTokenControl:IControl private readonly DerUtf8String token; + /// + /// Basic constructor - build from a UTF-8 string representing the token. + /// + /// UTF-8 string representing the token. public RegTokenControl(DerUtf8String token) { this.token = token; } - + /// + /// Basic constructor - build from a string representing the token. + /// + /// string representing the token. public RegTokenControl(String token) { this.token = new DerUtf8String(token); } + /// + /// Return the type of this control. + /// + /// CRMFObjectIdentifiers.id_regCtrl_regToken public DerObjectIdentifier Type { get { return type; } } + + /// + /// Return the token associated with this control (a UTF8String). + /// + /// a UTF8String. public Asn1Encodable Value { get { return token; } diff --git a/crypto/test/src/cmp/test/ProtectedMessageTest.cs b/crypto/test/src/cmp/test/ProtectedMessageTest.cs index 569ba6c7b7..24f2fe572f 100644 --- a/crypto/test/src/cmp/test/ProtectedMessageTest.cs +++ b/crypto/test/src/cmp/test/ProtectedMessageTest.cs @@ -1,27 +1,19 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Text; using NUnit.Framework; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Cmp; using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Crmf; -using Org.BouncyCastle.Asn1.Nist; -using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Cms; using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Digests; -using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Operators; using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Crypto.Signers; using Org.BouncyCastle.Math; using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.Encoders; using Org.BouncyCastle.Utilities.Test; using Org.BouncyCastle.X509; diff --git a/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs b/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs deleted file mode 100644 index 4ee0ae2c3c..0000000000 --- a/crypto/test/src/ejbca/test/EnrollmentExampleTest.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using NUnit.Framework; -using Org.BouncyCastle.Asn1; -using Org.BouncyCastle.Asn1.Cmp; -using Org.BouncyCastle.Asn1.Crmf; -using Org.BouncyCastle.Asn1.X509; -using Org.BouncyCastle.Cmp; -using Org.BouncyCastle.Crmf; -using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Generators; -using Org.BouncyCastle.Crypto.Operators; -using Org.BouncyCastle.Crypto.Parameters; -using Org.BouncyCastle.Math; -using Org.BouncyCastle.Security; -using Org.BouncyCastle.Utilities; -using Org.BouncyCastle.X509; - -namespace crypto.test.src.ejbca.test -{ - [TestFixture] - public class EnrollmentExampleTest - { - - [Test] - public void TestEnrollmentRAWithSharedSecret() - { - long certReqId = 1; - SecureRandom secureRandom = new SecureRandom(); - - byte[] senderNonce = new byte[20]; - secureRandom.NextBytes(senderNonce); - - byte[] transactionId = Strings.ToAsciiByteArray("MyTransactionId"); - - - RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator(); - rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(65537), new SecureRandom(), 2048, 100)); - AsymmetricCipherKeyPair rsaKeyPair = rsaKeyPairGenerator.GenerateKeyPair(); - - - CertificateRequestMessageBuilder msgbuilder = new CertificateRequestMessageBuilder(BigInteger.ValueOf(certReqId)); - X509NameEntryConverter dnconverter = new X509DefaultEntryConverter(); - - X509Name issuerDN = X509Name.GetInstance(new X509Name("CN=AdminCA1").ToAsn1Object()); - X509Name subjectDN = X509Name.GetInstance(new X509Name("CN=user", dnconverter).ToAsn1Object()); - msgbuilder.SetIssuer(issuerDN); - msgbuilder.SetSubject(subjectDN); - SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(rsaKeyPair.Public); - - msgbuilder.SetPublicKey(keyInfo); - GeneralName sender = new GeneralName(subjectDN); - msgbuilder.SetAuthInfoSender(sender); - // RAVerified POP - msgbuilder.SetProofOfPossessionRaVerified(); - CertificateRequestMessage msg = msgbuilder.Build(); - GeneralName recipient = new GeneralName(issuerDN); - - ProtectedPkiMessageBuilder pbuilder = new ProtectedPkiMessageBuilder(sender, recipient); - pbuilder.SetMessageTime(new DerGeneralizedTime(DateTime.Now)); - // senderNonce - pbuilder.SetSenderNonce(senderNonce); - // TransactionId - pbuilder.SetTransactionId(transactionId); - // Key Id used (required) by the recipient to do a lot of stuff - pbuilder.SetSenderKID(Strings.ToAsciiByteArray("KeyId")); - - - CertReqMessages msgs = new CertReqMessages(msg.ToAsn1Structure()); - PkiBody pkibody = new PkiBody(PkiBody.TYPE_INIT_REQ, msgs); - pbuilder.SetBody(pkibody); - - - AlgorithmIdentifier digAlg = new AlgorithmIdentifier("1.3.14.3.2.26"); // SHA1 - AlgorithmIdentifier macAlg = new AlgorithmIdentifier("1.2.840.113549.2.7"); // HMAC/SHA1 - - PkMacFactory macFactory = new PkMacFactory(digAlg,macAlg); - macFactory.Password = Strings.ToAsciiByteArray("password"); - - ProtectedPkiMessage message = pbuilder.Build(macFactory); - - - } - - } -} \ No newline at end of file From 82a9f9936cf5fe53f142c419fae1cbb2d4dfb404 Mon Sep 17 00:00:00 2001 From: Megan Woods Date: Tue, 15 Jan 2019 14:57:44 +1100 Subject: [PATCH 22/22] Missing from previous commit --- crypto/src/crmf/IControl.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crypto/src/crmf/IControl.cs b/crypto/src/crmf/IControl.cs index 3601633a4c..14fcc2cd39 100644 --- a/crypto/src/crmf/IControl.cs +++ b/crypto/src/crmf/IControl.cs @@ -6,11 +6,19 @@ namespace Org.BouncyCastle.Crmf { - + /// + /// Generic interface for a CertificateRequestMessage control value. + /// public interface IControl { + /// + /// Return the type of this control. + /// DerObjectIdentifier Type { get; } + /// + /// Return the value contained in this control object. + /// Asn1Encodable Value { get; } } }