Code:
import org.apache.commons.codec.binary.Base64;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AESTest
{
public static void main(String [] args)
{
String enc = AESEncryptToBase64("000000", "XJ5QJSVMKZGBOQO7HMSIJO5BERW2OYWDVNPM3BH32NLSWUCNJ4FIP3BML7EKUBNO");
System.out.println(enc);
}
/**
*
* #param secret
* #param cleartext
* #return encrypted b64 string
*/
public static String AESEncryptToBase64(String secret, String clearText) {
byte[] rawKey = new byte[32];
java.util.Arrays.fill(rawKey, (byte) 0);
byte[] secretBytes = secret.getBytes();
for(int i = 0; i < secretBytes.length; i++){
rawKey[i] = secretBytes[i];
}
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
try{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encryptedData = cipher.doFinal(clearText.getBytes());
if(encryptedData == null) return null;
// return "l";
return Base64.encodeBase64String(encryptedData);
} catch (Exception e){
e.printStackTrace();
}
return null;
}
}
Compile And run:
$ javac -cp "commons-codec-1.7.jar" AESTest.java
$ java -cp "commons-codec-1.7.jar" AESTest
Exception in thread "main" java.lang.NoClassDefFoundError: AESTest
Caused by: java.lang.ClassNotFoundException: AESTest
Here's the apache-commons-codec:
http://apache.mirrors.pair.com//commons/codec/binaries/commons-codec-1.7-bin.zip
Include . into your classpath: java -cp ".:commons-codec-1.7.jar" AESTest
This will tell JVM to include classes from current folder to classpath
Related
When I tried to import MessageDigest class, Eclipse told me this error. But isn't this class included in JDK? How can I solve this problem?
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Md5Util {
public String EncoderByMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException{
java.security.MessageDigest md5 = java.security.MessageDigest.getInstance("MD5");
BASE64Encoder base64en = new BASE64Encoder();
String newstr=base64en.encode(md5.digest(str.getBytes("utf-8")));
return newstr;
}
}
I hava Java code that do AES encryption, and i would like to convert it so it work in python. This is a main class, i made initVector constants so it would be easier to see if it work:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.*;
import java.io.BufferedWriter;
public class test {
public static void main(String [] args)
{
String ss = "pUypiz-7hJ0y_JtpKWaydp";
String url = "";
String toBeEncrypted = "";
String initVector = "IqtY8jgALtjZNLM5";
String encodedInitVector = Encryptor.encodeB64mod(initVector.getBytes());
toBeEncrypted = "ch21979714702=put";
ss = Encryptor.decodeB64Mod(ss);
url = "i=" + encodedInitVector + "\n&a=" + Encryptor.encrypt(initVector, ss, toBeEncrypted);
System.out.println(url);
}
}
And this is Encryptor class:
import android.util.Base64;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.io.IOUtils;
import java.util.*;
public class Encryptor {
public static String encrypt(String initVector, String key, String clearText) {
try {
byte[] value = clearText.getBytes("UTF-8");
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(Base64.decode(key, 0), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(1, skeySpec, iv);
return encodeB64mod(cipher.doFinal(value));
} catch (Exception ex) {
ex.printStackTrace();
return "";
}
}
public static String encodeB64mod(byte[] bytes) {
return Base64.encodeToString(bytes, 2).replace('+', '-').replace(IOUtils.DIR_SEPARATOR_UNIX, '_').replace("=", "");
}
}
I tried to make it work in python but i am doing something wrong
EDIT:
import base64
from Crypto.Cipher import AES
from Crypto import Random
raw ='ch21979714702=put'.encode('utf-8')
key = 'pUypiz-7hJ0y_JtpKWaydp' #should be conveted to hex
iv = b'IqtY8jgALtjZNLM5'
cipher = AES.new( key, AES.MODE_CBC, iv )
print (base64.b64encode( cipher.encrypt( raw ) ).decode('utf-8') )
You are requiring base64url decoding, which isn't the generic base 64 encoding: it replaces + with - and / with _. This is why the reverse replacement is performed in Java.
Here is the correct way to decode it. Please upvote it and downvote any answers that do not include the replacement characters or final padding with the = character.
The problem was in Python was that you need to use padding
from Crypto.Util import Padding
Padding.pad(raw, 16, style='pkcs7')
https://www.pycryptodome.org/en/latest/src/util/util.html
Unlike in java in Python you need to add that you want to use padding.
I am using below code to generate and add digital signatures to the Excel file:
SignedObject signedHashObject =null;
signatureConfig.setKey((PrivateKey) privateKey);signatureConfig.setSigningCertificateChain(Collections.singletonList(Util.getX509Certificate(certificateAlias)));
OPCPackage pkg = OPCPackage.open(selectedFileName, PackageAccess.READ_WRITE);
signatureConfig.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(signatureConfig);
si.confirmSignature();
pkg.close();
Here the private key is java.security.mscapi.rsaprivatekey.
After fixing all the version compatibility issues, I am stuck with the below error,
The specified key of type sun.security.mscapi.RSAPrivateKey is not an RSAPrivateKey
I've fixed this now via #62159.
Make sure to set -Dorg.apache.xml.security.ignoreLineBreaks=true in the JVM properties.
Complete example, i.e. my test code:
import java.awt.geom.Rectangle2D;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFTextBox;
public class CertTest {
static PrivateKey winKey;
static List<X509Certificate> winChain;
public static void main(String[] args) throws Exception {
System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
HashAlgorithm hashes[] = { HashAlgorithm.sha1, HashAlgorithm.sha256, HashAlgorithm.sha384, HashAlgorithm.sha512 };
loadWinKey();
SignatureConfig signatureConfig = new SignatureConfig();
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2018, 1, 18, 12, 0, 0);
signatureConfig.setExecutionTime(cal.getTime());
for (final HashAlgorithm ha : hashes) {
try (final OPCPackage pkg = loadOPC("test-" + ha + ".pptx")) {
signatureConfig.setDigestAlgo(ha);
signatureConfig.setKey(winKey);
signatureConfig.setSigningCertificateChain(winChain);
signatureConfig.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(signatureConfig);
si.confirmSignature();
}
}
}
static void loadWinKey() throws Exception {
String alias = "poitest";
KeyStore keystore = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
keystore.load(null, null);
winKey = (PrivateKey) keystore.getKey(alias, null);
winChain = Collections.singletonList((X509Certificate) keystore.getCertificate(alias));
}
static OPCPackage loadOPC(String fileName) throws Exception {
try (final XMLSlideShow ppt = new XMLSlideShow()) {
try (final FileOutputStream fos = new FileOutputStream(fileName)) {
XSLFTextBox tb = ppt.createSlide().createTextBox();
tb.setText("test");
tb.setAnchor(new Rectangle2D.Double(100, 100, 100, 100));
ppt.write(fos);
}
return OPCPackage.open(fileName, PackageAccess.READ_WRITE);
}
}
}
**This is my code to sign a String.</br>**
package my.package;
import java.io.FileInputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import sun.misc.BASE64Encoder;
public class SignMessage {
static final String KEYSTORE_FILE = "keys/certificates.p12";
static final String KEYSTORE_INSTANCE = "PKCS12";
static final String KEYSTORE_PWD = "test";
static final String KEYSTORE_ALIAS = "Key1";
public static void main(String[] args) throws Exception {
String text = "This is a message";
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
Key key = ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray());
//Sign
PrivateKey privKey = (PrivateKey) key;
Signature signature = Signature.getInstance("SHA1WithRSA", "BC");
signature.initSign(privKey);
signature.update(text.getBytes());
//Build CMS
X509Certificate cert = (X509Certificate) ks.getCertificate(KEYSTORE_ALIAS);
List certList = new ArrayList();
CMSTypedData msg = new CMSProcessableByteArray(signature.sign());
certList.add(cert);
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privKey);
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, cert));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, false);
FileOutputStream sigfos = new FileOutputStream("D:\\SBI-DATA\\file\\signature_1.txt");
sigfos.write(Base64.encodeBase64(sp.getEncoded()));
sigfos.close();
}
}
Now, the EnvelopedData output will be used in the process to verify the signature by this way:
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Iterator;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.encoders.Base64;
public class VerifySignature {
public static void main(String[] args) throws Exception {
File p7s = new File("D:\\SBI-DATA\\file\\signature_2.txt") ;
int size = ((int) p7s.length());
byte[] sig = new byte[size];
File f = new File("D:\\SBI-DATA\\file\\plain.txt") ;
int sizecontent = ((int) f.length());
byte[] Data_Bytes = new byte[sizecontent];
Security.addProvider(new BouncyCastleProvider());
CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(Data_Bytes), sig);
Store store = signedData.getCertificates();
SignerInformationStore signers = signedData.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = store.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
X509Certificate certFromSignedData = new JcaX509CertificateConverter().setProvider(BC_PROVIDER).getCertificate(certHolder);
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC_PROVIDER).build(certFromSignedData))) {
System.out.println("Signature verified");
} else {
System.out.println("Signature verification failed");
}
}
}
}
Everything works good until signer.verify(..) due to the following Exception:
Exception in thread "main" org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value
at org.bouncycastle.cms.SignerInformation.doVerify(Unknown Source)
at org.bouncycastle.cms.SignerInformation.verify(Unknown Source)
at my.package.VerifySignature.main(VerifySignature.java:64)
And I really don't know what I am doing wrong. Can someone please give me a hint of what is happening?
I think you are signing it twice, once directly using Signature and the other one using ContentSigner. You need only to sign the data, not the signature.
So the solution should be to replace signature.sign() with text.getBytes(), or text.getBytes(StandardCharsets.UTF_8) if you want to explicitly define a character set for text instead of using some system default.
Here's a ruby code to do AES in ECB and CBC:
require 'openssl'
require 'base64'
def encrypt(data, key, cipher_type)
aes = OpenSSL::Cipher::Cipher.new(cipher_type)
key = key.ljust(32, "\0")
aes.encrypt
aes.key = key
Base64.encode64(aes.update(data) + aes.final).tr("\n","")
end
puts encrypt("XJ5QJSVMKZGBOQO7HMSIJO5BERW2OYWDVNPM3BH32NLSWUCNJ4FIP3BML7EKUBNO", "000000", 'AES-256-ECB')
puts encrypt("XJ5QJSVMKZGBOQO7HMSIJO5BERW2OYWDVNPM3BH32NLSWUCNJ4FIP3BML7EKUBNO", "000000", 'AES-256-CBC')
And here's the Java Equivalent:
import org.apache.commons.codec.binary.Base64;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class AESTest
{
public static void main(String [] args)
{
System.out.println(AESEncryptToBase64("AES/ECB/PKCS5padding", "000000", "XJ5QJSVMKZGBOQO7HMSIJO5BERW2OYWDVNPM3BH32NLSWUCNJ4FIP3BML7EKUBNO"));
System.out.println(AESEncryptToBase64("AES/CBC/PKCS5padding", "000000", "XJ5QJSVMKZGBOQO7HMSIJO5BERW2OYWDVNPM3BH32NLSWUCNJ4FIP3BML7EKUBNO"));
}
/**
*
* #param secret
* #param cleartext
* #return encrypted b64 string
*/
public static String AESEncryptToBase64(String cypher, String secret, String clearText) {
byte[] rawKey = new byte[32];
java.util.Arrays.fill(rawKey, (byte) 0);
byte[] secretBytes = secret.getBytes();
for(int i = 0; i < secretBytes.length; i++){
rawKey[i] = secretBytes[i];
}
SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
try{
Cipher cipher = Cipher.getInstance(cypher);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encryptedData = cipher.doFinal(clearText.getBytes());
if(encryptedData == null) return null;
// return "l";
return Base64.encodeBase64String(encryptedData);
} catch (Exception e){
e.printStackTrace();
}
return null;
}
}
COMPILE: commons jar: http://apache.mirrors.pair.com//commons/codec/binaries/commons-codec-1.7-bin.zip
$ javac -cp .:commons-codec-1.7.jar AESTest.java
RUN
$ ruby aestest.rb
hYnClaUD9brJfNpEp4YDH0l1Y/QBlGkclnVN8MObNZFsvykd2da8iT2pcwLftNfox1HK/KFWrdfXt0qhP0Aq/fudP1FPIhF3vUTOEDzJbiY=
hYnClaUD9brJfNpEp4YDH5xcdKI4W5soPmWMpU+NikmAEKGSZkDP3KaJVSqRyOHt3JlcoyQzPbuoHxPV6kw6GH/4atDrcmCwV5LacTp+mBg=
$ java -cp .:commons-codec-1.7.jar AESTest
hYnClaUD9brJfNpEp4YDH0l1Y/QBlGkclnVN8MObNZFsvykd2da8iT2pcwLftNfox1HK/KFWrdfXt0qhP0Aq/fudP1FPIhF3vUTOEDzJbiY=
kZZNkbxis/W9UtEgRkxakGH28QetvK4lbf/SxBLrNDYPkGnf3w4MwonOCsoi9FjLAQ34aElOJ3KUjm62fiYLWxwNiE/wls7AcQnXLD19ano=
Notice that ECB mode works exactly on both. But CBC mode is different. I ran this also on C and it turns out that Ruby is correct, Java is not.
What am I doing wrong in Java?
For CBC you need to provide an initialization vector, for example:
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
have an if statement in your code and two different inits. The one for ECB is fine, and this one won't work with ECB.