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

HicServices / RDMP / 18197892088

02 Oct 2025 03:33PM UTC coverage: 57.31% (-0.2%) from 57.474%
18197892088

push

github

web-flow
Merge pull request #2237 from HicServices/task/RDMP-334-extractability

Task/rdmp 334 extractability

11401 of 21427 branches covered (53.21%)

Branch coverage included in aggregate %.

11 of 102 new or added lines in 11 files covered. (10.78%)

64 existing lines in 8 files now uncovered.

32365 of 54940 relevant lines covered (58.91%)

17596.01 hits per line

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

65.67
/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();
10,077✔
23

24
    private const string Key =
25
       @"<?xml version=""1.0"" encoding=""utf-16""?>
26
<RSAParameters xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
27
   <Exponent>AQAB</Exponent>
28
    <Modulus>sMDeszVErUmbqOxQavw5OsWpL3frccEGtTJYM8G54Fw7NK6xFVUrq79nWB6px4/B</Modulus>
29
    <P>6kcXnTVJrVuD9j6qUm+F71jIL2H92lgN</P>
30
    <Q>wSRbrdj1qGBPBnYMO5dx11gvfNCKKdWF</Q>
31
    <DP>aKdxaQzQ6Nwkyu+bbk/baNwkMOZ5W/xR</DP>
32
    <DQ>B/B8rErM3l0HIpbbrd9t2JJRcWoJI+sZ</DQ>
33
    <InverseQ>NFv4Z26nbMpOkOcAnO3rktoMffza+3Ul</InverseQ>
34
    <D>Y8zC8dUF7gI9zeeAkKfReInauV6wpg4iVh7jaTDN5DAmKFURTAyv6Il6LEyr07JB</D>
35
</RSAParameters>";
36

37
    public SimpleStringValueEncryption(string parameters)
10,077✔
38
    {
39
        _turing.FromXmlString(parameters ?? Key);
10,077!
40
    }
10,077✔
41

42
    /// <summary>
43
    /// 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.
44
    /// A prefix '$js1$' is added to the encrypted string to indicate that it is encrypted using the new (2023) RDMP default encryption algorithm.
45
    /// </summary>
46
    /// <returns></returns>
47
    public string Encrypt(string toEncrypt)
48
    {
49

50
        // Fall back on bad encryption if no private key is configured
51
        if (_turing.KeySize < 1024)
166!
UNCOV
52
            return string.Join('-',
×
UNCOV
53
                _turing.Encrypt(Encoding.UTF8.GetBytes(toEncrypt), false).Select(octet => octet.ToString("X2")));
×
54
        using var aes = Aes.Create();
166✔
55
        aes.KeySize = 256;
166✔
56
        aes.GenerateIV();
166✔
57
        aes.GenerateKey();
166✔
58
        var cipherText = Convert.ToBase64String(aes.EncryptCfb(Encoding.UTF8.GetBytes(toEncrypt), aes.IV));
166✔
59
        var keyBlock = new byte[1 + aes.IV.Length + aes.Key.Length];
166✔
60
        keyBlock[0] = (byte)aes.IV.Length; // Note: this encoding assumes IV cannot exceed 255 bytes!
166✔
61
        Array.Copy(aes.IV, 0, keyBlock, 1, aes.IV.Length);
166✔
62
        Array.Copy(aes.Key, 0, keyBlock, 1 + aes.IV.Length, aes.Key.Length);
166✔
63
        var key = Convert.ToBase64String(_turing.Encrypt(keyBlock, true));
166✔
64
        return $"$js1${key}${cipherText}$";
166✔
65
    }
166✔
66

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

95
        try
96
        {
UNCOV
97
            return Encoding.UTF8.GetString(_turing.Decrypt(ByteConverterGetBytes(toDecrypt), false));
×
98
        }
UNCOV
99
        catch (CryptographicException e)
×
100
        {
UNCOV
101
            throw new CryptographicException(
×
UNCOV
102
                "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
103
                e);
×
104
        }
105
    }
1,386✔
106

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

UNCOV
112
        for (var i = 0; i < arr.Length; i++)
×
UNCOV
113
            array[i] = Convert.ToByte(arr[i], 16);
×
114

UNCOV
115
        return array;
×
116
    }
117

118
    public bool IsStringEncrypted(string value)
119
    {
120
        return value != null && !string.IsNullOrWhiteSpace(value) &&
2,008!
121
               (value.StartsWith("$js1$") || value.Count(c => c == '-') >= 47);
4,748✔
122
    }
123
}
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