timkurvers / as3-crypto

Fork of Henri Torgemane's excellent as3 cryptography library
http://code.google.com/p/as3crypto
Other
93 stars 46 forks source link

Issue with Root CA Certificate from CAcert, GlobalSign, entrust while getting public key #11

Closed ghost closed 11 years ago

ghost commented 11 years ago

Hi Tim,

I was using your modified .swc and it was working fine for my Hybrid Crypto project which I am attempting to integrate as3crypto library with SAP, untill I found an error which is causing problem with most Root CA or Intermediate CA certificates while getting public key. The full error text is attached.

I resolved this issue by reverting to the original Henri's version swc, which was also working fine for my use cases, but had used your swc instead earlier thinking it was an updated version.

In another post, I found out the solution in the below which some people has adopted who was getting the same error.

http://code.google.com/p/as3crypto/issues/detail?id=25

Being a non-Flex person, I guess that this fix is already part of the original swc and some line might be broken or missing in your modified version. Please let me know when you have fixed this issue and released the latest swc so that I can download your version.

Thanks, Krish.

My test code for uploading certificate which you can run using flash player 10 minimum to replicate the problem while trying to upload Root CA from the list mentioned.

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
                xmlns="*" creationComplete="onCreationComplete();">

    <mx:Script>
        <![CDATA[
            import com.hurlant.crypto.Crypto;
            import com.hurlant.crypto.rsa.RSAKey;
            import com.hurlant.crypto.symmetric.AESKey;
            import com.hurlant.crypto.symmetric.ICipher;
            import com.hurlant.crypto.symmetric.IPad;
            import com.hurlant.crypto.symmetric.IVMode;
            import com.hurlant.crypto.symmetric.NullPad;
            import com.hurlant.crypto.symmetric.PKCS5;
            import com.hurlant.crypto.hash.IHash;
            import com.hurlant.crypto.prng.Random;  
            import com.hurlant.crypto.cert.X509CertificateCollection;
            import com.hurlant.crypto.cert.X509Certificate;
            import com.hurlant.util.der.PEM;                    
            import com.hurlant.util.Hex;
            import mx.utils.Base64Encoder;          
            import mx.utils.Base64Decoder;
            import flash.net.FileReference;         
            import mx.controls.Alert;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;            

            private var certFile:FileReference;
            private var certCAFile:FileReference;
            private var keyFile:FileReference;
            private var privkeystr:String;
            private var publicRSAKey: RSAKey;
            private var publicCARSAKey: RSAKey;
            private var x509cert:X509Certificate;
            private var x509CAcert:X509Certificate;

            private function onCreationComplete():void {
              BrowseCert.enabled = true;                                    
              BrowseCACert.enabled = false;
              BrowsePriv.enabled= false;
              Upload.enabled = false;                   

              Source.text = "Private Key";
              Target.text = "Encrypted Private Key";
              Encrypt.enabled = false;
              Decrypt.enabled = false;                                                                                                          
            }               

            private function browseCertificate():void
            {
              var certTypes:FileFilter = new FileFilter("Base64 Encoded X.509 (*.pem;*.cer;*.crt;*.txt)", "*.pem;*.cer;*.crt;*.txt");   
              var allTypes:Array = new Array(certTypes);
              certFile = new FileReference();             

              browseFileSystem(allTypes, certFile);        
            } 

            private function browseCACertificate():void
            {
              var certTypes:FileFilter = new FileFilter("Base64 Encoded X.509 (*.pem;*.cer;*.crt;*.txt)", "*.pem;*.cer;*.crt;*.txt");   
              var allTypes:Array = new Array(certTypes);
              certCAFile = new FileReference();             

              browseFileSystem(allTypes, certCAFile);        
            } 

            private function browsePrivateKey():void
            {
              var keyTypes:FileFilter = new FileFilter("Decrypted Private Key (*.pem;*.cer;*.crt;*.key;*.txt)", "*.pem;*.cer;*.crt;*.key;*.txt");   
              var allTypes:Array = new Array(keyTypes);             
              keyFile = new FileReference();               

              browseFileSystem(allTypes, keyFile);               
            }                

            private function reset():void
            {  
               UploadCA.selected = true;
               showCABrowse();

               certFile = null; 
               certCAFile = null;
               keyFile = null; 

               BrowseCert.enabled = true;
               BrowseCACert.enabled = false;                   
               BrowsePriv.enabled= false;
               Upload.enabled = false;  

               CertLoaded.text = "";
               CACertLoaded.text = "";
               PrivLoaded.text = "";
               Passphrase.text = "";         

               ErrorBrowseUpload.text = "";
               ErrorBrowseCert.text = "";
               ErrorBrowseCACert.text = "";
               ErrorBrowsePriv.text = "";     

               Encrypt.enabled = false;
               Decrypt.enabled = false;                                            
            }

            private function showCABrowse():void
            {               
                // If the verification against root CA was unchecked
                // and certificate specified, then verify that the 
                // certificate is a self signed one
                try{
                  if(x509cert != null){
                    if (!UploadCA.selected){
                      var time:Date = new Date;
                      if(!x509cert.isSelfSigned(time)){
                       certFile = null;
                       x509cert = null;
                       CertLoaded.text = "";
                       // It is a not self signed certificate, put back the checkbox
                       UploadCA.selected = true;                     
                       ErrorBrowseCert.text = "Certificate is not a self signed certificate. Specify Root CA.";                       
                      }                         
                    }                                                               
                  } 
                }
                catch (e:Error){
                  certFile = null;
                  x509cert = null;
                  CertLoaded.text = "";
                  // It is a not self signed certificate, put back the checkbox
                  UploadCA.selected = true;                  
                  ErrorBrowseCert.text = "Certificate is not a self signed certificate. Specify Root CA.";
                }                   

                if (!UploadCA.selected){
                    CALabel.visible = false;
                    CALabel.height = 0;
                    CABrowse.visible = false;
                    CABrowse.height = 0;

                    if(certFile == null){
                      BrowsePriv.enabled= false;    
                    }
                    else BrowsePriv.enabled= true;                                         

                }
                else{
                    CALabel.visible = true;
                    if(CALabel.height == 0){
                     CALabel.height = CertBox.height * 0.1; 
                    }                   

                    CABrowse.visible = true;   
                    if(CABrowse.height == 0){
                     CABrowse.height = CertBox.height * 0.1;    
                    }

                    if(certFile == null){
                      BrowseCACert.enabled = false; 
                    }
                    else BrowseCACert.enabled = true;

                    BrowsePriv.enabled= false;                                                                                      
                }   

                certCAFile = null;
                keyFile = null;                     
                BrowseCert.enabled = true;
                Upload.enabled = false; 
                CACertLoaded.text = "";
                PrivLoaded.text = "";
                Passphrase.text = "";
                ErrorBrowseUpload.text = "";
                ErrorBrowseCACert.text = "";
                ErrorBrowsePriv.text = "";  
                ErrorGen.text = "";                             

            } 

             private function browseFileSystem(filter:Array, file:FileReference):void {
                file.addEventListener(Event.SELECT, selectHandler);
                file.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
                file.addEventListener(ProgressEvent.PROGRESS, progressHandler);
                file.addEventListener(Event.COMPLETE, completeHandler);                 
                file.browse(filter);
            }

            private function selectHandler(event:Event):void {
                var file:FileReference = FileReference(event.target);
                file.load();
            }

            private function ioErrorHandler(event:IOErrorEvent):void {
                trace("ioErrorHandler: " + event); 
                ErrorGen.text = "Error in reading the file. Please contact system adminstrator";               
            }

            private function progressHandler(event:ProgressEvent):void {
                var file:FileReference = FileReference(event.target);
                trace("progressHandler: name=" + file.name + "bytesLoaded=" + 
                      event.bytesLoaded + " bytesTotal=" + event.bytesTotal);
            }

            private function completeHandler(event:Event):void {                
                trace("completeHandler: " + event);

                // Clear the error logs
                ErrorBrowseUpload.text = "";
                ErrorBrowseCert.text = "";
                ErrorBrowseCACert.text = "";
                ErrorBrowsePriv.text = "";
                ErrorGen.text = "";

                try{
                  var file:FileReference = FileReference(event.target);
                  var time:Date;

                  if(certFile == file){
                    CertLoaded.text = file.name;

                    certFile = file; 

                    // Read the X509 certificate file(.PEM base64 encoded)
                    var ngencert:ByteArray = certFile.data;
                    ngencert.position = 0;     
                    var certstr:String = ngencert.readUTFBytes(ngencert.length);

                    x509cert = new X509Certificate(PEM.readCertIntoArray(certstr));

                    // Get the certificate public key
                    publicRSAKey = x509cert.getPublicKey();    

                    // Get the certificate subject princical
                    var subPrin:String = x509cert.getSubjectPrincipal()
                    // Get the certificate issuer princical
                    var issuerPrin:String = x509cert.getIssuerPrincipal(); 
                    // Get the certificate algorithm identifier
                    var algoiden:String = x509cert.getAlgorithmIdentifier();
                    // Get the certificate validity dates             
                    var validfrom:Date = x509cert.getNotBefore();
                    var validto:Date = x509cert.getNotAfter();

                    // Validate the X509 Certificate

                    // Check the validity date
                    time = new Date;
                    if(time.getTime()< validfrom.getTime()){
                      throw new Error("The certificate start date is not yet arrived"); 
                    }

                    if(time.getTime()> validto.getTime()){
                      throw new Error("The certificate has expired. Please renew.");    
                    }

                    // Check if verification w.r.t Root CA is unchecked, then
                    // validate if the certificate is a self signed certificate

                    if (!UploadCA.selected){                      
                      try{
                        if(!x509cert.isSelfSigned(time)){
                          // It is a not self signed certificate, put back the checkbox
                          UploadCA.selected = true; 
                          showCABrowse(); 
                          throw new Error("Certificate is not a self signed certificate. Specify Root CA.");
                        }                   
                      }
                      catch (e:Error){                      
                          // It is a not self signed certificate, put back the checkbox
                          UploadCA.selected = true; 
                          showCABrowse(); 
                          throw new Error("Certificate is not a self signed certificate. Specify Root CA.");
                        }

                    }
                    // verification w.r.t Root CA checkbox is checked
                    else{

                      try{                          
                        if(x509cert.isSelfSigned(time)){
                           // It is a self signed certificate
                           UploadCA.selected = false; 
                           showCABrowse();                          
                        }                                               
                      }
                      catch (e:Error){
                        // do nothing..since verify with Root CA checkbox is checked
                      }                     
                    }

                    // Certificate is valid and ready to be uploaded.                    
                    BrowseCert.enabled = true;
                    if (UploadCA.selected){                     
                        BrowseCACert.enabled = true;
                        BrowsePriv.enabled= false;
                        Upload.enabled = false;
                    }
                    else {                      
                      BrowsePriv.enabled= true;
                      Upload.enabled = false;
                    }                  

                    certCAFile = null;
                    CACertLoaded.text = "";
                    keyFile = null;
                    PrivLoaded.text = "";                                             
                    Passphrase.text = "";                                         

                  }
                  else if(certCAFile == file){
                    CACertLoaded.text = file.name;

                    certCAFile = file; 

                    // Read the X509 certificate file(.PEM base64 encoded)
                    var ngencertCA:ByteArray = certCAFile.data;
                    ngencertCA.position = 0;     
                    var certCAstr:String = ngencertCA.readUTFBytes(ngencertCA.length);

                    x509CAcert = new X509Certificate(PEM.readCertIntoArray(certCAstr));

                    // Get the CA certificate public key
                    publicCARSAKey = x509CAcert.getPublicKey();    

                    // Get the CA certificate subject princical
                    var subPrinCA:String = x509CAcert.getSubjectPrincipal()
                    // Get the CA certificate issuer princical
                    var issuerPrinCA:String = x509CAcert.getIssuerPrincipal(); 
                    // Get the CA certificate algorithm identifier
                    var algoidenCA:String = x509CAcert.getAlgorithmIdentifier();
                    // Get the CA certificate validity dates             
                    var validfromCA:Date = x509CAcert.getNotBefore();
                    var validtoCA:Date = x509CAcert.getNotAfter();
                    time = new Date;

                    // Verify if the Root CA has expired 
                    if(time.getTime()< validfromCA.getTime()){
                      throw new Error("The certificate start date is not yet arrived"); 
                    }

                    if(time.getTime()> validtoCA.getTime()){
                      throw new Error("The certificate has expired. Please renew.");    
                    }                 

                    // Validate the X509 Certificate with the Root CA certificate
                    var cert:X509CertificateCollection = new X509CertificateCollection();
                    cert.addCertificate(x509cert);

                    var CAStore:X509CertificateCollection = new X509CertificateCollection();
                    CAStore.addCertificate(x509CAcert);                                        

                    try{
                      if(!x509cert.isSigned(cert,CAStore, time)){
                        throw new Error("Error in validating certificate with Root CA certificate");
                      }                     
                    }
                    catch(e:Error){
                    // try once more..due to some issue with 512 bit RSA certificate
                      if(!x509cert.isSigned(cert,CAStore, time)){
                        throw new Error("Error in validating certificate with Root CA certificate");
                      }  
                    }

                    // Certificate is valid w.r.t Root CA and ready to be uploaded.
                    BrowseCert.enabled = true;
                    BrowseCACert.enabled = true;

                    BrowsePriv.enabled= true;                   
                    Upload.enabled = false;

                    keyFile = null;
                    PrivLoaded.text = "";                                             
                    Passphrase.text = "";                                                                                        
                  }

                  else if(keyFile == file){
                    PrivLoaded.text = file.name;    

                    keyFile = file;

                    // Read the private key file(.PEM base64 encoded)   
                    var ngenkey:ByteArray = keyFile.data;
                    ngenkey.position = 0;
                    privkeystr = ngenkey.readUTFBytes(ngenkey.length);   

                    // Read the private RSA key string              
                    var privkey:RSAKey = PEM.readRSAPrivateKey(privkeystr); 

                    // Match private key modulus to the RSA certificate modulus 
                    var hash:IHash = Crypto.getHash("md5");
                    var data:ByteArray = Hex.toArray(privkey.n.toString()); 
                    var modulusPrivKey:String = Hex.fromArray(hash.hash(data)); 

                    if(publicRSAKey==null)
                    {
                    throw new Error("Certificate not provided");                            
                    }

                    data = Hex.toArray(publicRSAKey.n.toString()); 
                    var modulusPubKey:String = Hex.fromArray(hash.hash(data));   

                    if(modulusPrivKey != modulusPubKey){
                    throw new Error("RSA Private Key could not match to certificate");
                    }

                    // RSA private key file is valid and ready to be uploaded.
                    BrowsePriv.enabled= true;
                    BrowseCert.enabled = true;
                    BrowseCACert.enabled = true;
                    Upload.enabled = true;  
                    Passphrase.text = "";
                  }   

                }
                catch (e:Error){
                    trace(e.getStackTrace());

                    // Clear the uploaded file instance reference a
                    // and populate error log
                    if(certFile == file)
                    {
                        if(e.message == "Certificate is not a self signed certificate. Specify Root CA."
                           || e.message == "The certificate has expired. Please renew."
                           || e.message == "The certificate start date is not yet arrived"){
                         ErrorBrowseCert.text = e.message;  
                        }                       
                        else{
                         ErrorBrowseCert.text = "Invalid certificate file provided. Please verify.";    
                        }                       

                        certFile = null;
                        CertLoaded.text = "";
                        certCAFile = null;
                        CACertLoaded.text = "";
                        keyFile = null;
                        PrivLoaded.text = "";                                             
                        Passphrase.text = "";

                        BrowsePriv.enabled= false;
                        BrowseCACert.enabled = false;
                        Upload.enabled = false;                     
                        BrowseCert.enabled = true;                                                                  
                    }
                    else if(certCAFile == file)
                    {                           
                        if (e.message == "Error in validating certificate with Root CA certificate"
                           || e.message == "The certificate has expired. Please renew."
                           || e.message == "The certificate start date is not yet arrived"){
                          ErrorBrowseCACert.text = e.message;   
                        }
                        else{
                          ErrorBrowseCACert.text = "Invalid certificate file provided. Please verify.";
                        }

                        certCAFile = null;
                        CACertLoaded.text = "";                        
                        keyFile = null;
                        PrivLoaded.text = "";                                             
                        Passphrase.text = "";

                        BrowseCert.enabled = true;
                        BrowseCACert.enabled = true;
                        BrowsePriv.enabled= false;
                        Upload.enabled = false;
                    }                   
                    else if(keyFile == file){

                        if(e.message == "RSA Private Key could not match to certificate"
                           || e.message == "Certificate not provided")
                        {
                          ErrorBrowsePriv.text = e.message; 
                        }
                        else {
                          ErrorBrowsePriv.text = "Invalid private key file provided. Please verify.";   
                        }

                        keyFile = null;
                        PrivLoaded.text = "";                                             
                        Passphrase.text = "";

                        BrowseCert.enabled = true;
                        BrowseCACert.enabled = true;
                        BrowsePriv.enabled= true;
                        Upload.enabled = false;                     
                    } 
                    else{
                        trace("Error:", e.getStackTrace());
                        reset();
                        ErrorGen.text = e.message;
                    }               
                }
            }            

            private function uploadFiles():void
            {

              // Clear the error logs
              ErrorBrowseUpload.text = "";
              ErrorBrowseCert.text = "";
              ErrorBrowseCACert.text = "";
              ErrorBrowsePriv.text = "";
              ErrorGen.text = "";

              if (Passphrase.text.length == 0)
              {     
                ErrorBrowseUpload.text = "Please enter a passphrase";
              }  
              else if (CertLoaded.text.length == 0 || certFile == null){
                ErrorBrowseCert.text = "Please provide a X509 digital certificate (in PEM format)";
              }     
              else if (PrivLoaded.text.length == 0 || keyFile == null){
                ErrorBrowsePriv.text = "Please provide a private key file (in PEM format)";
              }         

              else {
               try{
                // Process the private key file             
                var passphrase:String = Passphrase.text;

                var hashalgo:String = "sha256";

                HashAlgo.text = hashalgo;

                var SymKey:String = computeHashFromPassphrase(passphrase, hashalgo);    

                SymmetricKey.text = SymKey;
                KeyBits.text = (Hex.toArray(SymKey).length * 8).toString();

                // Get the Private Key string from the key file (.PEM extension format)            
                RequestResult.text = privkeystr;

                var obj:Object = {};

                var algoname:String = "aes-cbc";

                SymAlgo.text = algoname;

                // Encrypt the data
                // input hex format Symmetric Key,  data in String, algoname in String 
                obj = encryptSymmetric(SymKey, privkeystr, algoname);

                ResponseResult.text = obj.encrypteddata;
                InitVector.text = obj.ivtext;     
                Encrypt.enabled = false;
                Decrypt.enabled = true;
               }
               catch(e:Error){
                ErrorGen.text = e.message;
               }                                 
              }

            }               

            private function decryptData():void
            {
             try{
               Decrypt.enabled = false;
               Encrypt.enabled = true;
               ErrorEncrypt.text = "";
               ErrorDecrypt.text = ""; 

               Source.text = "Encrypted Private Key";
               Target.text = "Private Key";

               RequestResult.text = ResponseResult.text;
               ResponseResult.text = "";

               var myencryptedprivkeystr:String = RequestResult.text; 

               var passphrase:String = Passphrase.text;          

               var hashalgo:String = HashAlgo.text;         

               var SymKey:String = computeHashFromPassphrase(passphrase, hashalgo); 

               SymmetricKey.text = SymKey;
               KeyBits.text = (Hex.toArray(SymKey).length * 8).toString();

               var algoname:String = SymAlgo.text;

               var initvector:String = InitVector.text;

               ResponseResult.text = decryptSymmetric(SymKey, myencryptedprivkeystr, algoname, initvector);                 
             }
             catch (e:Error){
                ErrorDecrypt.text = e.message;
                ErrorGen.text = e.message;
                Decrypt.enabled = false;
                Encrypt.enabled = false;                
             }

            }               

            private function encryptData():void
            {

             try{
               Encrypt.enabled = false;
               Decrypt.enabled = true;
               ErrorEncrypt.text = "";
               ErrorDecrypt.text = "";

               Source.text = "Private Key";
               Target.text = "Encrypted Private Key";

               RequestResult.text = ResponseResult.text;
               ResponseResult.text = "";
               InitVector.text = "";

               var passphrase:String = Passphrase.text;

               var hashalgo:String = HashAlgo.text;

               var SymKey:String = computeHashFromPassphrase(passphrase, hashalgo); 

               SymmetricKey.text = SymKey;
               KeyBits.text = (Hex.toArray(SymKey).length * 8).toString();

               var myprivkeystr:String = RequestResult.text;             

               var obj:Object = {};

               var algoname:String = SymAlgo.text;

              // Encrypt the data
              // input hex format Symmetric Key,  data in String, algoname in String 
              obj = encryptSymmetric(SymKey, myprivkeystr, algoname);

              ResponseResult.text = obj.encrypteddata;
              InitVector.text = obj.ivtext;                             
             }
             catch(e:Error){
                ErrorEncrypt.text = e.message;
                ErrorGen.text = e.message;
                Decrypt.enabled = false;
                Encrypt.enabled = false;                
             }

            }   

            private function computeHashFromPassphrase(passphrase:String, hashalgoname:String):String{
            var hash:IHash = Crypto.getHash(hashalgoname);
            var data:ByteArray = Hex.toArray(passphrase); 
            return Hex.fromArray(hash.hash(data));                        
            }

            //Hex format data input and Symmetric key                   
            private function encryptSymmetric(SymKey:String, datastr:String, algoname:String): Object {                                
                var k:String = SymKey;
                var kdata:ByteArray = Hex.toArray(k);
                var txt:String = datastr;
                var data:ByteArray = Hex.toArray(Hex.fromString(txt));;
                var name:String = algoname;   
                var pad:IPad =new PKCS5();
                var mode:ICipher = Crypto.getCipher(name, kdata, pad);
                pad.setBlockSize(mode.getBlockSize());

               // Compress the data(which is in ByteArray) in zlib format
                data.compress();

                mode.encrypt(data);         

                var obj:Object = {};
                obj.encrypteddata = Hex.fromArray(data);   //Hex format

                // Populate Initialization Vector.
                 if (mode is IVMode) {
                    var ivmode:IVMode = mode as IVMode;             
                // IV is set from the mode
                    obj.ivtext = Hex.fromArray(ivmode.IV);  //Hex format                
                  }         

                return obj;                                                                                                                                                     
            } 

            //Hex format data input, Hex format Symmetric key and Hex Format IV 
            private function decryptSymmetric(SymKey:String, datastr:String, algoname:String, ivText:String):String {                              

                    var k:String = SymKey;
                    var kdata:ByteArray = Hex.toArray(k);
                    var txt:String = datastr;
                    var data:ByteArray = Hex.toArray(txt);
                    var name:String = algoname;   
                    var pad:IPad =new PKCS5();
                    var mode:ICipher = Crypto.getCipher(name, kdata, pad);

                    // If an IV is present, set it before decryption
                    if (mode is IVMode) {
                    var ivmode:IVMode = mode as IVMode;
                    ivmode.IV = Hex.toArray(ivText);                    
                    }

                    pad.setBlockSize(mode.getBlockSize());                                                  
                    mode.decrypt(data);
                    if (data==null) return "";

                    //Uncompress the decrypted data (which is in ByteArray) 
                    data.uncompress();          

                    var txtDecrypted:String = Hex.toString(Hex.fromArray(data));                                
                    return txtDecrypted;
            }                       

        ]]>
    </mx:Script>    

    <mx:Panel id="Title" width="100%" height="80%" horizontalAlign="center" paddingBottom="5"
              paddingLeft="5" paddingRight="5" paddingTop="5" 
              title="NGenSecure v1.0 - Upload Digital Certificate and Private Key Files to SAP" 
              backgroundColor="#B1B4B6">    

        <mx:VBox id="CertBox" width="100%" height="65%" horizontalAlign="left" 
            verticalAlign="middle" cornerRadius="10" borderStyle="solid" 
            borderColor="#919598" backgroundColor="#B1B4B6">

          <mx:Label text="Step 1: Specify Certificate File" fontWeight="bold" 
             fontSize="15" width="20%" height="5%"/>

          <mx:VBox width="100%" height="10%" horizontalAlign="center" 
            verticalAlign="middle">                       
            <mx:HBox width="100%" height="10%" horizontalAlign="center" verticalAlign="middle">          
             <mx:Label text="Certificate File Name" fontWeight="bold" width="{CA.width}" height="5%"/>
             <mx:TextInput id="CertLoaded" text= "" editable="false" width="{CACertLoaded.width}" height="5%"/>
             <mx:Button id="BrowseCert" label="Browse Certificate File" 
                width="{BrowseCACert.width}" height="5%" click="browseCertificate()"/>
             <mx:Text id="ErrorBrowseCert" text= "" width="30%"
                 height="100%" color="#C40000"/>
            </mx:HBox>
          </mx:VBox>

          <mx:VBox width="100%" height="5%" horizontalAlign="left" 
            verticalAlign="middle">
           <mx:CheckBox id="UploadCA" label="Verify Certificate against Root CA Certificate"  
                fontWeight="bold" selected="true" click="showCABrowse()"/>          
          </mx:VBox>        

          <mx:VBox id="CALabel" width="100%" height="5%" horizontalAlign="left" 
            verticalAlign="middle">
           <mx:Label text="Step 2: Specify the Root CA Certificate File" fontWeight="bold" 
              fontSize="15" width="20%" height="5%"/>
          </mx:VBox>

          <mx:VBox id ="CABrowse" width="100%" height="10%" horizontalAlign="center" 
            verticalAlign="middle">                       
            <mx:HBox width="100%" height="10%" horizontalAlign="center" verticalAlign="middle">          
             <mx:Label id="CA" text="CA Certificate File Name" fontWeight="bold" width="12%" height="5%"/>
             <mx:TextInput id="CACertLoaded" text= "" editable="false" width="50%" height="5%"/>
             <mx:Button id="BrowseCACert" label="Browse CA Certificate File" 
                width="6%" height="5%" click="browseCACertificate()"/>
             <mx:Text id="ErrorBrowseCACert" text= "" width="30%"
                 height="100%" color="#C40000"/>
            </mx:HBox>
          </mx:VBox>

          <mx:Label text="Step 3: Specify Private Key File" fontWeight="bold" 
            fontSize="15" width="20%" height="5%"/> 

          <mx:VBox width="100%" height="10%" horizontalAlign="center" 
            verticalAlign="middle">             
            <mx:HBox width="100%" height="10%" horizontalAlign="center" verticalAlign="middle">          
             <mx:Label text="Private Key File Name" fontWeight="bold" width="{CA.width}" height="0.5%"/>
             <mx:TextInput id="PrivLoaded" text= "" editable="false" width="{CACertLoaded.width}" height="0.5%"/>
             <mx:Button id="BrowsePriv" label="Browse Private Key File" 
                width="{BrowseCACert.width}" height="0.5%" click="browsePrivateKey()"/>
             <mx:Text id="ErrorBrowsePriv" text= "" width="{ErrorBrowseCert.width}" height="30%" color="#C40000"/>
            </mx:HBox>
          </mx:VBox>

          <mx:Label text="Step 4: Specify Passphrase to protect Private Key" 
            fontSize="15" fontWeight="bold" width="20%" height="5%"/>

          <mx:VBox width="100%" height="10%" horizontalAlign="center" 
            verticalAlign="middle">                     
            <mx:HBox width="100%" height="10%" horizontalAlign="center" verticalAlign="middle">          
             <mx:Label id="Pwd" text="Enter your Passphrase" fontWeight="bold" 
                width="{CA.width}" height="0.5%"/>
             <mx:TextInput id="Passphrase" text= "" width="{CACertLoaded.width}" height="0.5%"/>     
             <mx:Button id="Upload" label="Upload Key Pair Files" 
               width="{BrowseCACert.width}" height="0.5%" click="uploadFiles()"/>
             <mx:Text id="ErrorBrowseUpload" text= "" width="{ErrorBrowseCert.width}" height="30%" color="#C40000"/>
            </mx:HBox>                      
          </mx:VBox>    

          <mx:VBox width="100%" height="0.5%" horizontalAlign="left" 
            verticalAlign="middle">          
            <mx:HBox width="100%" height="0.5%" horizontalAlign="center" verticalAlign="middle">      
              <mx:Button id="Reset" label="Reset Choices" width="5%" height="0.5%" click="reset()"/>
              <mx:Text id="ErrorGen" text= "" width="95%" height="0.5%" color="#C40000"/>
            </mx:HBox>
          </mx:VBox>

        </mx:VBox>  

        <mx:VBox id="ResultPanel" width="100%" height="35%" horizontalAlign="left" 
            visible="true" verticalAlign="middle"   cornerRadius="10" borderStyle="solid" 
             borderColor="#919598" backgroundColor="#B1B4B6">               
         <mx:Label id="Source" text="" fontWeight="bold" height="5%"/>
         <mx:TextArea id="RequestResult" text= "" editable="false" width="100%" height="40%"/>  
         <mx:Label id="Target" text="" fontWeight="bold" height="5%"/>
         <mx:TextArea id="ResponseResult" text="" editable="false" width="100%" height="40%"/>
         <mx:Label text="Hash Algorithm" fontWeight="bold" height="1%"/>    
         <mx:TextArea id="HashAlgo" text= "" editable="false" width="100%" height="0.25%"/>             
         <mx:Label text="Symmetric Algorithm" fontWeight="bold" height="1%"/>   
         <mx:TextArea id="SymAlgo" text= "" editable="false" width="100%" height="0.25%"/>          
         <mx:Label id="KeyGen" text="Symmetric Key Generated (Hex Format)" fontWeight="bold" height="1%"/>  
         <mx:TextArea id="SymmetricKey" text="" editable="false" width="100%" height="0.5%"/>   
         <mx:Label id="IVText" text="Initialization Vector (Hex Format)" fontWeight="bold" height="1%"/>    
         <mx:TextArea id="InitVector" text="" editable="false" width="100%" height="0.5%"/>     
         <mx:Label text="Generated Key bits" fontWeight="bold" height="1%"/>            
         <mx:TextArea id="KeyBits" text= "" editable="false" width="100%" height="0.5%"/>
         <mx:HBox width="100%" height="1%" horizontalAlign="center" verticalAlign="middle">
         <mx:Button id="Decrypt" label="Decrypt Encrypted Private Key" width="5%" height="0.5%" click="decryptData()"/>
         <mx:Text id="ErrorDecrypt" text= "" width="95%" height="30%" color="#C40000"/>
         </mx:HBox>
         <mx:HBox width="100%" height="1%" horizontalAlign="center" verticalAlign="middle">
         <mx:Button id="Encrypt" label="Re-Encrypt Private Key" width="5%" height="0.5%" click="encryptData()"/>
         <mx:Text id="ErrorEncrypt" text= "" width="95%" height="30%" color="#C40000"/>
         </mx:HBox>
        </mx:VBox>

    </mx:Panel>             

</mx:Application>
ghost commented 11 years ago

The error which was coming is as follows:

Error: pure virtual function call: fromDERContent
    at com.hurlant.util.asn1.type::ASN1Type/fromDERContent()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/ASN1Type.as:150]
    at com.hurlant.util.asn1.type::ASN1Type/fromDER()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/ASN1Type.as:131]
    at com.hurlant.util.asn1.type::SequenceType/fromDERContent()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/SequenceType.as:32]
    at com.hurlant.util.asn1.type::ASN1Type/fromDER()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/ASN1Type.as:131]
    at com.hurlant.util.asn1.type::SequenceType/fromDERContent()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/SequenceType.as:56]
    at com.hurlant.util.asn1.type::ASN1Type/fromDER()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/ASN1Type.as:131]
    at com.hurlant.util.asn1.type::SequenceType/fromDERContent()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/SequenceType.as:32]
    at com.hurlant.util.asn1.type::ASN1Type/fromDER()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/ASN1Type.as:131]
    at com.hurlant.util.asn1.type::SequenceType/fromDERContent()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/SequenceType.as:32]
    at com.hurlant.util.asn1.type::ASN1Type/fromDER()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/util/asn1/type/ASN1Type.as:131]
    at com.hurlant.crypto.cert::X509Certificate/load()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/crypto/cert/X509Certificate.as:56]
    at com.hurlant.crypto.cert::X509Certificate/getPublicKey()[/Users/timkurvers/Projects/personal/as3.crypto/src/com/hurlant/crypto/cert/X509Certificate.as:184]
timkurvers commented 11 years ago

Hi again Krish,

Thanks for the report. I'll hopefully be able to look into this shortly.

ghost commented 11 years ago

Thanks a lot Tim..I will wait for your release. Until that time, I will manage with the original library for my project work.

Also, I forgot to mention two minor things in the same test code:

1) There is some issue in isSigned(..) method of X509Certificate for verifying a 512bit RSA certificate with its Root CA certificate, where the famous unpad exception comes. This isSigned(...) method in your modified library works fine with 1024, 2048, and 4096. Not sure whether its inherited from the original library.

So, for 512bit RSA certificate verifying with its Root CA certicificate, I found that it works if you call the isSigned(..) method twice. Note in my test code line in method completeHandler() with comment "//try once more..due to some issue with 512 bit RSA certificate", where I had to call the method the second time to get 512bit RSA certificate verified. Its a cheap workaround which worked for me, but I guess there might be some issue verifying 512bit RSA certificate.

I took the 512bit Pem format RSA certificate and its Root CA from the below link, so that you can use the same. http://www.frank4dd.com/

2) I also have done a small implementation of OpenPGP (encrypting private RSA key with passphrase), by generating a hash on the passphrase using a hash algo e.g. sha256, next, using the hash generated as a Symmetric key to protect the naked private key file using a Symmetric algo e.g. aes-cbc 256 bits encryption.

Some folks were looking for this OpenPGP feature in the as3crypto library, so it might be helpful for them to refer my test code which works fine.

Also, I wrote a tiny code for verification of a RSA private key with its corresponding RSA certificate by comparing the hash of their modulus. So, this might be also a small help for guys looking for it in the library.

Thanks, Krish.

ghost commented 11 years ago

Hi Tim,

I solved the issue by debugging your modified library. Below is the modified code where the method fromDERContent was not implemented and was throwing the error. Tested the fix with some Root and Intermediate CA certificates and is working fine now. Please put the below fix.

Thanks, Krish.

package com.hurlant.util.asn1.type {
    import flash.net.registerClassAlias;
    import flash.utils.ByteArray;

    /**
     * This class represents a chunk of ASN1 definition.
     * 
     * This is used by parser to know what to look for.
     * 
     * @author henri
     * 
     */
    public class ASN1Type {
        registerClassAlias("com.hurlant.util.asn1.ASN1Type", ASN1Type);

        // Universal types and tag numbers
        public static const CHOICE:int = -2;
        public static const ANY:int = -1;
        public static const RESERVED:int = 0;
        public static const BOOLEAN:int = 1;
        public static const INTEGER:int = 2;
        public static const BIT_STRING:int = 3;
        public static const OCTET_STRING:int = 4;
        public static const NULL:int = 5;
        public static const OID:int = 6;
        public static const ODT:int = 7;
        public static const EXTERNAL:int = 8;
        public static const REAL:int = 9;
        public static const ENUMERATED:int = 10;
        public static const EMBEDDED:int = 11;
        public static const UTF8STRING:int = 12;
        public static const ROID:int = 13;
        public static const SEQUENCE:int = 16;
        public static const SET:int = 17;
        public static const NUMERIC_STRING:int = 18;
        public static const PRINTABLE_STRING:int = 19;
        public static const TELETEX_STRING:int = 20;
        public static const VIDEOTEX_STRING:int = 21;
        public static const IA5_STRING:int = 22;
        public static const UTC_TIME:int = 23;
        public static const GENERALIZED_TIME:int = 24;
        public static const GRAPHIC_STRING:int = 25;
        public static const VISIBLE_STRING:int = 26;
        public static const GENERAL_STRING:int = 27;
        public static const UNIVERSAL_STRING:int = 28;
        public static const BMP_STRING:int = 30;
        public static const UNSTRUCTURED_NAME:int = 31; // ??? no clue.

        // Classes of tags
        public static const UNIVERSAL:int   = 0;
        public static const APPLICATION:int = 1;
        public static const CONTEXT:int     = 2;
        public static const PRIVATE:int     = 3;

        // various type modifiers
        public var optional:Boolean = false;
        public var implicitTag:Number = NaN;
        public var implicitClass:int = 0;
        public var explicitTag:Number = NaN;
        public var explicitClass:int = 0;
        public var defaultValue:* = null;
        public var extract:Boolean = false; // if true, the constructed parent will copy the binary value in a [name]_bin slot.

        // core type, vs type used somewhere
//      public var core:Boolean = true;

        public var defaultTag:Number;
        public var parsedTag:Number; // used for ANY logic

        public function ASN1Type(tag:int) {
            defaultTag = tag;
        }

        public function matches(type:int, classValue:int, length:int):Boolean {
            return false;
        }

        public function clone():ASN1Type {
//          if (!core) {
//              return this;
//          }
            var copier:ByteArray = new ByteArray();
//          core = false; // the clone is not core
            copier.writeObject(this);
            copier.position = 0;
            var c:ASN1Type = copier.readObject();
//          if (c.core) {
//              throw new Error("sucks. copy should have core=false");
//          }
//          core = true;
            return c;
        }

        // ok, time to parse some shit

        /**
         * Read an ASN-1 value from a ByteArray and return it
         * If we can't read a value that matches our type, we return null;
         * 
         * @param s  a ByteArray containing some DER-encoded ASN-1 values.
         * @return   an ASN1Value object representing the value read. or null.
         * 
         */
        public function fromDER(s:ByteArray, size:int):* {
            var p:int = s.position; // We'll reset if things go wrong.
            var length:int;
            aleg: do {
                if (!isNaN(explicitTag)) {
                    // unwrap the explicit tag..
                    var tag:int = readDERTag(s, explicitClass, true); // explicit tags are always constructed
                    if (tag!=explicitTag) {
                        break aleg; // haha! The wit.
                    }
                    length = readDERLength(s);
                    // XXX I should use length to validate stuff.
                }
                if (!isNaN(implicitTag)) {
                    tag = readDERTag(s, implicitClass);
                    if (tag!=implicitTag) {
                        break aleg;
                    }
                } else {
                    tag = readDERTag(s);
                    if (defaultTag == ANY) {
                        parsedTag = tag;
                    } else if (tag!=defaultTag) {
                        break aleg;
                    }
                }
                length = readDERLength(s);
                var c:* = fromDERContent(s, length);
                if (c==null) {
                    break aleg;
                }
                return c;
            } while(false);
            // we failed to parse something meaningful. fall back.
            s.position = p;
            if (defaultValue!=null) {
                return fromDefaultValue();
            }
            return null;
        }

        protected function fromDefaultValue():* {
            return defaultValue;
        }

        protected function fromDERContent(s:ByteArray, length:int):* {
            return s.readBoolean();

            //throw new Error("pure virtual function call: fromDERContent");
        }

        protected function readDERTag(s:ByteArray, classValue:int=UNIVERSAL, 
                                    constructed:Boolean=false, any:Boolean=false):int {
            var type:int = s.readUnsignedByte();
            var c:Boolean = (type&0x20)!=0;
            var cv:int = (type&0xC0)>>6; // { universal, application, context, private }
            type &= 0x1F;
            if (type == 0x1F) {
                // multibyte tag. blah.
                type=0;
                do {
                    var o:int = s.readUnsignedByte();
                    var v:int = o&0x7F;
                    type = (type<<7) + v;
                } while (o&0x80!=0);
            }
            if (classValue!=cv) {
                // trace("Tag Class Mismatch. expected "+classValue+", found "+cv);
                //return -1; // tag class mismatch // XXX ignore that for now.. :(
            }
            return type;
            // checking for "constructed" is a bit tedious. skip for now. XXX
            //if (any || (c==constructed)) return type;
            //return -1; // constructed flag mismatch.
        }

        protected function readDERLength(s:ByteArray):int {
            // length
            var len:int = s.readUnsignedByte();
            if (len>=0x80) {
                // long form of length
                var count:int = len & 0x7f;
                len = 0;
                while (count>0) {
                    len = (len<<8) | s.readUnsignedByte();
                    count--;
                }
            }
            return len;
        }
    }
}
timkurvers commented 11 years ago

Hi again,

Thanks for looking into this issue a bit more thoroughly, appreciated!

I'm starting to think the original SWC is not in sync with the latest original source code (see line 149), which would explain this. Could you link me to or confirm the original project and SWC you used?

I am very close to being able to look into these ActionScript projects again, still meddling with my development environment.

ghost commented 11 years ago

Hi Tim,

Actually, first I was using your SWC, then due to the error, I reverted to the original SWC and that error went away. I also checked the latest SVN code of the original project, but could not understand why the fix is missing there. Also the zip package available for download in the original project has many things missing in the "util" folder, only one can view those in the svn in browse mode. So, I never used the original project, but only used its SWC.

But yesterday, I again deleted the original SWC from my project and I download your latest project source code (since I wanted to use some of the Type2.as things you have written and made some public getter methods to read certificate information the way keystore shows it). So, when I started getting the error again, I could now debug to find out where the error in coming e.g. line 149 and I pasted the missing line over there and it started working for all my test certificates.

Thanks, Krish.