• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

HicServices / RDMP / 20057586828

09 Dec 2025 08:56AM UTC coverage: 57.193% (-0.2%) from 57.422%
20057586828

Pull #2182

github

JFriel
Merge branch 'develop' of https://github.com/HicServices/RDMP into task/RDMP-33-dataset-integration-interface
Pull Request #2182: [Datasets] Task/rdmp 33 dataset integration interface

11510 of 21615 branches covered (53.25%)

Branch coverage included in aggregate %.

1226 of 2024 new or added lines in 85 files covered. (60.57%)

35 existing lines in 15 files now uncovered.

32654 of 55604 relevant lines covered (58.73%)

8854.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.01
/Rdmp.Core/Curation/SimpleStringValueEncryption.cs
1
// Copyright (c) The University of Dundee 2018-2019
2
// This file is part of the Research Data Management Platform (RDMP).
3
// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4
// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5
// You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
6

7
using System;
8
using System.Linq;
9
using System.Security.Cryptography;
10
using System.Text;
11

12
namespace Rdmp.Core.Curation;
13

14
/// <summary>
15
/// Core RDMP implementation of RSA public/private key encryption.  In order to be secure you should create a private key (See PasswordEncryptionKeyLocationUI).  If
16
/// no private key is configured then the default Key will be used (this is not secure and anyone with access to the RDMP source code could decrypt your strings - which
17
///  is open source!). Strings are encrypted based on the key file.  Note that because RSA is a good encryption technique you will get a different output (encrypted) string
18
/// value for repeated calls to Encrypt even with the same input string.
19
/// </summary>
20
public class SimpleStringValueEncryption : IEncryptStrings
21
{
22
    private readonly RSACryptoServiceProvider _turing = new();
5,131✔
23

24
    private const string Key =
25
       @"<?xml version=""1.0"" encoding=""utf-16""?>
26
        <RSAKeyValue>
27
            <Modulus>xZ7V6twqlj+3L1hsjB+BwhzQEgcs8tpqzgSjwgjPwzfisLFBN9HkwX2u+ZrgrKhKmZgSOVBYRhZiGJeF1hY5gLz3/Zo05fcvtgL0ylEhi6wnH9y6CPu8+4xCOJ26eywtNS02v2CC/0HUWnw6kNHBqdyyNjwuC12Ll5YJ3wZ1kQZ2mce3DMjpgRDvYqnm8ldwLWpgreK7I5vggQgons3v39x+Cx501Eo0qd+iHzXf6/8pUhYqb+xhDdb9gnHcitipXVFG3Ts27OUiuO0uLAO4Es400ApyWpvHdCCNrBJ/EQWZGEO/qVKmfP0CWBmokVL+IUawX9lPl/Luo9W5AFpaIQ==</Modulus>
28
            <Exponent>AQAB</Exponent>
29
            <P>xvQflEOfLYAXiMFrG/alxcgUbimbY8Qc1/3Jm21CFVrkQL1rNQ/PEXavXYyNP3jRwUrqKbJrtcdz5MPgKpvA/iWdqjp4Qx+V7N2DkdzKL7liViKoCHf1eXfyeWkCErHOgInTerOSlCaeABqkqpr+eaAtzy2j4df5U/1+j8g7I48=</P>
30
            <Q>/kjapfiD7aC1yQX8BYiJ29oTlvZx0sptfXA0Bx9qXvd1UFTqjDbVoIF7tXk+DsottRL97G52ImpYf4w4wJFb1F+d5sQCaCrEzIdQ/4mJdpn7FLYxErYXpoZl0e2knIoqQie7+vaw2oV9YSx3iJHoEVfDM+0NAW+aCUUJGNorD08=</Q>
31
            <DP>mAYms0ZQtZXxZcBWNhHsbgsLAXqtkDhkye7VRPzhyCuhyo5zAyLHWVLVgahKrjuGHCtAbwg1Ibv8pMu/2Q8XE5xus4rmJnRWPZ6uUKDjpkAEEkl9GKuBWYX8NCW3Pc28O6AVhub8lFRF21KAjRTOauWo22zGk2ZS0IkdUoTwG6U=</DP>
32
            <DQ>nQsrllNMT0bw3k0G3/f6hEBD1vkvROrmAhF44GlDjZEw78Lx9FStTOqLF4HglMvCvNEU55808H5TV7qnFi7v0tKWt32YqvK3BkYP/THZJtlkWt9GoXK6WosoeSVWg6NFBAR8MTuH7/1/eLM4w6yw8X0NPpWJcbiWHmF3g9TBwTs=</DQ>
33
            <InverseQ>fbI4APSSbOOd07goBWzZYjdrEVEZDiboK4jFKYoMI19PrbQBTvpRmLsJxhg96fgjiFfmw05tsPIsLSQPczsav6JDq25sQyOaz7VE33Y8CEV4rVvI+l7vlWqvn/EfThPqYdR4UOHa6G5wcBTI48O4+SmKMhLQl1GK/ZA+XEmlHx8=</InverseQ>
34
            <D>QC+Uv1F/K4nKT8BikShylr+Q/SoDeWVjp0Juhcki4f82y7jmu+CachYGTN/29V07zaNM1/y2jx0aA27Dc4OIbb3ythXt9HtSrcVMCKJNSPZDRuAENIK/INyvbYAdX4A7trfWvlX0dj/FXxZWV08pnagm4eKt+dcKTdPXpO6OJOnnSYMcdRBFdZLAj/sh8oMNXMHSxsCA7YHjL+4NGbpyTMYnfa6CUxFc36cLi2/Hm6JhV1nYEswTI5T1qRqmWpF1+H0ksxLKx6I5D61mzfaf7RQPJRCLhddXoLPIh8IaoNIomPcZyZQBxwBhtgj8pf9rcBN50aqNc5V2vKoEOt/gGQ==</D>
35
        </RSAKeyValue>";
36

37
    private readonly string _parameters;
38

39
    public SimpleStringValueEncryption(string parameters)
5,131✔
40
    {
41
        _parameters=parameters;
5,131✔
42
    }
5,131✔
43

44
    /// <summary>
45
    /// Encrypt the payload using a new AES key and IV.  The AES key is then encrypted using the RSA public key and the IV is prepended to the encrypted payload.
46
    /// A prefix '$js1$' is added to the encrypted string to indicate that it is encrypted using the new (2023) RDMP default encryption algorithm.
47
    /// </summary>
48
    /// <returns></returns>
49
    public string Encrypt(string toEncrypt)
50
    {
51
        _turing.FromXmlString(_parameters ?? Key);
85✔
52
        // Fall back on bad encryption if no private key is configured
53
        if (_turing.KeySize < 1024)
85!
UNCOV
54
            return string.Join('-',
×
UNCOV
55
                _turing.Encrypt(Encoding.UTF8.GetBytes(toEncrypt), false).Select(octet => octet.ToString("X2")));
×
56
        using var aes = Aes.Create();
85✔
57
        aes.KeySize = 256;
85✔
58
        aes.GenerateIV();
85✔
59
        aes.GenerateKey();
85✔
60
        var cipherText = Convert.ToBase64String(aes.EncryptCfb(Encoding.UTF8.GetBytes(toEncrypt), aes.IV));
85✔
61
        var keyBlock = new byte[1 + aes.IV.Length + aes.Key.Length];
85✔
62
        keyBlock[0] = (byte)aes.IV.Length; // Note: this encoding assumes IV cannot exceed 255 bytes!
85✔
63
        Array.Copy(aes.IV, 0, keyBlock, 1, aes.IV.Length);
85✔
64
        Array.Copy(aes.Key, 0, keyBlock, 1 + aes.IV.Length, aes.Key.Length);
85✔
65
        var key = Convert.ToBase64String(_turing.Encrypt(keyBlock, true));
85✔
66
        return $"$js1${key}${cipherText}$";
85✔
67
    }
85✔
68

69
    /// <summary>
70
    /// Takes an encrypted byte[] (in string format as produced by BitConverter.ToString()
71
    /// </summary>
72
    /// <param name="toDecrypt"></param>
73
    /// <returns></returns>
74
    public string Decrypt(string toDecrypt)
75
    {
76
        _turing.FromXmlString(_parameters ?? Key);
697✔
77
        if (toDecrypt.StartsWith("$js1$", StringComparison.Ordinal) && toDecrypt.EndsWith("$", StringComparison.Ordinal))
697!
78
        {
79
            // Good, it's a new-style AES+RSA encrypted string
80
            var parts = toDecrypt.Split('$');
697✔
81
            if (parts.Length != 5)
697!
82
                throw new CryptographicException(
×
83
                    "Could not decrypt an encrypted string, it was not in the expected format of $js1$<base64key>$<base64ciphertext>$");
×
84
            var keyBlock = _turing.Decrypt(Convert.FromBase64String(parts[2]), true);
697✔
85
            var ivLength = keyBlock[0];
696✔
86
            var iv = new byte[ivLength];
696✔
87
            var key = new byte[keyBlock.Length - 1 - ivLength];
696✔
88
            Array.Copy(keyBlock, 1, iv, 0, ivLength);
696✔
89
            Array.Copy(keyBlock, 1 + ivLength, key, 0, key.Length);
696✔
90
            var cipherText = Convert.FromBase64String(parts[3]);
696✔
91
            using var aes = Aes.Create();
696✔
92
            aes.KeySize = 256;
696✔
93
            aes.IV = iv;
696✔
94
            aes.Key = key;
696✔
95
            return Encoding.UTF8.GetString(aes.DecryptCfb(cipherText, aes.IV));
696✔
96
        }
97

98
        try
99
        {
UNCOV
100
            return Encoding.UTF8.GetString(_turing.Decrypt(ByteConverterGetBytes(toDecrypt), false));
×
101
        }
UNCOV
102
        catch (CryptographicException e)
×
103
        {
UNCOV
104
            throw new CryptographicException(
×
UNCOV
105
                "Could not decrypt an encrypted string, possibly you are trying to decrypt it after having changed the PrivateKey to a different one than at the time it was encrypted?",
×
UNCOV
106
                e);
×
107
        }
108
    }
696✔
109

110
    private static byte[] ByteConverterGetBytes(string encodedstring)
111
    {
UNCOV
112
        var arr = encodedstring.Split('-');
×
UNCOV
113
        var array = new byte[arr.Length];
×
114

UNCOV
115
        for (var i = 0; i < arr.Length; i++)
×
UNCOV
116
            array[i] = Convert.ToByte(arr[i], 16);
×
117

UNCOV
118
        return array;
×
119
    }
120

121
    public bool IsStringEncrypted(string value)
122
    {
123
        return value != null && !string.IsNullOrWhiteSpace(value) &&
1,006!
124
               (value.StartsWith("$js1$") || value.Count(c => c == '-') >= 47);
2,402✔
125
    }
126
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc