Skip to content

Commit

Permalink
Merge pull request #10 from PascalBayard/develop/#1-MobileID_authenti…
Browse files Browse the repository at this point in the history
…cation_fails_becau

Develop/#1 mobile id authentication fails becau
  • Loading branch information
phaupt committed Feb 3, 2022
2 parents a0bbf59 + 7569855 commit 01c3e1f
Show file tree
Hide file tree
Showing 15 changed files with 122 additions and 20 deletions.
2 changes: 1 addition & 1 deletion Admin/register_midadfs.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ $global:WarningPreference = "Continue"
$global:ErrorActionPreference = "Continue"

$shortVersion = "13"
$fullVersion = "1.3.1.0"
$fullVersion = "1.3.2.0"

if ($Args[0] -ne $null) {
$logFile = $Args[0];
Expand Down
4 changes: 2 additions & 2 deletions AuthnAdapter/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.3.1.0")]
[assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: AssemblyVersion("1.3.2.0")]
[assembly: AssemblyFileVersion("1.3.2.0")]
[assembly: NeutralResourcesLanguageAttribute("en")]
3 changes: 2 additions & 1 deletion Package/midadfs.iss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#define MyAppShortName "Mobile ID for ADFS"
#define MyAppAbb "MobileIdAdfs"
#define MyAppVersion "1.3"
#define MyAppFullVersion "1.3.1.0"
#define MyAppFullVersion "1.3.2.0"

[Setup]
AppId={{609C382B-1D2D-40F5-B2ED-742C603AD024}
Expand Down Expand Up @@ -67,6 +67,7 @@ Source: "..\Admin\unregister_etw.ps1"; DestDir: "{app}"; Flags: ignoreversion un
Source: "..\Admin\unregister_etw.cmd"; DestDir: "{app}"; Flags: ignoreversion uninsneveruninstall
Source: "..\certs\mobileid-ca-ssl.crt"; DestDir: "{app}\certs"
Source: "..\certs\codesigning-swisscom.crt"; DestDir: "{app}\certs"
Source: "..\certs\Swisscom_Root_CA_2_der.crt"; DestDir: "{app}\certs"
Source: "..\3RD_PARTY.md"; DestDir: "{app}\license"
Source: "..\LICENSE"; DestDir: "{app}\license"; DestName: "MobileId_LICENSE.txt"
Source: "install_midadfs.cmd"; DestDir: "{app}"; Flags: deleteafterinstall
Expand Down
2 changes: 1 addition & 1 deletion Package/params.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# hash-ref used to build $TOPDIR/README.md
{
VersionLong => '1.3.1.0',
VersionLong => '1.3.2.0',
VersionShort => '1.3',
VersionAbb => '13',
}
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,14 @@ while the element `mobileIdAdfs` specifies the integration of Mobile ID with ADF
+ `SslKeystore`: Store location of certificate/key used for Mobile ID connectivity. For ADFS, the value should be usually `LocalMachine`. Default: `CurrentUser`
+ `SslCertThumbprint`: The SHA1 Thumbprint of certificate used for Mobile ID connectivity. The thumbprint can be read out of the `Certificate` GUI (i.e. double-click the certificate file), or with a PowerShell cmdlet like `Get-ChildItem -Path cert:\\LocalMachine\My`. Mandatory.
+ `SslRootCaCertDN`: Distinguished Name of the Root Certificate in the certificate chain of Mobile ID servers. Default: "CN=Swisscom Root CA 2, OU=Digital Certificate Services, O=Swisscom, C=ch"
+ `SslRootCaCertFiles`: Additional certificate files
+ `UserSerialNumberPolicy`: Flags that determine how the serial number in user’s certificate is used in the authentication.
Supported flags are warnMismatch(1), allowAbsence(2), allowMismatch (4). Default: "6"
+ `SanitizePhoneNumber`: If this parameter is `true`, phone numbers read from the attribute store are transformed before use in Mobile ID calls. The transformation is specified by `SanitizePhoneNumberPattern` and `SanitizePhoneNumberReplacement`. Default: remove all non-digits
+ `SanitizePhoneNumberPattern`: Only effective when `SanitizePhoneNumber` is true. This parameter is the regular expression for matching a pattern in phone number. Default: `\D`
+ `SanitizePhoneNumberReplacement`: Only effective when `SanitizePhoneNumber` is true. This parameter is the replace string for matched pattern defined by `SanitizePhoneNumberPattern`. Default: ""
+ `SecurityProtocolType`: The TLS Version for Mobile ID connectivity. The following Values are allowed: "Tls", "Tls11", "Tls12", "Tls13". Default: "Tls" for Tls Version 1.0
+ `SignatureProfile`: The signature profile value for authentication requests. Default: "http://mid.swisscom.ch/MID/v1/AuthProfile1"

* Element `mobileIdAdfs`:
+ `AdAttrMobile`: Attribute name of AD user object for the mobile number. The attribute should have exactly one value. Default: `mobile`.
Expand Down
4 changes: 2 additions & 2 deletions Service/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.3.1.0")]
[assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: AssemblyVersion("1.3.2.0")]
[assembly: AssemblyFileVersion("1.3.2.0")]
[assembly: NeutralResourcesLanguageAttribute("en")]
24 changes: 23 additions & 1 deletion Service/WebClientConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ public class WebClientConfig

// optional input from caller
string _sslCaCertDN = "CN=Swisscom Root CA 2, OU=Digital Certificate Services, O=Swisscom, C=ch";
string _sslCaCertFiles = string.Empty;
StoreLocation _sslKeyStore = StoreLocation.CurrentUser;
UserLanguage _userLanguageDefault = UserLanguage.en;
string _serviceUrlPrefix = "https://mobileid.swisscom.com/soap/services/";
string _dtbsPrefix = "";
bool _srvSideValidation = false;
int _requestTimeOutSeconds = 80;
string _signatureProfile = "http://mid.swisscom.ch/MID/v1/AuthProfile1";
string _seedApTransId = "Some ASCII text to be used to build the unique AP_TransId in request";
bool _enableSubscriberInfo = false;
bool _ignoreUserSn = false;
Expand Down Expand Up @@ -90,13 +92,16 @@ public static WebClientConfig CreateConfig(TextReader cfgStream)
if (!string.IsNullOrWhiteSpace(s = xml["RequestTimeOutSeconds"]))
cfg.RequestTimeOutSeconds = int.Parse(s);
cfg.ServiceUrlPrefix = xml["ServiceUrlPrefix"];
cfg.SignatureProfile = xml["SignatureProfile"];
if (!string.IsNullOrEmpty(s = xml["SrvSideValidation"]))
cfg.SrvSideValidation = bool.Parse(s);
cfg.SslCertThumbprint = xml["SslCertThumbprint"];
if (!string.IsNullOrEmpty(s = xml["SslKeystore"]))
cfg.SslKeystore = Util.ParseKeyStoreLocation(s);
if (!string.IsNullOrEmpty(s = xml["SslRootCaCertDN"]))
cfg.SslRootCaCertDN = s;
if (!string.IsNullOrWhiteSpace(s = xml["SslRootCaCertFiles"]))
cfg.SslRootCaCertFiles = s;
if (!string.IsNullOrEmpty(s = xml["EnableSubscriberInfo"]))
cfg.EnableSubscriberInfo = Boolean.Parse(s);
cfg.SeedApTransId = xml["SeedApTransId"];
Expand All @@ -122,7 +127,6 @@ public static WebClientConfig CreateConfig(TextReader cfgStream)
};
if (!string.IsNullOrWhiteSpace(s = xml["SecurityProtocolType"]))
cfg.SecurityProtocolType = (SecurityProtocolType)Enum.Parse(typeof(SecurityProtocolType), s, true);
// TODO: update on change of properties

break;
}
Expand Down Expand Up @@ -172,6 +176,13 @@ public StoreLocation SslKeystore
}
}

[ConfigurationProperty("SignatureProfile", IsRequired = false, DefaultValue = "http://mid.swisscom.ch/MID/v1/AuthProfile1")]
public string SignatureProfile {
get { return _signatureProfile; }
set { if (!string.IsNullOrEmpty(value)) _signatureProfile = value; }
}


[ConfigurationProperty("SslCertThumbprint", IsRequired = true, DefaultValue = "CurrentUser")]
public string SslCertThumbprint {
get { return _sslCertThumbprint; }
Expand All @@ -189,6 +200,15 @@ public string SslRootCaCertDN {
set { if (! string.IsNullOrEmpty(value)) _sslCaCertDN = value; }
}

/// <summary>
/// Filename of Root CA Certificate in the CA Chain of the SSL Server Certificate for Mobile ID Service. If the Certificate is not loaded from the
/// </summary>
[ConfigurationProperty("SslRootCaCertFiles", IsRequired = false, DefaultValue = "")]
public string SslRootCaCertFiles{
get { return _sslCaCertFiles; }
set { if (! string.IsNullOrEmpty(value)) _sslCaCertFiles = value; }
}

public string ServiceUrlPrefix {
get { return _serviceUrlPrefix; }
set { if (! string.IsNullOrEmpty(value)) _serviceUrlPrefix = value;}
Expand Down Expand Up @@ -337,10 +357,12 @@ public override string ToString()
sb.Append("\"; SanitizePhoneNumberReplacement: \"").Append(_sanitizePhoneNumberReplacement);
sb.Append("\"; SeedApTransId:\"").Append(_seedApTransId);
sb.Append("\"; ServiceUrlPrefix=\"").Append(_serviceUrlPrefix);
sb.Append("\"; SignatureProfile=\"").Append(_signatureProfile);
sb.Append("\"; SrvSideValidation:").Append(_srvSideValidation);
sb.Append("; SslKeystore:").Append(_sslKeyStore);
sb.Append("; SslCertThumbprint:\"").Append(_sslCertThumbprint);
sb.Append("\"; SslRootCaCertDN:\"").Append(_sslCaCertDN);
sb.Append("\"; SslRootCaCertFiles:\"").Append(_sslCaCertFiles);
sb.Append("\"; UserLanguageDefault:\"").Append(_userLanguageDefault);
sb.Append("\"; UserSerialNumberPolicy:").Append(_userSericalNumberPolicy);
sb.Append("; SecurityProtocolType:").Append(_securityProtocolType);
Expand Down
91 changes: 81 additions & 10 deletions Service/WebClientImpl.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
Expand Down Expand Up @@ -53,7 +56,7 @@ protected string _formatSignReqAsSoap(AuthRequestDto req, bool async)
<mss:MSSP_Info><mss:MSSP_ID><mss:URI>http://mid.swisscom.ch/</mss:URI></mss:MSSP_ID></mss:MSSP_Info>
<mss:MobileUser><mss:MSISDN>{4}</mss:MSISDN></mss:MobileUser>
<mss:DataToBeSigned MimeType=""text/plain"" Encoding=""UTF-8"">{5}</mss:DataToBeSigned>
<mss:SignatureProfile><mss:mssURI>http://mid.swisscom.ch/MID/v1/AuthProfile1</mss:mssURI></mss:SignatureProfile>
<mss:SignatureProfile><mss:mssURI>{10}</mss:mssURI></mss:SignatureProfile>
<mss:AdditionalServices>
{6}
<mss:Service><mss:Description><mss:mssURI>http://mss.ficom.fi/TS102204/v1.0.0#userLang</mss:mssURI></mss:Description><fi:UserLang>{7:G}</fi:UserLang></mss:Service>
Expand All @@ -80,7 +83,7 @@ protected string _formatSignReqAsSoap(AuthRequestDto req, bool async)
<mss:MSSP_Info><mss:MSSP_ID><mss:URI>http://mid.swisscom.ch/</mss:URI></mss:MSSP_ID></mss:MSSP_Info>
<mss:MobileUser><mss:MSISDN>{4}</mss:MSISDN></mss:MobileUser>
<mss:DataToBeSigned MimeType=""text/plain"" Encoding=""UTF-8"">{5}</mss:DataToBeSigned>
<mss:SignatureProfile><mss:mssURI>http://mid.swisscom.ch/MID/v1/AuthProfile1</mss:mssURI></mss:SignatureProfile><mss:AdditionalServices>
<mss:SignatureProfile><mss:mssURI>{10}</mss:mssURI></mss:SignatureProfile><mss:AdditionalServices>
{6}<mss:Service><mss:Description><mss:mssURI>http://mss.ficom.fi/TS102204/v1.0.0#userLang</mss:mssURI></mss:Description><fi:UserLang>{7:G}</fi:UserLang></mss:Service>
{8}</mss:AdditionalServices></mss:MSS_SignatureReq></MSS_Signature></soapenv:Body></soapenv:Envelope>"
#endregion
Expand All @@ -95,6 +98,7 @@ protected string _formatSignReqAsSoap(AuthRequestDto req, bool async)
, req.UserLanguage
, (_cfg.EnableSubscriberInfo ? @"<mss:Service><mss:Description><mss:mssURI>http://mid.swisscom.ch/as#subscriberInfo</mss:mssURI></mss:Description></mss:Service>" : "")
, (async ? "asynchClientServer" : "synch")
, (_cfg.SignatureProfile)
);
}

Expand Down Expand Up @@ -1138,8 +1142,8 @@ private bool _isValidSignature(string dataToBeSigned, byte[] signature)
};

SignedCms signedCms = new SignedCms();
try
{
bool disableChainValidation = _cfg.DisableSignatureCertValidation;
try {
signedCms.Decode(signature);
byte[] dtbs_cms = signedCms.ContentInfo.Content;
if (Encoding.UTF8.GetString(dtbs_cms) != dataToBeSigned) {
Expand All @@ -1149,16 +1153,65 @@ private bool _isValidSignature(string dataToBeSigned, byte[] signature)
+ "', rsp_hex=" + BitConverter.ToString(dtbs_cms));
return false;
};
signedCms.CheckSignature(_cfg.DisableSignatureCertValidation);
logger.TraceEvent(TraceEventType.Verbose, (int)EventId.Service, "Signature Verified: signer_0='"
X509Certificate2 signerCert = signedCms.SignerInfos[0].Certificate;

//Check certificate trust
X509Certificate2Collection signatureTrustStore = GetSignatureTruststore();
//Check cutom-chain if check not disable and files in Truststore
if (!disableChainValidation && signatureTrustStore.Count > 0) {
if (signerCert == null) {
throw new SecurityException($"Could not retrieve signer certificate from signature. signature:='{signature}'");
}
//set to true, because chain.validation is made custom
disableChainValidation = true;

X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;

chain.ChainPolicy.ExtraStore.AddRange(signatureTrustStore);

var isValid = chain.Build(signerCert);
var chainRoot = chain.ChainElements[chain.ChainElements.Count - 1].Certificate;
//if not valid check if Root-Certificate in custom signatureTrustStore
//get last chain element that should contain root CA certificate but this may not be the case in partial chains
if (isValid || (chain.ChainStatus.First().Status == X509ChainStatusFlags.UntrustedRoot && signatureTrustStore.Contains(chainRoot))) {
if (Logging.Log.IsDebugEnabled()) Logging.Log.DebugMessage("Certificate trust validation succeeded");
} else {
if (Logging.Log.IsDebugEnabled()) Logging.Log.DebugMessage("Certificate trust validation failed");
throw new SecurityException($"{chain.ChainStatus[0].Status}: {chain.ChainStatus[0].StatusInformation}");
}
} else {
logger.TraceEvent(TraceEventType.Verbose, (int)EventId.KeyManagement, $"No MobileId trust store configured or {nameof(_cfg.DisableSignatureCertValidation)} configured. Certificate root trust is not checked.");
}

// Check signature
if (signatureTrustStore.Count > 0) {
signedCms.CheckSignature(signatureTrustStore, disableChainValidation);
} else {
signedCms.CheckSignature(disableChainValidation);
}

logger.TraceEvent(TraceEventType.Verbose, (int)EventId.Service, "Signature Verified: signer_0='"
+ signedCms.SignerInfos[0].Certificate.Subject + "', noChainValidation=" + _cfg.DisableSignatureCertValidation);
if (Logging.Log.IsDebugEnabled()) Logging.Log.DebugMessage3("ValidSignature", _cfg.DisableSignatureCertValidation.ToString(), signedCms.SignerInfos[0].Certificate.Subject);

// Check signature payload
string signedData = Encoding.UTF8.GetString(dtbs_cms);
if (signedData != dataToBeSigned) {
logger.TraceEvent(TraceEventType.Error, (int)EventId.Service, "DataToBeSigned differs: req='" + dataToBeSigned
+ "', rsp_hex=" + BitConverter.ToString(dtbs_cms));
Logging.Log.ServerResponseMessageError("isValidSignature", "dataToBeSigned differs: req='" + dataToBeSigned
+ "', rsp_hex=" + BitConverter.ToString(dtbs_cms));
return false;
} else {
if (Logging.Log.IsDebugEnabled()) Logging.Log.DebugMessage("Signature payload verification succeeded");
}

return true;
}
catch (Exception e)
{
} catch (Exception e) {
logger.TraceEvent(TraceEventType.Error, (int)EventId.Service, "INVALID_SIGNATURE: " + e.Message);
Logging.Log.ServerResponseMessageError("InvalidSiganture", e.Message);
Logging.Log.ServerResponseMessageError("InvalidSignature", e.Message);
return false;
}
}
Expand All @@ -1169,6 +1222,24 @@ private void _enrichAuthRspDto(AuthResponseDto rspDto)
return;
}


private X509Certificate2Collection GetSignatureTruststore() {
if (string.IsNullOrEmpty(_cfg.SslRootCaCertFiles)) {
return new X509Certificate2Collection();
}

X509Certificate2Collection trustStore = new X509Certificate2Collection();
var list = _cfg.SslRootCaCertFiles.Split(';').Select(s => s.Trim());
foreach (string singleFile in list) {
// Load the certificate into an X509Certificate object.
X509Certificate2 cert = new X509Certificate2();
cert.Import(singleFile);
trustStore.Add(cert);
}

return trustStore;
}

}

}
4 changes: 2 additions & 2 deletions ServiceTest/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.3.1.0")]
[assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: AssemblyVersion("1.3.2.0")]
[assembly: AssemblyFileVersion("1.3.2.0")]
1 change: 1 addition & 0 deletions ServiceTest/UnitTest2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public void T11_WebClientAuthConfig()
Assert.AreEqual("<\"#>", cfg.DtbsPrefix, "DtbsPrefix");
Assert.AreEqual(99, cfg.RequestTimeOutSeconds, "RequestTimeOutSeconds");
Assert.AreEqual("http://changeme.swisscom.ch/services", cfg.ServiceUrlPrefix, "ServiceUrlPrefix");
Assert.AreEqual("http://mid.swisscom.ch/MID/v1/AuthProfile1", cfg.SignatureProfile, "SignatureProfile");
Assert.AreEqual(false, cfg.SrvSideValidation, "SrvSideValidation");
Assert.AreEqual("ABcd12", cfg.SslCertThumbprint, "SslCertThumbprint");
Assert.AreEqual(System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser, cfg.SslKeystore, "SslKeystore");
Expand Down
1 change: 1 addition & 0 deletions ServiceTest/WebClientAuthConfig02.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
DtbsPrefix="&lt;&quot;#&gt;"
RequestTimeOutSeconds="99"
ServiceUrlPrefix="http://changeme.swisscom.ch/services"
SignatureProfile="http://mid.swisscom.ch/MID/v1/AuthProfile1"
SrvSideValidation="FALSE"
SslCertThumbprint="AB cd 1 2 "
SslKeystore="CurrentUser"
Expand Down
Binary file added binaries/midadfs-bin_1.3.2.0.zip
Binary file not shown.
Binary file added binaries/midadfs_setup_1.3.2.0.exe
Binary file not shown.
Binary file added certs/Swisscom_Root_CA_2_der.crt
Binary file not shown.
4 changes: 4 additions & 0 deletions release-notes.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
v1.3.2.0 (2022-02-01)
* Validate certificate against custom Truststore
* Value for mss:SignatureProfile is now configurable

v1.3.1.0 (2021-12-09)
* Normalize mobile number, remove "-" and whitespaces.

Expand Down

0 comments on commit 01c3e1f

Please sign in to comment.