/// <summary> /// Get the key identifier for the signature /// </summary> private String GetAppKeyId() { // Is there a key called jwsdefault? If so, use it as the default signature key if (m_signingService.GetKeys().Contains("jwsdefault")) { return("jwsdefault"); } // Otherwise use the configured secure application HMAC key as the default if (AuthenticationContext.Current.Principal is IClaimsPrincipal claimsPrincipal) { // Is there a tag for their application var appId = claimsPrincipal.FindFirst(SanteDBClaimTypes.SanteDBApplicationIdentifierClaim)?.Value; if (String.IsNullOrEmpty(appId)) { throw new InvalidOperationException("Can only generate signed pointers when authenticated"); } var keyId = $"SA.{appId}"; // Does the key provider have the key for this app? if (m_signingService.GetKeys().Any(k => k == keyId)) { return(keyId); } else { // Application identity var appIdentity = claimsPrincipal.Identities.OfType <IApplicationIdentity>().FirstOrDefault(); if (appIdentity == null) { this.m_tracer.TraceWarning("No application identity could be found in principal (available identities: {0})", String.Join(",", claimsPrincipal.Identities.Select(o => o.GetType()))); throw new InvalidOperationException("No application identity found in principal"); } var key = this.m_applicationIdService.GetSecureKey(appIdentity.Name); // Get the key this.m_signingService.AddSigningKey(keyId, key, "HS256"); return(keyId); } } else { throw new InvalidOperationException("Cannot generate a personal key without knowing application id"); } }
/// <summary> /// Read from the stream /// </summary> public static PeerTransferPayload Read(Stream s, IDataSigningService signingProvider, bool validateSignature) { byte[] hdr = new byte[7]; s.Read(hdr, 0, 7); if (!hdr.Take(5).SequenceEqual(MAGIC_HEADER)) { throw new FormatException("Invalid payload"); } else if (hdr[5] >= VERSION_ID) { throw new InvalidOperationException($"Payload version {hdr[5]} is greater than supported version of {VERSION_ID}"); } var retVal = new PeerTransferPayload(); retVal.Encoding = (PeerTransferEncodingFlags)hdr[6]; // Read the rest of the stream if (retVal.Encoding.HasFlag(PeerTransferEncodingFlags.Compressed)) { s = new GZipStream(s, SharpCompress.Compressors.CompressionMode.Decompress); } using (var sr = new StreamReader(s)) { var data = sr.ReadToEnd(); // Read the JWS header var match = s_jwsFormat.Match(data); if (!match.Success) { throw new FormatException("Payload must be in JWS format"); } // Get the parts of the header byte[] headerBytes = match.Groups[1].Value.ParseBase64UrlEncode(), bodyBytes = match.Groups[2].Value.ParseBase64UrlEncode(), signatureBytes = match.Groups[3].Value.ParseBase64UrlEncode(); // Now lets parse the JSON objects dynamic header = JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(headerBytes)); dynamic body = JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(bodyBytes)); // Now validate the payload if (!header.typ.ToString().StartsWith("x-santedb+")) { throw new InvalidOperationException("Cannot determine type of data"); } var type = new ModelSerializationBinder().BindToType(null, header.typ.ToString().Substring(10)); var algorithm = header.alg.ToString(); String keyId = header.key.ToString(); // Validate the signature if we have the key if (validateSignature) { // We have the key? if (!signingProvider.GetKeys().Any(k => k == keyId)) { throw new InvalidOperationException("Cannot find appropriate validation key"); } if (signingProvider.GetSignatureAlgorithm(keyId) != algorithm) { throw new InvalidOperationException("Invalid signature algorithm"); } var payload = System.Text.Encoding.UTF8.GetBytes($"{match.Groups[1].Value}.{match.Groups[2].Value}"); if (!signingProvider.Verify(payload, signatureBytes, keyId)) { throw new SecurityException("Cannot verify authenticity of the specified data payload"); } } retVal.Payload = JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(bodyBytes), type); // Return the result return(retVal); } }