In the (PK)ZIP specification at https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT, specifically in the Strong Encryption Specification (SES) section, there is a line on deriving a key from a password:
MasterSessionKey = DeriveKey(SHA1(Password))
What's DeriveKey?
(In WinZip's AES documentation at https://www.winzip.com/en/support/aes-encryption/, they use PBKDF2 with 1000 iterations. I don't see any similar explanation in APPNOTE)
PKWARE implemented a strong encryption in version 5, but did not provide the algorithm of encoding/decoding (Method For Strongly Encrypted .ZIP Files - Patent US 2020/0250329 A1). In this algorithm AES encryption was implemented as part of it. You can define this by strong encryption (bit 6) = yes in General Purpose Flag.
After that WinZip could not use this algo, so it invented another one. You can define this by strong encryption (bit 6) = no in General Purpose Flag and AesExtraFieldRecord with signature 0x990.
As you can see there're two ways to encrypt a zip file. All open source software use the second one. The first one is available only by PKWARE SecureZIP
You can find example of this alogirthm in (7zip) Strong.cpp:35. In java it should look like this:
public static byte[] getMasterKey(String password) {
byte[] data = password.getBytes(StandardCharsets.UTF_8);
byte[] sha1 = DigestUtils.sha1(data);
return DeriveKey(sha1);
}
private static byte[] DeriveKey(byte[] digest) {
byte[] buf = new byte[kDigestSize * 2]; // kDigestSize = 20
DeriveKey2(digest, (byte)0x36, buf, 0);
DeriveKey2(digest, (byte)0x5C, buf, kDigestSize);
return Arrays.copyOfRange(buf, 0, 32);
}
private static void DeriveKey2(byte[] digest, byte c, byte[] dest, int offs) {
byte[] buf = new byte[64];
Arrays.fill(buf, c);
for (int i = 0; i < kDigestSize; i++)
buf[i] ^= digest[i];
byte[] sha1 = DigestUtils.sha1(buf);
System.arraycopy(sha1, 0, dest, offs, sha1.length);
}
Demo:
String password = "JohnDoe";
byte[] masterKey = getMasterKey(password);
The next paragraph 'defines' it
7.2.5.3 The function names and parameter requirements will depend on
the choice of the cryptographic toolkit selected. Almost any
toolkit supporting the reference implementations for each
algorithm can be used. The RSA BSAFE(r), OpenSSL, and Microsoft
CryptoAPI libraries are all known to work well.
I guess it's up to you to decide which of the encryption algorithms you want to use and go from there
Related
I want to decrypt fmp4 segment.
This segment was encrypt with HLS Apple Tools (https://developer.apple.com/documentation/http_live_streaming/about_apple_s_http_live_streaming_tools)
METHOD is AES-128
IV is 1d48fc5dee84b5a3e9a428f055e03c2e
I have a key and IV (you can got the key, and segment in google drive https://drive.google.com/drive/folders/1xF-C9EXFvT8qjI--sBB6QMPn8cNW7L-D?usp=sharing)
To decrypt I use Poco library.
This is my code:
Poco::Crypto::Cipher::ByteVec readKey(const std::string& uri) {
Poco::Crypto::Cipher::ByteVec key;
auto stream = Stream::makeStream(uri);
if (stream->open(uri, {})) {
key.resize(KEY_SIZE);
stream->read((char*)&key[0], KEY_SIZE);
}
return key;
}
std::vector<uint8_t> _key = readKey("./unit-tests/resources/cipher-stream/file.key");
std::string ivSrc = "1d48fc5dee84b5a3e9a428f055e03c2e";
Poco::Crypto::Cipher::ByteVec iv {ivSrc.begin(), ivSrc.end()};
Poco::Crypto::CipherKey key("aes-128-cbc", _key, iv);
Poco::Crypto::Cipher::Ptr cipher = Poco::Crypto::CipherFactory::defaultFactory().createCipher(key);
Poco::FileInputStream src("./unit-tests/resources/cipher-stream/fileSequence1.m4s");
Poco::FileOutputStream dst("./unit-tests/resources/cipher-stream/fileSequence1_dec.m4s");
Poco::Crypto::CryptoOutputStream decryptor(dst, cipher->createDecryptor());
Poco::StreamCopier::copyStream(src, decryptor);
// decryptor.close();
src.close();
dst.close();
Problem description:
After decryption I got distorted data. You can see this at the beginning of the file. Please see picture below. On the right side of the image file is distorted.
The correct data you can see on the left side.
You're using the wrong IV; that will lead to the first block (16 bytes) being corrupted. Your IV hex value is 1d48fc5dee84b5a3e9a428f055e03c2e, but you're interpreting that as ASCII. It's using the first 16 bytes of your string and ignoring the rest.
I haven't used Poco in a long time and don't remember if there's a hex parser handy, but that's what you need. Or write the IV directly in hex rather than as an ASCII string.
I'm using .NET's implementation of RSA, and two things looked odd to me. I'd like to confirm that it's operating properly.
Background
Using System.Security.Cryptography.RSACryptoServiceProvider with 2048-bit keyword size to perform asymmetric encryption/decrpytion, initially following the example in this question, "AES 256 Encryption: public and private key how can I generate and use it .net".
As a first implementation, this seems to work:
public const int CSPPARAMETERS_FLAG = 1; // Specifies RSA: https://msdn.microsoft.com/en-us/library/ms148034(v=vs.110).aspx
public const bool USE_OAEP_PADDING = false;
public const int KEYWORD_SIZE = 2048;
public static byte[] Encrypt(byte[] publicKey, byte[] dataToEncrypt)
{
var cspParameters = new System.Security.Cryptography.CspParameters(CSPPARAMETERS_FLAG);
byte[] encryptedData = null;
using (var rsaProvider = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters))
{
try
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.ImportCspBlob(publicKey);
encryptedData = rsaProvider.Encrypt(dataToEncrypt, USE_OAEP_PADDING);
}
finally
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.Clear();
}
}
return encryptedData;
}
public static byte[] Decrypt(byte[] privateKey, byte[] dataToDecrypt)
{
var cspParameters = new System.Security.Cryptography.CspParameters(CSPPARAMETERS_FLAG);
byte[] encryptedData = null;
using (var rsaProvider = new System.Security.Cryptography.RSACryptoServiceProvider(cspParameters))
{
try
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.ImportCspBlob(privateKey);
encryptedData = rsaProvider.Decrypt(dataToDecrypt, USE_OAEP_PADDING);
}
finally
{
rsaProvider.PersistKeyInCsp = false;
rsaProvider.Clear();
}
}
return encryptedData;
}
After looking into these methods a bit more, it seems that the public key that I've been generating as from the example seemed to have a lot of very predictable data at its start, and it was 276-bytes long.
Apparently rsaProvider.ExportCspBlob(bool includePrivateParameters) is a functional alternative to rsaProvider.ExportParameters(bool includePrivateParameters); the main difference is that the blob is already serialized as a byte[] while the other emits the object version, RSAParameters.
Two observations about the methods:
The .Exponent is always 0x010001$=65537$.
The exported blobs contain 17 extra bytes versus the serialized typed versions.
rsaProvider.ExportCspBlob():
Public key is 276 bytes.
Private key is 1172 bytes.
RSAParameters:
Public key is 259 bytes.
.Exponent.Length = 3
.Modulus .Length = 256
Private key is 1155 bytes.
.D .Length = 256
.DP .Length = 128
.DQ .Length = 128
.Exponent.Length = 3
.InverseQ.Length = 128
.Modulus .Length = 256
.P .Length = 128
.Q .Length = 128
The extra 17 bytes appear to be at the header of the binary blob.
Concerns
From this, two concerns:
Is it okay for the exponent to not be random?
If the exponent is defined as a constant, then it'd seem like that's another 3 bytes I could shave off the serialization?
Another question, Should RSA public exponent be only in {3, 5, 17, 257 or 65537} due to security considerations?, seems to suggest that $\left{3, 5, 17, 257, 65537\right}$ are all common values for the exponent, so 0x101$=65537$ seems reasonable if it's true that there's no harm in always using the same constant exponent.
Are the 17 extra bytes an information leak?
Do they represent the option parameters like key length and method?
Is it a good idea to be transmitting option parameter information when I already know that both the sender and receiver are using the same, hard-coded method?
Question
Is RSACryptoServiceProvider's behavior a cause for concern, or are these things normal?
Update 1
In Should RSA public exponent be only in {3, 5, 17, 257 or 65537} due to security considerations?, the accepted answer starts off by noting:
There is no known weakness for any short or long public exponent for RSA, as long as the public exponent is "correct" (i.e. relatively prime to p-1 for all primes p which divide the modulus).
If this is so, then I'd guess that the apparently-constant exponent of 0x010001$=65537$ is sufficient as long as it's relatively prime to $p-1$. So, presumably the .NET implementation of RSA checks for this condition.
But then what does RSACryptoServiceProvider do if that condition isn't satisfied? If it selects a different exponent, then that'd seem to leak information about $p$ whenever the exponent isn't 0x010001. Or, if a different key is selected, then it'd seem like we can just assume that the exponent is always 0x010001 and omit it from the serialization.
Everything reported is normal, and non-alarming.
It is perfectly OK for the public exponent e to be short and non-random. e = 216+1 = 65537 = 0x010001 is common and safe. Some authorities mandate it (or some range including it). Using it (or/and something significantly larger than the bit size of the public modulus) gives some protection against some of the worst RSA paddings.
No, the 17 extra bytes in the public key are unlikely to be an information leak; they more likely are a header part of the data format chosen for an RSA public key by the software you use. My guess is that you are encountering the MS-specific format detailed in this answer (perhaps, within endianness), which also uses precisely 276 bytes for an RSA public key with a 2048-bit public modulus. In that case, you should find that the extra bytes are always the same (thus they demonstrably leak nothing). And there are countless more subtle ways to leak information about the private key, like in the public modulus itself.
Many RSA key generators used in practice, including I guess RSACryptoServiceProvider, first choose e, then somewhat avoid generating primes p such that gcd(e, p-1) ≠ 1. Since e = 65537 is prime, it is enough that ( p % e ) ≠ 1, and this is easily checked, or otherwise insured by the process generating p.
For public key encryption and diffie-hellman in libsodium, I typically make private keys simply by generating 32 random bytes with randombytes_buf and then derive the public key (when needed) using crypto_scalarmult_base.
Is there any benefit to using crypto_box_keypair to generate a keypair (other than syntax)? Or does this function basically do exactly that?
This is exactly what the crypto_box_keypair() function does.
The benefits of this function are clarity, and guarantee that the secret key is properly generated.
https://download.libsodium.org/doc/public-key_cryptography/public-key_signatures.html
for example:
unsigned char pk[crypto_sign_PUBLICKEYBYTES]; //Variable declarations
unsigned char sk[crypto_sign_SECRETKEYBYTES]; Variable declarations
crypto_sign_keypair(pk, sk);
NSData *privateKeyData = [NSData dataWithBytes:sk length:crypto_box_SECRETKEYBYTES];
NSData *publicKeyData = [NSData dataWithBytes:pk length:crypto_box_PUBLICKEYBYTES];
NSLog(#"%#",privateKeyData); // target publick key data and secret key data
NSLog(#"%#",publicKeyData);
//Other
NSLog(#"%s\n\n=====\n\n\n%s",pk,sk); //(nullable const void *)bytes
Byte *byte = (Byte *)[publicKeyData bytes];
NSLog(#"%s",byte);
I'm trying to protect a 2048-bit RSA private key (confidentiality & availability).
I have been looking around for more information on how to do that and I'm thinking of using a secret sharing scheme (Shamir's Secret Sharing would be fine).
Is it the best option ?
Does anyone know a GNU/GPL software implementation in order to accomplish this ?
I look at "ssss" (http://point-at-infinity.org/ssss/), but secret need to be at most 128 ASCII characters and it's too short for a 2048-bit RSA private key.
Thanks for your help.
Just as with public-key cryptography, you occasionally need to use a hybrid scheme when the data exceeds a certain size - you can encrypt the private key using a normal symmetric algorithm with a random key, and then split the symmetric key using a secret-splitting algorithm of choice.
I believe that the implementation here: https://github.com/moserware/SecretSplitter uses this method to split data that exceeds the size limit of the underlying splitting algorithm.
Is it the best option ? Does anyone know a GNU/GPL software implementation in order to accomplish this ?
Crypto++ offers the functionality. But the license is Public Domain (individual source files) or Boost Software 1.0 (library as a whole). Its not GNU/GPL.
Here's the code to do it from Crypto++. It was taken from test.cpp:
Splitting
void SecretShareFile(int threshold, int nShares, const char *filename, const char *seed)
{
RandomPool rng;
rng.IncorporateEntropy((byte *)seed, strlen(seed));
ChannelSwitch *channelSwitch;
FileSource source(filename, false, new SecretSharing(rng,
threshold, nShares, channelSwitch = new ChannelSwitch));
vector_member_ptrs<FileSink> fileSinks(nShares);
string channel;
for (int i=0; i<nShares; i++)
{
char extension[5] = ".000";
extension[1]='0'+byte(i/100);
extension[2]='0'+byte((i/10)%10);
extension[3]='0'+byte(i%10);
fileSinks[i].reset(new FileSink((string(filename)+extension).c_str()));
channel = WordToString<word32>(i);
fileSinks[i]->Put((byte *)channel.data(), 4);
channelSwitch->AddRoute(channel, *fileSinks[i], DEFAULT_CHANNEL);
}
source.PumpAll();
}
Combining
void SecretRecoverFile(int threshold, const char *outFilename, char *const *inFilenames)
{
SecretRecovery recovery(threshold, new FileSink(outFilename));
vector_member_ptrs<FileSource> fileSources(threshold);
SecByteBlock channel(4);
int i;
for (i=0; i<threshold; i++)
{
fileSources[i].reset(new FileSource(inFilenames[i], false));
fileSources[i]->Pump(4);
fileSources[i]->Get(channel, 4);
fileSources[i]->Attach(new ChannelSwitch(recovery, string((char *)channel.begin(), 4)));
}
while (fileSources[0]->Pump(256))
for (i=1; i<threshold; i++)
fileSources[i]->Pump(256);
for (i=0; i<threshold; i++)
fileSources[i]->PumpAll();
}
I want to store a password (no hash) on my disk. it's nothing sensitive but i just don't want it in plaintext on my disk.
what i tried till now is:
converting the string in binary and XOR it with the binary of a key.
bool ok = true;
QByteArray qbaPW("mypass");
long long intPW = qbaPW.toHex().toLongLong( &ok, 16 );
QString binPW = QString::number( intPW, 2);
but the thing is, that it only works with short passwords. if they are too long intPW gets too big for longlong. any ideas how can avoid that thing?
cheers
A QByteArray is like a char array[len] in C. You can access individual members and do whatever you please with them. For example:
QByteArray const key("mykey");
QByteArray password("password");
for (int ik = 0, ip = 0;
ip < password.length();
++ ip, ik = (ik+1 < key.length() ? ik+1 : 0)) {
password[ip] = password[ip] ^ key[ik];
}
Since this just XORs with the key, you repeat this procedure to decrypt the password. A good key will be generated randomly and will be longer than the longest password you envisage (say 64 characters).
Do note that this method is only reasonably safe if the users are explicitly informed not to reuse any other password in your application - otherwise you're essentially leaking passwords that are supposed to be secure.