/** * instead of a password, it's also possible to decrypt via certificate. * Warning: this code is experimental and hasn't been validated * * @see <a href="http://social.msdn.microsoft.com/Forums/en-US/cc9092bb-0c82-4b5b-ae21-abf643bdb37c/agile-encryption-with-certificates">Agile encryption with certificates</a> * * @param keyPair * @param x509 * @return true, when the data can be successfully decrypted with the given private key * @throws GeneralSecurityException */ public bool VerifyPassword(KeyPair keyPair, X509Certificate x509) { AgileEncryptionVerifier ver = (AgileEncryptionVerifier)builder.GetVerifier(); AgileEncryptionHeader header = (AgileEncryptionHeader)builder.GetHeader(); HashAlgorithm hashAlgo = header.HashAlgorithm; CipherAlgorithm cipherAlgo = header.CipherAlgorithm; int blockSize = header.BlockSize; AgileCertificateEntry ace = null; foreach (AgileCertificateEntry aceEntry in ver.GetCertificates()) { if (x509.Equals(aceEntry.x509)) { ace = aceEntry; break; } } if (ace == null) { return(false); } Cipher cipher = Cipher.GetInstance("RSA"); cipher.Init(Cipher.DECRYPT_MODE, keyPair.getPrivate()); byte[] keyspec = cipher.DoFinal(ace.encryptedKey); SecretKeySpec secretKey = new SecretKeySpec(keyspec, ver.CipherAlgorithm.jceId); Mac x509Hmac = CryptoFunctions.GetMac(hashAlgo); x509Hmac.Init(secretKey); byte[] certVerifier = x509Hmac.DoFinal(ace.x509.GetEncoded()); byte[] vec = CryptoFunctions.GenerateIv(hashAlgo, header.KeySalt, kIntegrityKeyBlock, blockSize); cipher = GetCipher(secretKey, cipherAlgo, ver.ChainingMode, vec, Cipher.DECRYPT_MODE); byte[] hmacKey = cipher.DoFinal(header.GetEncryptedHmacKey()); hmacKey = GetBlock0(hmacKey, hashAlgo.hashSize); vec = CryptoFunctions.GenerateIv(hashAlgo, header.KeySalt, kIntegrityValueBlock, blockSize); cipher = GetCipher(secretKey, cipherAlgo, ver.ChainingMode, vec, Cipher.DECRYPT_MODE); byte[] hmacValue = cipher.DoFinal(header.GetEncryptedHmacValue()); hmacValue = GetBlock0(hmacValue, hashAlgo.hashSize); if (Arrays.Equals(ace.certVerifier, certVerifier)) { SetSecretKey(secretKey); SetIntegrityHmacKey(hmacKey); SetIntegrityHmacValue(hmacValue); return(true); } else { return(false); } }
public void Initialize(EncryptionInfo info, ILittleEndianInput dis) { this.info = info; EncryptionDocument ed = ParseDescriptor((DocumentInputStream)dis); header = new AgileEncryptionHeader(ed); verifier = new AgileEncryptionVerifier(ed); if (info.VersionMajor == EncryptionMode.Agile.VersionMajor && info.VersionMinor == EncryptionMode.Agile.VersionMinor) { decryptor = new AgileDecryptor(this); encryptor = new AgileEncryptor(this); } }
public void Initialize(EncryptionInfo info, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) { this.info = info; if (cipherAlgorithm == null) { cipherAlgorithm = CipherAlgorithm.aes128; } if (cipherAlgorithm == CipherAlgorithm.rc4) { throw new EncryptedDocumentException("RC4 must not be used with agile encryption."); } if (hashAlgorithm == null) { hashAlgorithm = HashAlgorithm.sha1; } if (chainingMode == null) { chainingMode = ChainingMode.cbc; } if (!(chainingMode == ChainingMode.cbc || chainingMode == ChainingMode.cfb)) { throw new EncryptedDocumentException("Agile encryption only supports CBC/CFB chaining."); } if (keyBits == -1) { keyBits = cipherAlgorithm.defaultKeySize; } if (blockSize == -1) { blockSize = cipherAlgorithm.blockSize; } bool found = false; foreach (int ks in cipherAlgorithm.allowedKeySize) { found |= (ks == keyBits); } if (!found) { throw new EncryptedDocumentException("KeySize " + keyBits + " not allowed for Cipher " + cipherAlgorithm.ToString()); } header = new AgileEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode); verifier = new AgileEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode); decryptor = new AgileDecryptor(this); encryptor = new AgileEncryptor(this); }
/** * Generate an HMAC, as specified in [RFC2104], of the encrypted form of the data (message), * which the DataIntegrity element will verify by using the Salt generated in step 2 as the key. * Note that the entire EncryptedPackage stream (1), including the StreamSize field, MUST be * used as the message. * * Encrypt the HMAC as in step 3 by using a blockKey byte array consisting of the following bytes: * 0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, and 0x33. **/ protected void UpdateIntegrityHMAC(FileInfo tmpFile, int oleStreamSize) { // as the integrity hmac needs to contain the StreamSize, // it's not possible to calculate it on-the-fly while buffering // TODO: add stream size parameter to GetDataStream() AgileEncryptionVerifier ver = builder.GetVerifier(); HashAlgorithm hashAlgo = ver.HashAlgorithm; Mac integrityMD = CryptoFunctions.GetMac(hashAlgo); integrityMD.Init(new SecretKeySpec(integritySalt, hashAlgo.jceHmacId)); byte[] buf = new byte[1024]; LittleEndian.PutLong(buf, 0, oleStreamSize); integrityMD.Update(buf, 0, LittleEndian.LONG_SIZE); FileStream fis = tmpFile.Create(); try { int readBytes; while ((readBytes = fis.Read(buf, 0, buf.Length)) > 0) { integrityMD.Update(buf, 0, readBytes); } } finally { fis.Close(); } byte[] hmacValue = integrityMD.DoFinal(); AgileEncryptionHeader header = builder.GetHeader(); int blockSize = header.BlockSize; byte[] iv = CryptoFunctions.GenerateIv(header.HashAlgorithm, header.KeySalt, AgileDecryptor.kIntegrityValueBlock, blockSize); Cipher cipher = CryptoFunctions.GetCipher(GetSecretKey(), header.CipherAlgorithm, header.ChainingMode, iv, Cipher.ENCRYPT_MODE); byte[] hmacValueFilled = GetBlock0(hmacValue, AgileDecryptor.GetNextBlockSize(hmacValue.Length, blockSize)); byte[] encryptedHmacValue = cipher.DoFinal(hmacValueFilled); header.SetEncryptedHmacValue(encryptedHmacValue); }
/** * Set decryption password */ public override bool VerifyPassword(String password) { AgileEncryptionVerifier ver = (AgileEncryptionVerifier)builder.GetVerifier(); AgileEncryptionHeader header = (AgileEncryptionHeader)builder.GetHeader(); HashAlgorithm hashAlgo = header.HashAlgorithm; CipherAlgorithm cipherAlgo = header.CipherAlgorithm; int blockSize = header.BlockSize; int keySize = header.KeySize / 8; byte[] pwHash = CryptoFunctions.HashPassword(password, ver.HashAlgorithm, ver.Salt, ver.SpinCount); /** * encryptedVerifierHashInput: This attribute MUST be generated by using the following steps: * 1. Generate a random array of bytes with the number of bytes used specified by the saltSize * attribute. * 2. Generate an encryption key as specified in section 2.3.4.11 by using the user-supplied password, * the binary byte array used to create the saltValue attribute, and a blockKey byte array * consisting of the following bytes: 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, and 0x79. * 3. Encrypt the random array of bytes generated in step 1 by using the binary form of the saltValue * attribute as an Initialization vector as specified in section 2.3.4.12. If the array of bytes is not an * integral multiple of blockSize bytes, pad the array with 0x00 to the next integral multiple of * blockSize bytes. * 4. Use base64 to encode the result of step 3. */ byte[] verfierInputEnc = hashInput(builder, pwHash, kVerifierInputBlock, ver.EncryptedVerifier, Cipher.DECRYPT_MODE); SetVerifier(verfierInputEnc); MessageDigest hashMD = CryptoFunctions.GetMessageDigest(hashAlgo); byte[] verifierHash = hashMD.Digest(verfierInputEnc); /** * encryptedVerifierHashValue: This attribute MUST be generated by using the following steps: * 1. Obtain the hash value of the random array of bytes generated in step 1 of the steps for * encryptedVerifierHashInput. * 2. Generate an encryption key as specified in section 2.3.4.11 by using the user-supplied password, * the binary byte array used to create the saltValue attribute, and a blockKey byte array * consisting of the following bytes: 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, and 0x4e. * 3. Encrypt the hash value obtained in step 1 by using the binary form of the saltValue attribute as * an Initialization vector as specified in section 2.3.4.12. If hashSize is not an integral multiple of * blockSize bytes, pad the hash value with 0x00 to an integral multiple of blockSize bytes. * 4. Use base64 to encode the result of step 3. */ byte[] verifierHashDec = hashInput(builder, pwHash, kHashedVerifierBlock, ver.EncryptedVerifierHash, Cipher.DECRYPT_MODE); verifierHashDec = CryptoFunctions.GetBlock0(verifierHashDec, hashAlgo.hashSize); /** * encryptedKeyValue: This attribute MUST be generated by using the following steps: * 1. Generate a random array of bytes that is the same size as specified by the * Encryptor.KeyData.keyBits attribute of the parent element. * 2. Generate an encryption key as specified in section 2.3.4.11, using the user-supplied password, * the binary byte array used to create the saltValue attribute, and a blockKey byte array * consisting of the following bytes: 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, and 0xd6. * 3. Encrypt the random array of bytes generated in step 1 by using the binary form of the saltValue * attribute as an Initialization vector as specified in section 2.3.4.12. If the array of bytes is not an * integral multiple of blockSize bytes, pad the array with 0x00 to an integral multiple of * blockSize bytes. * 4. Use base64 to encode the result of step 3. */ byte[] keyspec = hashInput(builder, pwHash, kCryptoKeyBlock, ver.EncryptedKey, Cipher.DECRYPT_MODE); keyspec = CryptoFunctions.GetBlock0(keyspec, keySize); SecretKeySpec secretKey = new SecretKeySpec(keyspec, ver.CipherAlgorithm.jceId); /** * 1. Obtain the intermediate key by decrypting the encryptedKeyValue from a KeyEncryptor * Contained within the KeyEncryptors sequence. Use this key for encryption operations in the * remaining steps of this section. * 2. Generate a random array of bytes, known as Salt, of the same length as the value of the * KeyData.HashSize attribute. * 3. Encrypt the random array of bytes generated in step 2 by using the binary form of the * KeyData.saltValue attribute and a blockKey byte array consisting of the following bytes: 0x5f, * 0xb2, 0xad, 0x01, 0x0c, 0xb9, 0xe1, and 0xf6 used to form an Initialization vector as specified in * section 2.3.4.12. If the array of bytes is not an integral multiple of blockSize bytes, pad the * array with 0x00 to the next integral multiple of blockSize bytes. * 4. Assign the encryptedHmacKey attribute to the base64-encoded form of the result of step 3. */ byte[] vec = CryptoFunctions.GenerateIv(hashAlgo, header.KeySalt, kIntegrityKeyBlock, blockSize); Cipher cipher = CryptoFunctions.GetCipher(secretKey, cipherAlgo, ver.ChainingMode, vec, Cipher.DECRYPT_MODE); byte[] hmacKey = cipher.DoFinal(header.GetEncryptedHmacKey()); hmacKey = CryptoFunctions.GetBlock0(hmacKey, hashAlgo.hashSize); /** * 5. Generate an HMAC, as specified in [RFC2104], of the encrypted form of the data (message), * which the DataIntegrity element will verify by using the Salt generated in step 2 as the key. * Note that the entire EncryptedPackage stream (1), including the StreamSize field, MUST be * used as the message. * 6. Encrypt the HMAC as in step 3 by using a blockKey byte array consisting of the following bytes: * 0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, and 0x33. * 7. Assign the encryptedHmacValue attribute to the base64-encoded form of the result of step 6. */ vec = CryptoFunctions.GenerateIv(hashAlgo, header.KeySalt, kIntegrityValueBlock, blockSize); cipher = CryptoFunctions.GetCipher(secretKey, cipherAlgo, ver.ChainingMode, vec, Cipher.DECRYPT_MODE); byte[] hmacValue = cipher.DoFinal(header.GetEncryptedHmacValue()); hmacValue = CryptoFunctions.GetBlock0(hmacValue, hashAlgo.hashSize); if (Arrays.Equals(verifierHashDec, verifierHash)) { SetSecretKey(secretKey); SetIntegrityHmacKey(hmacKey); SetIntegrityHmacValue(hmacValue); return(true); } else { return(false); } }
public override void ConfirmPassword(String password, byte[] keySpec, byte[] keySalt, byte[] verifier, byte[] verifierSalt, byte[] integritySalt) { AgileEncryptionVerifier ver = builder.GetVerifier(); ver.Salt = (/*setter*/ verifierSalt); AgileEncryptionHeader header = builder.GetHeader(); header.KeySalt = (/*setter*/ keySalt); HashAlgorithm hashAlgo = ver.HashAlgorithm; int blockSize = header.BlockSize; pwHash = HashPassword(password, hashAlgo, verifierSalt, ver.SpinCount); /** * encryptedVerifierHashInput: This attribute MUST be generated by using the following steps: * 1. Generate a random array of bytes with the number of bytes used specified by the saltSize * attribute. * 2. Generate an encryption key as specified in section 2.3.4.11 by using the user-supplied password, * the binary byte array used to create the saltValue attribute, and a blockKey byte array * consisting of the following bytes: 0xfe, 0xa7, 0xd2, 0x76, 0x3b, 0x4b, 0x9e, and 0x79. * 3. Encrypt the random array of bytes generated in step 1 by using the binary form of the saltValue * attribute as an Initialization vector as specified in section 2.3.4.12. If the array of bytes is not an * integral multiple of blockSize bytes, pad the array with 0x00 to the next integral multiple of * blockSize bytes. * 4. Use base64 to encode the result of step 3. */ byte[] encryptedVerifier = AgileDecryptor.hashInput(builder, pwHash, AgileDecryptor.kVerifierInputBlock, verifier, Cipher.ENCRYPT_MODE); ver.EncryptedVerifier = (/*setter*/ encryptedVerifier); /** * encryptedVerifierHashValue: This attribute MUST be generated by using the following steps: * 1. Obtain the hash value of the random array of bytes generated in step 1 of the steps for * encryptedVerifierHashInput. * 2. Generate an encryption key as specified in section 2.3.4.11 by using the user-supplied password, * the binary byte array used to create the saltValue attribute, and a blockKey byte array * consisting of the following bytes: 0xd7, 0xaa, 0x0f, 0x6d, 0x30, 0x61, 0x34, and 0x4e. * 3. Encrypt the hash value obtained in step 1 by using the binary form of the saltValue attribute as * an Initialization vector as specified in section 2.3.4.12. If hashSize is not an integral multiple of * blockSize bytes, pad the hash value with 0x00 to an integral multiple of blockSize bytes. * 4. Use base64 to encode the result of step 3. */ MessageDigest hashMD = GetMessageDigest(hashAlgo); byte[] hashedVerifier = hashMD.Digest(verifier); byte[] encryptedVerifierHash = AgileDecryptor.hashInput(builder, pwHash, AgileDecryptor.kHashedVerifierBlock, hashedVerifier, Cipher.ENCRYPT_MODE); ver.EncryptedVerifierHash = (/*setter*/ encryptedVerifierHash); /** * encryptedKeyValue: This attribute MUST be generated by using the following steps: * 1. Generate a random array of bytes that is the same size as specified by the * Encryptor.KeyData.keyBits attribute of the parent element. * 2. Generate an encryption key as specified in section 2.3.4.11, using the user-supplied password, * the binary byte array used to create the saltValue attribute, and a blockKey byte array * consisting of the following bytes: 0x14, 0x6e, 0x0b, 0xe7, 0xab, 0xac, 0xd0, and 0xd6. * 3. Encrypt the random array of bytes generated in step 1 by using the binary form of the saltValue * attribute as an Initialization vector as specified in section 2.3.4.12. If the array of bytes is not an * integral multiple of blockSize bytes, pad the array with 0x00 to an integral multiple of * blockSize bytes. * 4. Use base64 to encode the result of step 3. */ byte[] encryptedKey = AgileDecryptor.hashInput(builder, pwHash, AgileDecryptor.kCryptoKeyBlock, keySpec, Cipher.ENCRYPT_MODE); ver.EncryptedKey = (/*setter*/ encryptedKey); ISecretKey secretKey = new SecretKeySpec(keySpec, ver.CipherAlgorithm.jceId); SetSecretKey(secretKey); /* * 2.3.4.14 DataIntegrity Generation (Agile Encryption) * * The DataIntegrity element Contained within an Encryption element MUST be generated by using * the following steps: * 1. Obtain the intermediate key by decrypting the encryptedKeyValue from a KeyEncryptor * Contained within the KeyEncryptors sequence. Use this key for encryption operations in the * remaining steps of this section. * 2. Generate a random array of bytes, known as Salt, of the same length as the value of the * KeyData.HashSize attribute. * 3. Encrypt the random array of bytes generated in step 2 by using the binary form of the * KeyData.saltValue attribute and a blockKey byte array consisting of the following bytes: * 0x5f, 0xb2, 0xad, 0x01, 0x0c, 0xb9, 0xe1, and 0xf6 used to form an Initialization vector as * specified in section 2.3.4.12. If the array of bytes is not an integral multiple of blockSize * bytes, pad the array with 0x00 to the next integral multiple of blockSize bytes. * 4. Assign the encryptedHmacKey attribute to the base64-encoded form of the result of step 3. * 5. Generate an HMAC, as specified in [RFC2104], of the encrypted form of the data (message), * which the DataIntegrity element will verify by using the Salt generated in step 2 as the key. * Note that the entire EncryptedPackage stream (1), including the StreamSize field, MUST be * used as the message. * 6. Encrypt the HMAC as in step 3 by using a blockKey byte array consisting of the following bytes: * 0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, and 0x33. * 7. Assign the encryptedHmacValue attribute to the base64-encoded form of the result of step 6. */ this.integritySalt = integritySalt; try { byte[] vec = CryptoFunctions.GenerateIv(hashAlgo, header.KeySalt, AgileDecryptor.kIntegrityKeyBlock, header.BlockSize); Cipher cipher = GetCipher(secretKey, ver.CipherAlgorithm, ver.ChainingMode, vec, Cipher.ENCRYPT_MODE); byte[] FilledSalt = GetBlock0(integritySalt, AgileDecryptor.GetNextBlockSize(integritySalt.Length, blockSize)); byte[] encryptedHmacKey = cipher.DoFinal(FilledSalt); header.SetEncryptedHmacKey(encryptedHmacKey); cipher = Cipher.GetInstance("RSA"); foreach (AgileCertificateEntry ace in ver.GetCertificates()) { cipher.Init(Cipher.ENCRYPT_MODE, ace.x509.GetPublicKey()); ace.encryptedKey = cipher.DoFinal(GetSecretKey().GetEncoded()); Mac x509Hmac = CryptoFunctions.GetMac(hashAlgo); x509Hmac.Init(GetSecretKey()); ace.certVerifier = x509Hmac.DoFinal(ace.x509.GetEncoded()); } } catch (Exception e) { throw new EncryptedDocumentException(e); } }
protected EncryptionDocument CreateEncryptionDocument() { AgileEncryptionVerifier ver = builder.GetVerifier(); AgileEncryptionHeader header = builder.GetHeader(); EncryptionDocument ed = EncryptionDocument.NewInstance(); CT_Encryption edRoot = ed.AddNewEncryption(); CT_KeyData keyData = edRoot.AddNewKeyData(); CT_KeyEncryptors keyEncList = edRoot.AddNewKeyEncryptors(); CT_KeyEncryptor keyEnc = keyEncList.AddNewKeyEncryptor(); keyEnc.uri = (/*setter*/ passwordUri); CT_PasswordKeyEncryptor keyPass = keyEnc.AddNewEncryptedPasswordKey(); keyPass.spinCount = (uint)ver.SpinCount; keyData.saltSize = (uint)header.BlockSize; keyPass.saltSize = (uint)header.BlockSize; keyData.blockSize = (uint)header.BlockSize; keyPass.blockSize = (uint)header.BlockSize; keyData.keyBits = (uint)header.KeySize; keyPass.keyBits = (uint)header.KeySize; HashAlgorithm hashAlgo = header.HashAlgorithm; keyData.hashSize = (uint)hashAlgo.hashSize; keyPass.hashSize = (uint)hashAlgo.hashSize; ST_CipherAlgorithm?xmlCipherAlgo = (ST_CipherAlgorithm?)Enum.Parse(typeof(ST_CipherAlgorithm), header.CipherAlgorithm.xmlId); if (xmlCipherAlgo == null) { throw new EncryptedDocumentException("CipherAlgorithm " + header.CipherAlgorithm + " not supported."); } keyData.cipherAlgorithm = (/*setter*/ xmlCipherAlgo.Value); keyPass.cipherAlgorithm = (/*setter*/ xmlCipherAlgo.Value); switch (header.ChainingMode.jceId) { case "cbc": keyData.cipherChaining = (/*setter*/ ST_CipherChaining.ChainingModeCBC); keyPass.cipherChaining = (/*setter*/ ST_CipherChaining.ChainingModeCBC); break; case "cfb": keyData.cipherChaining = (/*setter*/ ST_CipherChaining.ChainingModeCFB); keyPass.cipherChaining = (/*setter*/ ST_CipherChaining.ChainingModeCFB); break; default: throw new EncryptedDocumentException("ChainingMode " + header.ChainingMode + " not supported."); } ST_HashAlgorithm?xmlHashAlgo = (ST_HashAlgorithm?)Enum.Parse(typeof(ST_HashAlgorithm), hashAlgo.ecmaString); if (xmlHashAlgo == null) { throw new EncryptedDocumentException("HashAlgorithm " + hashAlgo + " not supported."); } keyData.hashAlgorithm = (/*setter*/ xmlHashAlgo.Value); keyPass.hashAlgorithm = (/*setter*/ xmlHashAlgo.Value); keyData.saltValue = (/*setter*/ header.KeySalt); keyPass.saltValue = (/*setter*/ ver.Salt); keyPass.encryptedVerifierHashInput = (/*setter*/ ver.EncryptedVerifier); keyPass.encryptedVerifierHashValue = (/*setter*/ ver.EncryptedVerifierHash); keyPass.encryptedKeyValue = (/*setter*/ ver.EncryptedKey); CT_DataIntegrity hmacData = edRoot.AddNewDataIntegrity(); hmacData.encryptedHmacKey = (/*setter*/ header.GetEncryptedHmacKey()); hmacData.encryptedHmacValue = (/*setter*/ header.GetEncryptedHmacValue()); foreach (AgileCertificateEntry ace in ver.GetCertificates()) { keyEnc = keyEncList.AddNewKeyEncryptor(); keyEnc.uri = (/*setter*/ certificateUri); CT_CertificateKeyEncryptor certData = keyEnc.AddNewEncryptedCertificateKey(); try { certData.X509Certificate = ace.x509.GetEncoded(); } catch (Exception e) { throw new EncryptedDocumentException(e); } certData.encryptedKeyValue = (/*setter*/ ace.encryptedKey); certData.certVerifier = (/*setter*/ ace.certVerifier); } return(ed); }